| /** |
| * @file me4600_ai.c |
| * |
| * @brief ME-4000 analog input subdevice instance. |
| * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de) |
| * @author Guenter Gebhardt |
| * @author Krzysztof Gantzke (k.gantzke@meilhaus.de) |
| */ |
| |
| /* |
| * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de) |
| * |
| * This file is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| */ |
| |
| #ifndef __KERNEL__ |
| # define __KERNEL__ |
| #endif |
| |
| /* |
| * Includes |
| */ |
| #include <linux/module.h> |
| |
| #include <linux/slab.h> |
| #include <linux/spinlock.h> |
| #include <linux/io.h> |
| #include <linux/uaccess.h> |
| #include <linux/types.h> |
| #include <linux/interrupt.h> |
| #include <linux/delay.h> |
| |
| #include "medefines.h" |
| #include "meinternal.h" |
| #include "meerror.h" |
| #include "medebug.h" |
| #include "meids.h" |
| |
| #include "me4600_reg.h" |
| #include "me4600_ai_reg.h" |
| #include "me4600_ai.h" |
| |
| /* |
| * Declarations (local) |
| */ |
| |
| static void me4600_ai_destructor(struct me_subdevice *subdevice); |
| static int me4600_ai_io_reset_subdevice(me_subdevice_t *subdevice, |
| struct file *filep, int flags); |
| |
| static int me4600_ai_io_single_config(me_subdevice_t *subdevice, |
| struct file *filep, |
| int channel, |
| int single_config, |
| int ref, |
| int trig_chan, |
| int trig_type, int trig_edge, int flags); |
| |
| static int me4600_ai_io_single_read(me_subdevice_t *subdevice, |
| struct file *filep, |
| int channel, |
| int *value, int time_out, int flags); |
| |
| static int me4600_ai_io_stream_config(me_subdevice_t *subdevice, |
| struct file *filep, |
| meIOStreamConfig_t *config_list, |
| int count, |
| meIOStreamTrigger_t *trigger, |
| int fifo_irq_threshold, int flags); |
| static int me4600_ai_io_stream_read(me_subdevice_t *subdevice, |
| struct file *filep, |
| int read_mode, |
| int *values, int *count, int flags); |
| static int me4600_ai_io_stream_new_values(me_subdevice_t *subdevice, |
| struct file *filep, |
| int time_out, int *count, int flags); |
| static inline int me4600_ai_io_stream_read_get_value(me4600_ai_subdevice_t * |
| instance, int *values, |
| const int count, |
| const int flags); |
| |
| static int me4600_ai_io_stream_start(me_subdevice_t *subdevice, |
| struct file *filep, |
| int start_mode, int time_out, int flags); |
| static int me4600_ai_io_stream_stop(me_subdevice_t *subdevice, |
| struct file *filep, |
| int stop_mode, int flags); |
| static int me4600_ai_io_stream_status(me_subdevice_t *subdevice, |
| struct file *filep, |
| int wait, |
| int *status, int *values, int flags); |
| |
| static int me4600_ai_query_range_by_min_max(me_subdevice_t *subdevice, |
| int unit, |
| int *min, |
| int *max, int *maxdata, int *range); |
| static int me4600_ai_query_number_ranges(me_subdevice_t *subdevice, |
| int unit, int *count); |
| static int me4600_ai_query_range_info(me_subdevice_t *subdevice, |
| int range, |
| int *unit, |
| int *min, int *max, int *maxdata); |
| static int me4600_ai_query_timer(me_subdevice_t *subdevice, |
| int timer, |
| int *base_frequency, |
| long long *min_ticks, long long *max_ticks); |
| static int me4600_ai_query_number_channels(me_subdevice_t *subdevice, |
| int *number); |
| static int me4600_ai_query_subdevice_type(me_subdevice_t *subdevice, |
| int *type, int *subtype); |
| static int me4600_ai_query_subdevice_caps(me_subdevice_t *subdevice, |
| int *caps); |
| static int me4600_ai_query_subdevice_caps_args(struct me_subdevice *subdevice, |
| int cap, int *args, int count); |
| |
| static irqreturn_t me4600_ai_isr(int irq, void *dev_id); |
| |
| static int ai_mux_toggler(me4600_ai_subdevice_t *subdevice); |
| |
| /** Immidiate stop. |
| * Reset all IRQ's sources. (block laches) |
| * Preserve FIFO |
| */ |
| static int ai_stop_immediately(me4600_ai_subdevice_t *instance); |
| |
| /** Immidiate stop. |
| * Reset all IRQ's sources. (block laches) |
| * Reset data FIFO |
| */ |
| inline void ai_stop_isr(me4600_ai_subdevice_t *instance); |
| |
| /** Interrupt logics. |
| * Read datas |
| * Reset latches |
| */ |
| void ai_limited_isr(me4600_ai_subdevice_t *instance, const uint32_t irq_status, |
| const uint32_t ctrl_status); |
| void ai_infinite_isr(me4600_ai_subdevice_t *instance, |
| const uint32_t irq_status, const uint32_t ctrl_status); |
| |
| /** Last chunck of datas. We must reschedule sample counter. |
| * Leaving SC_RELOAD doesn't do any harm, but in some bad case can make extra interrupts. |
| * When threshold is wrongly set some IRQ are lost.(!!!) |
| */ |
| inline void ai_reschedule_SC(me4600_ai_subdevice_t *instance); |
| |
| /** Read datas from FIFO and copy them to buffer */ |
| static inline int ai_read_data(me4600_ai_subdevice_t *instance, |
| const int count); |
| |
| /** Copy rest of data from fifo to circular buffer.*/ |
| static inline int ai_read_data_pooling(me4600_ai_subdevice_t *instance); |
| |
| /** Set ISM to next state for infinite data aqusation mode*/ |
| inline void ai_infinite_ISM(me4600_ai_subdevice_t *instance); |
| |
| /** Set ISM to next state for define amount of data aqusation mode*/ |
| inline void ai_limited_ISM(me4600_ai_subdevice_t *instance, |
| uint32_t irq_status); |
| |
| /** Set ISM to next stage for limited mode */ |
| inline void ai_data_acquisition_logic(me4600_ai_subdevice_t *instance); |
| |
| static void me4600_ai_work_control_task(struct work_struct *work); |
| |
| /* Definitions |
| */ |
| |
| me4600_ai_subdevice_t *me4600_ai_constructor(uint32_t reg_base, |
| unsigned int channels, |
| unsigned int ranges, |
| int isolated, |
| int sh, |
| int irq, |
| spinlock_t *ctrl_reg_lock, |
| struct workqueue_struct *me4600_wq) |
| { |
| me4600_ai_subdevice_t *subdevice; |
| int err; |
| unsigned int i; |
| |
| PDEBUG("executed. idx=0\n"); |
| |
| // Allocate memory for subdevice instance. |
| subdevice = kmalloc(sizeof(me4600_ai_subdevice_t), GFP_KERNEL); |
| |
| if (!subdevice) { |
| PERROR("Cannot get memory for subdevice instance.\n"); |
| return NULL; |
| } |
| |
| memset(subdevice, 0, sizeof(me4600_ai_subdevice_t)); |
| |
| // Initialize subdevice base class. |
| err = me_subdevice_init(&subdevice->base); |
| |
| if (err) { |
| PERROR("Cannot initialize subdevice base class instance.\n"); |
| kfree(subdevice); |
| return NULL; |
| } |
| // Initialize spin locks. |
| spin_lock_init(&subdevice->subdevice_lock); |
| |
| subdevice->ctrl_reg_lock = ctrl_reg_lock; |
| |
| // Initialize circular buffer. |
| subdevice->circ_buf.mask = ME4600_AI_CIRC_BUF_COUNT - 1; |
| |
| subdevice->circ_buf.buf = |
| (void *)__get_free_pages(GFP_KERNEL, ME4600_AI_CIRC_BUF_SIZE_ORDER); |
| PDEBUG("circ_buf = %p size=%ld\n", subdevice->circ_buf.buf, |
| ME4600_AI_CIRC_BUF_SIZE); |
| |
| if (!subdevice->circ_buf.buf) { |
| PERROR("Cannot get circular buffer.\n"); |
| me_subdevice_deinit((me_subdevice_t *) subdevice); |
| kfree(subdevice); |
| return NULL; |
| } |
| |
| memset(subdevice->circ_buf.buf, 0, ME4600_AI_CIRC_BUF_SIZE); |
| subdevice->circ_buf.head = 0; |
| subdevice->circ_buf.tail = 0; |
| subdevice->status = ai_status_none; |
| |
| // Initialize wait queue. |
| init_waitqueue_head(&subdevice->wait_queue); |
| |
| // Save the number of channels. |
| subdevice->channels = channels; |
| |
| /* Initialize the single config entries to reset values */ |
| for (i = 0; i < channels; i++) { |
| subdevice->single_config[i].status = ME_SINGLE_CHANNEL_NOT_CONFIGURED; //not configured |
| } |
| |
| // Save if isolated device. |
| subdevice->isolated = isolated; |
| |
| // Save if sample and hold is available. |
| subdevice->sh = sh; |
| |
| // Set stream config to not configured state. |
| subdevice->fifo_irq_threshold = 0; |
| subdevice->data_required = 0; |
| subdevice->chan_list_len = 0; |
| |
| // Initialize registers addresses. |
| subdevice->ctrl_reg = reg_base + ME4600_AI_CTRL_REG; |
| subdevice->status_reg = reg_base + ME4600_AI_STATUS_REG; |
| subdevice->channel_list_reg = reg_base + ME4600_AI_CHANNEL_LIST_REG; |
| subdevice->data_reg = reg_base + ME4600_AI_DATA_REG; |
| subdevice->chan_timer_reg = reg_base + ME4600_AI_CHAN_TIMER_REG; |
| subdevice->chan_pre_timer_reg = reg_base + ME4600_AI_CHAN_PRE_TIMER_REG; |
| subdevice->scan_timer_low_reg = reg_base + ME4600_AI_SCAN_TIMER_LOW_REG; |
| subdevice->scan_timer_high_reg = |
| reg_base + ME4600_AI_SCAN_TIMER_HIGH_REG; |
| subdevice->scan_pre_timer_low_reg = |
| reg_base + ME4600_AI_SCAN_PRE_TIMER_LOW_REG; |
| subdevice->scan_pre_timer_high_reg = |
| reg_base + ME4600_AI_SCAN_PRE_TIMER_HIGH_REG; |
| subdevice->start_reg = reg_base + ME4600_AI_START_REG; |
| subdevice->irq_status_reg = reg_base + ME4600_IRQ_STATUS_REG; |
| subdevice->sample_counter_reg = reg_base + ME4600_AI_SAMPLE_COUNTER_REG; |
| #ifdef MEDEBUG_DEBUG_REG |
| subdevice->reg_base = reg_base; |
| #endif |
| |
| // Initialize ranges. |
| subdevice->ranges_len = ranges; |
| subdevice->ranges[0].min = -10E6; |
| subdevice->ranges[0].max = 9999694; |
| |
| subdevice->ranges[1].min = 0; |
| subdevice->ranges[1].max = 9999847; |
| |
| subdevice->ranges[2].min = -25E5; |
| subdevice->ranges[2].max = 2499923; |
| |
| subdevice->ranges[3].min = 0; |
| subdevice->ranges[3].max = 2499961; |
| |
| // We have to switch the mux in order to get it work correctly. |
| ai_mux_toggler(subdevice); |
| |
| // Register interrupt service routine. |
| subdevice->irq = irq; |
| if (request_irq(subdevice->irq, me4600_ai_isr, |
| IRQF_DISABLED | IRQF_SHARED, |
| ME4600_NAME, subdevice)) { |
| PERROR("Cannot register interrupt service routine.\n"); |
| me_subdevice_deinit((me_subdevice_t *) subdevice); |
| free_pages((unsigned long)subdevice->circ_buf.buf, |
| ME4600_AI_CIRC_BUF_SIZE_ORDER); |
| subdevice->circ_buf.buf = NULL; |
| kfree(subdevice); |
| return NULL; |
| } |
| PINFO("Registered irq=%d.\n", subdevice->irq); |
| |
| // Override base class methods. |
| subdevice->base.me_subdevice_destructor = me4600_ai_destructor; |
| subdevice->base.me_subdevice_io_reset_subdevice = |
| me4600_ai_io_reset_subdevice; |
| subdevice->base.me_subdevice_io_single_config = |
| me4600_ai_io_single_config; |
| subdevice->base.me_subdevice_io_single_read = me4600_ai_io_single_read; |
| subdevice->base.me_subdevice_io_stream_config = |
| me4600_ai_io_stream_config; |
| subdevice->base.me_subdevice_io_stream_new_values = |
| me4600_ai_io_stream_new_values; |
| subdevice->base.me_subdevice_io_stream_read = me4600_ai_io_stream_read; |
| subdevice->base.me_subdevice_io_stream_start = |
| me4600_ai_io_stream_start; |
| subdevice->base.me_subdevice_io_stream_status = |
| me4600_ai_io_stream_status; |
| subdevice->base.me_subdevice_io_stream_stop = me4600_ai_io_stream_stop; |
| subdevice->base.me_subdevice_query_number_channels = |
| me4600_ai_query_number_channels; |
| subdevice->base.me_subdevice_query_subdevice_type = |
| me4600_ai_query_subdevice_type; |
| subdevice->base.me_subdevice_query_subdevice_caps = |
| me4600_ai_query_subdevice_caps; |
| subdevice->base.me_subdevice_query_subdevice_caps_args = |
| me4600_ai_query_subdevice_caps_args; |
| subdevice->base.me_subdevice_query_range_by_min_max = |
| me4600_ai_query_range_by_min_max; |
| subdevice->base.me_subdevice_query_number_ranges = |
| me4600_ai_query_number_ranges; |
| subdevice->base.me_subdevice_query_range_info = |
| me4600_ai_query_range_info; |
| subdevice->base.me_subdevice_query_timer = me4600_ai_query_timer; |
| |
| // Prepare work queue. |
| subdevice->me4600_workqueue = me4600_wq; |
| |
| /* workqueue API changed in kernel 2.6.20 */ |
| INIT_DELAYED_WORK(&subdevice->ai_control_task, |
| me4600_ai_work_control_task); |
| |
| return subdevice; |
| } |
| |
| static void me4600_ai_destructor(struct me_subdevice *subdevice) |
| { |
| me4600_ai_subdevice_t *instance; |
| |
| instance = (me4600_ai_subdevice_t *) subdevice; |
| |
| PDEBUG("executed. idx=0\n"); |
| |
| instance->ai_control_task_flag = 0; |
| // Reset subdevice to asure clean exit. |
| me4600_ai_io_reset_subdevice(subdevice, NULL, |
| ME_IO_RESET_SUBDEVICE_NO_FLAGS); |
| |
| // Remove any tasks from work queue. This is paranoic because it was done allready in reset(). |
| if (!cancel_delayed_work(&instance->ai_control_task)) { //Wait 2 ticks to be sure that control task is removed from queue. |
| set_current_state(TASK_INTERRUPTIBLE); |
| schedule_timeout(2); |
| } |
| |
| free_irq(instance->irq, instance); |
| free_pages((unsigned long)instance->circ_buf.buf, |
| ME4600_AI_CIRC_BUF_SIZE_ORDER); |
| me_subdevice_deinit(&instance->base); |
| kfree(instance); |
| } |
| |
| static int me4600_ai_io_reset_subdevice(me_subdevice_t *subdevice, |
| struct file *filep, int flags) |
| { |
| me4600_ai_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| volatile uint32_t ctrl; |
| unsigned long status; |
| const int timeout = HZ / 10; //100ms |
| int i; |
| |
| PDEBUG("executed. idx=0\n"); |
| |
| if (flags) { |
| PERROR("Invalid flag specified.\n"); |
| return ME_ERRNO_INVALID_FLAGS; |
| } |
| |
| instance = (me4600_ai_subdevice_t *) subdevice; |
| |
| ME_SUBDEVICE_ENTER; |
| |
| instance->ai_control_task_flag = 0; |
| instance->status = ai_status_none; |
| |
| for (i = 0; i <= timeout; i++) { |
| spin_lock_irqsave(instance->ctrl_reg_lock, status); |
| ctrl = inl(instance->ctrl_reg); |
| //Stop DMA |
| ctrl &= ~ME4600_AI_CTRL_RPCI_FIFO; |
| // Stop all actions. No conditions! |
| ctrl &= ~ME4600_AI_CTRL_BIT_STOP; |
| ctrl |= ME4600_AI_CTRL_BIT_IMMEDIATE_STOP; |
| |
| outl(ctrl, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, ctrl); |
| spin_unlock_irqrestore(instance->ctrl_reg_lock, status); |
| |
| if (!(inl(instance->status_reg) & ME4600_AI_STATUS_BIT_FSM)) |
| break; |
| |
| set_current_state(TASK_INTERRUPTIBLE); |
| schedule_timeout(1); |
| } |
| |
| if (i > timeout) { |
| PERROR("FSM is still busy.\n"); |
| ME_SUBDEVICE_EXIT; |
| return ME_ERRNO_INTERNAL; |
| } |
| |
| spin_lock_irqsave(instance->ctrl_reg_lock, status); |
| ctrl = inl(instance->ctrl_reg); |
| // Clear all features. Dissable interrupts. |
| ctrl &= ~(ME4600_AI_CTRL_BIT_STOP |
| | ME4600_AI_CTRL_BIT_LE_IRQ |
| | ME4600_AI_CTRL_BIT_HF_IRQ | ME4600_AI_CTRL_BIT_SC_IRQ); |
| ctrl |= (ME4600_AI_CTRL_BIT_IMMEDIATE_STOP |
| | ME4600_AI_CTRL_BIT_LE_IRQ_RESET |
| | ME4600_AI_CTRL_BIT_HF_IRQ_RESET |
| | ME4600_AI_CTRL_BIT_SC_IRQ_RESET); |
| |
| outl(ctrl, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, ctrl); |
| spin_unlock_irqrestore(instance->ctrl_reg_lock, status); |
| |
| outl(ME4600_AI_MIN_CHAN_TICKS - 1, instance->chan_timer_reg); |
| PDEBUG_REG("chan_timer_reg outl(0x%lX+0x%lX)=0x%llx\n", |
| instance->reg_base, |
| instance->chan_timer_reg - instance->reg_base, |
| ME4600_AI_MIN_CHAN_TICKS); |
| outl(ME4600_AI_MIN_ACQ_TICKS - 1, instance->chan_pre_timer_reg); |
| PDEBUG_REG("chan_pre_timer_reg outl(0x%lX+0x%lX)=0x%llx\n", |
| instance->reg_base, |
| instance->chan_pre_timer_reg - instance->reg_base, |
| ME4600_AI_MIN_ACQ_TICKS); |
| outl(0, instance->scan_timer_low_reg); |
| PDEBUG_REG("scan_timer_low_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->scan_timer_low_reg - instance->reg_base, 0); |
| outl(0, instance->scan_timer_high_reg); |
| PDEBUG_REG("scan_timer_high_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->scan_timer_high_reg - instance->reg_base, 0); |
| outl(0, instance->scan_pre_timer_low_reg); |
| PDEBUG_REG("scan_pre_timer_low_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->scan_pre_timer_low_reg - instance->reg_base, 0); |
| outl(0, instance->scan_pre_timer_high_reg); |
| PDEBUG_REG("scan_pre_timer_high_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->scan_pre_timer_high_reg - instance->reg_base, 0); |
| outl(0xEFFFFFFF, instance->sample_counter_reg); |
| PDEBUG_REG("sample_counter_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->sample_counter_reg - instance->reg_base, |
| 0xEFFFFFFF); |
| |
| instance->circ_buf.head = 0; |
| instance->circ_buf.tail = 0; |
| |
| instance->fifo_irq_threshold = 0; |
| instance->data_required = 0; |
| instance->chan_list_len = 0; |
| |
| // Initialize the single config entries to reset values. |
| for (i = 0; i < instance->channels; i++) { |
| instance->single_config[i].status = |
| ME_SINGLE_CHANNEL_NOT_CONFIGURED; |
| } |
| instance->status = ai_status_none; |
| |
| //Signal reset if user is on wait. |
| wake_up_interruptible_all(&instance->wait_queue); |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| static int me4600_ai_io_single_config(me_subdevice_t *subdevice, |
| struct file *filep, |
| int channel, |
| int single_config, |
| int ref, |
| int trig_chan, |
| int trig_type, int trig_edge, int flags) |
| { |
| me4600_ai_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| unsigned long cpu_flags; |
| int i; |
| |
| instance = (me4600_ai_subdevice_t *) subdevice; |
| |
| PDEBUG("executed. idx=0\n"); |
| |
| if (flags & ~ME_IO_SINGLE_CONFIG_CONTINUE) { |
| PERROR("Invalid flag specified.\n"); |
| return ME_ERRNO_INVALID_FLAGS; |
| } |
| |
| switch (trig_type) { |
| case ME_TRIG_TYPE_SW: |
| if (trig_edge != ME_TRIG_EDGE_NONE) { |
| PERROR |
| ("Invalid trigger edge. Software trigger has not edge.\n"); |
| return ME_ERRNO_INVALID_TRIG_EDGE; |
| } |
| break; |
| |
| case ME_TRIG_TYPE_EXT_ANALOG: |
| if (instance->channels <= 16) //Only versions with 32 channels have analog trigger (4670 and 4680) |
| { |
| PERROR("Invalid trigger type specified.\n"); |
| return ME_ERRNO_INVALID_TRIG_TYPE; |
| } |
| |
| case ME_TRIG_TYPE_EXT_DIGITAL: |
| if ((trig_edge != ME_TRIG_EDGE_ANY) |
| && (trig_edge != ME_TRIG_EDGE_RISING) |
| && (trig_edge != ME_TRIG_EDGE_FALLING)) { |
| PERROR("Invalid trigger edge specified.\n"); |
| return ME_ERRNO_INVALID_TRIG_EDGE; |
| } |
| break; |
| |
| default: |
| PERROR("Invalid trigger type specified.\n"); |
| return ME_ERRNO_INVALID_TRIG_TYPE; |
| } |
| |
| if (trig_chan != ME_TRIG_CHAN_DEFAULT) { |
| PERROR("Invalid trigger channel specified.\n"); |
| return ME_ERRNO_INVALID_TRIG_CHAN; |
| } |
| |
| if ((single_config < 0) || (single_config >= instance->ranges_len)) { |
| PERROR("Invalid single config specified.\n"); |
| return ME_ERRNO_INVALID_SINGLE_CONFIG; |
| } |
| |
| if ((ref != ME_REF_AI_GROUND) && (ref != ME_REF_AI_DIFFERENTIAL)) { |
| PERROR("Invalid analog reference specified.\n"); |
| return ME_ERRNO_INVALID_REF; |
| } |
| |
| if ((single_config % 2) && (ref != ME_REF_AI_GROUND)) { |
| PERROR("Invalid analog reference specified.\n"); |
| return ME_ERRNO_INVALID_REF; |
| } |
| |
| if ((ref == ME_REF_AI_DIFFERENTIAL) |
| && ((instance->channels == 16) || (channel >= 16))) { |
| PERROR("Invalid analog reference specified.\n"); |
| return ME_ERRNO_INVALID_REF; |
| } |
| |
| if (channel < 0) { |
| PERROR("Invalid channel number specified.\n"); |
| return ME_ERRNO_INVALID_CHANNEL; |
| } |
| |
| if (channel >= instance->channels) { |
| PERROR("Invalid channel number specified.\n"); |
| return ME_ERRNO_INVALID_CHANNEL; |
| } |
| |
| ME_SUBDEVICE_ENTER; |
| |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| //Prepare data entry. |
| // Common for all modes. |
| instance->single_config[channel].entry = |
| channel | ME4600_AI_LIST_LAST_ENTRY; |
| |
| if (ref == ME_REF_AI_DIFFERENTIAL) { // ME_REF_AI_DIFFERENTIAL |
| instance->single_config[channel].entry |= |
| ME4600_AI_LIST_INPUT_DIFFERENTIAL; |
| } |
| /* |
| // ME4600_AI_LIST_INPUT_SINGLE_ENDED = 0x0000 |
| // 'entry |= ME4600_AI_LIST_INPUT_SINGLE_ENDED' <== Do nothing. Removed. |
| else |
| {// ME_REF_AI_GROUND |
| instance->single_config[channel].entry |= ME4600_AI_LIST_INPUT_SINGLE_ENDED; |
| } |
| */ |
| switch (single_config) { |
| case 0: //-10V..10V |
| /* |
| // ME4600_AI_LIST_RANGE_BIPOLAR_10 = 0x0000 |
| // 'entry |= ME4600_AI_LIST_RANGE_BIPOLAR_10' <== Do nothing. Removed. |
| instance->single_config[channel].entry |= ME4600_AI_LIST_RANGE_BIPOLAR_10; |
| */ break; |
| |
| case 1: //0V..10V |
| instance->single_config[channel].entry |= |
| ME4600_AI_LIST_RANGE_UNIPOLAR_10; |
| break; |
| |
| case 2: //-2.5V..2.5V |
| instance->single_config[channel].entry |= |
| ME4600_AI_LIST_RANGE_BIPOLAR_2_5; |
| break; |
| |
| case 3: //0V..2.5V |
| instance->single_config[channel].entry |= |
| ME4600_AI_LIST_RANGE_UNIPOLAR_2_5; |
| break; |
| } |
| |
| // Prepare control register. |
| // Common for all modes. |
| instance->single_config[channel].ctrl = |
| ME4600_AI_CTRL_BIT_CHANNEL_FIFO | ME4600_AI_CTRL_BIT_DATA_FIFO; |
| |
| switch (trig_type) { |
| case ME_TRIG_TYPE_SW: |
| // Nothing to set. |
| break; |
| |
| case ME_TRIG_TYPE_EXT_ANALOG: |
| instance->single_config[channel].ctrl |= |
| ME4600_AI_CTRL_BIT_EX_TRIG_ANALOG; |
| |
| case ME_TRIG_TYPE_EXT_DIGITAL: |
| instance->single_config[channel].ctrl |= |
| ME4600_AI_CTRL_BIT_EX_TRIG; |
| break; |
| } |
| |
| switch (trig_edge) { |
| case ME_TRIG_EDGE_RISING: |
| // Nothing to set. |
| break; |
| |
| case ME_TRIG_EDGE_ANY: |
| instance->single_config[channel].ctrl |= |
| ME4600_AI_CTRL_BIT_EX_TRIG_BOTH; |
| |
| case ME_TRIG_EDGE_FALLING: |
| instance->single_config[channel].ctrl |= |
| ME4600_AI_CTRL_BIT_EX_TRIG_FALLING; |
| break; |
| } |
| |
| // Enable this channel |
| instance->single_config[channel].status = ME_SINGLE_CHANNEL_CONFIGURED; |
| |
| // Copy this settings to other outputs. |
| if (flags == ME_IO_SINGLE_CONFIG_CONTINUE) { |
| for (i = channel + 1; i < instance->channels; i++) { |
| instance->single_config[i].ctrl = |
| instance->single_config[channel].ctrl; |
| instance->single_config[i].entry = |
| instance->single_config[channel].entry; |
| instance->single_config[i].status = |
| ME_SINGLE_CHANNEL_CONFIGURED; |
| } |
| } |
| |
| instance->status = ai_status_single_configured; |
| spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags); |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| static int me4600_ai_io_single_read(me_subdevice_t *subdevice, |
| struct file *filep, |
| int channel, |
| int *value, int time_out, int flags) |
| { |
| me4600_ai_subdevice_t *instance; |
| volatile uint32_t tmp; |
| volatile uint32_t val; |
| unsigned long cpu_flags; |
| int err = ME_ERRNO_SUCCESS; |
| |
| unsigned long j; |
| unsigned long delay = 0; |
| |
| PDEBUG("executed. idx=0\n"); |
| |
| instance = (me4600_ai_subdevice_t *) subdevice; |
| |
| if (flags) { |
| PERROR("Invalid flag specified.\n"); |
| return ME_ERRNO_INVALID_FLAGS; |
| } |
| |
| if (instance->status != ai_status_single_configured) { |
| PERROR("Subdevice not configured to work in single mode!\n"); |
| return ME_ERRNO_PREVIOUS_CONFIG; |
| } |
| |
| if ((channel > instance->channels) || (channel < 0)) { |
| PERROR("Invalid channel specified.\n"); |
| return ME_ERRNO_INVALID_CHANNEL; |
| } |
| |
| if (time_out < 0) { |
| PERROR("Invalid timeout specified.\n"); |
| return ME_ERRNO_INVALID_TIMEOUT; |
| } |
| |
| if (instance->single_config[channel].status != |
| ME_SINGLE_CHANNEL_CONFIGURED) { |
| PERROR("Channel is not configured to work in single mode!\n"); |
| return ME_ERRNO_PREVIOUS_CONFIG; |
| } |
| |
| if (inl(instance->status_reg) & ME4600_AI_STATUS_BIT_FSM) { |
| PERROR("Subdevice is busy.\n"); |
| return ME_ERRNO_SUBDEVICE_BUSY; |
| } |
| |
| ME_SUBDEVICE_ENTER; |
| |
| // Cancel control task |
| PDEBUG("Cancel control task.\n"); |
| instance->ai_control_task_flag = 0; |
| cancel_delayed_work(&instance->ai_control_task); |
| |
| if (time_out) { |
| delay = (time_out * HZ) / 1000; |
| |
| if (delay == 0) |
| delay = 1; |
| } |
| |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| |
| // Mark that StreamConfig is removed. |
| instance->chan_list_len = 0; |
| |
| spin_lock_irqsave(instance->ctrl_reg_lock, cpu_flags); |
| /// @note Imprtant: Preserve EXT IRQ settings. |
| tmp = inl(instance->ctrl_reg); |
| // Clear FIFOs and dissable interrupts |
| tmp &= |
| ~(ME4600_AI_CTRL_BIT_CHANNEL_FIFO | ME4600_AI_CTRL_BIT_DATA_FIFO); |
| |
| tmp &= |
| ~(ME4600_AI_CTRL_BIT_SC_IRQ | ME4600_AI_CTRL_BIT_HF_IRQ | |
| ME4600_AI_CTRL_BIT_LE_IRQ); |
| tmp |= |
| ME4600_AI_CTRL_BIT_SC_IRQ_RESET | ME4600_AI_CTRL_BIT_HF_IRQ_RESET | |
| ME4600_AI_CTRL_BIT_LE_IRQ_RESET; |
| |
| tmp |= ME4600_AI_CTRL_BIT_IMMEDIATE_STOP; |
| outl(tmp, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, tmp); |
| |
| outl(0, instance->scan_pre_timer_low_reg); |
| PDEBUG_REG("scan_pre_timer_low_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->scan_pre_timer_low_reg - instance->reg_base, 0); |
| outl(0, instance->scan_pre_timer_high_reg); |
| PDEBUG_REG("scan_pre_timer_high_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->scan_pre_timer_high_reg - instance->reg_base, 0); |
| outl(0, instance->scan_timer_low_reg); |
| PDEBUG_REG("scan_timer_low_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->scan_timer_low_reg - instance->reg_base, 0); |
| outl(0, instance->scan_timer_high_reg); |
| PDEBUG_REG("scan_timer_high_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->scan_timer_high_reg - instance->reg_base, 0); |
| outl(65, instance->chan_timer_reg); |
| PDEBUG_REG("chan_timer_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->chan_timer_reg - instance->reg_base, 65); |
| outl(65, instance->chan_pre_timer_reg); |
| PDEBUG_REG("chan_pre_timer_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->chan_pre_timer_reg - instance->reg_base, 65); |
| |
| //Reactive FIFOs. Enable work. |
| tmp |= ME4600_AI_CTRL_BIT_CHANNEL_FIFO | ME4600_AI_CTRL_BIT_DATA_FIFO; |
| outl(tmp, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, tmp); |
| |
| outl(instance->single_config[channel].entry, |
| instance->channel_list_reg); |
| PDEBUG_REG("channel_list_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->channel_list_reg - instance->reg_base, |
| instance->single_config[channel].entry); |
| |
| // Preserve EXT IRQ settings. |
| tmp &= (ME4600_AI_CTRL_BIT_EX_IRQ | ME4600_AI_CTRL_BIT_EX_IRQ_RESET); |
| outl(instance->single_config[channel].ctrl | tmp, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, |
| instance->single_config[channel].ctrl | tmp); |
| |
| spin_unlock_irqrestore(instance->ctrl_reg_lock, cpu_flags); |
| |
| if (!(instance->single_config[channel].ctrl & ME4600_AI_CTRL_BIT_EX_TRIG)) { // Software start |
| inl(instance->start_reg); |
| PDEBUG_REG("start_reg inl(0x%lX+0x%lX)\n", instance->reg_base, |
| instance->start_reg - instance->reg_base); |
| |
| delay = 2; |
| } |
| |
| j = jiffies; |
| |
| while (!(inl(instance->status_reg) & ME4600_AI_STATUS_BIT_EF_DATA)) { |
| if (delay && ((jiffies - j) >= delay)) { |
| if (!(instance->single_config[channel].ctrl & ME4600_AI_CTRL_BIT_EX_TRIG)) { // Software start. |
| PERROR("Value not available after wait.\n"); |
| err = ME_ERRNO_INTERNAL; |
| } else { // External start. |
| PERROR("Timeout reached.\n"); |
| err = ME_ERRNO_TIMEOUT; |
| } |
| break; |
| } |
| // Wait |
| set_current_state(TASK_INTERRUPTIBLE); |
| schedule_timeout(1); |
| |
| if (signal_pending(current)) { |
| PERROR |
| ("Wait on external trigger interrupted by signal.\n"); |
| err = ME_ERRNO_SIGNAL; |
| break; |
| } |
| |
| if (instance->status != ai_status_single_configured) { |
| PERROR("Wait interrupted by reset.\n"); |
| err = ME_ERRNO_CANCELLED; |
| break; |
| } |
| } |
| |
| // Read value. |
| if (!err) { |
| val = inl(instance->data_reg) ^ 0x8000; |
| PDEBUG_REG("data_reg inl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->data_reg - instance->reg_base, val); |
| *value = val & ME4600_AI_MAX_DATA; |
| } else { |
| *value = 0xFFFFFFFF; |
| } |
| |
| // Restore settings. |
| spin_lock_irqsave(instance->ctrl_reg_lock, cpu_flags); |
| tmp = inl(instance->ctrl_reg); |
| // Clear FIFOs and dissable interrupts. |
| tmp &= |
| ~(ME4600_AI_CTRL_BIT_CHANNEL_FIFO | ME4600_AI_CTRL_BIT_DATA_FIFO); |
| tmp |= ME4600_AI_CTRL_BIT_SC_IRQ | ME4600_AI_CTRL_BIT_HF_IRQ; |
| tmp |= |
| ME4600_AI_CTRL_BIT_SC_IRQ_RESET | ME4600_AI_CTRL_BIT_HF_IRQ_RESET | |
| ME4600_AI_CTRL_BIT_LE_IRQ_RESET | ME4600_AI_CTRL_BIT_IMMEDIATE_STOP; |
| outl(tmp, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, tmp); |
| spin_unlock_irqrestore(instance->ctrl_reg_lock, cpu_flags); |
| |
| spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags); |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| static int me4600_ai_io_stream_config(me_subdevice_t *subdevice, |
| struct file *filep, |
| meIOStreamConfig_t *config_list, |
| int count, |
| meIOStreamTrigger_t *trigger, |
| int fifo_irq_threshold, int flags) |
| { |
| me4600_ai_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| int i; // internal multipurpose variable |
| unsigned long long data_required; |
| |
| volatile uint32_t entry; |
| volatile uint32_t ctrl = ME4600_AI_CTRL_BIT_IMMEDIATE_STOP; |
| volatile uint32_t tmp; // use when current copy of register's value needed |
| unsigned long cpu_flags; |
| |
| uint64_t acq_ticks; |
| uint64_t scan_ticks; |
| uint64_t conv_ticks; |
| unsigned int acq_start_ticks_low = trigger->iAcqStartTicksLow; |
| unsigned int acq_start_ticks_high = trigger->iAcqStartTicksHigh; |
| unsigned int scan_start_ticks_low = trigger->iScanStartTicksLow; |
| unsigned int scan_start_ticks_high = trigger->iScanStartTicksHigh; |
| unsigned int conv_start_ticks_low = trigger->iConvStartTicksLow; |
| unsigned int conv_start_ticks_high = trigger->iConvStartTicksHigh; |
| |
| PDEBUG("executed. idx=0\n"); |
| |
| instance = (me4600_ai_subdevice_t *) subdevice; |
| |
| if (flags) { |
| PERROR("Invalid flag specified.\n"); |
| return ME_ERRNO_INVALID_FLAGS; |
| } |
| |
| ME_SUBDEVICE_ENTER |
| // Convert ticks to 64 bit long values |
| acq_ticks = |
| (uint64_t) acq_start_ticks_low + |
| ((uint64_t) acq_start_ticks_high << 32); |
| scan_ticks = |
| (uint64_t) scan_start_ticks_low + |
| ((uint64_t) scan_start_ticks_high << 32); |
| conv_ticks = |
| (uint64_t) conv_start_ticks_low + |
| ((uint64_t) conv_start_ticks_high << 32); |
| |
| // Check settings - begin |
| switch (trigger->iAcqStartTrigType) { |
| case ME_TRIG_TYPE_SW: |
| case ME_TRIG_TYPE_EXT_DIGITAL: |
| case ME_TRIG_TYPE_EXT_ANALOG: |
| break; |
| |
| default: |
| PERROR("Invalid acquisition start trigger type specified.\n"); |
| err = ME_ERRNO_INVALID_ACQ_START_TRIG_TYPE; |
| goto ERROR; |
| break; |
| } |
| |
| if ((trigger->iAcqStartTrigType == ME_TRIG_TYPE_SW) |
| && (trigger->iAcqStartTrigEdge != ME_TRIG_EDGE_NONE)) { |
| PERROR("Invalid acquisition start trigger edge specified.\n"); |
| err = ME_ERRNO_INVALID_ACQ_START_TRIG_EDGE; |
| goto ERROR; |
| } |
| |
| if (trigger->iAcqStartTrigType != ME_TRIG_TYPE_SW) { |
| switch (trigger->iAcqStartTrigEdge) { |
| case ME_TRIG_EDGE_RISING: |
| case ME_TRIG_EDGE_FALLING: |
| case ME_TRIG_EDGE_ANY: |
| break; |
| |
| default: |
| PERROR |
| ("Invalid acquisition start trigger edge specified.\n"); |
| err = ME_ERRNO_INVALID_ACQ_START_TRIG_EDGE; |
| goto ERROR; |
| break; |
| } |
| } |
| |
| if (trigger->iAcqStartTrigChan != ME_TRIG_CHAN_DEFAULT) { |
| PERROR |
| ("Invalid acquisition start trigger channel specified.\n"); |
| err = ME_ERRNO_INVALID_ACQ_START_TRIG_CHAN; |
| goto ERROR; |
| } |
| |
| if ((acq_ticks < ME4600_AI_MIN_ACQ_TICKS) |
| || (acq_ticks > ME4600_AI_MAX_ACQ_TICKS)) { |
| PERROR |
| ("Invalid acquisition start trigger argument specified.\n"); |
| err = ME_ERRNO_INVALID_ACQ_START_ARG; |
| goto ERROR; |
| } |
| |
| switch (trigger->iScanStartTrigType) { |
| |
| case ME_TRIG_TYPE_TIMER: |
| if ((scan_ticks < ME4600_AI_MIN_SCAN_TICKS) |
| || (scan_ticks > ME4600_AI_MAX_SCAN_TICKS) |
| || (scan_ticks < count * conv_ticks) |
| ) { |
| PERROR("Invalid scan start argument specified.\n"); |
| err = ME_ERRNO_INVALID_SCAN_START_ARG; |
| goto ERROR; |
| } |
| break; |
| |
| case ME_TRIG_TYPE_EXT_DIGITAL: |
| if (trigger->iAcqStartTrigType != ME_TRIG_TYPE_EXT_DIGITAL) { |
| PERROR |
| ("Invalid scan start trigger type specified (Acq is HW digital)\n"); |
| err = ME_ERRNO_INVALID_SCAN_START_TRIG_TYPE; |
| goto ERROR; |
| } |
| break; |
| |
| case ME_TRIG_TYPE_EXT_ANALOG: |
| if (trigger->iAcqStartTrigType != ME_TRIG_TYPE_EXT_ANALOG) { |
| PERROR |
| ("Invalid scan start trigger type specified (Acq is HW analog)\n"); |
| err = ME_ERRNO_INVALID_SCAN_START_TRIG_TYPE; |
| goto ERROR; |
| } |
| break; |
| |
| case ME_TRIG_TYPE_FOLLOW: |
| break; |
| |
| default: |
| PERROR("Invalid scan start trigger type specified.\n"); |
| err = ME_ERRNO_INVALID_SCAN_START_TRIG_TYPE; |
| goto ERROR; |
| break; |
| } |
| |
| switch (trigger->iConvStartTrigType) { |
| |
| case ME_TRIG_TYPE_TIMER: |
| if ((conv_ticks < ME4600_AI_MIN_CHAN_TICKS) |
| || (conv_ticks > ME4600_AI_MAX_CHAN_TICKS)) { |
| PERROR |
| ("Invalid conv start trigger argument specified.\n"); |
| err = ME_ERRNO_INVALID_CONV_START_ARG; |
| goto ERROR; |
| } |
| break; |
| |
| case ME_TRIG_TYPE_EXT_DIGITAL: |
| if ((trigger->iScanStartTrigType != ME_TRIG_TYPE_FOLLOW) |
| || (trigger->iAcqStartTrigType != |
| ME_TRIG_TYPE_EXT_DIGITAL)) { |
| PERROR("Invalid conv start trigger type specified.\n"); |
| err = ME_ERRNO_INVALID_CONV_START_TRIG_TYPE; |
| goto ERROR; |
| } |
| break; |
| |
| case ME_TRIG_TYPE_EXT_ANALOG: |
| if ((trigger->iScanStartTrigType != ME_TRIG_TYPE_FOLLOW) |
| || (trigger->iAcqStartTrigType != |
| ME_TRIG_TYPE_EXT_ANALOG)) { |
| PERROR("Invalid conv start trigger type specified.\n"); |
| err = ME_ERRNO_INVALID_CONV_START_TRIG_TYPE; |
| goto ERROR; |
| } |
| break; |
| |
| default: |
| PERROR("Invalid conv start trigger type specified.\n"); |
| err = ME_ERRNO_INVALID_CONV_START_TRIG_TYPE; |
| goto ERROR; |
| |
| break; |
| } |
| /** |
| * Aceptable settings: |
| * iScanStopTrigType : iAcqStopTrigType |
| * |
| * ME_TRIG_TYPE_NONE : ME_TRIG_TYPE_NONE -> infinite count with manual stop |
| * ME_TRIG_TYPE_NONE : ME_TRIG_TYPE_COUNT -> stop after getting iScanStopCount list of values (iScanStopCount * count) |
| * ME_TRIG_TYPE_COUNT : ME_TRIG_TYPE_FOLLOW -> stop after getting iAcqStopCount values (it can stops in midle of the list) |
| */ |
| switch (trigger->iScanStopTrigType) { |
| |
| case ME_TRIG_TYPE_NONE: |
| break; |
| |
| case ME_TRIG_TYPE_COUNT: |
| if (trigger->iScanStopCount <= 0) { |
| PERROR("Invalid scan stop argument specified.\n"); |
| err = ME_ERRNO_INVALID_SCAN_STOP_ARG; |
| goto ERROR; |
| } |
| break; |
| |
| default: |
| PERROR("Invalid scan stop trigger type specified.\n"); |
| err = ME_ERRNO_INVALID_SCAN_STOP_TRIG_TYPE; |
| goto ERROR; |
| break; |
| } |
| |
| switch (trigger->iAcqStopTrigType) { |
| |
| case ME_TRIG_TYPE_NONE: |
| if (trigger->iScanStopTrigType != ME_TRIG_TYPE_NONE) { |
| PERROR("Invalid acq stop trigger type specified.\n"); |
| err = ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE; |
| goto ERROR; |
| } |
| break; |
| |
| case ME_TRIG_TYPE_FOLLOW: |
| if (trigger->iScanStopTrigType != ME_TRIG_TYPE_COUNT) { |
| PERROR("Invalid acq stop trigger type specified.\n"); |
| err = ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE; |
| goto ERROR; |
| } |
| break; |
| |
| case ME_TRIG_TYPE_COUNT: |
| if (trigger->iScanStopTrigType != ME_TRIG_TYPE_NONE) { |
| PERROR("Invalid acq stop trigger type specified.\n"); |
| err = ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE; |
| goto ERROR; |
| } |
| |
| if (trigger->iAcqStopCount <= 0) { |
| PERROR |
| ("Invalid acquisition or scan stop argument specified.\n"); |
| err = ME_ERRNO_INVALID_ACQ_STOP_ARG; |
| goto ERROR; |
| } |
| break; |
| |
| default: |
| PERROR("Invalid acq stop trigger type specified.\n"); |
| err = ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE; |
| goto ERROR; |
| break; |
| } |
| |
| if ((count <= 0) || (count > ME4600_AI_LIST_COUNT)) { |
| PERROR("Invalid channel list count specified.\n"); |
| err = ME_ERRNO_INVALID_CONFIG_LIST_COUNT; |
| goto ERROR; |
| } |
| ///This is general limitation |
| // if (fifo_irq_threshold < 0 || fifo_irq_threshold >= ME4600_AI_CIRC_BUF_COUNT) |
| ///This is limitation from Windows. I use it for compatibility. |
| if (fifo_irq_threshold < 0 |
| || fifo_irq_threshold >= ME4600_AI_FIFO_COUNT) { |
| PERROR("Invalid fifo irq threshold specified.\n"); |
| err = ME_ERRNO_INVALID_FIFO_IRQ_THRESHOLD; |
| goto ERROR; |
| } |
| |
| if ((config_list[0].iRef == ME_REF_AI_DIFFERENTIAL) |
| && (instance->channels == 16)) { |
| PERROR |
| ("Differential reference is not available on this subdevice.\n"); |
| err = ME_ERRNO_INVALID_REF; |
| goto ERROR; |
| } |
| |
| if (flags & ME_IO_STREAM_CONFIG_SAMPLE_AND_HOLD) { |
| if (!instance->sh) { |
| PERROR |
| ("Sample and hold is not available for this board.\n"); |
| err = ME_ERRNO_INVALID_FLAGS; |
| goto ERROR; |
| } |
| if (config_list[0].iRef == ME_REF_AI_DIFFERENTIAL) { |
| PERROR |
| ("Sample and hold is not available in differential mode.\n"); |
| err = ME_ERRNO_INVALID_FLAGS; |
| goto ERROR; |
| } |
| } |
| |
| for (i = 0; i < count; i++) { |
| if ((config_list[i].iStreamConfig < 0) |
| || (config_list[i].iStreamConfig >= instance->ranges_len)) { |
| PERROR("Invalid stream config specified.\n"); |
| err = ME_ERRNO_INVALID_STREAM_CONFIG; |
| goto ERROR; |
| } |
| |
| if ((config_list[i].iRef != ME_REF_AI_GROUND) |
| && (config_list[i].iRef != ME_REF_AI_DIFFERENTIAL)) { |
| PERROR("Invalid references in the list. Ref=0x%x\n", |
| config_list[i].iRef); |
| err = ME_ERRNO_INVALID_REF; |
| goto ERROR; |
| } |
| |
| if (config_list[i].iStreamConfig % 2) { // StreamConfig: 1 or 3 |
| if (config_list[i].iRef == ME_REF_AI_DIFFERENTIAL) { |
| PERROR |
| ("Only bipolar modes support differential measurement.\n"); |
| err = ME_ERRNO_INVALID_REF; |
| goto ERROR; |
| } |
| } |
| |
| if (config_list[i].iRef != config_list[0].iRef) { |
| PERROR |
| ("Not all references in the configuration list are equal. Ref[0]=0x%x Ref[%d]=0x%x\n", |
| config_list[0].iRef, i, config_list[i].iRef); |
| err = ME_ERRNO_INVALID_REF; |
| goto ERROR; |
| } |
| |
| if ((config_list[i].iRef == ME_REF_AI_DIFFERENTIAL) |
| && (config_list[i].iChannel >= 16)) { |
| PERROR("Channel not available in differential mode.\n"); |
| err = ME_ERRNO_INVALID_CHANNEL; |
| goto ERROR; |
| } |
| |
| if ((config_list[i].iChannel < 0) |
| || (config_list[i].iChannel >= instance->channels)) { |
| PERROR("Invalid channel number specified.\n"); |
| err = ME_ERRNO_INVALID_CHANNEL; |
| goto ERROR; |
| } |
| } |
| |
| // Check settings - end |
| |
| //Cancel control task |
| PDEBUG("Cancel control task.\n"); |
| instance->ai_control_task_flag = 0; |
| cancel_delayed_work(&instance->ai_control_task); |
| |
| // Work around from Keith Hartley - begin |
| if (trigger->iScanStartTrigType == ME_TRIG_TYPE_TIMER) { |
| if (count == 1) { |
| // The hardware does not work properly with a non-zero scan time |
| // if there is only ONE channel in the channel list. In this case |
| // we must set the scan time to zero and use the channel time. |
| |
| conv_ticks = scan_ticks; |
| trigger->iScanStartTrigType = ME_TRIG_TYPE_FOLLOW; |
| } else if (scan_ticks == count * conv_ticks) { |
| // Another hardware problem. If the number of scan ticks is |
| // exactly equal to the number of channel ticks multiplied by |
| // the number of channels then the sampling rate is reduced |
| // by half. |
| trigger->iScanStartTrigType = ME_TRIG_TYPE_FOLLOW; |
| } |
| } |
| // Work around from Keith Hartley - end |
| |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| |
| if (inl(instance->status_reg) & ME4600_AI_STATUS_BIT_FSM) { |
| PERROR("Subdevice is busy.\n"); |
| spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags); |
| ME_SUBDEVICE_EXIT; |
| return ME_ERRNO_SUBDEVICE_BUSY; |
| } |
| |
| instance->status = ai_status_none; |
| spin_lock_irqsave(instance->ctrl_reg_lock, cpu_flags); |
| // Stop all actions. Block all interrupts. Clear (disable) FIFOs. |
| ctrl = |
| ME4600_AI_CTRL_BIT_LE_IRQ_RESET | ME4600_AI_CTRL_BIT_HF_IRQ_RESET | |
| ME4600_AI_CTRL_BIT_SC_IRQ_RESET; |
| |
| tmp = inl(instance->ctrl_reg); |
| // Preserve EXT IRQ and OFFSET settings. Clean other bits. |
| tmp &= |
| (ME4600_AI_CTRL_BIT_EX_IRQ | ME4600_AI_CTRL_BIT_EX_IRQ_RESET | |
| ME4600_AI_CTRL_BIT_FULLSCALE | ME4600_AI_CTRL_BIT_OFFSET); |
| |
| // Send it to register. |
| outl(tmp | ctrl, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, tmp | ctrl); |
| |
| // Enable channel fifo -> data fifo in stream_start(). |
| ctrl |= ME4600_AI_CTRL_BIT_CHANNEL_FIFO; |
| outl(tmp | ctrl, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, tmp | ctrl); |
| spin_unlock_irqrestore(instance->ctrl_reg_lock, cpu_flags); |
| |
| // Write the channel list |
| for (i = 0; i < count; i++) { |
| entry = config_list[i].iChannel; |
| |
| switch (config_list[i].iStreamConfig) { |
| case 0: //BIPOLAR 10V |
| /* |
| // ME4600_AI_LIST_RANGE_BIPOLAR_10 = 0x0000 |
| // 'entry |= ME4600_AI_LIST_RANGE_BIPOLAR_10' <== Do nothing. Removed. |
| entry |= ME4600_AI_LIST_RANGE_BIPOLAR_10; |
| */ |
| break; |
| case 1: //UNIPOLAR 10V |
| entry |= ME4600_AI_LIST_RANGE_UNIPOLAR_10; |
| break; |
| case 2: //BIPOLAR 2.5V |
| entry |= ME4600_AI_LIST_RANGE_BIPOLAR_2_5; |
| break; |
| case 3: //UNIPOLAR 2.5V |
| entry |= ME4600_AI_LIST_RANGE_UNIPOLAR_2_5; |
| break; |
| default: |
| PERROR_CRITICAL("UNCHECK ERROR in config_list!\n"); |
| PERROR_CRITICAL |
| ("WRONG range\nPosition:%d Range:0x%04X\n", i, |
| config_list[i].iStreamConfig); |
| goto VERIFY_ERROR; |
| break; |
| } |
| |
| switch (config_list[i].iRef) { |
| case ME_REF_AI_GROUND: //SINGLE ENDED |
| /* |
| // ME4600_AI_LIST_INPUT_SINGLE_ENDED = 0x0000 |
| // 'entry |= ME4600_AI_LIST_INPUT_SINGLE_ENDED' ==> Do nothing. Removed. |
| entry |= ME4600_AI_LIST_INPUT_SINGLE_ENDED; |
| */ break; |
| case ME_REF_AI_DIFFERENTIAL: //DIFFERENTIAL |
| entry |= ME4600_AI_LIST_INPUT_DIFFERENTIAL; |
| break; |
| default: |
| PERROR_CRITICAL("UNCHECK ERROR in config_list!\n"); |
| PERROR_CRITICAL |
| ("WRONG reference\nPosition:%d Reference:0x%04X\n", |
| i, config_list[i].iRef); |
| goto VERIFY_ERROR; |
| break; |
| } |
| |
| //Add last entry flag |
| if (i == (count - 1)) { |
| entry |= ME4600_AI_LIST_LAST_ENTRY; |
| } |
| |
| outl(entry, instance->channel_list_reg); |
| PDEBUG_REG("channel_list_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->channel_list_reg - instance->reg_base, |
| entry); |
| } |
| |
| // Set triggering registers |
| --acq_ticks; |
| outl(acq_ticks, instance->chan_pre_timer_reg); |
| PDEBUG_REG("chan_pre_timer_reg outl(0x%lX+0x%lX)=0x%llX\n", |
| instance->reg_base, |
| instance->chan_pre_timer_reg - instance->reg_base, |
| acq_ticks); |
| outl(acq_ticks, instance->scan_pre_timer_low_reg); |
| PDEBUG_REG("scan_pre_timer_low_reg outl(0x%lX+0x%lX)=0x%llX\n", |
| instance->reg_base, |
| instance->scan_pre_timer_low_reg - instance->reg_base, |
| acq_ticks & 0xFFFFFFFF); |
| outl((acq_ticks >> 32), instance->scan_pre_timer_high_reg); |
| PDEBUG_REG("scan_pre_timer_high_reg outl(0x%lX+0x%lX)=0x%llX\n", |
| instance->reg_base, |
| instance->scan_pre_timer_high_reg - instance->reg_base, |
| (acq_ticks >> 32) & 0xFFFFFFFF); |
| |
| // Set triggers |
| switch (trigger->iAcqStartTrigType) { |
| // Internal |
| case ME_TRIG_TYPE_SW: |
| // Nothing to set. |
| break; |
| |
| // External |
| case ME_TRIG_TYPE_EXT_ANALOG: |
| ctrl |= ME4600_AI_CTRL_BIT_EX_TRIG_ANALOG; |
| case ME_TRIG_TYPE_EXT_DIGITAL: |
| ctrl |= ME4600_AI_CTRL_BIT_EX_TRIG; |
| |
| // External trigger needs edge's definition |
| switch (trigger->iAcqStartTrigEdge) { |
| case ME_TRIG_EDGE_RISING: |
| // Nothing to set. |
| break; |
| |
| case ME_TRIG_EDGE_FALLING: |
| ctrl |= ME4600_AI_CTRL_BIT_EX_TRIG_FALLING; |
| break; |
| |
| case ME_TRIG_EDGE_ANY: |
| ctrl |= |
| ME4600_AI_CTRL_BIT_EX_TRIG_FALLING | |
| ME4600_AI_CTRL_BIT_EX_TRIG_BOTH; |
| break; |
| |
| default: |
| PERROR_CRITICAL |
| ("UNCHECK TRIGGER EDGE in triggers structure!\n"); |
| PERROR_CRITICAL |
| ("WRONG acquisition start trigger:0x%04X.\n", |
| trigger->iAcqStartTrigEdge); |
| err = ME_ERRNO_INVALID_ACQ_START_TRIG_EDGE; |
| goto VERIFY_ERROR; |
| break; |
| } |
| break; |
| |
| default: |
| PERROR_CRITICAL("UNCHECK TRIGGER in triggers structure!\n"); |
| PERROR_CRITICAL("WRONG acquisition start trigger:0x%04X.\n", |
| trigger->iAcqStartTrigType); |
| err = ME_ERRNO_INVALID_ACQ_START_TRIG_TYPE; |
| goto VERIFY_ERROR; |
| break; |
| } |
| |
| switch (trigger->iScanStartTrigType) { |
| case ME_TRIG_TYPE_TIMER: |
| --scan_ticks; |
| outl(scan_ticks, instance->scan_timer_low_reg); |
| PDEBUG_REG("scan_timer_low_reg outl(0x%lX+0x%lX)=0x%llX\n", |
| instance->reg_base, |
| instance->scan_timer_low_reg - instance->reg_base, |
| scan_ticks & 0xFFFFFFFF); |
| outl((scan_ticks >> 32), instance->scan_timer_high_reg); |
| PDEBUG_REG("scan_timer_high_reg outl(0x%lX+0x%lX)=0x%llX\n", |
| instance->reg_base, |
| instance->scan_timer_high_reg - instance->reg_base, |
| (scan_ticks >> 32) & 0xFFFFFFFF); |
| |
| if (trigger->iAcqStartTrigType == ME_TRIG_TYPE_SW) { |
| ctrl |= ME4600_AI_CTRL_BIT_MODE_0; |
| } else { |
| ctrl |= ME4600_AI_CTRL_BIT_MODE_1; |
| } |
| break; |
| |
| case ME_TRIG_TYPE_EXT_DIGITAL: |
| case ME_TRIG_TYPE_EXT_ANALOG: |
| outl(0, instance->scan_timer_low_reg); |
| PDEBUG_REG("scan_timer_low_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->scan_timer_low_reg - instance->reg_base, |
| 0); |
| outl(0, instance->scan_timer_high_reg); |
| PDEBUG_REG("scan_timer_high_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->scan_timer_high_reg - instance->reg_base, |
| 0); |
| ctrl |= ME4600_AI_CTRL_BIT_MODE_2; |
| break; |
| |
| case ME_TRIG_TYPE_FOLLOW: |
| outl(0, instance->scan_timer_low_reg); |
| PDEBUG_REG("scan_timer_low_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->scan_timer_low_reg - instance->reg_base, |
| 0); |
| outl(0, instance->scan_timer_high_reg); |
| PDEBUG_REG("scan_timer_high_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->scan_timer_high_reg - instance->reg_base, |
| 0); |
| |
| if (trigger->iAcqStartTrigType == ME_TRIG_TYPE_SW) { |
| ctrl |= ME4600_AI_CTRL_BIT_MODE_0; |
| } else { |
| ctrl |= ME4600_AI_CTRL_BIT_MODE_1; |
| } |
| break; |
| |
| default: |
| PERROR_CRITICAL("UNCHECK TRIGGER in triggers structure!\n"); |
| PERROR_CRITICAL("WRONG scan start trigger:0x%04X.\n", |
| trigger->iScanStartTrigType); |
| err = ME_ERRNO_INVALID_SCAN_START_TRIG_TYPE; |
| goto VERIFY_ERROR; |
| break; |
| } |
| |
| switch (trigger->iConvStartTrigType) { |
| |
| case ME_TRIG_TYPE_TIMER: |
| --conv_ticks; |
| outl(conv_ticks, instance->chan_timer_reg); |
| PDEBUG_REG("chan_timer_reg outl(0x%lX+0x%lX)=0x%llX\n", |
| instance->reg_base, |
| instance->chan_timer_reg - instance->reg_base, |
| conv_ticks); |
| break; |
| |
| case ME_TRIG_TYPE_EXT_DIGITAL: |
| case ME_TRIG_TYPE_EXT_ANALOG: |
| outl(0, instance->chan_timer_reg); |
| PDEBUG_REG("chan_timer_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->chan_timer_reg - instance->reg_base, 0); |
| ctrl |= ME4600_AI_CTRL_BIT_MODE_0 | ME4600_AI_CTRL_BIT_MODE_1; |
| break; |
| |
| default: |
| PERROR_CRITICAL("UNCHECK TRIGGER in triggers structure!\n"); |
| PERROR_CRITICAL("WRONG conv start trigger:0x%04X.\n", |
| trigger->iConvStartTrigType); |
| err = ME_ERRNO_INVALID_CONV_START_TRIG_TYPE; |
| goto VERIFY_ERROR; |
| |
| break; |
| } |
| |
| //Sample & Hold feature |
| if (flags & ME_IO_STREAM_CONFIG_SAMPLE_AND_HOLD) { |
| if (instance->sh) { |
| ctrl |= ME4600_AI_CTRL_BIT_SAMPLE_HOLD; |
| } else { |
| PERROR_CRITICAL("UNCHECK S&H feature!\n"); |
| err = ME_ERRNO_INVALID_FLAGS; |
| goto VERIFY_ERROR; |
| } |
| } |
| //Enable IRQs sources but leave latches blocked. |
| ctrl |= (ME4600_AI_CTRL_BIT_HF_IRQ | ME4600_AI_CTRL_BIT_SC_IRQ | ME4600_AI_CTRL_BIT_LE_IRQ); //The last IRQ source (ME4600_AI_CTRL_BIT_LE_IRQ) is unused! |
| |
| //Everything is good. Finalize |
| spin_lock_irqsave(instance->ctrl_reg_lock, cpu_flags); |
| tmp = inl(instance->ctrl_reg); |
| |
| //Preserve EXT IRQ and OFFSET settings. Clean other bits. |
| tmp &= |
| (ME4600_AI_CTRL_BIT_EX_IRQ | ME4600_AI_CTRL_BIT_EX_IRQ_RESET | |
| ME4600_AI_CTRL_BIT_FULLSCALE | ME4600_AI_CTRL_BIT_OFFSET); |
| |
| // write the control word |
| outl(ctrl | tmp, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, ctrl | tmp); |
| spin_unlock_irqrestore(instance->ctrl_reg_lock, cpu_flags); |
| |
| //Set the global parameters end exit. |
| instance->chan_list_len = count; |
| instance->fifo_irq_threshold = fifo_irq_threshold; |
| |
| if (trigger->iAcqStopTrigType == ME_TRIG_TYPE_COUNT) { |
| data_required = |
| (unsigned long long)trigger->iAcqStopCount * |
| (unsigned long long)count; |
| if (data_required > UINT_MAX) |
| data_required = UINT_MAX; |
| instance->data_required = (unsigned int)data_required; |
| } else if (trigger->iScanStopTrigType == ME_TRIG_TYPE_COUNT) |
| instance->data_required = |
| (unsigned long long)trigger->iScanStopCount; |
| else |
| instance->data_required = 0; |
| |
| // Mark subdevice as configured to work in stream mode. |
| instance->status = ai_status_stream_configured; |
| |
| // Deinit single config. Set all entries to NOT_CONFIGURED. |
| for (i = 0; i < instance->channels; i++) { |
| instance->single_config[i].status = |
| ME_SINGLE_CHANNEL_NOT_CONFIGURED; |
| } |
| |
| VERIFY_ERROR: // Error in code. Wrong setting check. This should never ever happend! |
| spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags); |
| ERROR: // Error in settings. |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| static int me4600_ai_io_stream_new_values(me_subdevice_t *subdevice, |
| struct file *filep, |
| int time_out, int *count, int flags) |
| { |
| me4600_ai_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| unsigned long t; |
| unsigned long j; |
| int volatile head; |
| |
| PDEBUG("executed. idx=0\n"); |
| |
| if (flags) { |
| PERROR("Invalid flag specified.\n"); |
| return ME_ERRNO_INVALID_FLAGS; |
| } |
| |
| if (time_out < 0) { |
| PERROR("Invalid time_out specified.\n"); |
| return ME_ERRNO_INVALID_TIMEOUT; |
| } |
| |
| if (time_out) { |
| t = (time_out * HZ) / 1000; |
| |
| if (t == 0) |
| t = 1; |
| } else { // Max time. |
| t = LONG_MAX; |
| } |
| |
| instance = (me4600_ai_subdevice_t *) subdevice; |
| |
| ME_SUBDEVICE_ENTER; |
| |
| j = jiffies; |
| |
| while (1) { |
| // Only runing device can generate break. |
| head = instance->circ_buf.head; |
| wait_event_interruptible_timeout(instance->wait_queue, |
| ((head != |
| instance->circ_buf.head) |
| || |
| ((instance->status <= |
| ai_status_stream_run_wait) |
| && (instance->status >= |
| ai_status_stream_end_wait))), |
| t); |
| |
| if (head != instance->circ_buf.head) { // New data in buffer. |
| break; |
| } else if (instance->status == ai_status_stream_end) { // End of work. |
| break; |
| } else if (instance->status == ai_status_stream_fifo_error) { |
| err = ME_ERRNO_FIFO_BUFFER_OVERFLOW; |
| break; |
| } else if (instance->status == ai_status_stream_buffer_error) { |
| err = ME_ERRNO_RING_BUFFER_OVERFLOW; |
| break; |
| } else if (instance->status == ai_status_stream_error) { |
| err = ME_ERRNO_INTERNAL; |
| break; |
| } else if ((jiffies - j) >= t) { |
| PERROR("Wait on values timed out.\n"); |
| err = ME_ERRNO_TIMEOUT; |
| break; |
| } else if (signal_pending(current)) { |
| PERROR("Wait on values interrupted from signal.\n"); |
| err = ME_ERRNO_SIGNAL; |
| break; |
| } |
| // Correct timeout. |
| t -= jiffies - j; |
| } |
| |
| *count = me_circ_buf_values(&instance->circ_buf); |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| static inline int me4600_ai_io_stream_read_get_value(me4600_ai_subdevice_t * |
| instance, int *values, |
| const int count, |
| const int flags) |
| { |
| int n; |
| int i; |
| uint32_t value; |
| |
| ///Checking how many datas can be copied. |
| n = me_circ_buf_values(&instance->circ_buf); |
| if (n <= 0) |
| return 0; |
| |
| if (n > count) |
| n = count; |
| |
| if (flags & ME_IO_STREAM_READ_FRAMES) { |
| if (n < instance->chan_list_len) //Not enough data! |
| return 0; |
| n -= n % instance->chan_list_len; |
| } |
| |
| for (i = 0; i < n; i++) { |
| value = *(instance->circ_buf.buf + instance->circ_buf.tail); |
| if (put_user(value, values + i)) { |
| PERROR("Cannot copy new values to user.\n"); |
| return -ME_ERRNO_INTERNAL; |
| } |
| instance->circ_buf.tail++; |
| instance->circ_buf.tail &= instance->circ_buf.mask; |
| } |
| return n; |
| } |
| |
| static int me4600_ai_io_stream_read(me_subdevice_t *subdevice, |
| struct file *filep, |
| int read_mode, |
| int *values, int *count, int flags) |
| { |
| me4600_ai_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| int ret; |
| |
| int c = *count; |
| int min = c; |
| |
| PDEBUG("executed. idx=0\n"); |
| |
| if (flags & ~ME_IO_STREAM_READ_FRAMES) { |
| PERROR("Invalid flag specified.\n"); |
| return ME_ERRNO_INVALID_FLAGS; |
| } |
| |
| if (!values || !count) { |
| PERROR("Request has invalid pointer.\n"); |
| return ME_ERRNO_INVALID_POINTER; |
| } |
| |
| if (c < 0) { |
| PERROR("Request has invalid value's counter.\n"); |
| return ME_ERRNO_INVALID_VALUE_COUNT; |
| } |
| |
| if ((read_mode != ME_READ_MODE_BLOCKING) |
| && (read_mode != ME_READ_MODE_NONBLOCKING)) { |
| PERROR("Invalid read mode specified.\n"); |
| return ME_ERRNO_INVALID_READ_MODE; |
| } |
| |
| if (c == 0) { //You get what you want! Nothing more or less. |
| return ME_ERRNO_SUCCESS; |
| } |
| |
| instance = (me4600_ai_subdevice_t *) subdevice; |
| ME_SUBDEVICE_ENTER; |
| |
| //Check if subdevice is configured. |
| if (instance->chan_list_len <= 0) { |
| PERROR("Subdevice wasn't configured.\n"); |
| ME_SUBDEVICE_EXIT; |
| return ME_ERRNO_PREVIOUS_CONFIG; |
| } |
| |
| if (flags & ME_IO_STREAM_READ_FRAMES) { |
| if (c < instance->chan_list_len) { //Not enough data requested. |
| PERROR |
| ("When using FRAME_READ mode minimal size is defined by channel list.\n"); |
| ME_SUBDEVICE_EXIT; |
| return ME_ERRNO_INVALID_VALUE_COUNT; |
| } |
| } |
| |
| if (c > (ME4600_AI_CIRC_BUF_COUNT - instance->chan_list_len)) { // To return acceptable amount of data when user pass too big value. |
| min = ME4600_AI_CIRC_BUF_COUNT - instance->chan_list_len; |
| } |
| |
| if (flags & ME_IO_STREAM_READ_FRAMES) { |
| //Wait for whole list. |
| if (read_mode == ME_READ_MODE_BLOCKING) { |
| min = c - (c % instance->chan_list_len); |
| } |
| |
| if (read_mode == ME_READ_MODE_NONBLOCKING) { |
| min = instance->chan_list_len; |
| } |
| } |
| |
| if ((inl(instance->status_reg) & ME4600_AI_STATUS_BIT_FSM)) { //Working |
| //If blocking mode -> wait for data. |
| if ((me_circ_buf_values(&instance->circ_buf) < min) |
| && (read_mode == ME_READ_MODE_BLOCKING)) { |
| wait_event_interruptible(instance->wait_queue, |
| ((me_circ_buf_values |
| (&instance->circ_buf) >= min) |
| || !(inl(instance->status_reg) |
| & |
| ME4600_AI_STATUS_BIT_FSM))); |
| |
| if (signal_pending(current)) { |
| PERROR |
| ("Wait on values interrupted from signal.\n"); |
| err = ME_ERRNO_SIGNAL; |
| } |
| } |
| } |
| |
| ret = me4600_ai_io_stream_read_get_value(instance, values, c, flags); |
| if (ret < 0) { |
| err = -ret; |
| *count = 0; |
| } else if (ret == 0) { |
| *count = 0; |
| if (instance->status == ai_status_stream_fifo_error) { |
| err = ME_ERRNO_FIFO_BUFFER_OVERFLOW; |
| instance->status = ai_status_stream_end; |
| } else if (instance->status == ai_status_stream_buffer_error) { |
| err = ME_ERRNO_RING_BUFFER_OVERFLOW; |
| instance->status = ai_status_stream_end; |
| } else if (instance->status == ai_status_stream_end) { |
| err = ME_ERRNO_SUBDEVICE_NOT_RUNNING; |
| } else if (instance->status == ai_status_stream_error) { |
| err = ME_ERRNO_INTERNAL; |
| } else if (instance->status == ai_status_none) { |
| PDEBUG("Stream canceled.\n"); |
| err = ME_ERRNO_INTERNAL; |
| } |
| } else { |
| *count = ret; |
| } |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| /** @brief Stop aqusation. Preserve FIFOs. |
| * |
| * @param instance The subdevice instance (pointer). |
| */ |
| |
| static int ai_stop_immediately(me4600_ai_subdevice_t *instance) |
| { |
| unsigned long cpu_flags = 0; |
| volatile uint32_t ctrl; |
| const int timeout = HZ / 10; //100ms |
| int i; |
| |
| for (i = 0; i <= timeout; i++) { |
| spin_lock_irqsave(instance->ctrl_reg_lock, cpu_flags); |
| ctrl = inl(instance->ctrl_reg); |
| ctrl &= ~ME4600_AI_CTRL_BIT_STOP; |
| ctrl |= |
| (ME4600_AI_CTRL_BIT_IMMEDIATE_STOP | |
| ME4600_AI_CTRL_BIT_HF_IRQ_RESET | |
| ME4600_AI_CTRL_BIT_SC_IRQ_RESET); |
| outl(ctrl, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, ctrl); |
| spin_unlock_irqrestore(instance->ctrl_reg_lock, cpu_flags); |
| |
| if (!(inl(instance->status_reg) & ME4600_AI_STATUS_BIT_FSM)) { // Exit. |
| break; |
| } |
| |
| PINFO("Wait for stop: %d\n", i + 1); |
| //Still working! |
| set_current_state(TASK_INTERRUPTIBLE); |
| schedule_timeout(1); |
| } |
| |
| if (i > timeout) { |
| PERROR_CRITICAL("FSM IS BUSY!\n"); |
| return ME_ERRNO_INTERNAL; |
| } |
| |
| return ME_ERRNO_SUCCESS; |
| } |
| |
| static int me4600_ai_io_stream_start(me_subdevice_t *subdevice, |
| struct file *filep, |
| int start_mode, int time_out, int flags) |
| { |
| me4600_ai_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| unsigned long cpu_flags = 0; |
| unsigned long ref; |
| unsigned long delay = 0; |
| |
| volatile uint32_t tmp; |
| |
| PDEBUG("executed. idx=0\n"); |
| |
| instance = (me4600_ai_subdevice_t *) subdevice; |
| |
| if (flags) { |
| PERROR("Invalid flag specified.\n"); |
| return ME_ERRNO_INVALID_FLAGS; |
| } |
| |
| if ((start_mode != ME_START_MODE_BLOCKING) |
| && (start_mode != ME_START_MODE_NONBLOCKING)) { |
| PERROR("Invalid start mode specified.\n"); |
| return ME_ERRNO_INVALID_START_MODE; |
| } |
| |
| if (time_out < 0) { |
| PERROR("Invalid timeout specified.\n"); |
| return ME_ERRNO_INVALID_TIMEOUT; |
| } |
| |
| if (time_out) { |
| delay = (time_out * HZ) / 1000; |
| |
| if (delay == 0) |
| delay = 1; |
| } |
| |
| ME_SUBDEVICE_ENTER |
| spin_lock_irqsave(instance->ctrl_reg_lock, cpu_flags); |
| |
| tmp = inl(instance->ctrl_reg); |
| |
| if ((tmp & ME4600_AI_STATUS_BIT_FSM)) { |
| PERROR("Conversion is already running.\n"); |
| spin_unlock_irqrestore(instance->ctrl_reg_lock, cpu_flags); |
| err = ME_ERRNO_SUBDEVICE_BUSY; |
| goto ERROR; |
| } |
| |
| if (instance->chan_list_len == 0) { //Not configured! |
| PERROR("Subdevice is not configured to work in stream mode!\n"); |
| spin_unlock_irqrestore(instance->ctrl_reg_lock, cpu_flags); |
| err = ME_ERRNO_PREVIOUS_CONFIG; |
| goto ERROR; |
| } |
| |
| if (!(tmp & (ME4600_AI_CTRL_BIT_MODE_0 | ME4600_AI_CTRL_BIT_MODE_1 | ME4600_AI_CTRL_BIT_MODE_2))) { //Mode 0 = single work => no stream config |
| PERROR("Subdevice is configured to work in single mode.\n"); |
| spin_unlock_irqrestore(instance->ctrl_reg_lock, cpu_flags); |
| err = ME_ERRNO_PREVIOUS_CONFIG; |
| goto ERROR; |
| } |
| //Reset stop bits. |
| tmp |= ME4600_AI_CTRL_BIT_IMMEDIATE_STOP | ME4600_AI_CTRL_BIT_STOP; |
| outl(tmp, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, tmp); |
| |
| //Start datas' FIFO. |
| tmp |= ME4600_AI_CTRL_BIT_DATA_FIFO; |
| //Free stop bits. |
| tmp &= ~(ME4600_AI_CTRL_BIT_IMMEDIATE_STOP | ME4600_AI_CTRL_BIT_STOP); |
| outl(tmp, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, tmp); |
| spin_unlock_irqrestore(instance->ctrl_reg_lock, cpu_flags); |
| |
| //Cancel control task |
| PDEBUG("Cancel control task.\n"); |
| instance->ai_control_task_flag = 0; |
| cancel_delayed_work(&instance->ai_control_task); |
| |
| //Set the starting values. |
| instance->ISM.global_read = 0; |
| instance->ISM.read = 0; |
| //Clear circular buffer |
| instance->circ_buf.head = 0; |
| instance->circ_buf.tail = 0; |
| |
| //Set everything. |
| ai_data_acquisition_logic(instance); |
| |
| //Set status to 'wait for start' |
| instance->status = ai_status_stream_run_wait; |
| |
| // Set control task's timeout |
| instance->timeout.delay = delay; |
| instance->timeout.start_time = jiffies; |
| |
| //Lets go! Start work |
| inl(instance->start_reg); |
| PDEBUG_REG("start_reg inl(0x%lX+0x%lX)\n", instance->reg_base, |
| instance->start_reg - instance->reg_base); |
| |
| // Schedule control task |
| instance->ai_control_task_flag = 1; |
| queue_delayed_work(instance->me4600_workqueue, |
| &instance->ai_control_task, 1); |
| |
| PDEVELOP("Delay:%ld\n", delay); |
| |
| if (start_mode == ME_START_MODE_BLOCKING) { //Wait for start. |
| ref = jiffies; |
| //Only runing process will interrupt this call. Events are signaled when status change. Extra timeout add for safe reason. |
| wait_event_interruptible_timeout(instance->wait_queue, |
| (instance->status != |
| ai_status_stream_run_wait), |
| (delay) ? delay + |
| 1 : LONG_MAX); |
| |
| if ((instance->status != ai_status_stream_run) |
| && (instance->status != ai_status_stream_end)) { |
| PDEBUG("Starting stream canceled. %d\n", |
| instance->status); |
| err = ME_ERRNO_CANCELLED; |
| } |
| |
| if (signal_pending(current)) { |
| PERROR("Wait on start of state machine interrupted.\n"); |
| instance->status = ai_status_none; |
| ai_stop_isr(instance); |
| err = ME_ERRNO_SIGNAL; |
| } else if ((delay) && ((jiffies - ref) > delay)) { |
| if (instance->status != ai_status_stream_run) { |
| if (instance->status == ai_status_stream_end) { |
| PDEBUG("Timeout reached.\n"); |
| } else if ((jiffies - ref) > delay + 1) { |
| PERROR |
| ("Timeout reached. Not handled by control task!\n"); |
| ai_stop_isr(instance); |
| instance->status = |
| ai_status_stream_error; |
| } else { |
| PERROR |
| ("Timeout reached. Signal come but status is strange: %d\n", |
| instance->status); |
| ai_stop_isr(instance); |
| instance->status = |
| ai_status_stream_error; |
| } |
| |
| instance->ai_control_task_flag = 0; |
| cancel_delayed_work(&instance->ai_control_task); |
| err = ME_ERRNO_TIMEOUT; |
| } |
| } |
| } |
| #ifdef MEDEBUG_INFO |
| tmp = inl(instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg inl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, tmp); |
| |
| PINFO("STATUS_BIT_FSM=%s.\n", |
| (tmp & ME4600_AI_STATUS_BIT_FSM) ? "on" : "off"); |
| PINFO("CTRL_BIT_HF_IRQ=%s.\n", |
| (tmp & ME4600_AI_CTRL_BIT_HF_IRQ) ? "enable" : "disable"); |
| PINFO("CTRL_BIT_HF_IRQ_RESET=%s.\n", |
| (tmp & ME4600_AI_CTRL_BIT_HF_IRQ_RESET) ? "reset" : "work"); |
| PINFO("CTRL_BIT_SC_IRQ=%s.\n", |
| (tmp & ME4600_AI_CTRL_BIT_SC_IRQ) ? "enable" : "disable"); |
| PINFO("CTRL_BIT_SC_RELOAD=%s.\n", |
| (tmp & ME4600_AI_CTRL_BIT_SC_RELOAD) ? "on" : "off"); |
| PINFO("CTRL_BIT_SC_IRQ_RESET=%s.\n", |
| (tmp & ME4600_AI_CTRL_BIT_SC_IRQ_RESET) ? "reset" : "work"); |
| #endif |
| |
| ERROR: |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| static int me4600_ai_io_stream_status(me_subdevice_t *subdevice, |
| struct file *filep, |
| int wait, |
| int *status, int *values, int flags) |
| { |
| me4600_ai_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| |
| PDEBUG("executed. idx=0\n"); |
| |
| instance = (me4600_ai_subdevice_t *) subdevice; |
| |
| if (flags) { |
| PERROR("Invalid flag specified.\n"); |
| return ME_ERRNO_INVALID_FLAGS; |
| } |
| |
| ME_SUBDEVICE_ENTER; |
| |
| switch (instance->status) { |
| case ai_status_single_configured: |
| case ai_status_stream_configured: |
| case ai_status_stream_end: |
| case ai_status_stream_fifo_error: |
| case ai_status_stream_buffer_error: |
| case ai_status_stream_error: |
| *status = ME_STATUS_IDLE; |
| break; |
| |
| case ai_status_stream_run_wait: |
| case ai_status_stream_run: |
| case ai_status_stream_end_wait: |
| *status = ME_STATUS_BUSY; |
| break; |
| |
| case ai_status_none: |
| default: |
| *status = |
| (inl(instance->status_reg) & ME4600_AI_STATUS_BIT_FSM) ? |
| ME_STATUS_BUSY : ME_STATUS_IDLE; |
| break; |
| } |
| |
| if ((wait == ME_WAIT_IDLE) && (*status == ME_STATUS_BUSY)) { |
| // Only runing process will interrupt this call. Events are signaled when status change. Extra timeout add for safe reason. |
| wait_event_interruptible_timeout(instance->wait_queue, |
| ((instance->status != |
| ai_status_stream_run_wait) |
| && (instance->status != |
| ai_status_stream_run) |
| && (instance->status != |
| ai_status_stream_end_wait)), |
| LONG_MAX); |
| |
| if (instance->status != ai_status_stream_end) { |
| PDEBUG("Wait for IDLE canceled. %d\n", |
| instance->status); |
| err = ME_ERRNO_CANCELLED; |
| } |
| |
| if (signal_pending(current)) { |
| PERROR("Wait for IDLE interrupted.\n"); |
| instance->status = ai_status_none; |
| ai_stop_isr(instance); |
| err = ME_ERRNO_SIGNAL; |
| } |
| |
| *status = ME_STATUS_IDLE; |
| } |
| |
| *values = me_circ_buf_values(&instance->circ_buf); |
| PDEBUG("me_circ_buf_values(&instance->circ_buf)=%d.\n", *values); |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| static int me4600_ai_io_stream_stop(me_subdevice_t *subdevice, |
| struct file *filep, |
| int stop_mode, int flags) |
| { |
| /** |
| @note Stop is implemented only in blocking mode. |
| @note Function return when state machine is stoped. |
| */ |
| me4600_ai_subdevice_t *instance; |
| unsigned long cpu_flags; |
| uint32_t ctrl; |
| int ret; |
| |
| PDEBUG("executed. idx=0\n"); |
| |
| if (flags) { |
| PERROR("Invalid flag specified.\n"); |
| return ME_ERRNO_INVALID_FLAGS; |
| } |
| |
| if ((stop_mode != ME_STOP_MODE_IMMEDIATE) |
| && (stop_mode != ME_STOP_MODE_LAST_VALUE)) { |
| PERROR("Invalid stop mode specified.\n"); |
| return ME_ERRNO_INVALID_STOP_MODE; |
| } |
| |
| instance = (me4600_ai_subdevice_t *) subdevice; |
| |
| ME_SUBDEVICE_ENTER; |
| |
| // Mark as stopping. => Software stop. |
| instance->status = ai_status_stream_end_wait; |
| |
| if (stop_mode == ME_STOP_MODE_IMMEDIATE) { |
| ret = ai_stop_immediately(instance); |
| |
| if (ret) { |
| PERROR("FSM is still busy.\n"); |
| ME_SUBDEVICE_EXIT; |
| return ME_ERRNO_SUBDEVICE_BUSY; |
| } |
| instance->ai_control_task_flag = 0; |
| |
| } else if (stop_mode == ME_STOP_MODE_LAST_VALUE) { |
| // Set stop bit in registry. |
| spin_lock_irqsave(instance->ctrl_reg_lock, cpu_flags); |
| ctrl = inl(instance->ctrl_reg); |
| ctrl |= ME4600_AI_CTRL_BIT_STOP; |
| outl(ctrl, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, ctrl); |
| spin_unlock_irqrestore(instance->ctrl_reg_lock, cpu_flags); |
| |
| // Only runing process will interrupt this call. Events are signaled when status change. |
| wait_event_interruptible_timeout(instance->wait_queue, |
| (instance->status != |
| ai_status_stream_end_wait), |
| LONG_MAX); |
| |
| if (instance->status != ai_status_stream_end) { |
| PDEBUG("Stopping stream canceled.\n"); |
| ret = ME_ERRNO_CANCELLED; |
| } |
| |
| if (signal_pending(current)) { |
| PERROR("Stopping stream interrupted.\n"); |
| instance->status = ai_status_none; |
| ret = ME_ERRNO_SIGNAL; |
| } |
| // End of work. |
| ai_stop_immediately(instance); |
| |
| } |
| |
| ret = ai_read_data_pooling(instance); |
| if (ret > 0) { // Everything fine. More datas put to software buffer. |
| instance->status = ai_status_stream_end; |
| ret = ME_ERRNO_SUCCESS; |
| // Signal that we put last data to software buffer. |
| wake_up_interruptible_all(&instance->wait_queue); |
| } else if (ret == 0) { // Everything fine. No more datas in FIFO. |
| instance->status = ai_status_stream_end; |
| ret = ME_ERRNO_SUCCESS; |
| } else if (ret == -ME_ERRNO_RING_BUFFER_OVERFLOW) { // Stop is unsuccessful, buffer is overflow. |
| instance->status = ai_status_stream_buffer_error; |
| ret = ME_ERRNO_SUCCESS; |
| } else { // Stop is unsuccessful |
| instance->status = ai_status_stream_end; |
| ret = -ret; |
| } |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return ret; |
| } |
| |
| static int me4600_ai_query_range_by_min_max(me_subdevice_t *subdevice, |
| int unit, |
| int *min, |
| int *max, int *maxdata, int *range) |
| { |
| me4600_ai_subdevice_t *instance; |
| int i; |
| int r = -1; |
| int diff = 21E6; |
| |
| PDEBUG("executed. idx=0\n"); |
| |
| instance = (me4600_ai_subdevice_t *) subdevice; |
| |
| if ((*max - *min) < 0) { |
| PERROR("Invalid minimum and maximum values specified.\n"); |
| return ME_ERRNO_INVALID_MIN_MAX; |
| } |
| |
| if ((unit == ME_UNIT_VOLT) || (unit == ME_UNIT_ANY)) { |
| for (i = 0; i < instance->ranges_len; i++) { |
| if ((instance->ranges[i].min <= *min) |
| && ((instance->ranges[i].max + 1000) >= *max)) { |
| if ((instance->ranges[i].max - |
| instance->ranges[i].min) - (*max - *min) < |
| diff) { |
| r = i; |
| diff = |
| (instance->ranges[i].max - |
| instance->ranges[i].min) - (*max - |
| *min); |
| } |
| } |
| } |
| |
| if (r < 0) { |
| PERROR("No matching range found.\n"); |
| return ME_ERRNO_NO_RANGE; |
| } else { |
| *min = instance->ranges[r].min; |
| *max = instance->ranges[r].max; |
| *maxdata = ME4600_AI_MAX_DATA; |
| *range = r; |
| } |
| } else { |
| PERROR("Invalid physical unit specified.\n"); |
| return ME_ERRNO_INVALID_UNIT; |
| } |
| |
| return ME_ERRNO_SUCCESS; |
| } |
| |
| static int me4600_ai_query_number_ranges(me_subdevice_t *subdevice, |
| int unit, int *count) |
| { |
| me4600_ai_subdevice_t *instance; |
| |
| PDEBUG("executed. idx=0\n"); |
| |
| instance = (me4600_ai_subdevice_t *) subdevice; |
| |
| if ((unit == ME_UNIT_VOLT) || (unit == ME_UNIT_ANY)) { |
| *count = instance->ranges_len; |
| } else { |
| *count = 0; |
| } |
| |
| return ME_ERRNO_SUCCESS; |
| } |
| |
| static int me4600_ai_query_range_info(me_subdevice_t *subdevice, |
| int range, |
| int *unit, |
| int *min, int *max, int *maxdata) |
| { |
| me4600_ai_subdevice_t *instance; |
| |
| PDEBUG("executed. idx=0\n"); |
| |
| instance = (me4600_ai_subdevice_t *) subdevice; |
| |
| if ((range < instance->ranges_len) && (range >= 0)) { |
| *unit = ME_UNIT_VOLT; |
| *min = instance->ranges[range].min; |
| *max = instance->ranges[range].max; |
| *maxdata = ME4600_AI_MAX_DATA; |
| } else { |
| PERROR("Invalid range number specified.\n"); |
| return ME_ERRNO_INVALID_RANGE; |
| } |
| |
| return ME_ERRNO_SUCCESS; |
| } |
| |
| static int me4600_ai_query_timer(me_subdevice_t *subdevice, |
| int timer, |
| int *base_frequency, |
| long long *min_ticks, long long *max_ticks) |
| { |
| me4600_ai_subdevice_t *instance; |
| |
| PDEBUG("executed. idx=0\n"); |
| |
| instance = (me4600_ai_subdevice_t *) subdevice; |
| |
| switch (timer) { |
| |
| case ME_TIMER_ACQ_START: |
| *base_frequency = ME4600_AI_BASE_FREQUENCY; |
| *min_ticks = ME4600_AI_MIN_ACQ_TICKS; |
| *max_ticks = ME4600_AI_MAX_ACQ_TICKS; |
| break; |
| |
| case ME_TIMER_SCAN_START: |
| *base_frequency = ME4600_AI_BASE_FREQUENCY; |
| *min_ticks = ME4600_AI_MIN_SCAN_TICKS; |
| *max_ticks = ME4600_AI_MAX_SCAN_TICKS; |
| break; |
| |
| case ME_TIMER_CONV_START: |
| *base_frequency = ME4600_AI_BASE_FREQUENCY; |
| *min_ticks = ME4600_AI_MIN_CHAN_TICKS; |
| *max_ticks = ME4600_AI_MAX_CHAN_TICKS; |
| break; |
| |
| default: |
| PERROR("Invalid timer specified.(0x%04x)\n", timer); |
| |
| return ME_ERRNO_INVALID_TIMER; |
| } |
| |
| return ME_ERRNO_SUCCESS; |
| } |
| |
| static int me4600_ai_query_number_channels(me_subdevice_t *subdevice, |
| int *number) |
| { |
| me4600_ai_subdevice_t *instance; |
| |
| PDEBUG("executed. idx=0\n"); |
| |
| instance = (me4600_ai_subdevice_t *) subdevice; |
| *number = instance->channels; |
| |
| return ME_ERRNO_SUCCESS; |
| } |
| |
| static int me4600_ai_query_subdevice_type(me_subdevice_t *subdevice, |
| int *type, int *subtype) |
| { |
| PDEBUG("executed. idx=0\n"); |
| |
| *type = ME_TYPE_AI; |
| *subtype = ME_SUBTYPE_STREAMING; |
| |
| return ME_ERRNO_SUCCESS; |
| } |
| |
| static int me4600_ai_query_subdevice_caps(me_subdevice_t *subdevice, int *caps) |
| { |
| PDEBUG("executed. idx=0\n"); |
| |
| *caps = |
| ME_CAPS_AI_TRIG_SYNCHRONOUS | ME_CAPS_AI_FIFO | |
| ME_CAPS_AI_FIFO_THRESHOLD; |
| |
| return ME_ERRNO_SUCCESS; |
| } |
| |
| static int me4600_ai_query_subdevice_caps_args(struct me_subdevice *subdevice, |
| int cap, int *args, int count) |
| { |
| me4600_ai_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| |
| instance = (me4600_ai_subdevice_t *) subdevice; |
| |
| PDEBUG("executed. idx=0\n"); |
| |
| if (count != 1) { |
| PERROR("Invalid capability argument count.\n"); |
| return ME_ERRNO_INVALID_CAP_ARG_COUNT; |
| } |
| |
| switch (cap) { |
| case ME_CAP_AI_FIFO_SIZE: |
| args[0] = ME4600_AI_FIFO_COUNT; |
| break; |
| |
| case ME_CAP_AI_BUFFER_SIZE: |
| args[0] = |
| (instance->circ_buf.buf) ? ME4600_AI_CIRC_BUF_COUNT : 0; |
| break; |
| |
| default: |
| PERROR("Invalid capability.\n"); |
| err = ME_ERRNO_INVALID_CAP; |
| args[0] = 0; |
| } |
| |
| return err; |
| } |
| |
| void ai_limited_isr(me4600_ai_subdevice_t *instance, const uint32_t irq_status, |
| const uint32_t ctrl_status) |
| { |
| int to_read; |
| |
| if (!instance->fifo_irq_threshold) { //No threshold provided. SC ends work. HF need reseting. |
| if (irq_status & ME4600_IRQ_STATUS_BIT_SC) { |
| if (ai_read_data(instance, instance->ISM.next) != instance->ISM.next) { //ERROR! |
| PERROR |
| ("Limited amounts aqusition with TH=0: Circular buffer full!\n"); |
| instance->status = |
| ai_status_stream_buffer_error; |
| } else { |
| instance->status = ai_status_stream_end; |
| } |
| //End of work. |
| ai_stop_isr(instance); |
| } else if (irq_status & ME4600_IRQ_STATUS_BIT_AI_HF) { |
| instance->ISM.global_read += ME4600_AI_FIFO_HALF; |
| |
| if (ai_read_data(instance, ME4600_AI_FIFO_HALF) != ME4600_AI_FIFO_HALF) { //ERROR! |
| PERROR |
| ("Limited amounts aqusition with TH = 0: Circular buffer full!\n"); |
| //End of work. |
| ai_stop_isr(instance); |
| instance->status = |
| ai_status_stream_buffer_error; |
| } else { |
| //Continue. |
| ai_limited_ISM(instance, irq_status); |
| } |
| } |
| //Signal user. |
| wake_up_interruptible_all(&instance->wait_queue); |
| } else //if(instance->fifo_irq_threshold) |
| { |
| if (irq_status & ME4600_IRQ_STATUS_BIT_SC) { |
| instance->ISM.read = 0; |
| if ((instance->fifo_irq_threshold < ME4600_AI_FIFO_HALF) |
| && (!(ctrl_status & ME4600_AI_STATUS_BIT_HF_DATA))) |
| { |
| to_read = |
| ME4600_AI_FIFO_HALF - |
| (ME4600_AI_FIFO_HALF % |
| instance->fifo_irq_threshold); |
| PDEBUG |
| ("Limited amounts aqusition with TH != 0: Not fast enough data aqusition! correction=%d\n", |
| to_read); |
| } else { |
| to_read = instance->ISM.next; |
| } |
| instance->ISM.global_read += to_read; |
| |
| ai_reschedule_SC(instance); |
| |
| if (ai_read_data(instance, to_read) != to_read) { //ERROR! |
| PERROR |
| ("Limited amounts aqusition with TH != 0: Circular buffer full!\n"); |
| //End of work. |
| ai_stop_isr(instance); |
| instance->status = |
| ai_status_stream_buffer_error; |
| } else { |
| //Continue. |
| ai_limited_ISM(instance, irq_status); |
| } |
| |
| //Signal user. |
| wake_up_interruptible_all(&instance->wait_queue); |
| } else if (irq_status & ME4600_IRQ_STATUS_BIT_AI_HF) { |
| instance->ISM.read += ME4600_AI_FIFO_HALF; |
| instance->ISM.global_read += ME4600_AI_FIFO_HALF; |
| |
| if (ai_read_data(instance, ME4600_AI_FIFO_HALF) != ME4600_AI_FIFO_HALF) { //ERROR! |
| PERROR |
| ("Limited amounts aqusition with TH != 0: Circular buffer full!\n"); |
| ai_stop_isr(instance); |
| |
| instance->status = |
| ai_status_stream_buffer_error; |
| //Signal user. |
| wake_up_interruptible_all(&instance-> |
| wait_queue); |
| } else { |
| //Countinue. |
| ai_limited_ISM(instance, irq_status); |
| } |
| } |
| |
| if (instance->ISM.global_read >= instance->data_required) { //End of work. Next paranoid pice of code: '>=' instead od '==' only to be sure. |
| ai_stop_isr(instance); |
| if (instance->status < ai_status_stream_end) { |
| instance->status = ai_status_stream_end; |
| } |
| #ifdef MEDEBUG_ERROR |
| if (instance->ISM.global_read > instance->data_required) { //This is security check case. This should never ever happend! |
| PERROR |
| ("Limited amounts aqusition: Read more data than necessary! data_required=%d < read=%d\n", |
| instance->data_required, |
| instance->ISM.global_read); |
| //Signal error (warning??). |
| instance->status = ai_status_stream_error; |
| } |
| #endif |
| } |
| } |
| } |
| |
| void ai_infinite_isr(me4600_ai_subdevice_t *instance, |
| const uint32_t irq_status, const uint32_t ctrl_status) |
| { |
| int to_read; |
| |
| if (irq_status & ME4600_IRQ_STATUS_BIT_SC) { //next chunck of data -> read fifo |
| //Set new state in ISM. |
| if ((instance->fifo_irq_threshold < ME4600_AI_FIFO_HALF) && (!(ctrl_status & ME4600_AI_STATUS_BIT_HF_DATA))) { //There is more data than we ecpected. Propably we aren't fast enough. Read as many as possible. |
| if (instance->fifo_irq_threshold) { |
| to_read = |
| ME4600_AI_FIFO_HALF - |
| (ME4600_AI_FIFO_HALF % |
| instance->fifo_irq_threshold); |
| if (to_read > instance->fifo_irq_threshold) { |
| PDEBUG |
| ("Infinite aqusition: Not fast enough data aqusition! TH != 0: correction=%d\n", |
| to_read); |
| } |
| } else { //No threshold specified. |
| to_read = ME4600_AI_FIFO_HALF; |
| } |
| } else { |
| to_read = instance->ISM.next; |
| } |
| |
| instance->ISM.read += to_read; |
| |
| //Get data |
| if (ai_read_data(instance, to_read) != to_read) { //ERROR! |
| PERROR("Infinite aqusition: Circular buffer full!\n"); |
| ai_stop_isr(instance); |
| instance->status = ai_status_stream_buffer_error; |
| } else { |
| ai_infinite_ISM(instance); |
| instance->ISM.global_read += instance->ISM.read; |
| instance->ISM.read = 0; |
| } |
| |
| //Signal data to user |
| wake_up_interruptible_all(&instance->wait_queue); |
| } else if (irq_status & ME4600_IRQ_STATUS_BIT_AI_HF) { //fifo is half full -> read fifo Large blocks only! |
| instance->ISM.read += ME4600_AI_FIFO_HALF; |
| |
| if (ai_read_data(instance, ME4600_AI_FIFO_HALF) != ME4600_AI_FIFO_HALF) { //ERROR! |
| PERROR("Infinite aqusition: Circular buffer full!\n"); |
| ai_stop_isr(instance); |
| instance->status = ai_status_stream_buffer_error; |
| |
| //Signal it. |
| wake_up_interruptible_all(&instance->wait_queue); |
| } else { |
| ai_infinite_ISM(instance); |
| } |
| } |
| } |
| |
| static irqreturn_t me4600_ai_isr(int irq, void *dev_id) |
| { /// @note This is time critical function! |
| uint32_t irq_status; |
| uint32_t ctrl_status; |
| me4600_ai_subdevice_t *instance = dev_id; |
| //int to_read; |
| |
| PDEBUG("executed. idx=0\n"); |
| |
| if (irq != instance->irq) { |
| PERROR("Incorrect interrupt num: %d.\n", irq); |
| return IRQ_NONE; |
| } |
| |
| irq_status = inl(instance->irq_status_reg); |
| if (! |
| (irq_status & |
| (ME4600_IRQ_STATUS_BIT_AI_HF | ME4600_IRQ_STATUS_BIT_SC))) { |
| #ifdef MEDEBUG_INFO |
| if ((irq_status & (ME4600_IRQ_STATUS_BIT_AI_HF | ME4600_IRQ_STATUS_BIT_SC | ME4600_IRQ_STATUS_BIT_LE)) == ME4600_IRQ_STATUS_BIT_LE) { //This is security check case. LE is unused. This should never ever happend. |
| PINFO |
| ("%ld Shared interrupt. %s(): irq_status_reg=LE_IRQ\n", |
| jiffies, __func__); |
| } else { |
| PINFO |
| ("%ld Shared interrupt. %s(): irq_status_reg=0x%04X\n", |
| jiffies, __func__, irq_status); |
| } |
| #endif |
| return IRQ_NONE; |
| } |
| |
| if (!instance->circ_buf.buf) { //Security check. |
| PERROR_CRITICAL("CIRCULAR BUFFER NOT EXISTS!\n"); |
| ai_stop_isr(instance); |
| return IRQ_HANDLED; |
| } |
| //Get the status register. |
| ctrl_status = inl(instance->status_reg); |
| |
| #ifdef MEDEBUG_INFO |
| if (irq_status & ME4600_IRQ_STATUS_BIT_AI_HF) |
| PINFO("HF interrupt active\n"); |
| if (irq_status & ME4600_IRQ_STATUS_BIT_SC) |
| PINFO("SC interrupt active\n"); |
| if (irq_status & ME4600_IRQ_STATUS_BIT_LE) |
| PINFO("LE interrupt active\n"); |
| #endif |
| |
| //This is safety check! |
| if ((irq_status & ME4600_IRQ_STATUS_BIT_AI_HF) |
| && (ctrl_status & ME4600_AI_STATUS_BIT_HF_DATA)) { |
| PDEBUG("HF interrupt active but FIFO under half\n"); |
| //Reset HF interrupt latch. |
| spin_lock(instance->ctrl_reg_lock); |
| outl(ctrl_status | ME4600_AI_CTRL_BIT_HF_IRQ_RESET, |
| instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, |
| ctrl_status); |
| outl(ctrl_status, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, |
| ctrl_status); |
| spin_unlock(instance->ctrl_reg_lock); |
| return IRQ_HANDLED; |
| } |
| #ifdef MEDEBUG_INFO |
| PINFO("STATUS_BIT_FSM=%s.\n", |
| (ctrl_status & ME4600_AI_STATUS_BIT_FSM) ? "on" : "off"); |
| |
| PINFO("STATUS_BIT_EF_CHANNEL=%s.\n", |
| (ctrl_status & ME4600_AI_STATUS_BIT_EF_CHANNEL) ? "not empty" : |
| "empty"); |
| PINFO("STATUS_BIT_HF_CHANNEL=%s.\n", |
| (ctrl_status & ME4600_AI_STATUS_BIT_HF_CHANNEL) ? " < HF" : |
| " > HF"); |
| PINFO("STATUS_BIT_FF_CHANNEL=%s.\n", |
| (ctrl_status & ME4600_AI_STATUS_BIT_FF_CHANNEL) ? "not full" : |
| "full"); |
| |
| PINFO("STATUS_BIT_EF_DATA=%s.\n", |
| (ctrl_status & ME4600_AI_STATUS_BIT_EF_DATA) ? "not empty" : |
| "empty"); |
| PINFO("STATUS_BIT_HF_DATA=%s.\n", |
| (ctrl_status & ME4600_AI_STATUS_BIT_HF_DATA) ? " < HF" : " > HF"); |
| PINFO("STATUS_BIT_FF_DATA=%s.\n", |
| (ctrl_status & ME4600_AI_STATUS_BIT_FF_DATA) ? "not full" : |
| "full"); |
| |
| PINFO("CTRL_BIT_HF_IRQ=%s.\n", |
| (ctrl_status & ME4600_AI_CTRL_BIT_HF_IRQ) ? "enable" : "disable"); |
| PINFO("CTRL_BIT_HF_IRQ_RESET=%s.\n", |
| (ctrl_status & ME4600_AI_CTRL_BIT_HF_IRQ_RESET) ? "reset" : |
| "work"); |
| PINFO("CTRL_BIT_SC_IRQ=%s.\n", |
| (ctrl_status & ME4600_AI_CTRL_BIT_SC_IRQ) ? "enable" : "disable"); |
| PINFO("CTRL_BIT_SC_RELOAD=%s.\n", |
| (ctrl_status & ME4600_AI_CTRL_BIT_SC_RELOAD) ? "on" : "off"); |
| PINFO("CTRL_BIT_SC_IRQ_RESET=%s.\n", |
| (ctrl_status & ME4600_AI_CTRL_BIT_SC_IRQ_RESET) ? "reset" : |
| "work"); |
| #endif |
| |
| //Look for overflow error. |
| if (!(ctrl_status & ME4600_AI_STATUS_BIT_FF_DATA)) { |
| //FIFO is full. Read datas and reset all settings. |
| PERROR("FIFO overflow.\n"); |
| ai_read_data(instance, ME4600_AI_FIFO_COUNT); |
| ai_stop_isr(instance); |
| |
| instance->status = ai_status_stream_fifo_error; |
| //Signal it. |
| wake_up_interruptible_all(&instance->wait_queue); |
| |
| return IRQ_HANDLED; |
| } |
| |
| if (!instance->data_required) { //This is infinite aqusition. |
| #ifdef MEDEBUG_ERROR |
| if ((irq_status & |
| (ME4600_IRQ_STATUS_BIT_AI_HF | ME4600_IRQ_STATUS_BIT_SC)) |
| == |
| (ME4600_IRQ_STATUS_BIT_AI_HF | ME4600_IRQ_STATUS_BIT_SC)) { |
| ///In infinite mode only one interrupt source should be reported! |
| PERROR |
| ("Error in ISM! Infinite aqusition: HF and SC interrupts active! threshold=%d next=%d ctrl=0x%04X irq_status_reg=0x%04X", |
| instance->fifo_irq_threshold, instance->ISM.next, |
| ctrl_status, irq_status); |
| } |
| #endif |
| |
| ai_infinite_isr(instance, irq_status, ctrl_status); |
| |
| #ifdef MEDEBUG_INFO |
| ctrl_status = inl(instance->ctrl_reg); |
| #endif |
| } else { |
| |
| ai_limited_isr(instance, irq_status, ctrl_status); |
| ctrl_status = inl(instance->status_reg); |
| if (!(ctrl_status & (ME4600_AI_STATUS_BIT_HF_DATA | ME4600_AI_CTRL_BIT_HF_IRQ_RESET))) { //HF active, but we have more than half already => HF will never come |
| PDEBUG |
| ("MISSED HF. data_required=%d ISM.read=%d ISM.global=%d ISM.next=%d\n", |
| instance->data_required, instance->ISM.read, |
| instance->ISM.global_read, instance->ISM.next); |
| ai_limited_isr(instance, ME4600_IRQ_STATUS_BIT_AI_HF, |
| ctrl_status); |
| } |
| } |
| |
| #ifdef MEDEBUG_INFO |
| PINFO("STATUS_BIT_FSM=%s.\n", |
| (ctrl_status & ME4600_AI_STATUS_BIT_FSM) ? "on" : "off"); |
| |
| PINFO("STATUS_BIT_EF_CHANNEL=%s.\n", |
| (ctrl_status & ME4600_AI_STATUS_BIT_EF_CHANNEL) ? "not empty" : |
| "empty"); |
| PINFO("STATUS_BIT_HF_CHANNEL=%s.\n", |
| (ctrl_status & ME4600_AI_STATUS_BIT_HF_CHANNEL) ? " < HF" : |
| " > HF"); |
| PINFO("STATUS_BIT_FF_CHANNEL=%s.\n", |
| (ctrl_status & ME4600_AI_STATUS_BIT_FF_CHANNEL) ? "not full" : |
| "full"); |
| |
| PINFO("STATUS_BIT_EF_DATA=%s.\n", |
| (ctrl_status & ME4600_AI_STATUS_BIT_EF_DATA) ? "not empty" : |
| "empty"); |
| PINFO("STATUS_BIT_HF_DATA=%s.\n", |
| (ctrl_status & ME4600_AI_STATUS_BIT_HF_DATA) ? " < HF" : " > HF"); |
| PINFO("STATUS_BIT_FF_DATA=%s.\n", |
| (ctrl_status & ME4600_AI_STATUS_BIT_FF_DATA) ? "not full" : |
| "full"); |
| |
| PINFO("CTRL_BIT_HF_IRQ_RESET=%s.\n", |
| (ctrl_status & ME4600_AI_CTRL_BIT_HF_IRQ_RESET) ? "reset" : |
| "work"); |
| PINFO("CTRL_BIT_SC_IRQ=%s.\n", |
| (ctrl_status & ME4600_AI_CTRL_BIT_SC_IRQ) ? "enable" : "disable"); |
| PINFO("CTRL_BIT_SC_RELOAD=%s.\n", |
| (ctrl_status & ME4600_AI_CTRL_BIT_SC_RELOAD) ? "on" : "off"); |
| PINFO("CTRL_BIT_SC_IRQ_RESET=%s.\n", |
| (ctrl_status & ME4600_AI_CTRL_BIT_SC_IRQ_RESET) ? "reset" : |
| "work"); |
| PINFO("%ld END\n", jiffies); |
| #endif |
| |
| return IRQ_HANDLED; |
| } |
| |
| /** @brief Stop aqusation of data. Reset interrupts' laches. Clear data's FIFO. |
| * |
| * @param instance The subdevice instance (pointer). |
| */ |
| inline void ai_stop_isr(me4600_ai_subdevice_t *instance) |
| { /// @note This is soft time critical function! |
| register uint32_t tmp; |
| |
| spin_lock(instance->ctrl_reg_lock); |
| //Stop all. Reset interrupt laches. Reset data FIFO. |
| tmp = inl(instance->ctrl_reg); |
| tmp |= |
| (ME4600_AI_CTRL_BIT_IMMEDIATE_STOP | ME4600_AI_CTRL_BIT_HF_IRQ_RESET |
| | ME4600_AI_CTRL_BIT_LE_IRQ_RESET | |
| ME4600_AI_CTRL_BIT_SC_IRQ_RESET); |
| tmp &= ~ME4600_AI_CTRL_BIT_DATA_FIFO; |
| outl(tmp, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, tmp); |
| spin_unlock(instance->ctrl_reg_lock); |
| } |
| |
| /** @brief Copy data from fifo to circular buffer. |
| * |
| * @param instance The subdevice instance (pointer). |
| * @param count The number of requested data. |
| * |
| * @return On success: Number of copied values. |
| * @return On error: -ME_ERRNO_RING_BUFFER_OVERFLOW. |
| */ |
| static inline int ai_read_data(me4600_ai_subdevice_t *instance, |
| const int count) |
| { /// @note This is time critical function! |
| int c = count; |
| int empty_space; |
| int copied = 0; |
| int i, j; |
| |
| empty_space = me_circ_buf_space_to_end(&instance->circ_buf); |
| if (empty_space <= 0) { |
| PDEBUG("Circular buffer full.\n"); |
| return -ME_ERRNO_RING_BUFFER_OVERFLOW; |
| } |
| |
| if (empty_space < c) { //Copy first part. Max to end of buffer. |
| PDEBUG |
| ("Try to copy %d values from FIFO to circular buffer (pass 1).\n", |
| empty_space); |
| for (i = 0; i < empty_space; i++) { |
| *(instance->circ_buf.buf + instance->circ_buf.head) = |
| (inw(instance->data_reg) ^ 0x8000); |
| instance->circ_buf.head++; |
| } |
| instance->circ_buf.head &= instance->circ_buf.mask; |
| c -= empty_space; |
| copied = empty_space; |
| |
| empty_space = me_circ_buf_space_to_end(&instance->circ_buf); |
| } |
| |
| if (empty_space > 0) { |
| j = (empty_space < c) ? empty_space : c; |
| PDEBUG |
| ("Try to copy %d values from FIFO to circular buffer (pass 2).\n", |
| c); |
| for (i = 0; i < j; i++) { |
| *(instance->circ_buf.buf + instance->circ_buf.head) = |
| (inw(instance->data_reg) ^ 0x8000); |
| instance->circ_buf.head++; |
| } |
| instance->circ_buf.head &= instance->circ_buf.mask; |
| copied += j; |
| } |
| return copied; |
| } |
| |
| inline void ai_infinite_ISM(me4600_ai_subdevice_t *instance) |
| { /// @note This is time critical function! |
| register volatile uint32_t ctrl_set, ctrl_reset, tmp; |
| |
| if (instance->fifo_irq_threshold < ME4600_AI_FIFO_MAX_SC) { // Only sample counter with reloadnig is working. Reset it. |
| PINFO |
| ("Only sample counter with reloadnig is working. Reset it.\n"); |
| ctrl_set = ME4600_AI_CTRL_BIT_SC_IRQ_RESET; |
| ctrl_reset = ~ME4600_AI_CTRL_BIT_SC_IRQ_RESET; |
| } else if (instance->fifo_irq_threshold == instance->ISM.read) { //This is SC interrupt for large block. The whole section is done. Reset SC_IRQ an HF_IRQ and start everything again from beginning. |
| PINFO |
| ("This is SC interrupt for large block. The whole section is done. Reset SC_IRQ an HF_IRQ and start everything again from beginning.\n"); |
| ctrl_set = |
| ME4600_AI_CTRL_BIT_SC_IRQ_RESET | |
| ME4600_AI_CTRL_BIT_HF_IRQ_RESET; |
| ctrl_reset = |
| ~(ME4600_AI_CTRL_BIT_SC_IRQ_RESET | |
| ME4600_AI_CTRL_BIT_HF_IRQ_RESET); |
| } else if (instance->fifo_irq_threshold >= (ME4600_AI_FIFO_MAX_SC + instance->ISM.read)) { //This is HF interrupt for large block.The next interrupt should be from HF, also. Reset HF. |
| PINFO |
| ("This is HF interrupt for large block.The next interrupt should be from HF, also. Reset HF.\n"); |
| ctrl_set = ME4600_AI_CTRL_BIT_HF_IRQ_RESET; |
| ctrl_reset = ~ME4600_AI_CTRL_BIT_HF_IRQ_RESET; |
| } else { //This is HF interrupt for large block.The next interrupt should be from SC. Don't reset HF! |
| PINFO |
| ("This is HF interrupt for large block.The next interrupt should be from SC. Don't reset HF!\n"); |
| ctrl_set = ME4600_AI_CTRL_BIT_HF_IRQ_RESET; |
| ctrl_reset = 0xFFFFFFFF; |
| } |
| |
| //Reset interrupt latch. |
| spin_lock(instance->ctrl_reg_lock); |
| tmp = inl(instance->ctrl_reg); |
| PINFO("ctrl=0x%x ctrl_set=0x%x ctrl_reset=0x%x\n", tmp, ctrl_set, |
| ctrl_reset); |
| tmp |= ctrl_set; |
| outl(tmp, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, tmp); |
| if (ctrl_reset != 0xFFFFFFFF) { |
| outl(tmp & ctrl_reset, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reset outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, |
| tmp & ctrl_reset); |
| } |
| spin_unlock(instance->ctrl_reg_lock); |
| |
| } |
| |
| inline void ai_limited_ISM(me4600_ai_subdevice_t *instance, |
| uint32_t irq_status) |
| { /// @note This is time critical function! |
| register volatile uint32_t ctrl_set, ctrl_reset = 0xFFFFFFFF, tmp; |
| |
| if (!instance->fifo_irq_threshold) { //No threshold provided. SC ends work. |
| PINFO("No threshold provided. SC ends work.\n"); |
| ctrl_set = ME4600_AI_CTRL_BIT_HF_IRQ_RESET; |
| if (instance->data_required > (ME4600_AI_FIFO_COUNT - 1 + instance->ISM.global_read)) { //HF need reseting. |
| ctrl_reset &= ~ME4600_AI_CTRL_BIT_HF_IRQ_RESET; |
| } |
| } else //if(instance->fifo_irq_threshold) |
| { |
| if (irq_status & ME4600_IRQ_STATUS_BIT_AI_HF) { |
| PINFO("Threshold provided. Clear HF latch.\n"); |
| ctrl_set = ME4600_AI_CTRL_BIT_HF_IRQ_RESET; |
| |
| if (instance->fifo_irq_threshold >= (ME4600_AI_FIFO_MAX_SC + instance->ISM.read)) { //This is not the last one. HF need reseting. |
| PINFO |
| ("The next interrupt is HF. HF need be activating.\n"); |
| ctrl_reset = ~ME4600_AI_CTRL_BIT_HF_IRQ_RESET; |
| } |
| } |
| |
| if (irq_status & ME4600_IRQ_STATUS_BIT_SC) { |
| PINFO("Threshold provided. Restart SC.\n"); |
| ctrl_set = ME4600_AI_CTRL_BIT_SC_IRQ_RESET; |
| ctrl_reset &= ~ME4600_AI_CTRL_BIT_SC_IRQ_RESET; |
| |
| if (instance->fifo_irq_threshold >= ME4600_AI_FIFO_MAX_SC) { //This is not the last one. HF need to be activating. |
| PINFO |
| ("The next interrupt is HF. HF need to be activating.\n"); |
| ctrl_reset &= ~ME4600_AI_CTRL_BIT_HF_IRQ_RESET; |
| } |
| } |
| } |
| |
| //Reset interrupt latch. |
| spin_lock(instance->ctrl_reg_lock); |
| tmp = inl(instance->ctrl_reg); |
| tmp |= ctrl_set; |
| outl(tmp, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, tmp); |
| |
| if (ctrl_reset != 0xFFFFFFFF) { |
| outl(tmp & ctrl_reset, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, |
| tmp & ctrl_reset); |
| } |
| spin_unlock(instance->ctrl_reg_lock); |
| |
| } |
| |
| /** @brief Last chunck of datas. We must reschedule sample counter. |
| * @note Last chunck. |
| * Leaving SC_RELOAD doesn't do any harm, but in some bad case can make extra interrupts. |
| * @warning When threshold is wrongly set some IRQ are lost.(!!!) |
| */ |
| inline void ai_reschedule_SC(me4600_ai_subdevice_t *instance) |
| { |
| register uint32_t rest; |
| |
| if (instance->data_required <= instance->ISM.global_read) |
| return; |
| |
| rest = instance->data_required - instance->ISM.global_read; |
| if (rest < instance->fifo_irq_threshold) { //End of work soon .... |
| PDEBUG("Rescheduling SC from %d to %d.\n", |
| instance->fifo_irq_threshold, rest); |
| /// @note Write new value to SC <== DANGER! This is not safe solution! We can miss some inputs. |
| outl(rest, instance->sample_counter_reg); |
| PDEBUG_REG("sample_counter_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->sample_counter_reg - instance->reg_base, |
| rest); |
| instance->fifo_irq_threshold = rest; |
| |
| if (rest < ME4600_AI_FIFO_MAX_SC) { |
| instance->ISM.next = rest; |
| } else { |
| instance->ISM.next = rest % ME4600_AI_FIFO_HALF; |
| if (instance->ISM.next + ME4600_AI_FIFO_HALF < |
| ME4600_AI_FIFO_MAX_SC) { |
| instance->ISM.next += ME4600_AI_FIFO_HALF; |
| } |
| } |
| } |
| } |
| |
| /** Start the ISM. All must be reseted before enter to this function. */ |
| inline void ai_data_acquisition_logic(me4600_ai_subdevice_t *instance) |
| { |
| register uint32_t tmp; |
| |
| if (!instance->data_required) { //This is infinite aqusition. |
| if (!instance->fifo_irq_threshold) { //No threshold provided. Set SC to 0.5*FIFO. Clear the SC's latch. |
| //Set the sample counter |
| outl(ME4600_AI_FIFO_HALF, instance->sample_counter_reg); |
| PDEBUG_REG |
| ("sample_counter_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->sample_counter_reg - instance->reg_base, |
| ME4600_AI_FIFO_HALF); |
| } else { //Threshold provided. Set SC to treshold. Clear the SC's latch. |
| //Set the sample counter |
| outl(instance->fifo_irq_threshold, |
| instance->sample_counter_reg); |
| PDEBUG_REG |
| ("sample_counter_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->sample_counter_reg - instance->reg_base, |
| instance->fifo_irq_threshold); |
| } |
| |
| if (instance->fifo_irq_threshold < ME4600_AI_FIFO_MAX_SC) { //Enable only sample counter's interrupt. Set reload bit. Clear the SC's latch. |
| spin_lock(instance->ctrl_reg_lock); |
| tmp = inl(instance->ctrl_reg); |
| tmp |= ME4600_AI_CTRL_BIT_SC_RELOAD; |
| tmp &= ~ME4600_AI_CTRL_BIT_SC_IRQ_RESET; |
| outl(tmp, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, |
| tmp); |
| spin_unlock(instance->ctrl_reg_lock); |
| if (!instance->fifo_irq_threshold) { //No threshold provided. Set ISM.next to 0.5*FIFO. |
| instance->ISM.next = ME4600_AI_FIFO_HALF; |
| } else { //Threshold provided. Set ISM.next to treshold. |
| instance->ISM.next = |
| instance->fifo_irq_threshold; |
| } |
| } else { //Enable sample counter's and HF's interrupts. |
| spin_lock(instance->ctrl_reg_lock); |
| tmp = inl(instance->ctrl_reg); |
| tmp |= ME4600_AI_CTRL_BIT_SC_RELOAD; |
| tmp &= |
| ~(ME4600_AI_CTRL_BIT_SC_IRQ_RESET | |
| ME4600_AI_CTRL_BIT_HF_IRQ_RESET); |
| outl(tmp, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, |
| tmp); |
| spin_unlock(instance->ctrl_reg_lock); |
| |
| instance->ISM.next = |
| instance->fifo_irq_threshold % ME4600_AI_FIFO_HALF; |
| if (instance->ISM.next + ME4600_AI_FIFO_HALF < |
| ME4600_AI_FIFO_MAX_SC) { |
| instance->ISM.next += ME4600_AI_FIFO_HALF; |
| } |
| } |
| } else { //This aqusition is limited to set number of data. |
| if (instance->fifo_irq_threshold >= instance->data_required) { //Stupid situation. |
| instance->fifo_irq_threshold = 0; |
| PDEBUG |
| ("Stupid situation: data_required(%d) < threshold(%d).\n", |
| instance->fifo_irq_threshold, |
| instance->data_required); |
| } |
| |
| if (!instance->fifo_irq_threshold) { //No threshold provided. Easy case: HF=read and SC=end. |
| //Set the sample counter to data_required. |
| outl(instance->data_required, |
| instance->sample_counter_reg); |
| PDEBUG_REG |
| ("sample_counter_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->sample_counter_reg - instance->reg_base, |
| instance->data_required); |
| |
| //Reset the latches of sample counter and HF (if SC>FIFO). |
| //No SC reload! |
| spin_lock(instance->ctrl_reg_lock); |
| tmp = inl(instance->ctrl_reg); |
| tmp &= |
| ~(ME4600_AI_CTRL_BIT_SC_IRQ_RESET | |
| ME4600_AI_CTRL_BIT_SC_RELOAD); |
| if (instance->data_required > |
| (ME4600_AI_FIFO_COUNT - 1)) { |
| tmp &= ~ME4600_AI_CTRL_BIT_HF_IRQ_RESET; |
| instance->ISM.next = |
| instance->data_required % |
| ME4600_AI_FIFO_HALF; |
| instance->ISM.next += ME4600_AI_FIFO_HALF; |
| |
| } else { |
| instance->ISM.next = instance->data_required; |
| } |
| outl(tmp, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, |
| tmp); |
| spin_unlock(instance->ctrl_reg_lock); |
| |
| } else { //The most general case. We have concret numbe of required data and threshold. SC=TH |
| //Set the sample counter to threshold. |
| outl(instance->fifo_irq_threshold, |
| instance->sample_counter_reg); |
| PDEBUG_REG |
| ("sample_counter_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->sample_counter_reg - instance->reg_base, |
| instance->fifo_irq_threshold); |
| |
| spin_lock(instance->ctrl_reg_lock); |
| tmp = inl(instance->ctrl_reg); |
| //In this moment we are sure that SC will come more than once. |
| tmp |= ME4600_AI_CTRL_BIT_SC_RELOAD; |
| |
| if (instance->fifo_irq_threshold < ME4600_AI_FIFO_MAX_SC) { //The threshold is so small that we do need HF. |
| tmp &= ~ME4600_AI_CTRL_BIT_SC_IRQ_RESET; |
| instance->ISM.next = |
| instance->fifo_irq_threshold; |
| } else { //The threshold is large. The HF must be use. |
| tmp &= |
| ~(ME4600_AI_CTRL_BIT_SC_IRQ_RESET | |
| ME4600_AI_CTRL_BIT_HF_IRQ_RESET); |
| instance->ISM.next = |
| instance->fifo_irq_threshold % |
| ME4600_AI_FIFO_HALF; |
| if (instance->ISM.next + ME4600_AI_FIFO_HALF < |
| ME4600_AI_FIFO_MAX_SC) { |
| instance->ISM.next += |
| ME4600_AI_FIFO_HALF; |
| } |
| } |
| outl(tmp, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, |
| tmp); |
| spin_unlock(instance->ctrl_reg_lock); |
| } |
| } |
| } |
| |
| static int ai_mux_toggler(me4600_ai_subdevice_t *instance) |
| { |
| uint32_t tmp; |
| |
| PDEBUG("executed. idx=0\n"); |
| |
| outl(0, instance->scan_pre_timer_low_reg); |
| PDEBUG_REG("scan_pre_timer_low_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->scan_pre_timer_low_reg - instance->reg_base, 0); |
| outl(0, instance->scan_pre_timer_high_reg); |
| PDEBUG_REG("scan_pre_timer_high_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->scan_pre_timer_high_reg - instance->reg_base, 0); |
| outl(0, instance->scan_timer_low_reg); |
| PDEBUG_REG("scan_timer_low_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->scan_timer_low_reg - instance->reg_base, 0); |
| outl(0, instance->scan_timer_high_reg); |
| PDEBUG_REG("scan_timer_high_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->scan_timer_high_reg - instance->reg_base, 0); |
| outl(65, instance->chan_timer_reg); |
| PDEBUG_REG("chan_timer_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->chan_timer_reg - instance->reg_base, 65); |
| outl(65, instance->chan_pre_timer_reg); |
| PDEBUG_REG("chan_pre_timer_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->chan_pre_timer_reg - instance->reg_base, 65); |
| |
| // Turn on internal reference. |
| tmp = inl(instance->ctrl_reg); |
| tmp |= ME4600_AI_CTRL_BIT_FULLSCALE; |
| outl(tmp, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, tmp); |
| |
| // Clear data and channel fifo. |
| tmp &= |
| ~(ME4600_AI_CTRL_BIT_CHANNEL_FIFO | ME4600_AI_CTRL_BIT_DATA_FIFO); |
| outl(tmp, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, tmp); |
| tmp |= ME4600_AI_CTRL_BIT_CHANNEL_FIFO | ME4600_AI_CTRL_BIT_DATA_FIFO; |
| outl(tmp, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, tmp); |
| |
| // Write channel entry. |
| outl(ME4600_AI_LIST_INPUT_DIFFERENTIAL | |
| ME4600_AI_LIST_RANGE_UNIPOLAR_2_5 | 31, |
| instance->channel_list_reg); |
| PDEBUG_REG("channel_list_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->channel_list_reg - instance->reg_base, |
| ME4600_AI_LIST_INPUT_DIFFERENTIAL | |
| ME4600_AI_LIST_RANGE_UNIPOLAR_2_5 | 31); |
| |
| // Start conversion. |
| inl(instance->start_reg); |
| PDEBUG_REG("start_reg inl(0x%lX+0x%lX)\n", instance->reg_base, |
| instance->start_reg - instance->reg_base); |
| udelay(10); |
| |
| // Clear data and channel fifo. |
| tmp &= |
| ~(ME4600_AI_CTRL_BIT_CHANNEL_FIFO | ME4600_AI_CTRL_BIT_DATA_FIFO); |
| outl(tmp, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, tmp); |
| tmp |= ME4600_AI_CTRL_BIT_CHANNEL_FIFO | ME4600_AI_CTRL_BIT_DATA_FIFO; |
| outl(tmp, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, tmp); |
| |
| // Write channel entry. |
| // ME4600_AI_LIST_INPUT_SINGLE_ENDED | ME4600_AI_LIST_RANGE_BIPOLAR_10 <= 0x0000 |
| outl(ME4600_AI_LIST_INPUT_SINGLE_ENDED | |
| ME4600_AI_LIST_RANGE_BIPOLAR_10, instance->channel_list_reg); |
| PDEBUG_REG("channel_list_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->channel_list_reg - instance->reg_base, |
| ME4600_AI_LIST_INPUT_SINGLE_ENDED | |
| ME4600_AI_LIST_RANGE_BIPOLAR_10); |
| |
| // Start conversion. |
| inl(instance->start_reg); |
| PDEBUG_REG("start_reg inl(0x%lX+0x%lX)\n", instance->reg_base, |
| instance->start_reg - instance->reg_base); |
| udelay(10); |
| |
| // Clear control register. |
| tmp &= (ME4600_AI_CTRL_BIT_EX_IRQ | ME4600_AI_CTRL_BIT_EX_IRQ_RESET); |
| outl(tmp, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, tmp); |
| |
| return ME_ERRNO_SUCCESS; |
| } |
| |
| /** @brief Copy rest of data from fifo to circular buffer. |
| * @note Helper for STOP command. After FSM is stopped. |
| * @note This is slow function that copy all remainig data from FIFO to buffer. |
| * |
| * @param instance The subdevice instance (pointer). |
| * |
| * @return On success: Number of copied values. |
| * @return On error: Negative error code -ME_ERRNO_RING_BUFFER_OVERFLOW. |
| */ |
| static inline int ai_read_data_pooling(me4600_ai_subdevice_t *instance) |
| { /// @note This is time critical function! |
| int empty_space; |
| int copied = 0; |
| int status = ME_ERRNO_SUCCESS; |
| |
| PDEBUG("Space left in circular buffer = %d.\n", |
| me_circ_buf_space(&instance->circ_buf)); |
| |
| while ((empty_space = me_circ_buf_space(&instance->circ_buf))) { |
| if (!(status = inl(instance->status_reg) & ME4600_AI_STATUS_BIT_EF_DATA)) { //No more data. status = ME_ERRNO_SUCCESS = 0 |
| break; |
| } |
| *(instance->circ_buf.buf + instance->circ_buf.head) = |
| (inw(instance->data_reg) ^ 0x8000); |
| instance->circ_buf.head++; |
| instance->circ_buf.head &= instance->circ_buf.mask; |
| } |
| |
| #ifdef MEDEBUG_ERROR |
| if (!status) |
| PDEBUG |
| ("Copied all remaining datas (%d) from FIFO to circular buffer.\n", |
| copied); |
| else { |
| PDEBUG("No more empty space in buffer.\n"); |
| PDEBUG("Copied %d datas from FIFO to circular buffer.\n", |
| copied); |
| PDEBUG("FIFO still not empty.\n"); |
| } |
| #endif |
| return (!status) ? copied : -ME_ERRNO_RING_BUFFER_OVERFLOW; |
| } |
| |
| static void me4600_ai_work_control_task(struct work_struct *work) |
| { |
| me4600_ai_subdevice_t *instance; |
| uint32_t status; |
| uint32_t ctrl; |
| unsigned long cpu_flags = 0; |
| int reschedule = 0; |
| int signaling = 0; |
| |
| instance = |
| container_of((void *)work, me4600_ai_subdevice_t, ai_control_task); |
| PINFO("<%s: %ld> executed.\n", __func__, jiffies); |
| |
| status = inl(instance->status_reg); |
| PDEBUG_REG("status_reg inl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->status_reg - instance->reg_base, status); |
| |
| switch (instance->status) { // Checking actual mode. |
| // Not configured for work. |
| case ai_status_none: |
| break; |
| |
| //This are stable modes. No need to do anything. (?) |
| case ai_status_single_configured: |
| case ai_status_stream_configured: |
| case ai_status_stream_fifo_error: |
| case ai_status_stream_buffer_error: |
| case ai_status_stream_error: |
| PERROR("Shouldn't be running!.\n"); |
| break; |
| |
| // Stream modes |
| case ai_status_stream_run_wait: |
| if (status & ME4600_AI_STATUS_BIT_FSM) { // ISM started.. |
| instance->status = ai_status_stream_run; |
| // Signal the end of wait for start. |
| signaling = 1; |
| // Wait now for stop. |
| reschedule = 1; |
| break; |
| |
| // Check timeout. |
| if ((instance->timeout.delay) && ((jiffies - instance->timeout.start_time) >= instance->timeout.delay)) { // Timeout |
| PDEBUG("Timeout reached.\n"); |
| // Stop all actions. No conditions! Block interrupts. Reset FIFO => Too late! |
| ai_stop_isr(instance); |
| |
| instance->status = ai_status_stream_end; |
| |
| // Signal the end. |
| signaling = 1; |
| } |
| } |
| break; |
| |
| case ai_status_stream_run: |
| // Wait for stop ISM. |
| reschedule = 1; |
| break; |
| |
| case ai_status_stream_end_wait: |
| if (!(status & ME4600_AI_STATUS_BIT_FSM)) { // ISM stoped. Overwrite ISR. |
| instance->status = ai_status_stream_end; |
| // Signal the end of wait for stop. |
| signaling = 1; |
| } else { |
| // Wait for stop ISM. |
| reschedule = 1; |
| } |
| break; |
| |
| case ai_status_stream_end: |
| //End work. |
| if (status & ME4600_AI_STATUS_BIT_FSM) { // Still working? Stop it! |
| PERROR |
| ("Status is 'ai_status_stream_end' but hardware is still working!\n"); |
| spin_lock_irqsave(instance->ctrl_reg_lock, cpu_flags); |
| ctrl = inl(instance->ctrl_reg); |
| ctrl |= |
| (ME4600_AI_CTRL_BIT_IMMEDIATE_STOP | |
| ME4600_AI_CTRL_BIT_HF_IRQ_RESET | |
| ME4600_AI_CTRL_BIT_SC_IRQ_RESET); |
| outl(ctrl, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, |
| ctrl); |
| spin_unlock_irqrestore(instance->ctrl_reg_lock, |
| cpu_flags); |
| } |
| break; |
| |
| default: |
| PERROR_CRITICAL("Status is in wrong state (%d)!\n", |
| instance->status); |
| instance->status = ai_status_stream_error; |
| // Signal the end. |
| signaling = 1; |
| break; |
| |
| } |
| |
| if (signaling) { //Signal it. |
| wake_up_interruptible_all(&instance->wait_queue); |
| } |
| |
| if (instance->ai_control_task_flag && reschedule) { // Reschedule task |
| queue_delayed_work(instance->me4600_workqueue, |
| &instance->ai_control_task, 1); |
| } else { |
| PINFO("<%s> Ending control task.\n", __func__); |
| } |
| |
| } |