| /** |
| * @file me4600_ao.c |
| * |
| * @brief ME-4000 analog output 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 |
| |
| ///Common part. (For normal and Bosch builds.) |
| |
| /* Includes |
| */ |
| |
| #include <linux/module.h> |
| |
| #include <linux/slab.h> |
| #include <linux/spinlock.h> |
| #include <asm/io.h> |
| #include <asm/uaccess.h> |
| #include <linux/types.h> |
| #include <linux/version.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_ao_reg.h" |
| #include "me4600_ao.h" |
| |
| /* Defines |
| */ |
| |
| static int me4600_ao_query_range_by_min_max(me_subdevice_t * subdevice, |
| int unit, |
| int *min, |
| int *max, int *maxdata, int *range); |
| |
| static int me4600_ao_query_number_ranges(me_subdevice_t * subdevice, |
| int unit, int *count); |
| |
| static int me4600_ao_query_range_info(me_subdevice_t * subdevice, |
| int range, |
| int *unit, |
| int *min, int *max, int *maxdata); |
| |
| static int me4600_ao_query_timer(me_subdevice_t * subdevice, |
| int timer, |
| int *base_frequency, |
| long long *min_ticks, long long *max_ticks); |
| |
| static int me4600_ao_query_number_channels(me_subdevice_t * subdevice, |
| int *number); |
| |
| static int me4600_ao_query_subdevice_type(me_subdevice_t * subdevice, |
| int *type, int *subtype); |
| |
| static int me4600_ao_query_subdevice_caps(me_subdevice_t * subdevice, |
| int *caps); |
| |
| static int me4600_ao_query_subdevice_caps_args(struct me_subdevice *subdevice, |
| int cap, int *args, int count); |
| |
| #ifndef BOSCH |
| /// @note NORMAL BUILD |
| /// @author Krzysztof Gantzke (k.gantzke@meilhaus.de) |
| /* Includes |
| */ |
| |
| # include <linux/workqueue.h> |
| |
| /* Defines |
| */ |
| |
| /** Remove subdevice. |
| */ |
| static void me4600_ao_destructor(struct me_subdevice *subdevice); |
| |
| /** Reset subdevice. Stop all actions. Reset registry. Disable FIFO. Set output to 0V and status to 'none'. |
| */ |
| static int me4600_ao_io_reset_subdevice(me_subdevice_t * subdevice, |
| struct file *filep, int flags); |
| |
| /** Set output as single |
| */ |
| static int me4600_ao_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); |
| |
| /** Pass to user actual value of output. |
| */ |
| static int me4600_ao_io_single_read(me_subdevice_t * subdevice, |
| struct file *filep, |
| int channel, |
| int *value, int time_out, int flags); |
| |
| /** Write to output requed value. |
| */ |
| static int me4600_ao_io_single_write(me_subdevice_t * subdevice, |
| struct file *filep, |
| int channel, |
| int value, int time_out, int flags); |
| |
| /** Set output as streamed device. |
| */ |
| static int me4600_ao_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); |
| |
| /** Wait for / Check empty space in buffer. |
| */ |
| static int me4600_ao_io_stream_new_values(me_subdevice_t * subdevice, |
| struct file *filep, |
| int time_out, int *count, int flags); |
| |
| /** Start streaming. |
| */ |
| static int me4600_ao_io_stream_start(me_subdevice_t * subdevice, |
| struct file *filep, |
| int start_mode, int time_out, int flags); |
| |
| /** Check actual state. / Wait for end. |
| */ |
| static int me4600_ao_io_stream_status(me_subdevice_t * subdevice, |
| struct file *filep, |
| int wait, |
| int *status, int *values, int flags); |
| |
| /** Stop streaming. |
| */ |
| static int me4600_ao_io_stream_stop(me_subdevice_t * subdevice, |
| struct file *filep, |
| int stop_mode, int flags); |
| |
| /** Write datas to buffor. |
| */ |
| static int me4600_ao_io_stream_write(me_subdevice_t * subdevice, |
| struct file *filep, |
| int write_mode, |
| int *values, int *count, int flags); |
| |
| /** Interrupt handler. Copy from buffer to FIFO. |
| */ |
| static irqreturn_t me4600_ao_isr(int irq, void *dev_id |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) |
| , struct pt_regs *regs |
| #endif |
| ); |
| /** Copy data from circular buffer to fifo (fast) in wraparound mode. |
| */ |
| int inline ao_write_data_wraparound(me4600_ao_subdevice_t * instance, int count, |
| int start_pos); |
| |
| /** Copy data from circular buffer to fifo (fast). |
| */ |
| int inline ao_write_data(me4600_ao_subdevice_t * instance, int count, |
| int start_pos); |
| |
| /** Copy data from circular buffer to fifo (slow). |
| */ |
| int inline ao_write_data_pooling(me4600_ao_subdevice_t * instance, int count, |
| int start_pos); |
| |
| /** Copy data from user space to circular buffer. |
| */ |
| int inline ao_get_data_from_user(me4600_ao_subdevice_t * instance, int count, |
| int *user_values); |
| |
| /** Stop presentation. Preserve FIFOs. |
| */ |
| int inline ao_stop_immediately(me4600_ao_subdevice_t * instance); |
| |
| /** Task for asynchronical state verifying. |
| */ |
| static void me4600_ao_work_control_task( |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) |
| void *subdevice |
| #else |
| struct work_struct *work |
| #endif |
| ); |
| /* Functions |
| */ |
| |
| static int me4600_ao_io_reset_subdevice(me_subdevice_t * subdevice, |
| struct file *filep, int flags) |
| { |
| me4600_ao_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| uint32_t tmp; |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| PDEBUG("executed. idx=%d\n", instance->ao_idx); |
| |
| if (flags) { |
| PERROR("Invalid flag specified.\n"); |
| return ME_ERRNO_INVALID_FLAGS; |
| } |
| |
| ME_SUBDEVICE_ENTER; |
| |
| instance->status = ao_status_none; |
| instance->ao_control_task_flag = 0; |
| cancel_delayed_work(&instance->ao_control_task); |
| instance->timeout.delay = 0; |
| instance->timeout.start_time = jiffies; |
| |
| //Stop state machine. |
| err = ao_stop_immediately(instance); |
| |
| //Remove from synchronous start. |
| spin_lock(instance->preload_reg_lock); |
| tmp = inl(instance->preload_reg); |
| tmp &= |
| ~((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance-> |
| ao_idx); |
| outl(tmp, instance->preload_reg); |
| PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->preload_reg - instance->reg_base, tmp); |
| *instance->preload_flags &= |
| ~((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance-> |
| ao_idx); |
| spin_unlock(instance->preload_reg_lock); |
| |
| //Set single mode, dissable FIFO, dissable external trigger, set output to analog, block interrupt. |
| outl(ME4600_AO_MODE_SINGLE | ME4600_AO_CTRL_BIT_STOP | |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP | ME4600_AO_CTRL_BIT_RESET_IRQ, |
| instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, |
| ME4600_AO_MODE_SINGLE | ME4600_AO_CTRL_BIT_STOP | |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP | |
| ME4600_AO_CTRL_BIT_RESET_IRQ); |
| |
| //Set output to 0V |
| outl(0x8000, instance->single_reg); |
| PDEBUG_REG("single_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->single_reg - instance->reg_base, 0x8000); |
| |
| instance->circ_buf.head = 0; |
| instance->circ_buf.tail = 0; |
| instance->preloaded_count = 0; |
| instance->data_count = 0; |
| instance->single_value = 0x8000; |
| instance->single_value_in_fifo = 0x8000; |
| |
| //Set status to signal that device is unconfigured. |
| instance->status = ao_status_none; |
| |
| //Signal reset if user is on wait. |
| wake_up_interruptible_all(&instance->wait_queue); |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| static int me4600_ao_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_ao_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| uint32_t ctrl; |
| uint32_t sync; |
| unsigned long cpu_flags; |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| PDEBUG("executed. idx=%d\n", instance->ao_idx); |
| |
| // Checking parameters |
| if (flags) { |
| PERROR |
| ("Invalid flag specified. Must be ME_IO_SINGLE_CONFIG_NO_FLAGS.\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_DIGITAL: |
| switch (trig_edge) { |
| case ME_TRIG_EDGE_ANY: |
| case ME_TRIG_EDGE_RISING: |
| case ME_TRIG_EDGE_FALLING: |
| break; |
| |
| default: |
| PERROR("Invalid trigger edge.\n"); |
| return ME_ERRNO_INVALID_TRIG_EDGE; |
| } |
| break; |
| |
| default: |
| PERROR |
| ("Invalid trigger type. Trigger must be software or digital.\n"); |
| return ME_ERRNO_INVALID_TRIG_TYPE; |
| } |
| |
| if ((trig_chan != ME_TRIG_CHAN_DEFAULT) |
| && (trig_chan != ME_TRIG_CHAN_SYNCHRONOUS)) { |
| PERROR("Invalid trigger channel specified.\n"); |
| return ME_ERRNO_INVALID_TRIG_CHAN; |
| } |
| |
| if (ref != ME_REF_AO_GROUND) { |
| PERROR |
| ("Invalid reference. Analog outputs have to have got REF_AO_GROUND.\n"); |
| return ME_ERRNO_INVALID_REF; |
| } |
| |
| if (single_config != 0) { |
| PERROR |
| ("Invalid single config specified. Only one range for anlog outputs is available.\n"); |
| return ME_ERRNO_INVALID_SINGLE_CONFIG; |
| } |
| |
| if (channel != 0) { |
| PERROR |
| ("Invalid channel number specified. Analog output have only one channel.\n"); |
| return ME_ERRNO_INVALID_CHANNEL; |
| } |
| |
| ME_SUBDEVICE_ENTER; |
| |
| //Subdevice running in stream mode! |
| if ((instance->status >= ao_status_stream_run_wait) |
| && (instance->status < ao_status_stream_end)) { |
| PERROR("Subdevice is busy.\n"); |
| ME_SUBDEVICE_EXIT; |
| |
| return ME_ERRNO_SUBDEVICE_BUSY; |
| } |
| /// @note For single all calls (config and write) are erasing previous state! |
| |
| instance->status = ao_status_none; |
| |
| // Correct single mirrors |
| instance->single_value_in_fifo = instance->single_value; |
| |
| //Stop device |
| err = ao_stop_immediately(instance); |
| if (err) { |
| PERROR_CRITICAL("FSM IS BUSY!\n"); |
| ME_SUBDEVICE_EXIT; |
| |
| return ME_ERRNO_SUBDEVICE_BUSY; |
| } |
| // Set control register. |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| // Set stop bit. Stop streaming mode. |
| ctrl = inl(instance->ctrl_reg); |
| //Reset all bits. |
| ctrl = ME4600_AO_CTRL_BIT_IMMEDIATE_STOP | ME4600_AO_CTRL_BIT_STOP; |
| |
| if (trig_type == ME_TRIG_TYPE_EXT_DIGITAL) { |
| PINFO("External digital trigger.\n"); |
| |
| if (trig_edge == ME_TRIG_EDGE_ANY) { |
| // ctrl |= ME4600_AO_CTRL_BIT_EX_TRIG_EDGE | ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH; |
| instance->ctrl_trg = |
| ME4600_AO_CTRL_BIT_EX_TRIG_EDGE | |
| ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH; |
| } else if (trig_edge == ME_TRIG_EDGE_FALLING) { |
| // ctrl |= ME4600_AO_CTRL_BIT_EX_TRIG_EDGE; |
| instance->ctrl_trg = ME4600_AO_CTRL_BIT_EX_TRIG_EDGE; |
| } else if (trig_edge == ME_TRIG_EDGE_RISING) { |
| instance->ctrl_trg = 0x0; |
| } |
| } else if (trig_type == ME_TRIG_TYPE_SW) { |
| PDEBUG("Software trigger\n"); |
| instance->ctrl_trg = 0x0; |
| } |
| |
| 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->subdevice_lock, cpu_flags); |
| |
| // Set preload/synchronization register. |
| spin_lock(instance->preload_reg_lock); |
| if (trig_type == ME_TRIG_TYPE_SW) { |
| *instance->preload_flags &= |
| ~(ME4600_AO_SYNC_EXT_TRIG << instance->ao_idx); |
| } else //if (trig_type == ME_TRIG_TYPE_EXT_DIGITAL) |
| { |
| *instance->preload_flags |= |
| ME4600_AO_SYNC_EXT_TRIG << instance->ao_idx; |
| } |
| |
| if (trig_chan == ME_TRIG_CHAN_DEFAULT) { |
| *instance->preload_flags &= |
| ~(ME4600_AO_SYNC_HOLD << instance->ao_idx); |
| } else //if (trig_chan == ME_TRIG_CHAN_SYNCHRONOUS) |
| { |
| *instance->preload_flags |= |
| ME4600_AO_SYNC_HOLD << instance->ao_idx; |
| } |
| |
| //Reset hardware register |
| sync = inl(instance->preload_reg); |
| PDEBUG_REG("preload_reg inl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->preload_reg - instance->reg_base, sync); |
| sync &= ~(ME4600_AO_SYNC_EXT_TRIG << instance->ao_idx); |
| sync |= ME4600_AO_SYNC_HOLD << instance->ao_idx; |
| |
| //Output configured in default (safe) mode. |
| outl(sync, instance->preload_reg); |
| PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->preload_reg - instance->reg_base, sync); |
| spin_unlock(instance->preload_reg_lock); |
| |
| instance->status = ao_status_single_configured; |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| static int me4600_ao_io_single_read(me_subdevice_t * subdevice, |
| struct file *filep, |
| int channel, |
| int *value, int time_out, int flags) |
| { |
| me4600_ao_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| |
| unsigned long j; |
| unsigned long delay = 0; |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| PDEBUG("executed. idx=%d\n", instance->ao_idx); |
| |
| if (flags & ~ME_IO_SINGLE_NONBLOCKING) { |
| PERROR("Invalid flag specified. %d\n", flags); |
| return ME_ERRNO_INVALID_FLAGS; |
| } |
| |
| if (time_out < 0) { |
| PERROR("Invalid timeout specified.\n"); |
| return ME_ERRNO_INVALID_TIMEOUT; |
| } |
| |
| if (channel != 0) { |
| PERROR("Invalid channel number specified.\n"); |
| return ME_ERRNO_INVALID_CHANNEL; |
| } |
| |
| if ((instance->status >= ao_status_stream_configured) |
| && (instance->status <= ao_status_stream_end)) { |
| PERROR("Subdevice not configured to work in single mode!\n"); |
| return ME_ERRNO_PREVIOUS_CONFIG; |
| } |
| |
| ME_SUBDEVICE_ENTER; |
| if ((!flags) && (instance->status == ao_status_single_run_wait)) { //Blocking mode. Wait for trigger. |
| if (time_out) { |
| delay = (time_out * HZ) / 1000; |
| if (delay == 0) |
| delay = 1; |
| } |
| |
| j = jiffies; |
| |
| //Only runing process will interrupt this call. Events are signaled when status change. This procedure has own timeout. |
| wait_event_interruptible_timeout(instance->wait_queue, |
| (instance->status != |
| ao_status_single_run_wait), |
| (delay) ? delay + |
| 1 : LONG_MAX); |
| |
| if (instance->status == ao_status_none) { |
| PDEBUG("Single canceled.\n"); |
| err = ME_ERRNO_CANCELLED; |
| } |
| |
| if (signal_pending(current)) { |
| PERROR("Wait on start of state machine interrupted.\n"); |
| instance->status = ao_status_none; |
| ao_stop_immediately(instance); |
| err = ME_ERRNO_SIGNAL; |
| } |
| |
| if ((delay) && ((jiffies - j) >= delay)) { |
| |
| PDEBUG("Timeout reached.\n"); |
| err = ME_ERRNO_TIMEOUT; |
| } |
| |
| *value = |
| (!err) ? instance->single_value_in_fifo : instance-> |
| single_value; |
| } else { //Non-blocking mode |
| //Read value |
| *value = instance->single_value; |
| } |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| static int me4600_ao_io_single_write(me_subdevice_t * subdevice, |
| struct file *filep, |
| int channel, |
| int value, int time_out, int flags) |
| { |
| me4600_ao_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| unsigned long cpu_flags; |
| unsigned long j; |
| unsigned long delay = 0x0; |
| |
| //Registry handling variables. |
| uint32_t sync_mask; |
| uint32_t mode; |
| uint32_t tmp; |
| uint32_t ctrl; |
| uint32_t status; |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| PDEBUG("executed. idx=%d\n", instance->ao_idx); |
| |
| if (flags & |
| ~(ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS | |
| ME_IO_SINGLE_TYPE_WRITE_NONBLOCKING)) { |
| PERROR("Invalid flag specified.\n"); |
| return ME_ERRNO_INVALID_FLAGS; |
| } |
| |
| if (time_out < 0) { |
| PERROR("Invalid timeout specified.\n"); |
| return ME_ERRNO_INVALID_TIMEOUT; |
| } |
| |
| if (value & ~ME4600_AO_MAX_DATA) { |
| PERROR("Invalid value provided.\n"); |
| return ME_ERRNO_VALUE_OUT_OF_RANGE; |
| } |
| |
| if (channel != 0) { |
| PERROR("Invalid channel number specified.\n"); |
| return ME_ERRNO_INVALID_CHANNEL; |
| } |
| |
| if ((instance->status == ao_status_none) |
| || (instance->status > ao_status_single_end)) { |
| PERROR("Subdevice not configured to work in single mode!\n"); |
| return ME_ERRNO_PREVIOUS_CONFIG; |
| } |
| |
| ME_SUBDEVICE_ENTER; |
| |
| /// @note For single all calls (config and write) are erasing previous state! |
| |
| //Cancel control task |
| PDEBUG("Cancel control task. idx=%d\n", instance->ao_idx); |
| instance->ao_control_task_flag = 0; |
| cancel_delayed_work(&instance->ao_control_task); |
| |
| // Correct single mirrors |
| instance->single_value_in_fifo = instance->single_value; |
| |
| //Stop device |
| err = ao_stop_immediately(instance); |
| if (err) { |
| PERROR_CRITICAL("FSM IS BUSY!\n"); |
| ME_SUBDEVICE_EXIT; |
| |
| return ME_ERRNO_SUBDEVICE_BUSY; |
| } |
| |
| if (time_out) { |
| delay = (time_out * HZ) / 1000; |
| |
| if (delay == 0) |
| delay = 1; |
| } |
| |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| |
| instance->single_value_in_fifo = value; |
| |
| ctrl = inl(instance->ctrl_reg); |
| |
| if (!instance->fifo) { //No FIFO |
| //Set the single mode. |
| ctrl &= ~ME4600_AO_CTRL_MODE_MASK; |
| |
| //Write value |
| PDEBUG("Write value\n"); |
| outl(value, instance->single_reg); |
| PDEBUG_REG("single_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->single_reg - instance->reg_base, value); |
| } else { // mix-mode |
| //Set speed |
| outl(ME4600_AO_MIN_CHAN_TICKS - 1, instance->timer_reg); |
| PDEBUG_REG("timer_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->timer_reg - instance->reg_base, |
| (int)ME4600_AO_MIN_CHAN_TICKS); |
| instance->hardware_stop_delay = HZ / 10; //100ms |
| |
| status = inl(instance->status_reg); |
| |
| //Set the continous mode. |
| ctrl &= ~ME4600_AO_CTRL_MODE_MASK; |
| ctrl |= ME4600_AO_MODE_CONTINUOUS; |
| |
| //Prepare FIFO |
| if (!(ctrl & ME4600_AO_CTRL_BIT_ENABLE_FIFO)) { //FIFO wasn't enabeled. Do it. |
| PINFO("Enableing FIFO.\n"); |
| ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| ctrl |= |
| ME4600_AO_CTRL_BIT_ENABLE_FIFO | |
| ME4600_AO_CTRL_BIT_RESET_IRQ; |
| } else { //Check if FIFO is empty |
| if (status & ME4600_AO_STATUS_BIT_EF) { //FIFO not empty |
| PINFO("Reseting FIFO.\n"); |
| ctrl &= |
| ~(ME4600_AO_CTRL_BIT_ENABLE_FIFO | |
| ME4600_AO_CTRL_BIT_ENABLE_IRQ); |
| ctrl |= ME4600_AO_CTRL_BIT_RESET_IRQ; |
| 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); |
| |
| ctrl |= |
| ME4600_AO_CTRL_BIT_ENABLE_FIFO | |
| ME4600_AO_CTRL_BIT_RESET_IRQ; |
| } else { //FIFO empty, only interrupt needs to be disabled! |
| ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| ctrl |= ME4600_AO_CTRL_BIT_RESET_IRQ; |
| } |
| } |
| |
| 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); |
| |
| //Write output - 1 value to FIFO |
| if (instance->ao_idx & 0x1) { |
| outl(value <<= 16, instance->fifo_reg); |
| PDEBUG_REG("fifo_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->fifo_reg - instance->reg_base, |
| value <<= 16); |
| } else { |
| outl(value, instance->fifo_reg); |
| PDEBUG_REG("fifo_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->fifo_reg - instance->reg_base, |
| value); |
| } |
| } |
| |
| mode = *instance->preload_flags >> instance->ao_idx; |
| mode &= (ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG); |
| |
| PINFO("Triggering mode: 0x%x\n", mode); |
| |
| spin_lock(instance->preload_reg_lock); |
| sync_mask = inl(instance->preload_reg); |
| PDEBUG_REG("preload_reg inl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->preload_reg - instance->reg_base, sync_mask); |
| switch (mode) { |
| case 0: //Individual software |
| ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG; |
| |
| if (!instance->fifo) { // No FIFO - In this case resetting 'ME4600_AO_SYNC_HOLD' will trigger output. |
| if ((sync_mask & ((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->ao_idx)) != ME4600_AO_SYNC_HOLD) { //Now we can set correct mode. This is exception. It is set to synchronous and triggered later. |
| sync_mask &= |
| ~(ME4600_AO_SYNC_EXT_TRIG << instance-> |
| ao_idx); |
| sync_mask |= |
| ME4600_AO_SYNC_HOLD << instance->ao_idx; |
| |
| outl(sync_mask, instance->preload_reg); |
| PDEBUG_REG |
| ("preload_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->preload_reg - instance->reg_base, |
| sync_mask); |
| } |
| } else { // FIFO |
| if ((sync_mask & ((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->ao_idx)) != 0x0) { //Now we can set correct mode. |
| sync_mask &= |
| ~((ME4600_AO_SYNC_EXT_TRIG | |
| ME4600_AO_SYNC_HOLD) << instance-> |
| ao_idx); |
| |
| outl(sync_mask, instance->preload_reg); |
| PDEBUG_REG |
| ("preload_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->preload_reg - instance->reg_base, |
| sync_mask); |
| } |
| } |
| instance->single_value = value; |
| break; |
| |
| case ME4600_AO_SYNC_EXT_TRIG: //Individual hardware |
| ctrl |= ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG; |
| |
| if (!instance->fifo) { // No FIFO - In this case resetting 'ME4600_AO_SYNC_HOLD' will trigger output. |
| if ((sync_mask & ((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->ao_idx)) != ME4600_AO_SYNC_HOLD) { //Now we can set correct mode |
| sync_mask &= |
| ~(ME4600_AO_SYNC_EXT_TRIG << instance-> |
| ao_idx); |
| sync_mask |= |
| ME4600_AO_SYNC_HOLD << instance->ao_idx; |
| |
| outl(sync_mask, instance->preload_reg); |
| PDEBUG_REG |
| ("preload_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->preload_reg - instance->reg_base, |
| sync_mask); |
| } |
| } else { // FIFO |
| if ((sync_mask & ((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->ao_idx)) != 0x0) { //Now we can set correct mode. |
| sync_mask &= |
| ~((ME4600_AO_SYNC_EXT_TRIG | |
| ME4600_AO_SYNC_HOLD) << instance-> |
| ao_idx); |
| |
| outl(sync_mask, instance->preload_reg); |
| PDEBUG_REG |
| ("preload_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->preload_reg - instance->reg_base, |
| sync_mask); |
| } |
| } |
| break; |
| |
| case ME4600_AO_SYNC_HOLD: //Synchronous software |
| ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG; |
| |
| // if((sync_mask & ((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->ao_idx)) != ME4600_AO_SYNC_HOLD) |
| if ((sync_mask & ((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->ao_idx)) != (ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG)) { //Now we can set correct mode |
| sync_mask |= |
| ME4600_AO_SYNC_EXT_TRIG << instance->ao_idx; |
| // sync_mask &= ~(ME4600_AO_SYNC_EXT_TRIG << instance->ao_idx); |
| sync_mask |= ME4600_AO_SYNC_HOLD << instance->ao_idx; |
| |
| outl(sync_mask, instance->preload_reg); |
| PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->preload_reg - instance->reg_base, |
| sync_mask); |
| } |
| break; |
| |
| case (ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG): //Synchronous hardware |
| ctrl |= ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG; |
| if ((sync_mask & ((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->ao_idx)) != (ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG)) { //Now we can set correct mode |
| sync_mask |= |
| (ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << |
| instance->ao_idx; |
| |
| outl(sync_mask, instance->preload_reg); |
| PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->preload_reg - instance->reg_base, |
| sync_mask); |
| } |
| break; |
| } |
| // spin_unlock(instance->preload_reg_lock); // Moved down. |
| |
| //Activate ISM (remove 'stop' bits) |
| ctrl &= |
| ~(ME4600_AO_CTRL_BIT_EX_TRIG_EDGE | |
| ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH); |
| ctrl |= instance->ctrl_trg; |
| ctrl &= ~(ME4600_AO_CTRL_BIT_STOP | ME4600_AO_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->subdevice_lock, cpu_flags); |
| |
| /// @note When flag 'ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS' is set than output is triggered. ALWAYS! |
| |
| if (!instance->fifo) { //No FIFO |
| if (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) { //Fired all software synchronous outputs. |
| tmp = ~(*instance->preload_flags | 0xFFFF0000); |
| PINFO |
| ("Fired all software synchronous outputs. mask:0x%08x\n", |
| tmp); |
| tmp |= sync_mask & 0xFFFF0000; |
| // Add this channel to list |
| tmp &= ~(ME4600_AO_SYNC_HOLD << instance->ao_idx); |
| |
| //Fire |
| PINFO("Software trigger.\n"); |
| outl(tmp, instance->preload_reg); |
| PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->preload_reg - instance->reg_base, |
| tmp); |
| |
| //Restore save settings |
| outl(sync_mask, instance->preload_reg); |
| PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->preload_reg - instance->reg_base, |
| sync_mask); |
| } else if (!mode) { // Add this channel to list |
| outl(sync_mask & |
| ~(ME4600_AO_SYNC_HOLD << instance->ao_idx), |
| instance->preload_reg); |
| PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->preload_reg - instance->reg_base, |
| sync_mask & ~(ME4600_AO_SYNC_HOLD << |
| instance->ao_idx)); |
| |
| //Fire |
| PINFO("Software trigger.\n"); |
| |
| //Restore save settings |
| outl(sync_mask, instance->preload_reg); |
| PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->preload_reg - instance->reg_base, |
| sync_mask); |
| } |
| |
| } else { // mix-mode - begin |
| if (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) { //Trigger outputs |
| //Add channel to start list |
| outl(sync_mask | |
| (ME4600_AO_SYNC_HOLD << instance->ao_idx), |
| instance->preload_reg); |
| PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->preload_reg - instance->reg_base, |
| sync_mask | (ME4600_AO_SYNC_HOLD << |
| instance->ao_idx)); |
| |
| //Fire |
| PINFO |
| ("Fired all software synchronous outputs by software trigger.\n"); |
| outl(0x8000, instance->single_reg); |
| PDEBUG_REG("single_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->single_reg - instance->reg_base, |
| 0x8000); |
| |
| //Restore save settings |
| outl(sync_mask, instance->preload_reg); |
| PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->preload_reg - instance->reg_base, |
| sync_mask); |
| } else if (!mode) { //Trigger outputs |
| /* //Remove channel from start list //<== Unnecessary. Removed. |
| outl(sync_mask & ~(ME4600_AO_SYNC_HOLD << instance->ao_idx), instance->preload_reg); |
| PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, instance->preload_reg - instance->reg_base, tmp); |
| */ |
| //Fire |
| PINFO("Software trigger.\n"); |
| outl(0x8000, instance->single_reg); |
| PDEBUG_REG("single_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->single_reg - instance->reg_base, |
| 0x8000); |
| |
| /* //Restore save settings //<== Unnecessary. Removed. |
| outl(sync_mask, instance->preload_reg); |
| PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, instance->preload_reg - instance->reg_base, sync_mask); |
| */ |
| } |
| } |
| spin_unlock(instance->preload_reg_lock); |
| |
| j = jiffies; |
| instance->status = ao_status_single_run_wait; |
| |
| instance->timeout.delay = delay; |
| instance->timeout.start_time = j; |
| instance->ao_control_task_flag = 1; |
| queue_delayed_work(instance->me4600_workqueue, |
| &instance->ao_control_task, 1); |
| |
| if (!(flags & ME_IO_SINGLE_TYPE_WRITE_NONBLOCKING)) { |
| |
| //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 != |
| ao_status_single_run_wait), |
| (delay) ? delay + |
| 1 : LONG_MAX); |
| |
| if (((!delay) || ((jiffies - j) <= delay)) |
| && (instance->status != ao_status_single_end)) { |
| PDEBUG("Single canceled.\n"); |
| err = ME_ERRNO_CANCELLED; |
| } |
| |
| if (signal_pending(current)) { |
| PERROR("Wait on start of state machine interrupted.\n"); |
| instance->ao_control_task_flag = 0; |
| cancel_delayed_work(&instance->ao_control_task); |
| ao_stop_immediately(instance); |
| instance->status = ao_status_none; |
| err = ME_ERRNO_SIGNAL; |
| } |
| |
| if ((delay) && ((jiffies - j) >= delay)) { |
| if (instance->status == ao_status_single_end) { |
| PDEBUG("Timeout reached.\n"); |
| } else { |
| if ((jiffies - j) > delay) { |
| PERROR |
| ("Timeout reached. Not handled by control task!\n"); |
| } else { |
| PERROR |
| ("Timeout reached. Signal come but status is strange: %d\n", |
| instance->status); |
| } |
| |
| ao_stop_immediately(instance); |
| } |
| |
| instance->ao_control_task_flag = 0; |
| cancel_delayed_work(&instance->ao_control_task); |
| instance->status = ao_status_single_end; |
| err = ME_ERRNO_TIMEOUT; |
| } |
| } |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| static int me4600_ao_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_ao_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| uint32_t ctrl; |
| unsigned long cpu_flags; |
| uint64_t conv_ticks; |
| unsigned int conv_start_ticks_low = trigger->iConvStartTicksLow; |
| unsigned int conv_start_ticks_high = trigger->iConvStartTicksHigh; |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| PDEBUG("executed. idx=%d\n", instance->ao_idx); |
| |
| if (!instance->fifo) { |
| PERROR("Not a streaming ao.\n"); |
| return ME_ERRNO_NOT_SUPPORTED; |
| } |
| |
| conv_ticks = |
| (uint64_t) conv_start_ticks_low + |
| ((uint64_t) conv_start_ticks_high << 32); |
| |
| if (flags & |
| ~(ME_IO_STREAM_CONFIG_HARDWARE_ONLY | ME_IO_STREAM_CONFIG_WRAPAROUND |
| | ME_IO_STREAM_CONFIG_BIT_PATTERN)) { |
| PERROR("Invalid flags.\n"); |
| return ME_ERRNO_INVALID_FLAGS; |
| } |
| |
| if (flags & ME_IO_STREAM_CONFIG_HARDWARE_ONLY) { |
| if (!flags & ME_IO_STREAM_CONFIG_WRAPAROUND) { |
| PERROR |
| ("Hardware ME_IO_STREAM_CONFIG_HARDWARE_ONLY has to be with ME_IO_STREAM_CONFIG_WRAPAROUND.\n"); |
| return ME_ERRNO_INVALID_FLAGS; |
| } |
| |
| if ((trigger->iAcqStopTrigType != ME_TRIG_TYPE_NONE) |
| || (trigger->iScanStopTrigType != ME_TRIG_TYPE_NONE)) { |
| PERROR |
| ("Hardware wraparound mode must be in infinite mode.\n"); |
| return ME_ERRNO_INVALID_FLAGS; |
| } |
| } |
| |
| if (count != 1) { |
| PERROR("Only 1 entry in config list acceptable.\n"); |
| return ME_ERRNO_INVALID_CONFIG_LIST_COUNT; |
| } |
| |
| if (config_list[0].iChannel != 0) { |
| PERROR("Invalid channel number specified.\n"); |
| return ME_ERRNO_INVALID_CHANNEL; |
| } |
| |
| if (config_list[0].iStreamConfig != 0) { |
| PERROR("Only one range available.\n"); |
| return ME_ERRNO_INVALID_STREAM_CONFIG; |
| } |
| |
| if (config_list[0].iRef != ME_REF_AO_GROUND) { |
| PERROR("Output is referenced to ground.\n"); |
| return ME_ERRNO_INVALID_REF; |
| } |
| |
| if ((trigger->iAcqStartTicksLow != 0) |
| || (trigger->iAcqStartTicksHigh != 0)) { |
| PERROR |
| ("Invalid acquisition start trigger argument specified.\n"); |
| return ME_ERRNO_INVALID_ACQ_START_ARG; |
| } |
| |
| if (config_list[0].iFlags) { |
| PERROR("Invalid config list flag.\n"); |
| return ME_ERRNO_INVALID_FLAGS; |
| } |
| |
| switch (trigger->iAcqStartTrigType) { |
| case ME_TRIG_TYPE_SW: |
| if (trigger->iAcqStartTrigEdge != ME_TRIG_EDGE_NONE) { |
| PERROR |
| ("Invalid acquisition start trigger edge specified.\n"); |
| return ME_ERRNO_INVALID_ACQ_START_TRIG_EDGE; |
| } |
| break; |
| |
| case ME_TRIG_TYPE_EXT_DIGITAL: |
| switch (trigger->iAcqStartTrigEdge) { |
| case ME_TRIG_EDGE_ANY: |
| case ME_TRIG_EDGE_RISING: |
| case ME_TRIG_EDGE_FALLING: |
| break; |
| |
| default: |
| PERROR |
| ("Invalid acquisition start trigger edge specified.\n"); |
| return ME_ERRNO_INVALID_ACQ_START_TRIG_EDGE; |
| } |
| break; |
| |
| default: |
| PERROR("Invalid acquisition start trigger type specified.\n"); |
| return ME_ERRNO_INVALID_ACQ_START_TRIG_TYPE; |
| } |
| |
| if (trigger->iScanStartTrigType != ME_TRIG_TYPE_FOLLOW) { |
| PERROR("Invalid scan start trigger type specified.\n"); |
| return ME_ERRNO_INVALID_SCAN_START_TRIG_TYPE; |
| } |
| |
| if (trigger->iConvStartTrigType != ME_TRIG_TYPE_TIMER) { |
| PERROR("Invalid conv start trigger type specified.\n"); |
| return ME_ERRNO_INVALID_CONV_START_TRIG_TYPE; |
| } |
| |
| if ((conv_ticks < ME4600_AO_MIN_CHAN_TICKS) |
| || (conv_ticks > ME4600_AO_MAX_CHAN_TICKS)) { |
| PERROR("Invalid conv start trigger argument specified.\n"); |
| return ME_ERRNO_INVALID_CONV_START_ARG; |
| } |
| |
| if (trigger->iAcqStartTicksLow || trigger->iAcqStartTicksHigh) { |
| PERROR("Invalid acq start trigger argument specified.\n"); |
| return ME_ERRNO_INVALID_ACQ_START_ARG; |
| } |
| |
| if (trigger->iScanStartTicksLow || trigger->iScanStartTicksHigh) { |
| PERROR("Invalid scan start trigger argument specified.\n"); |
| return ME_ERRNO_INVALID_SCAN_START_ARG; |
| } |
| |
| switch (trigger->iScanStopTrigType) { |
| case ME_TRIG_TYPE_NONE: |
| if (trigger->iScanStopCount != 0) { |
| PERROR("Invalid scan stop count specified.\n"); |
| return ME_ERRNO_INVALID_SCAN_STOP_ARG; |
| } |
| break; |
| |
| case ME_TRIG_TYPE_COUNT: |
| if (flags & ME_IO_STREAM_CONFIG_WRAPAROUND) { |
| if (trigger->iScanStopCount <= 0) { |
| PERROR("Invalid scan stop count specified.\n"); |
| return ME_ERRNO_INVALID_SCAN_STOP_ARG; |
| } |
| } else { |
| PERROR("The continous mode has not 'scan' contects.\n"); |
| return ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE; |
| } |
| break; |
| |
| default: |
| PERROR("Invalid scan stop trigger type specified.\n"); |
| return ME_ERRNO_INVALID_SCAN_STOP_TRIG_TYPE; |
| } |
| |
| switch (trigger->iAcqStopTrigType) { |
| case ME_TRIG_TYPE_NONE: |
| if (trigger->iAcqStopCount != 0) { |
| PERROR("Invalid acq stop count specified.\n"); |
| return ME_ERRNO_INVALID_ACQ_STOP_ARG; |
| } |
| break; |
| |
| case ME_TRIG_TYPE_COUNT: |
| if (trigger->iScanStopTrigType != ME_TRIG_TYPE_NONE) { |
| PERROR("Invalid acq stop trigger type specified.\n"); |
| return ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE; |
| } |
| |
| if (flags & ME_IO_STREAM_CONFIG_WRAPAROUND) { |
| if (trigger->iAcqStopCount <= 0) { |
| PERROR |
| ("The continous mode has not 'scan' contects.\n"); |
| return ME_ERRNO_INVALID_ACQ_STOP_ARG; |
| } |
| } |
| break; |
| |
| default: |
| PERROR("Invalid acq stop trigger type specified.\n"); |
| return ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE; |
| } |
| |
| switch (trigger->iAcqStartTrigChan) { |
| case ME_TRIG_CHAN_DEFAULT: |
| case ME_TRIG_CHAN_SYNCHRONOUS: |
| break; |
| |
| default: |
| PERROR("Invalid acq start trigger channel specified.\n"); |
| return ME_ERRNO_INVALID_ACQ_START_TRIG_CHAN; |
| } |
| |
| ME_SUBDEVICE_ENTER; |
| |
| if ((flags & ME_IO_STREAM_CONFIG_BIT_PATTERN) && !instance->bitpattern) { |
| PERROR("This subdevice not support output redirection.\n"); |
| ME_SUBDEVICE_EXIT; |
| return ME_ERRNO_INVALID_FLAGS; |
| } |
| //Stop device |
| |
| //Cancel control task |
| PDEBUG("Cancel control task. idx=%d\n", instance->ao_idx); |
| instance->ao_control_task_flag = 0; |
| cancel_delayed_work(&instance->ao_control_task); |
| |
| //Check if state machine is stopped. |
| err = ao_stop_immediately(instance); |
| if (err) { |
| PERROR_CRITICAL("FSM IS BUSY!\n"); |
| ME_SUBDEVICE_EXIT; |
| |
| return ME_ERRNO_SUBDEVICE_BUSY; |
| } |
| |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| //Reset control register. Block all actions. Disable IRQ. Disable FIFO. |
| ctrl = |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP | ME4600_AO_CTRL_BIT_STOP | |
| ME4600_AO_CTRL_BIT_RESET_IRQ; |
| 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); |
| |
| //This is paranoic, but to be sure. |
| instance->preloaded_count = 0; |
| instance->data_count = 0; |
| instance->circ_buf.head = 0; |
| instance->circ_buf.tail = 0; |
| |
| /* Set mode. */ |
| if (flags & ME_IO_STREAM_CONFIG_WRAPAROUND) { //Wraparound |
| if (flags & ME_IO_STREAM_CONFIG_HARDWARE_ONLY) { //Hardware wraparound |
| PINFO("Hardware wraparound.\n"); |
| ctrl |= ME4600_AO_MODE_WRAPAROUND; |
| instance->mode = ME4600_AO_HW_WRAP_MODE; |
| } else { //Software wraparound |
| PINFO("Software wraparound.\n"); |
| ctrl |= ME4600_AO_MODE_CONTINUOUS; |
| instance->mode = ME4600_AO_SW_WRAP_MODE; |
| } |
| } else { //Continous |
| PINFO("Continous.\n"); |
| ctrl |= ME4600_AO_MODE_CONTINUOUS; |
| instance->mode = ME4600_AO_CONTINOUS; |
| } |
| |
| //Set the trigger edge. |
| if (trigger->iAcqStartTrigType == ME_TRIG_TYPE_EXT_DIGITAL) { //Set the trigger type and edge for external trigger. |
| PINFO("External digital trigger.\n"); |
| instance->start_mode = ME4600_AO_EXT_TRIG; |
| /* |
| ctrl |= ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG; |
| */ |
| switch (trigger->iAcqStartTrigEdge) { |
| case ME_TRIG_EDGE_RISING: |
| PINFO("Set the trigger edge: rising.\n"); |
| instance->ctrl_trg = 0x0; |
| break; |
| |
| case ME_TRIG_EDGE_FALLING: |
| PINFO("Set the trigger edge: falling.\n"); |
| // ctrl |= ME4600_AO_CTRL_BIT_EX_TRIG_EDGE; |
| instance->ctrl_trg = ME4600_AO_CTRL_BIT_EX_TRIG_EDGE; |
| break; |
| |
| case ME_TRIG_EDGE_ANY: |
| PINFO("Set the trigger edge: both edges.\n"); |
| // ctrl |= ME4600_AO_CTRL_BIT_EX_TRIG_EDGE | ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH; |
| instance->ctrl_trg = |
| ME4600_AO_CTRL_BIT_EX_TRIG_EDGE | |
| ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH; |
| break; |
| } |
| } else { |
| PINFO("Internal software trigger.\n"); |
| instance->start_mode = 0; |
| } |
| |
| //Set the stop mode and value. |
| if (trigger->iAcqStopTrigType == ME_TRIG_TYPE_COUNT) { //Amount of data |
| instance->stop_mode = ME4600_AO_ACQ_STOP_MODE; |
| instance->stop_count = trigger->iAcqStopCount; |
| } else if (trigger->iScanStopTrigType == ME_TRIG_TYPE_COUNT) { //Amount of 'scans' |
| instance->stop_mode = ME4600_AO_SCAN_STOP_MODE; |
| instance->stop_count = trigger->iScanStopCount; |
| } else { //Infinite |
| instance->stop_mode = ME4600_AO_INF_STOP_MODE; |
| instance->stop_count = 0; |
| } |
| |
| PINFO("Stop count: %d.\n", instance->stop_count); |
| |
| if (trigger->iAcqStartTrigChan == ME_TRIG_CHAN_SYNCHRONOUS) { //Synchronous start |
| instance->start_mode |= ME4600_AO_SYNC_HOLD; |
| if (trigger->iAcqStartTrigType == ME_TRIG_TYPE_EXT_DIGITAL) { //Externaly triggered |
| PINFO("Synchronous start. Externaly trigger active.\n"); |
| instance->start_mode |= ME4600_AO_SYNC_EXT_TRIG; |
| } |
| #ifdef MEDEBUG_INFO |
| else { |
| PINFO |
| ("Synchronous start. Externaly trigger dissabled.\n"); |
| } |
| #endif |
| |
| } |
| //Set speed |
| outl(conv_ticks - 2, instance->timer_reg); |
| PDEBUG_REG("timer_reg outl(0x%lX+0x%lX)=0x%llx\n", instance->reg_base, |
| instance->timer_reg - instance->reg_base, conv_ticks - 2); |
| instance->hardware_stop_delay = (int)(conv_ticks * HZ) / ME4600_AO_BASE_FREQUENCY; //<== MUST be with cast! |
| |
| //Conect outputs to analog or digital port. |
| if (flags & ME_IO_STREAM_CONFIG_BIT_PATTERN) { |
| ctrl |= ME4600_AO_CTRL_BIT_ENABLE_DO; |
| } |
| // Write the control word |
| 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); |
| |
| //Set status. |
| instance->status = ao_status_stream_configured; |
| spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags); |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| static int me4600_ao_io_stream_new_values(me_subdevice_t * subdevice, |
| struct file *filep, |
| int time_out, int *count, int flags) |
| { |
| me4600_ao_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| long t = 0; |
| long j; |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| PDEBUG("executed. idx=%d\n", instance->ao_idx); |
| |
| if (!instance->fifo) { |
| PERROR("Not a streaming ao.\n"); |
| return ME_ERRNO_NOT_SUPPORTED; |
| } |
| |
| if (flags) { |
| PERROR("Invalid flag specified.\n"); |
| return ME_ERRNO_INVALID_FLAGS; |
| } |
| |
| if (!instance->circ_buf.buf) { |
| PERROR("Circular buffer not exists.\n"); |
| return ME_ERRNO_INTERNAL; |
| } |
| |
| if (time_out < 0) { |
| PERROR("Invalid time_out specified.\n"); |
| return ME_ERRNO_INVALID_TIMEOUT; |
| } |
| |
| ME_SUBDEVICE_ENTER; |
| |
| if (me_circ_buf_space(&instance->circ_buf)) { //The buffer is NOT full. |
| *count = me_circ_buf_space(&instance->circ_buf); |
| } else { //The buffer is full. |
| if (time_out) { |
| t = (time_out * HZ) / 1000; |
| |
| if (t == 0) |
| t = 1; |
| } else { //Max time. |
| t = LONG_MAX; |
| } |
| |
| *count = 0; |
| |
| j = jiffies; |
| |
| //Only runing process will interrupt this call. Interrupts are when FIFO HF is signaled. |
| wait_event_interruptible_timeout(instance->wait_queue, |
| ((me_circ_buf_space |
| (&instance->circ_buf)) |
| || !(inl(instance->status_reg) |
| & |
| ME4600_AO_STATUS_BIT_FSM)), |
| t); |
| |
| if (!(inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM)) { |
| PERROR("AO subdevice is not running.\n"); |
| err = ME_ERRNO_SUBDEVICE_NOT_RUNNING; |
| } else if (signal_pending(current)) { |
| PERROR("Wait on values interrupted from signal.\n"); |
| instance->status = ao_status_none; |
| ao_stop_immediately(instance); |
| err = ME_ERRNO_SIGNAL; |
| } else if ((jiffies - j) >= t) { |
| PERROR("Wait on values timed out.\n"); |
| err = ME_ERRNO_TIMEOUT; |
| } else { //Uff... all is good. Inform user about empty space. |
| *count = me_circ_buf_space(&instance->circ_buf); |
| } |
| } |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| static int me4600_ao_io_stream_start(me_subdevice_t * subdevice, |
| struct file *filep, |
| int start_mode, int time_out, int flags) |
| { |
| me4600_ao_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| unsigned long cpu_flags = 0; |
| uint32_t status; |
| uint32_t ctrl; |
| uint32_t synch; |
| int count = 0; |
| int circ_buffer_count; |
| |
| unsigned long ref; |
| unsigned long delay = 0; |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| PDEBUG("executed. idx=%d\n", instance->ao_idx); |
| |
| if (!instance->fifo) { |
| PERROR("Not a streaming ao.\n"); |
| return ME_ERRNO_NOT_SUPPORTED; |
| } |
| |
| if (flags & ~ME_IO_STREAM_START_TYPE_TRIG_SYNCHRONOUS) { |
| PERROR("Invalid flags.\n"); |
| return ME_ERRNO_INVALID_FLAGS; |
| } |
| |
| if (time_out < 0) { |
| PERROR("Invalid timeout specified.\n"); |
| return ME_ERRNO_INVALID_TIMEOUT; |
| } |
| |
| 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) { |
| delay = (time_out * HZ) / 1000; |
| if (delay == 0) |
| delay = 1; |
| } |
| |
| switch (instance->status) { //Checking actual mode. |
| case ao_status_stream_configured: |
| case ao_status_stream_end: |
| //Correct modes! |
| break; |
| |
| //The device is in wrong mode. |
| case ao_status_none: |
| case ao_status_single_configured: |
| case ao_status_single_run_wait: |
| case ao_status_single_run: |
| case ao_status_single_end_wait: |
| PERROR |
| ("Subdevice must be preinitialize correctly for streaming.\n"); |
| return ME_ERRNO_PREVIOUS_CONFIG; |
| |
| case ao_status_stream_fifo_error: |
| case ao_status_stream_buffer_error: |
| case ao_status_stream_error: |
| PDEBUG("Before restart broke stream 'STOP' must be caled.\n"); |
| return ME_STATUS_ERROR; |
| |
| case ao_status_stream_run_wait: |
| case ao_status_stream_run: |
| case ao_status_stream_end_wait: |
| PDEBUG("Stream is already working.\n"); |
| return ME_ERRNO_SUBDEVICE_BUSY; |
| |
| default: |
| instance->status = ao_status_stream_error; |
| PERROR_CRITICAL("Status is in wrong state!\n"); |
| return ME_ERRNO_INTERNAL; |
| |
| } |
| |
| ME_SUBDEVICE_ENTER; |
| |
| if (instance->mode == ME4600_AO_CONTINOUS) { //Continous |
| instance->circ_buf.tail += instance->preloaded_count; |
| instance->circ_buf.tail &= instance->circ_buf.mask; |
| } |
| circ_buffer_count = me_circ_buf_values(&instance->circ_buf); |
| |
| if (!circ_buffer_count && !instance->preloaded_count) { //No values in buffer |
| ME_SUBDEVICE_EXIT; |
| PERROR("No values in buffer!\n"); |
| return ME_ERRNO_LACK_OF_RESOURCES; |
| } |
| |
| //Cancel control task |
| PDEBUG("Cancel control task. idx=%d\n", instance->ao_idx); |
| instance->ao_control_task_flag = 0; |
| cancel_delayed_work(&instance->ao_control_task); |
| |
| //Stop device |
| err = ao_stop_immediately(instance); |
| if (err) { |
| PERROR_CRITICAL("FSM IS BUSY!\n"); |
| ME_SUBDEVICE_EXIT; |
| |
| return ME_ERRNO_SUBDEVICE_BUSY; |
| } |
| //Set values for single_read() |
| instance->single_value = ME4600_AO_MAX_DATA + 1; |
| instance->single_value_in_fifo = ME4600_AO_MAX_DATA + 1; |
| |
| //Setting stop points |
| if (instance->stop_mode == ME4600_AO_SCAN_STOP_MODE) { |
| instance->stop_data_count = |
| instance->stop_count * circ_buffer_count; |
| } else { |
| instance->stop_data_count = instance->stop_count; |
| } |
| |
| if ((instance->stop_data_count != 0) |
| && (instance->stop_data_count < circ_buffer_count)) { |
| PERROR("More data in buffer than previously set limit!\n"); |
| } |
| |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| ctrl = inl(instance->ctrl_reg); |
| //Check FIFO |
| if (!(ctrl & ME4600_AO_CTRL_BIT_ENABLE_FIFO)) { //FIFO wasn't enabeled. Do it. <= This should be done by user call with ME_WRITE_MODE_PRELOAD |
| PINFO("Enableing FIFO.\n"); |
| ctrl |= |
| ME4600_AO_CTRL_BIT_ENABLE_FIFO | |
| ME4600_AO_CTRL_BIT_RESET_IRQ; |
| |
| instance->preloaded_count = 0; |
| instance->data_count = 0; |
| } else { //Block IRQ |
| ctrl |= ME4600_AO_CTRL_BIT_RESET_IRQ; |
| } |
| 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 | ME4600_AO_CTRL_BIT_RESET_IRQ); |
| |
| //Fill FIFO <= Generaly this should be done by user pre-load call but this is second place to do it. |
| status = inl(instance->status_reg); |
| if (!(status & ME4600_AO_STATUS_BIT_EF)) { //FIFO empty |
| if (instance->stop_data_count == 0) { |
| count = ME4600_AO_FIFO_COUNT; |
| } else { |
| count = |
| (ME4600_AO_FIFO_COUNT < |
| instance-> |
| stop_data_count) ? ME4600_AO_FIFO_COUNT : |
| instance->stop_data_count; |
| } |
| |
| //Copy data |
| count = |
| ao_write_data(instance, count, instance->preloaded_count); |
| |
| if (count < 0) { //This should never happend! |
| PERROR_CRITICAL("COPY FINISH WITH ERROR!\n"); |
| spin_unlock_irqrestore(&instance->subdevice_lock, |
| cpu_flags); |
| ME_SUBDEVICE_EXIT; |
| return ME_ERRNO_INTERNAL; |
| } |
| } |
| //Set pre-load features. |
| spin_lock(instance->preload_reg_lock); |
| synch = inl(instance->preload_reg); |
| synch &= |
| ~((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance-> |
| ao_idx); |
| synch |= |
| (instance->start_mode & ~ME4600_AO_EXT_TRIG) << instance->ao_idx; |
| outl(synch, instance->preload_reg); |
| PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, |
| instance->preload_reg - instance->reg_base, synch); |
| spin_unlock(instance->preload_reg_lock); |
| |
| //Default count is '0' |
| if (instance->mode == ME4600_AO_CONTINOUS) { //Continous |
| instance->preloaded_count = 0; |
| instance->circ_buf.tail += count; |
| instance->circ_buf.tail &= instance->circ_buf.mask; |
| } else { //Wraparound |
| instance->preloaded_count += count; |
| instance->data_count += count; |
| |
| //Special case: Infinite wraparound with less than FIFO datas always should runs in hardware mode. |
| if ((instance->stop_mode == ME4600_AO_INF_STOP_MODE) |
| && (circ_buffer_count <= ME4600_AO_FIFO_COUNT)) { //Change to hardware wraparound |
| PDEBUG |
| ("Changeing mode from software wraparound to hardware wraparound.\n"); |
| //Copy all data |
| count = |
| ao_write_data(instance, circ_buffer_count, |
| instance->preloaded_count); |
| ctrl &= ~ME4600_AO_CTRL_MODE_MASK; |
| ctrl |= ME4600_AO_MODE_WRAPAROUND; |
| } |
| |
| if (instance->preloaded_count == me_circ_buf_values(&instance->circ_buf)) { //Reset position indicator. |
| instance->preloaded_count = 0; |
| } else if (instance->preloaded_count > me_circ_buf_values(&instance->circ_buf)) { //This should never happend! |
| PERROR_CRITICAL |
| ("PRELOADED MORE VALUES THAN ARE IN BUFFER!\n"); |
| spin_unlock_irqrestore(&instance->subdevice_lock, |
| cpu_flags); |
| ME_SUBDEVICE_EXIT; |
| return ME_ERRNO_INTERNAL; |
| } |
| } |
| |
| //Set status to 'wait for start' |
| instance->status = ao_status_stream_run_wait; |
| |
| status = inl(instance->status_reg); |
| //Start state machine and interrupts |
| ctrl &= ~(ME4600_AO_CTRL_BIT_STOP | ME4600_AO_CTRL_BIT_IMMEDIATE_STOP); |
| if (instance->start_mode == ME4600_AO_EXT_TRIG) { // External trigger. |
| PINFO("External trigger.\n"); |
| ctrl |= ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG; |
| } |
| if (!(status & ME4600_AO_STATUS_BIT_HF)) { //More than half! |
| if ((ctrl & ME4600_AO_CTRL_MODE_MASK) == ME4600_AO_MODE_CONTINUOUS) { //Enable IRQ only when hardware_continous is set and FIFO is more than half |
| ctrl &= ~ME4600_AO_CTRL_BIT_RESET_IRQ; |
| ctrl |= ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| } |
| } |
| 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->subdevice_lock, cpu_flags); |
| |
| //Trigger output |
| if (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) { //Trigger outputs |
| spin_lock(instance->preload_reg_lock); |
| synch = inl(instance->preload_reg); |
| //Add channel to start list |
| outl(synch | (ME4600_AO_SYNC_HOLD << instance->ao_idx), |
| instance->preload_reg); |
| PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->preload_reg - instance->reg_base, |
| synch | (ME4600_AO_SYNC_HOLD << instance->ao_idx)); |
| |
| //Fire |
| PINFO |
| ("Fired all software synchronous outputs by software trigger.\n"); |
| outl(0x8000, instance->single_reg); |
| PDEBUG_REG("single_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->single_reg - instance->reg_base, 0x8000); |
| |
| //Restore save settings |
| outl(synch, instance->preload_reg); |
| PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->preload_reg - instance->reg_base, synch); |
| spin_unlock(instance->preload_reg_lock); |
| } else if (!instance->start_mode) { //Trigger outputs |
| /* |
| //Remove channel from start list. // <== Unnecessary. Removed. |
| spin_lock(instance->preload_reg_lock); |
| synch = inl(instance->preload_reg); |
| outl(synch & ~(ME4600_AO_SYNC_HOLD << instance->ao_idx), instance->preload_reg); |
| PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, instance->preload_reg - instance->reg_base, synch & ~(ME4600_AO_SYNC_HOLD << instance->ao_idx)); |
| */ |
| //Fire |
| PINFO("Software trigger.\n"); |
| outl(0x8000, instance->single_reg); |
| PDEBUG_REG("single_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->single_reg - instance->reg_base, 0x8000); |
| |
| /* |
| //Restore save settings. // <== Unnecessary. Removed. |
| outl(synch, instance->preload_reg); |
| PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, instance->preload_reg - instance->reg_base, synch); |
| spin_unlock(instance->preload_reg_lock); |
| */ |
| } |
| // Set control task's timeout |
| ref = jiffies; |
| instance->timeout.delay = delay; |
| instance->timeout.start_time = ref; |
| |
| if (status & ME4600_AO_STATUS_BIT_HF) { //Less than half but not empty! |
| PINFO("Less than half.\n"); |
| if (instance->stop_data_count != 0) { |
| count = ME4600_AO_FIFO_COUNT / 2; |
| } else { |
| count = |
| ((ME4600_AO_FIFO_COUNT / 2) < |
| instance->stop_data_count) ? ME4600_AO_FIFO_COUNT / |
| 2 : instance->stop_data_count; |
| } |
| |
| //Copy data |
| count = |
| ao_write_data(instance, count, instance->preloaded_count); |
| |
| if (count < 0) { //This should never happend! |
| PERROR_CRITICAL("COPY FINISH WITH ERROR!\n"); |
| ME_SUBDEVICE_EXIT; |
| return ME_ERRNO_INTERNAL; |
| } |
| |
| if (instance->mode == ME4600_AO_CONTINOUS) { //Continous |
| instance->circ_buf.tail += count; |
| instance->circ_buf.tail &= instance->circ_buf.mask; |
| } else { //Wraparound |
| instance->data_count += count; |
| instance->preloaded_count += count; |
| |
| if (instance->preloaded_count == me_circ_buf_values(&instance->circ_buf)) { //Reset position indicator. |
| instance->preloaded_count = 0; |
| } else if (instance->preloaded_count > me_circ_buf_values(&instance->circ_buf)) { //This should never happend! |
| PERROR_CRITICAL |
| ("PRELOADED MORE VALUES THAN ARE IN BUFFER!\n"); |
| ME_SUBDEVICE_EXIT; |
| return ME_ERRNO_INTERNAL; |
| } |
| } |
| |
| status = inl(instance->status_reg); |
| if (!(status & ME4600_AO_STATUS_BIT_HF)) { //More than half! |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| ctrl = inl(instance->ctrl_reg); |
| ctrl &= ~ME4600_AO_CTRL_BIT_RESET_IRQ; |
| ctrl |= ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| 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->subdevice_lock, |
| cpu_flags); |
| } |
| } |
| //Special case: Limited wraparound with less than HALF FIFO datas need work around to generate first interrupt. |
| if ((instance->stop_mode != ME4600_AO_INF_STOP_MODE) |
| && (instance->mode == ME4600_AO_SW_WRAP_MODE) |
| && (circ_buffer_count <= (ME4600_AO_FIFO_COUNT / 2))) { //Put more data to FIFO |
| PINFO("Limited wraparound with less than HALF FIFO datas.\n"); |
| if (instance->preloaded_count) { //This should never happend! |
| PERROR_CRITICAL |
| ("ERROR WHEN LOADING VALUES FOR WRAPAROUND!\n"); |
| ME_SUBDEVICE_EXIT; |
| return ME_ERRNO_INTERNAL; |
| } |
| |
| while (instance->stop_data_count > instance->data_count) { //Maximum data not set jet. |
| //Copy to buffer |
| if (circ_buffer_count != ao_write_data(instance, circ_buffer_count, 0)) { //This should never happend! |
| PERROR_CRITICAL |
| ("ERROR WHEN LOADING VALUES FOR WRAPAROUND!\n"); |
| ME_SUBDEVICE_EXIT; |
| return ME_ERRNO_INTERNAL; |
| } |
| instance->data_count += circ_buffer_count; |
| |
| if (!((status = inl(instance->status_reg)) & ME4600_AO_STATUS_BIT_HF)) { //FIFO is more than half. Enable IRQ and end copy. |
| spin_lock_irqsave(&instance->subdevice_lock, |
| cpu_flags); |
| ctrl = inl(instance->ctrl_reg); |
| ctrl &= ~ME4600_AO_CTRL_BIT_RESET_IRQ; |
| ctrl |= ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| 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-> |
| subdevice_lock, |
| cpu_flags); |
| break; |
| } |
| } |
| } |
| // Schedule control task. |
| instance->ao_control_task_flag = 1; |
| queue_delayed_work(instance->me4600_workqueue, |
| &instance->ao_control_task, 1); |
| |
| if (start_mode == ME_START_MODE_BLOCKING) { //Wait for start. |
| //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 != |
| ao_status_stream_run_wait), |
| (delay) ? delay + |
| 1 : LONG_MAX); |
| |
| if ((instance->status != ao_status_stream_run) |
| && (instance->status != ao_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 = ao_status_none; |
| ao_stop_immediately(instance); |
| err = ME_ERRNO_SIGNAL; |
| } else if ((delay) && ((jiffies - ref) >= delay)) { |
| if (instance->status != ao_status_stream_run) { |
| if (instance->status == ao_status_stream_end) { |
| PDEBUG("Timeout reached.\n"); |
| } else { |
| if ((jiffies - ref) > delay) { |
| PERROR |
| ("Timeout reached. Not handled by control task!\n"); |
| } else { |
| PERROR |
| ("Timeout reached. Signal come but status is strange: %d\n", |
| instance->status); |
| } |
| ao_stop_immediately(instance); |
| } |
| |
| instance->ao_control_task_flag = 0; |
| cancel_delayed_work(&instance->ao_control_task); |
| instance->status = ao_status_stream_end; |
| err = ME_ERRNO_TIMEOUT; |
| } |
| } |
| } |
| |
| ME_SUBDEVICE_EXIT; |
| return err; |
| } |
| |
| static int me4600_ao_io_stream_status(me_subdevice_t * subdevice, |
| struct file *filep, |
| int wait, |
| int *status, int *values, int flags) |
| { |
| me4600_ao_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| PDEBUG("executed. idx=%d\n", instance->ao_idx); |
| |
| if (!instance->fifo) { |
| PERROR("Not a streaming ao.\n"); |
| return ME_ERRNO_NOT_SUPPORTED; |
| } |
| |
| if (flags) { |
| PERROR("Invalid flag specified.\n"); |
| return ME_ERRNO_INVALID_FLAGS; |
| } |
| |
| if ((wait != ME_WAIT_NONE) && (wait != ME_WAIT_IDLE)) { |
| PERROR("Invalid wait argument specified.\n"); |
| *status = ME_STATUS_INVALID; |
| return ME_ERRNO_INVALID_WAIT; |
| } |
| |
| ME_SUBDEVICE_ENTER; |
| |
| switch (instance->status) { |
| case ao_status_single_configured: |
| case ao_status_single_end: |
| case ao_status_stream_configured: |
| case ao_status_stream_end: |
| case ao_status_stream_fifo_error: |
| case ao_status_stream_buffer_error: |
| case ao_status_stream_error: |
| *status = ME_STATUS_IDLE; |
| break; |
| |
| case ao_status_single_run_wait: |
| case ao_status_single_run: |
| case ao_status_single_end_wait: |
| case ao_status_stream_run_wait: |
| case ao_status_stream_run: |
| case ao_status_stream_end_wait: |
| *status = ME_STATUS_BUSY; |
| break; |
| |
| case ao_status_none: |
| default: |
| *status = |
| (inl(instance->status_reg) & ME4600_AO_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 != |
| ao_status_single_run_wait) |
| && (instance->status != |
| ao_status_single_run) |
| && (instance->status != |
| ao_status_single_end_wait) |
| && (instance->status != |
| ao_status_stream_run_wait) |
| && (instance->status != |
| ao_status_stream_run) |
| && (instance->status != |
| ao_status_stream_end_wait)), |
| LONG_MAX); |
| |
| if (instance->status != ao_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 = ao_status_none; |
| ao_stop_immediately(instance); |
| err = ME_ERRNO_SIGNAL; |
| } |
| |
| *status = ME_STATUS_IDLE; |
| } |
| |
| *values = me_circ_buf_space(&instance->circ_buf); |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| static int me4600_ao_io_stream_stop(me_subdevice_t * subdevice, |
| struct file *filep, |
| int stop_mode, int flags) |
| { // Stop work and empty buffer and FIFO |
| int err = ME_ERRNO_SUCCESS; |
| me4600_ao_subdevice_t *instance; |
| unsigned long cpu_flags; |
| volatile uint32_t ctrl; |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| PDEBUG("executed. idx=%d\n", instance->ao_idx); |
| |
| if (flags & ~ME_IO_STREAM_STOP_PRESERVE_BUFFERS) { |
| 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; |
| } |
| |
| if (!instance->fifo) { |
| PERROR("Not a streaming ao.\n"); |
| return ME_ERRNO_NOT_SUPPORTED; |
| } |
| |
| if (instance->status < ao_status_stream_configured) { |
| //There is nothing to stop! |
| PERROR("Subdevice not in streaming mode. %d\n", |
| instance->status); |
| return ME_ERRNO_PREVIOUS_CONFIG; |
| } |
| |
| ME_SUBDEVICE_ENTER; |
| |
| //Mark as stopping. => Software stop. |
| instance->status = ao_status_stream_end_wait; |
| |
| if (stop_mode == ME_STOP_MODE_IMMEDIATE) { //Stopped now! |
| err = ao_stop_immediately(instance); |
| } else if (stop_mode == ME_STOP_MODE_LAST_VALUE) { |
| ctrl = inl(instance->ctrl_reg) & ME4600_AO_CTRL_MODE_MASK; |
| if (ctrl == ME4600_AO_MODE_WRAPAROUND) { //Hardware wraparound => Hardware stop. |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| ctrl = inl(instance->ctrl_reg); |
| ctrl |= |
| ME4600_AO_CTRL_BIT_STOP | |
| ME4600_AO_CTRL_BIT_RESET_IRQ; |
| ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| 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->subdevice_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 != |
| ao_status_stream_end_wait), |
| LONG_MAX); |
| |
| if (instance->status != ao_status_stream_end) { |
| PDEBUG("Stopping stream canceled.\n"); |
| err = ME_ERRNO_CANCELLED; |
| } |
| |
| if (signal_pending(current)) { |
| PERROR("Stopping stream interrupted.\n"); |
| instance->status = ao_status_none; |
| ao_stop_immediately(instance); |
| err = ME_ERRNO_SIGNAL; |
| } |
| } |
| |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| ctrl = inl(instance->ctrl_reg); |
| ctrl |= |
| ME4600_AO_CTRL_BIT_STOP | ME4600_AO_CTRL_BIT_IMMEDIATE_STOP | |
| ME4600_AO_CTRL_BIT_RESET_IRQ; |
| ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| if (!flags) { //Reset FIFO |
| ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_FIFO; |
| } |
| 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->subdevice_lock, cpu_flags); |
| |
| if (!flags) { //Reset software buffer |
| instance->circ_buf.head = 0; |
| instance->circ_buf.tail = 0; |
| instance->preloaded_count = 0; |
| instance->data_count = 0; |
| } |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| static int me4600_ao_io_stream_write(me_subdevice_t * subdevice, |
| struct file *filep, |
| int write_mode, |
| int *values, int *count, int flags) |
| { |
| int err = ME_ERRNO_SUCCESS; |
| me4600_ao_subdevice_t *instance; |
| unsigned long cpu_flags = 0; |
| uint32_t reg_copy; |
| |
| int copied_from_user = 0; |
| int left_to_copy_from_user = *count; |
| |
| int copied_values; |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| PDEBUG("executed. idx=%d\n", instance->ao_idx); |
| |
| //Checking arguments |
| if (!instance->fifo) { |
| PERROR("Not a streaming ao.\n"); |
| return ME_ERRNO_NOT_SUPPORTED; |
| } |
| |
| if (flags) { |
| PERROR("Invalid flag specified.\n"); |
| return ME_ERRNO_INVALID_FLAGS; |
| } |
| |
| if (*count <= 0) { |
| PERROR("Invalid count of values specified.\n"); |
| return ME_ERRNO_INVALID_VALUE_COUNT; |
| } |
| |
| if (values == NULL) { |
| PERROR("Invalid address of values specified.\n"); |
| return ME_ERRNO_INVALID_POINTER; |
| } |
| |
| if ((instance->status == ao_status_none) || (instance->status == ao_status_single_configured)) { //The device is in single mode. |
| PERROR |
| ("Subdevice must be preinitialize correctly for streaming.\n"); |
| return ME_ERRNO_PREVIOUS_CONFIG; |
| } |
| /// @note If no 'pre-load' is used. stream_start() will move data to FIFO. |
| switch (write_mode) { |
| case ME_WRITE_MODE_PRELOAD: |
| |
| //Device must be stopped. |
| if ((instance->status != ao_status_stream_configured) |
| && (instance->status != ao_status_stream_end)) { |
| PERROR |
| ("Subdevice mustn't be runing when 'pre-load' mode is used.\n"); |
| return ME_ERRNO_PREVIOUS_CONFIG; |
| } |
| break; |
| case ME_WRITE_MODE_NONBLOCKING: |
| case ME_WRITE_MODE_BLOCKING: |
| /// @note In blocking mode: When device is not runing and there is not enought space call will blocked up! |
| /// @note Some other thread must empty buffer by starting engine. |
| break; |
| |
| default: |
| PERROR("Invalid write mode specified.\n"); |
| return ME_ERRNO_INVALID_WRITE_MODE; |
| } |
| |
| if (instance->mode & ME4600_AO_WRAP_MODE) { //Wraparound mode. Device must be stopped. |
| if ((instance->status != ao_status_stream_configured) |
| && (instance->status != ao_status_stream_end)) { |
| PERROR |
| ("Subdevice mustn't be runing when 'pre-load' mode is used.\n"); |
| return ME_ERRNO_INVALID_WRITE_MODE; |
| } |
| } |
| |
| if ((instance->mode == ME4600_AO_HW_WRAP_MODE) && (write_mode != ME_WRITE_MODE_PRELOAD)) { // hardware wrap_around mode. |
| //This is transparent for user. |
| PDEBUG("Changing write_mode to ME_WRITE_MODE_PRELOAD.\n"); |
| write_mode = ME_WRITE_MODE_PRELOAD; |
| } |
| |
| ME_SUBDEVICE_ENTER; |
| |
| if (write_mode == ME_WRITE_MODE_PRELOAD) { //Init enviroment - preload |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| reg_copy = inl(instance->ctrl_reg); |
| //Check FIFO |
| if (!(reg_copy & ME4600_AO_CTRL_BIT_ENABLE_FIFO)) { //FIFO not active. Enable it. |
| reg_copy |= ME4600_AO_CTRL_BIT_ENABLE_FIFO; |
| outl(reg_copy, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, |
| reg_copy); |
| instance->preloaded_count = 0; |
| } |
| spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags); |
| } |
| |
| while (1) { |
| //Copy to buffer. This step is common for all modes. |
| copied_from_user = |
| ao_get_data_from_user(instance, left_to_copy_from_user, |
| values + (*count - |
| left_to_copy_from_user)); |
| left_to_copy_from_user -= copied_from_user; |
| |
| reg_copy = inl(instance->status_reg); |
| if ((instance->status == ao_status_stream_run) && !(reg_copy & ME4600_AO_STATUS_BIT_FSM)) { //BROKEN PIPE! The state machine is stoped but logical status show that should be working. |
| PERROR("Broken pipe in write.\n"); |
| err = ME_ERRNO_SUBDEVICE_NOT_RUNNING; |
| break; |
| } |
| |
| if ((instance->status == ao_status_stream_run) && (instance->mode == ME4600_AO_CONTINOUS) && (reg_copy & ME4600_AO_STATUS_BIT_HF)) { //Continous mode runing and data are below half! |
| |
| // Block interrupts. |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| reg_copy = inl(instance->ctrl_reg); |
| //reg_copy &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| reg_copy |= ME4600_AO_CTRL_BIT_RESET_IRQ; |
| outl(reg_copy, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, |
| reg_copy); |
| spin_unlock_irqrestore(&instance->subdevice_lock, |
| cpu_flags); |
| |
| //Fast copy |
| copied_values = |
| ao_write_data(instance, ME4600_AO_FIFO_COUNT / 2, |
| 0); |
| if (copied_values > 0) { |
| instance->circ_buf.tail += copied_values; |
| instance->circ_buf.tail &= |
| instance->circ_buf.mask; |
| continue; |
| } |
| // Activate interrupts. |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| reg_copy = inl(instance->ctrl_reg); |
| //reg_copy |= ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| reg_copy &= ~ME4600_AO_CTRL_BIT_RESET_IRQ; |
| outl(reg_copy, instance->ctrl_reg); |
| PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->ctrl_reg - instance->reg_base, |
| reg_copy); |
| spin_unlock_irqrestore(&instance->subdevice_lock, |
| cpu_flags); |
| |
| if (copied_values == 0) { //This was checked and never should happend! |
| PERROR_CRITICAL("COPING FINISH WITH 0!\n"); |
| } |
| |
| if (copied_values < 0) { //This was checked and never should happend! |
| PERROR_CRITICAL |
| ("COPING FINISH WITH AN ERROR!\n"); |
| instance->status = ao_status_stream_fifo_error; |
| err = ME_ERRNO_FIFO_BUFFER_OVERFLOW; |
| break; |
| } |
| } |
| |
| if (!left_to_copy_from_user) { //All datas were copied. |
| break; |
| } else { //Not all datas were copied. |
| if (instance->mode & ME4600_AO_WRAP_MODE) { //Error too much datas! Wraparound is limited in size! |
| PERROR |
| ("Too much data for wraparound mode! Exceeded size of %ld.\n", |
| ME4600_AO_CIRC_BUF_COUNT - 1); |
| err = ME_ERRNO_RING_BUFFER_OVERFLOW; |
| break; |
| } |
| |
| if (write_mode != ME_WRITE_MODE_BLOCKING) { //Non blocking calls |
| break; |
| } |
| |
| wait_event_interruptible(instance->wait_queue, |
| me_circ_buf_space(&instance-> |
| circ_buf)); |
| |
| if (signal_pending(current)) { |
| PERROR("Writing interrupted by signal.\n"); |
| instance->status = ao_status_none; |
| ao_stop_immediately(instance); |
| err = ME_ERRNO_SIGNAL; |
| break; |
| } |
| |
| if (instance->status == ao_status_none) { //Reset |
| PERROR("Writing interrupted by reset.\n"); |
| err = ME_ERRNO_CANCELLED; |
| break; |
| } |
| } |
| } |
| |
| if (write_mode == ME_WRITE_MODE_PRELOAD) { //Copy data to FIFO - preload |
| copied_values = |
| ao_write_data_pooling(instance, ME4600_AO_FIFO_COUNT, |
| instance->preloaded_count); |
| instance->preloaded_count += copied_values; |
| instance->data_count += copied_values; |
| |
| if ((instance->mode == ME4600_AO_HW_WRAP_MODE) |
| && (me_circ_buf_values(&instance->circ_buf) > |
| ME4600_AO_FIFO_COUNT)) { |
| PERROR |
| ("Too much data for hardware wraparound mode! Exceeded size of %d.\n", |
| ME4600_AO_FIFO_COUNT); |
| err = ME_ERRNO_FIFO_BUFFER_OVERFLOW; |
| } |
| } |
| |
| *count = *count - left_to_copy_from_user; |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| static irqreturn_t me4600_ao_isr(int irq, void *dev_id |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) |
| , struct pt_regs *regs |
| #endif |
| ) |
| { |
| me4600_ao_subdevice_t *instance = dev_id; |
| uint32_t irq_status; |
| uint32_t ctrl; |
| uint32_t status; |
| int count = 0; |
| |
| PDEBUG("executed. idx=%d\n", instance->ao_idx); |
| |
| 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_AO_HF << instance->ao_idx))) { |
| PINFO("%ld Shared interrupt. %s(): ID=%d: status_reg=0x%04X\n", |
| jiffies, __func__, instance->ao_idx, irq_status); |
| return IRQ_NONE; |
| } |
| |
| if (!instance->circ_buf.buf) { |
| instance->status = ao_status_stream_error; |
| PERROR_CRITICAL("CIRCULAR BUFFER NOT EXISTS!\n"); |
| //Block interrupts. Stop machine. |
| ctrl = inl(instance->ctrl_reg); |
| ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| ctrl |= |
| ME4600_AO_CTRL_BIT_RESET_IRQ | |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP | ME4600_AO_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); |
| |
| //Inform user |
| wake_up_interruptible_all(&instance->wait_queue); |
| return IRQ_HANDLED; |
| } |
| |
| status = inl(instance->status_reg); |
| if (!(status & ME4600_AO_STATUS_BIT_FSM)) { //Too late. Not working! END? BROKEN PIPE? |
| PDEBUG("Interrupt come but ISM is not working!\n"); |
| //Block interrupts. Stop machine. |
| ctrl = inl(instance->ctrl_reg); |
| ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| ctrl |= |
| ME4600_AO_CTRL_BIT_RESET_IRQ | ME4600_AO_CTRL_BIT_STOP | |
| ME4600_AO_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); |
| |
| return IRQ_HANDLED; |
| } |
| //General procedure. Process more datas. |
| |
| #ifdef MEDEBUG_DEBUG |
| if (!me_circ_buf_values(&instance->circ_buf)) { //Buffer is empty! |
| PDEBUG("Circular buffer empty!\n"); |
| } |
| #endif |
| |
| //Check FIFO |
| if (status & ME4600_AO_STATUS_BIT_HF) { //OK less than half |
| |
| //Block interrupts |
| ctrl = inl(instance->ctrl_reg); |
| ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| ctrl |= ME4600_AO_CTRL_BIT_RESET_IRQ; |
| 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); |
| |
| do { |
| //Calculate how many should be copied. |
| count = |
| (instance->stop_data_count) ? instance-> |
| stop_data_count - |
| instance->data_count : ME4600_AO_FIFO_COUNT / 2; |
| if (ME4600_AO_FIFO_COUNT / 2 < count) { |
| count = ME4600_AO_FIFO_COUNT / 2; |
| } |
| //Copy data |
| if (instance->mode == ME4600_AO_CONTINOUS) { //Continous |
| count = ao_write_data(instance, count, 0); |
| if (count > 0) { |
| instance->circ_buf.tail += count; |
| instance->circ_buf.tail &= |
| instance->circ_buf.mask; |
| instance->data_count += count; |
| |
| if ((instance->status == ao_status_stream_end_wait) && !me_circ_buf_values(&instance->circ_buf)) { //Stoping. Whole buffer was copied. |
| break; |
| } |
| } |
| } else if ((instance->mode == ME4600_AO_SW_WRAP_MODE) && ((ctrl & ME4600_AO_CTRL_MODE_MASK) == ME4600_AO_MODE_CONTINUOUS)) { //Wraparound (software) |
| if (instance->status == ao_status_stream_end_wait) { //We stoping => Copy to the end of the buffer. |
| count = |
| ao_write_data(instance, count, 0); |
| } else { //Copy in wraparound mode. |
| count = |
| ao_write_data_wraparound(instance, |
| count, |
| instance-> |
| preloaded_count); |
| } |
| |
| if (count > 0) { |
| instance->data_count += count; |
| instance->preloaded_count += count; |
| instance->preloaded_count %= |
| me_circ_buf_values(&instance-> |
| circ_buf); |
| |
| if ((instance->status == ao_status_stream_end_wait) && !instance->preloaded_count) { //Stoping. Whole buffer was copied. |
| break; |
| } |
| } |
| } |
| |
| if ((count <= 0) || (instance->stop_data_count && (instance->stop_data_count <= instance->data_count))) { //End of work. |
| break; |
| } |
| } //Repeat if still is under half fifo |
| while ((status = |
| inl(instance->status_reg)) & ME4600_AO_STATUS_BIT_HF); |
| |
| //Unblock interrupts |
| ctrl = inl(instance->ctrl_reg); |
| if (count >= 0) { //Copy was successful. |
| if (instance->stop_data_count && (instance->stop_data_count <= instance->data_count)) { //Finishing work. No more interrupts. |
| PDEBUG("Finishing work. Interrupt disabled.\n"); |
| instance->status = ao_status_stream_end_wait; |
| } else if (count > 0) { //Normal work. Enable interrupt. |
| PDEBUG("Normal work. Enable interrupt.\n"); |
| ctrl &= ~ME4600_AO_CTRL_BIT_RESET_IRQ; |
| ctrl |= ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| } else { //Normal work but there are no more data in buffer. Interrupt active but blocked. stream_write() will unblock it. |
| PDEBUG |
| ("No data in software buffer. Interrupt blocked.\n"); |
| ctrl |= ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| } |
| } else { //Error during copy. |
| instance->status = ao_status_stream_fifo_error; |
| } |
| |
| 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); |
| } else { //?? more than half |
| PDEBUG |
| ("Interrupt come but FIFO more than half full! Reset interrupt.\n"); |
| //Reset pending interrupt |
| ctrl = inl(instance->ctrl_reg); |
| ctrl |= ME4600_AO_CTRL_BIT_RESET_IRQ; |
| 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); |
| ctrl &= ~ME4600_AO_CTRL_BIT_RESET_IRQ; |
| 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); |
| } |
| |
| PINFO("ISR: Buffer count: %d.(T:%d H:%d)\n", |
| me_circ_buf_values(&instance->circ_buf), instance->circ_buf.tail, |
| instance->circ_buf.head); |
| PINFO("ISR: Stop count: %d.\n", instance->stop_count); |
| PINFO("ISR: Stop data count: %d.\n", instance->stop_data_count); |
| PINFO("ISR: Data count: %d.\n", instance->data_count); |
| |
| //Inform user |
| wake_up_interruptible_all(&instance->wait_queue); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static void me4600_ao_destructor(struct me_subdevice *subdevice) |
| { |
| me4600_ao_subdevice_t *instance; |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| PDEBUG("executed. idx=%d\n", instance->ao_idx); |
| |
| instance->ao_control_task_flag = 0; |
| |
| // Reset subdevice to asure clean exit. |
| me4600_ao_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->ao_control_task)) { //Wait 2 ticks to be sure that control task is removed from queue. |
| set_current_state(TASK_INTERRUPTIBLE); |
| schedule_timeout(2); |
| } |
| |
| if (instance->fifo) { |
| if (instance->irq) { |
| free_irq(instance->irq, instance); |
| instance->irq = 0; |
| } |
| |
| if (instance->circ_buf.buf) { |
| free_pages((unsigned long)instance->circ_buf.buf, |
| ME4600_AO_CIRC_BUF_SIZE_ORDER); |
| } |
| instance->circ_buf.buf = NULL; |
| } |
| |
| me_subdevice_deinit(&instance->base); |
| kfree(instance); |
| } |
| |
| me4600_ao_subdevice_t *me4600_ao_constructor(uint32_t reg_base, |
| spinlock_t * preload_reg_lock, |
| uint32_t * preload_flags, |
| int ao_idx, |
| int fifo, |
| int irq, |
| struct workqueue_struct *me4600_wq) |
| { |
| me4600_ao_subdevice_t *subdevice; |
| int err; |
| |
| PDEBUG("executed. idx=%d\n", ao_idx); |
| |
| // Allocate memory for subdevice instance. |
| subdevice = kmalloc(sizeof(me4600_ao_subdevice_t), GFP_KERNEL); |
| |
| if (!subdevice) { |
| PERROR("Cannot get memory for subdevice instance.\n"); |
| return NULL; |
| } |
| |
| memset(subdevice, 0, sizeof(me4600_ao_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->preload_reg_lock = preload_reg_lock; |
| subdevice->preload_flags = preload_flags; |
| |
| // Store analog output index. |
| subdevice->ao_idx = ao_idx; |
| |
| // Store if analog output has fifo. |
| subdevice->fifo = (ao_idx < fifo) ? 1 : 0; |
| |
| if (subdevice->fifo) { // Allocate and initialize circular buffer. |
| subdevice->circ_buf.mask = ME4600_AO_CIRC_BUF_COUNT - 1; |
| |
| subdevice->circ_buf.buf = |
| (void *)__get_free_pages(GFP_KERNEL, |
| ME4600_AO_CIRC_BUF_SIZE_ORDER); |
| PDEBUG("circ_buf = %p size=%ld\n", subdevice->circ_buf.buf, |
| ME4600_AO_CIRC_BUF_SIZE); |
| |
| if (!subdevice->circ_buf.buf) { |
| PERROR |
| ("Cannot initialize subdevice base class instance.\n"); |
| kfree(subdevice); |
| return NULL; |
| } |
| |
| memset(subdevice->circ_buf.buf, 0, ME4600_AO_CIRC_BUF_SIZE); |
| } else { // No FIFO. |
| subdevice->circ_buf.mask = 0; |
| subdevice->circ_buf.buf = NULL; |
| } |
| |
| subdevice->circ_buf.head = 0; |
| subdevice->circ_buf.tail = 0; |
| |
| subdevice->status = ao_status_none; |
| subdevice->ao_control_task_flag = 0; |
| subdevice->timeout.delay = 0; |
| subdevice->timeout.start_time = jiffies; |
| |
| // Initialize wait queue. |
| init_waitqueue_head(&subdevice->wait_queue); |
| |
| // Initialize single value to 0V. |
| subdevice->single_value = 0x8000; |
| subdevice->single_value_in_fifo = 0x8000; |
| |
| // Register interrupt service routine. |
| if (subdevice->fifo) { |
| subdevice->irq = irq; |
| if (request_irq(subdevice->irq, me4600_ao_isr, |
| #ifdef IRQF_DISABLED |
| IRQF_DISABLED | IRQF_SHARED, |
| #else |
| SA_INTERRUPT | SA_SHIRQ, |
| #endif |
| ME4600_NAME, subdevice)) { |
| PERROR("Cannot get interrupt line.\n"); |
| PDEBUG("free circ_buf = %p size=%d", |
| subdevice->circ_buf.buf, |
| PAGE_SHIFT << ME4600_AO_CIRC_BUF_SIZE_ORDER); |
| free_pages((unsigned long)subdevice->circ_buf.buf, |
| ME4600_AO_CIRC_BUF_SIZE_ORDER); |
| me_subdevice_deinit((me_subdevice_t *) subdevice); |
| kfree(subdevice); |
| return NULL; |
| } |
| PINFO("Registered irq=%d.\n", subdevice->irq); |
| } else { |
| subdevice->irq = 0; |
| } |
| |
| // Initialize registers. |
| subdevice->irq_status_reg = reg_base + ME4600_IRQ_STATUS_REG; |
| subdevice->preload_reg = reg_base + ME4600_AO_SYNC_REG; |
| if (ao_idx == 0) { |
| subdevice->ctrl_reg = reg_base + ME4600_AO_00_CTRL_REG; |
| subdevice->status_reg = reg_base + ME4600_AO_00_STATUS_REG; |
| subdevice->fifo_reg = reg_base + ME4600_AO_00_FIFO_REG; |
| subdevice->single_reg = reg_base + ME4600_AO_00_SINGLE_REG; |
| subdevice->timer_reg = reg_base + ME4600_AO_00_TIMER_REG; |
| subdevice->reg_base = reg_base; |
| subdevice->bitpattern = 0; |
| } else if (ao_idx == 1) { |
| subdevice->ctrl_reg = reg_base + ME4600_AO_01_CTRL_REG; |
| subdevice->status_reg = reg_base + ME4600_AO_01_STATUS_REG; |
| subdevice->fifo_reg = reg_base + ME4600_AO_01_FIFO_REG; |
| subdevice->single_reg = reg_base + ME4600_AO_01_SINGLE_REG; |
| subdevice->timer_reg = reg_base + ME4600_AO_01_TIMER_REG; |
| subdevice->reg_base = reg_base; |
| subdevice->bitpattern = 0; |
| } else if (ao_idx == 2) { |
| subdevice->ctrl_reg = reg_base + ME4600_AO_02_CTRL_REG; |
| subdevice->status_reg = reg_base + ME4600_AO_02_STATUS_REG; |
| subdevice->fifo_reg = reg_base + ME4600_AO_02_FIFO_REG; |
| subdevice->single_reg = reg_base + ME4600_AO_02_SINGLE_REG; |
| subdevice->timer_reg = reg_base + ME4600_AO_02_TIMER_REG; |
| subdevice->reg_base = reg_base; |
| subdevice->bitpattern = 0; |
| } else if (ao_idx == 3) { |
| subdevice->ctrl_reg = reg_base + ME4600_AO_03_CTRL_REG; |
| subdevice->status_reg = reg_base + ME4600_AO_03_STATUS_REG; |
| subdevice->fifo_reg = reg_base + ME4600_AO_03_FIFO_REG; |
| subdevice->single_reg = reg_base + ME4600_AO_03_SINGLE_REG; |
| subdevice->timer_reg = reg_base + ME4600_AO_03_TIMER_REG; |
| subdevice->reg_base = reg_base; |
| subdevice->bitpattern = 1; |
| } else { |
| PERROR_CRITICAL("WRONG SUBDEVICE idx=%d!", ao_idx); |
| me_subdevice_deinit((me_subdevice_t *) subdevice); |
| if (subdevice->fifo) { |
| free_pages((unsigned long)subdevice->circ_buf.buf, |
| ME4600_AO_CIRC_BUF_SIZE_ORDER); |
| } |
| subdevice->circ_buf.buf = NULL; |
| kfree(subdevice); |
| return NULL; |
| } |
| |
| // Override base class methods. |
| subdevice->base.me_subdevice_destructor = me4600_ao_destructor; |
| subdevice->base.me_subdevice_io_reset_subdevice = |
| me4600_ao_io_reset_subdevice; |
| subdevice->base.me_subdevice_io_single_config = |
| me4600_ao_io_single_config; |
| subdevice->base.me_subdevice_io_single_read = me4600_ao_io_single_read; |
| subdevice->base.me_subdevice_io_single_write = |
| me4600_ao_io_single_write; |
| subdevice->base.me_subdevice_io_stream_config = |
| me4600_ao_io_stream_config; |
| subdevice->base.me_subdevice_io_stream_new_values = |
| me4600_ao_io_stream_new_values; |
| subdevice->base.me_subdevice_io_stream_write = |
| me4600_ao_io_stream_write; |
| subdevice->base.me_subdevice_io_stream_start = |
| me4600_ao_io_stream_start; |
| subdevice->base.me_subdevice_io_stream_status = |
| me4600_ao_io_stream_status; |
| subdevice->base.me_subdevice_io_stream_stop = me4600_ao_io_stream_stop; |
| subdevice->base.me_subdevice_query_number_channels = |
| me4600_ao_query_number_channels; |
| subdevice->base.me_subdevice_query_subdevice_type = |
| me4600_ao_query_subdevice_type; |
| subdevice->base.me_subdevice_query_subdevice_caps = |
| me4600_ao_query_subdevice_caps; |
| subdevice->base.me_subdevice_query_subdevice_caps_args = |
| me4600_ao_query_subdevice_caps_args; |
| subdevice->base.me_subdevice_query_range_by_min_max = |
| me4600_ao_query_range_by_min_max; |
| subdevice->base.me_subdevice_query_number_ranges = |
| me4600_ao_query_number_ranges; |
| subdevice->base.me_subdevice_query_range_info = |
| me4600_ao_query_range_info; |
| subdevice->base.me_subdevice_query_timer = me4600_ao_query_timer; |
| |
| // Prepare work queue |
| subdevice->me4600_workqueue = me4600_wq; |
| |
| /* workqueue API changed in kernel 2.6.20 */ |
| #if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ) |
| INIT_WORK(&subdevice->ao_control_task, me4600_ao_work_control_task, |
| (void *)subdevice); |
| #else |
| INIT_DELAYED_WORK(&subdevice->ao_control_task, |
| me4600_ao_work_control_task); |
| #endif |
| |
| if (subdevice->fifo) { // Set speed for single operations. |
| outl(ME4600_AO_MIN_CHAN_TICKS - 1, subdevice->timer_reg); |
| subdevice->hardware_stop_delay = HZ / 10; //100ms |
| } |
| |
| return subdevice; |
| } |
| |
| /** @brief Stop presentation. Preserve FIFOs. |
| * |
| * @param instance The subdevice instance (pointer). |
| */ |
| int inline ao_stop_immediately(me4600_ao_subdevice_t * instance) |
| { |
| unsigned long cpu_flags; |
| uint32_t ctrl; |
| int timeout; |
| int i; |
| |
| timeout = |
| (instance->hardware_stop_delay > |
| (HZ / 10)) ? instance->hardware_stop_delay : HZ / 10; |
| for (i = 0; i <= timeout; i++) { |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| // Stop all actions. No conditions! Block interrupts. Leave FIFO untouched! |
| ctrl = inl(instance->ctrl_reg); |
| ctrl |= |
| ME4600_AO_CTRL_BIT_STOP | ME4600_AO_CTRL_BIT_IMMEDIATE_STOP |
| | ME4600_AO_CTRL_BIT_RESET_IRQ; |
| ctrl &= |
| ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ | |
| ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG); |
| 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->subdevice_lock, cpu_flags); |
| |
| if (!(inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM)) { // Exit. |
| break; |
| } |
| //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; |
| } |
| |
| /** @brief Copy data from circular buffer to fifo (fast) in wraparound. |
| * @note This is time critical function. Checking is done at begining and end only. |
| * @note The is not reasonable way to check how many walues was in FIFO at begining. The count must be managed externaly. |
| * |
| * @param instance The subdevice instance (pointer). |
| * @param count Maximum number of copied data. |
| * @param start_pos Position of the firs value in buffer. |
| * |
| * @return On success: Number of copied data. |
| * @return On error/success: 0. No datas were copied => no data in buffer. |
| * @return On error: -ME_ERRNO_FIFO_BUFFER_OVERFLOW. |
| */ |
| int inline ao_write_data_wraparound(me4600_ao_subdevice_t * instance, int count, |
| int start_pos) |
| { /// @note This is time critical function! |
| uint32_t status; |
| uint32_t value; |
| int pos = |
| (instance->circ_buf.tail + start_pos) & instance->circ_buf.mask; |
| int local_count = count; |
| int i = 1; |
| |
| if (count <= 0) { //Wrong count! |
| return 0; |
| } |
| |
| while (i < local_count) { |
| //Get value from buffer |
| value = *(instance->circ_buf.buf + pos); |
| //Prepare it |
| if (instance->ao_idx & 0x1) { |
| value <<= 16; |
| } |
| //Put value to FIFO |
| outl(value, instance->fifo_reg); |
| //PDEBUG_REG("idx=%d fifo_reg outl(0x%lX+0x%lX)=0x%x\n", instance->ao_idx, instance->reg_base, instance->fifo_reg - instance->reg_base, value); |
| |
| pos++; |
| pos &= instance->circ_buf.mask; |
| if (pos == instance->circ_buf.head) { |
| pos = instance->circ_buf.tail; |
| } |
| i++; |
| } |
| |
| status = inl(instance->status_reg); |
| if (!(status & ME4600_AO_STATUS_BIT_FF)) { //FIFO is full before all datas were copied! |
| PERROR("FIFO was full before all datas were copied! idx=%d\n", |
| instance->ao_idx); |
| return -ME_ERRNO_FIFO_BUFFER_OVERFLOW; |
| } else { //Add last value |
| value = *(instance->circ_buf.buf + pos); |
| if (instance->ao_idx & 0x1) { |
| value <<= 16; |
| } |
| //Put value to FIFO |
| outl(value, instance->fifo_reg); |
| //PDEBUG_REG("idx=%d fifo_reg outl(0x%lX+0x%lX)=0x%x\n", instance->ao_idx, instance->reg_base, instance->fifo_reg - instance->reg_base, value); |
| } |
| |
| PINFO("WRAPAROUND LOADED %d values. idx=%d\n", local_count, |
| instance->ao_idx); |
| return local_count; |
| } |
| |
| /** @brief Copy data from software buffer to fifo (fast). |
| * @note This is time critical function. Checking is done at begining and end only. |
| * @note The is not reasonable way to check how many walues was in FIFO at begining. The count must be managed externaly. |
| * |
| * @param instance The subdevice instance (pointer). |
| * @param count Maximum number of copied data. |
| * @param start_pos Position of the firs value in buffer. |
| * |
| * @return On success: Number of copied data. |
| * @return On error/success: 0. No datas were copied => no data in buffer. |
| * @return On error: -ME_ERRNO_FIFO_BUFFER_OVERFLOW. |
| */ |
| int inline ao_write_data(me4600_ao_subdevice_t * instance, int count, |
| int start_pos) |
| { /// @note This is time critical function! |
| uint32_t status; |
| uint32_t value; |
| int pos = |
| (instance->circ_buf.tail + start_pos) & instance->circ_buf.mask; |
| int local_count = count; |
| int max_count; |
| int i = 1; |
| |
| if (count <= 0) { //Wrong count! |
| return 0; |
| } |
| |
| max_count = me_circ_buf_values(&instance->circ_buf) - start_pos; |
| if (max_count <= 0) { //No data to copy! |
| return 0; |
| } |
| |
| if (max_count < count) { |
| local_count = max_count; |
| } |
| |
| while (i < local_count) { |
| //Get value from buffer |
| value = *(instance->circ_buf.buf + pos); |
| //Prepare it |
| if (instance->ao_idx & 0x1) { |
| value <<= 16; |
| } |
| //Put value to FIFO |
| outl(value, instance->fifo_reg); |
| //PDEBUG_REG("idx=%d fifo_reg outl(0x%lX+0x%lX)=0x%x\n", instance->ao_idx, instance->reg_base, instance->fifo_reg - instance->reg_base, value); |
| |
| pos++; |
| pos &= instance->circ_buf.mask; |
| i++; |
| } |
| |
| status = inl(instance->status_reg); |
| if (!(status & ME4600_AO_STATUS_BIT_FF)) { //FIFO is full before all datas were copied! |
| PERROR("FIFO was full before all datas were copied! idx=%d\n", |
| instance->ao_idx); |
| return -ME_ERRNO_FIFO_BUFFER_OVERFLOW; |
| } else { //Add last value |
| value = *(instance->circ_buf.buf + pos); |
| if (instance->ao_idx & 0x1) { |
| value <<= 16; |
| } |
| //Put value to FIFO |
| outl(value, instance->fifo_reg); |
| //PDEBUG_REG("idx=%d fifo_reg outl(0x%lX+0x%lX)=0x%x\n", instance->ao_idx, instance->reg_base, instance->fifo_reg - instance->reg_base, value); |
| } |
| |
| PINFO("FAST LOADED %d values. idx=%d\n", local_count, instance->ao_idx); |
| return local_count; |
| } |
| |
| /** @brief Copy data from software buffer to fifo (slow). |
| * @note This is slow function that copy all data from buffer to FIFO with full control. |
| * |
| * @param instance The subdevice instance (pointer). |
| * @param count Maximum number of copied data. |
| * @param start_pos Position of the firs value in buffer. |
| * |
| * @return On success: Number of copied values. |
| * @return On error/success: 0. FIFO was full at begining. |
| * @return On error: -ME_ERRNO_RING_BUFFER_UNDEFFLOW. |
| */ |
| int inline ao_write_data_pooling(me4600_ao_subdevice_t * instance, int count, |
| int start_pos) |
| { /// @note This is slow function! |
| uint32_t status; |
| uint32_t value; |
| int pos = |
| (instance->circ_buf.tail + start_pos) & instance->circ_buf.mask; |
| int local_count = count; |
| int i; |
| int max_count; |
| |
| if (count <= 0) { //Wrong count! |
| PERROR("SLOW LOADED: Wrong count! idx=%d\n", instance->ao_idx); |
| return 0; |
| } |
| |
| max_count = me_circ_buf_values(&instance->circ_buf) - start_pos; |
| if (max_count <= 0) { //No data to copy! |
| PERROR("SLOW LOADED: No data to copy! idx=%d\n", |
| instance->ao_idx); |
| return 0; |
| } |
| |
| if (max_count < count) { |
| local_count = max_count; |
| } |
| |
| for (i = 0; i < local_count; i++) { |
| status = inl(instance->status_reg); |
| if (!(status & ME4600_AO_STATUS_BIT_FF)) { //FIFO is full! |
| return i; |
| } |
| //Get value from buffer |
| value = *(instance->circ_buf.buf + pos); |
| //Prepare it |
| if (instance->ao_idx & 0x1) { |
| value <<= 16; |
| } |
| //Put value to FIFO |
| outl(value, instance->fifo_reg); |
| //PDEBUG_REG("idx=%d fifo_reg outl(0x%lX+0x%lX)=0x%x\n", instance->ao_idx, instance->reg_base, instance->fifo_reg - instance->reg_base, value); |
| |
| pos++; |
| pos &= instance->circ_buf.mask; |
| } |
| |
| PINFO("SLOW LOADED %d values. idx=%d\n", local_count, instance->ao_idx); |
| return local_count; |
| } |
| |
| /** @brief Copy data from user space to circular buffer. |
| * @param instance The subdevice instance (pointer). |
| * @param count Number of datas in user space. |
| * @param user_values Buffer's pointer. |
| * |
| * @return On success: Number of copied values. |
| * @return On error: -ME_ERRNO_INTERNAL. |
| */ |
| int inline ao_get_data_from_user(me4600_ao_subdevice_t * instance, int count, |
| int *user_values) |
| { |
| int i, err; |
| int empty_space; |
| int copied; |
| int value; |
| |
| empty_space = me_circ_buf_space(&instance->circ_buf); |
| //We have only this space free. |
| copied = (count < empty_space) ? count : empty_space; |
| for (i = 0; i < copied; i++) { //Copy from user to buffer |
| if ((err = get_user(value, (int *)(user_values + i)))) { |
| PERROR |
| ("BUFFER LOADED: get_user(0x%p) return an error: %d. idx=%d\n", |
| user_values + i, err, instance->ao_idx); |
| return -ME_ERRNO_INTERNAL; |
| } |
| /// @note The analog output in me4600 series has size of 16 bits. |
| *(instance->circ_buf.buf + instance->circ_buf.head) = |
| (uint16_t) value; |
| instance->circ_buf.head++; |
| instance->circ_buf.head &= instance->circ_buf.mask; |
| } |
| |
| PINFO("BUFFER LOADED %d values. idx=%d\n", copied, instance->ao_idx); |
| return copied; |
| } |
| |
| /** @brief Checking actual hardware and logical state. |
| * @param instance The subdevice instance (pointer). |
| */ |
| static void me4600_ao_work_control_task( |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) |
| void *subdevice |
| #else |
| struct work_struct *work |
| #endif |
| ) |
| { |
| me4600_ao_subdevice_t *instance; |
| unsigned long cpu_flags = 0; |
| uint32_t status; |
| uint32_t ctrl; |
| uint32_t synch; |
| int reschedule = 0; |
| int signaling = 0; |
| |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| #else |
| instance = |
| container_of((void *)work, me4600_ao_subdevice_t, ao_control_task); |
| #endif |
| PINFO("<%s: %ld> executed. idx=%d\n", __func__, jiffies, |
| instance->ao_idx); |
| |
| 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 ao_status_none: |
| break; |
| |
| //This are stable modes. No need to do anything. (?) |
| case ao_status_single_configured: |
| case ao_status_stream_configured: |
| case ao_status_stream_fifo_error: |
| case ao_status_stream_buffer_error: |
| case ao_status_stream_error: |
| PERROR("Shouldn't be running!.\n"); |
| break; |
| |
| case ao_status_stream_end: |
| if (!instance->fifo) { |
| PERROR_CRITICAL |
| ("Streaming on single device! This feature is not implemented in this version!\n"); |
| instance->status = ao_status_stream_error; |
| // Signal the end. |
| signaling = 1; |
| break; |
| } |
| case ao_status_single_end: |
| if (status & ME4600_AO_STATUS_BIT_FSM) { // State machine is working but the status is set to end. Force stop. |
| |
| // Wait for stop. |
| reschedule = 1; |
| } |
| |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| // Stop all actions. No conditions! Block interrupts and trigger. Leave FIFO untouched! |
| ctrl = inl(instance->ctrl_reg); |
| ctrl |= |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP | ME4600_AO_CTRL_BIT_STOP |
| | ME4600_AO_CTRL_BIT_RESET_IRQ; |
| ctrl &= |
| ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ | |
| ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG); |
| ctrl &= |
| ~(ME4600_AO_CTRL_BIT_EX_TRIG_EDGE | |
| ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH); |
| 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->subdevice_lock, cpu_flags); |
| break; |
| |
| // Single modes |
| case ao_status_single_run_wait: |
| case ao_status_single_run: |
| case ao_status_single_end_wait: |
| |
| if (!(status & ME4600_AO_STATUS_BIT_FSM)) { // State machine is not working. |
| if (((instance->fifo) |
| && (!(status & ME4600_AO_STATUS_BIT_EF))) |
| || (!(instance->fifo))) { // Single is in end state. |
| PDEBUG("Single call has been complited.\n"); |
| |
| // Set correct value for single_read(); |
| instance->single_value = |
| instance->single_value_in_fifo; |
| |
| // Set status as 'ao_status_single_end' |
| instance->status = ao_status_single_end; |
| |
| // Signal the end. |
| signaling = 1; |
| // Wait for stop ISM. |
| 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 and trigger. Leave FIFO untouched! |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| ctrl = inl(instance->ctrl_reg); |
| ctrl |= |
| ME4600_AO_CTRL_BIT_STOP | |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP | |
| ME4600_AO_CTRL_BIT_RESET_IRQ; |
| ctrl &= |
| ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ | |
| ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG); |
| /// Fix for timeout error. |
| ctrl &= |
| ~(ME4600_AO_CTRL_BIT_EX_TRIG_EDGE | |
| ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH); |
| if (instance->fifo) { //Disabling FIFO |
| ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_FIFO; |
| } |
| 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->subdevice_lock, |
| cpu_flags); |
| |
| spin_lock(instance->preload_reg_lock); |
| //Remove from synchronous start. Block triggering from this output. |
| synch = inl(instance->preload_reg); |
| synch &= |
| ~((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << |
| instance->ao_idx); |
| if (!(instance->fifo)) { // No FIFO - set to single safe mode |
| synch |= |
| ME4600_AO_SYNC_HOLD << instance->ao_idx; |
| } |
| outl(synch, instance->preload_reg); |
| PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->preload_reg - instance->reg_base, |
| synch); |
| spin_unlock(instance->preload_reg_lock); |
| |
| if (!(instance->fifo)) { // No FIFO |
| // Restore old settings. |
| PDEBUG("Write old value back to register.\n"); |
| outl(instance->single_value, |
| instance->single_reg); |
| PDEBUG_REG |
| ("single_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->single_reg - instance->reg_base, |
| instance->single_value); |
| } |
| // Set correct value for single_read(); |
| instance->single_value_in_fifo = instance->single_value; |
| |
| instance->status = ao_status_single_end; |
| |
| // Signal the end. |
| signaling = 1; |
| } |
| // Wait for stop. |
| reschedule = 1; |
| break; |
| |
| // Stream modes |
| case ao_status_stream_run_wait: |
| if (!instance->fifo) { |
| PERROR_CRITICAL |
| ("Streaming on single device! This feature is not implemented in this version!\n"); |
| instance->status = ao_status_stream_error; |
| // Signal the end. |
| signaling = 1; |
| break; |
| } |
| |
| if (status & ME4600_AO_STATUS_BIT_FSM) { // State machine is working. Waiting for start finish. |
| instance->status = ao_status_stream_run; |
| |
| // Signal end of this step |
| signaling = 1; |
| } else { // State machine is not working. |
| if (!(status & ME4600_AO_STATUS_BIT_EF)) { // FIFO is empty. Procedure has started and finish already! |
| instance->status = ao_status_stream_end; |
| |
| // Signal the end. |
| signaling = 1; |
| // Wait 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. Leave FIFO untouched! |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| ctrl = inl(instance->ctrl_reg); |
| ctrl |= |
| ME4600_AO_CTRL_BIT_STOP | |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP | |
| ME4600_AO_CTRL_BIT_RESET_IRQ; |
| ctrl &= |
| ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ | |
| ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG); |
| 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->subdevice_lock, |
| cpu_flags); |
| spin_lock(instance->preload_reg_lock); |
| //Remove from synchronous start. Block triggering from this output. |
| synch = inl(instance->preload_reg); |
| synch &= |
| ~((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << |
| instance->ao_idx); |
| outl(synch, instance->preload_reg); |
| PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", |
| instance->reg_base, |
| instance->preload_reg - instance->reg_base, |
| synch); |
| spin_unlock(instance->preload_reg_lock); |
| |
| instance->status = ao_status_stream_end; |
| |
| // Signal the end. |
| signaling = 1; |
| } |
| // Wait for stop. |
| reschedule = 1; |
| break; |
| |
| case ao_status_stream_run: |
| if (!instance->fifo) { |
| PERROR_CRITICAL |
| ("Streaming on single device! This feature is not implemented in this version!\n"); |
| instance->status = ao_status_stream_error; |
| // Signal the end. |
| signaling = 1; |
| break; |
| } |
| |
| if (!(status & ME4600_AO_STATUS_BIT_FSM)) { // State machine is not working. This is an error. |
| // BROKEN PIPE! |
| if (!(status & ME4600_AO_STATUS_BIT_EF)) { // FIFO is empty. |
| if (me_circ_buf_values(&instance->circ_buf)) { // Software buffer is not empty. |
| if (instance->stop_data_count && (instance->stop_data_count <= instance->data_count)) { //Finishing work. Requed data shown. |
| PDEBUG |
| ("ISM stoped. No data in FIFO. Buffer is not empty.\n"); |
| instance->status = |
| ao_status_stream_end; |
| } else { |
| PERROR |
| ("Output stream has been broken. ISM stoped. No data in FIFO. Buffer is not empty.\n"); |
| instance->status = |
| ao_status_stream_buffer_error; |
| } |
| } else { // Software buffer is empty. |
| PDEBUG |
| ("ISM stoped. No data in FIFO. Buffer is empty.\n"); |
| instance->status = ao_status_stream_end; |
| } |
| } else { // There are still datas in FIFO. |
| if (me_circ_buf_values(&instance->circ_buf)) { // Software buffer is not empty. |
| PERROR |
| ("Output stream has been broken. ISM stoped but some data in FIFO and buffer.\n"); |
| } else { // Software buffer is empty. |
| PERROR |
| ("Output stream has been broken. ISM stoped but some data in FIFO. Buffer is empty.\n"); |
| } |
| instance->status = ao_status_stream_fifo_error; |
| |
| } |
| |
| // Signal the failure. |
| signaling = 1; |
| break; |
| } |
| // Wait for stop. |
| reschedule = 1; |
| break; |
| |
| case ao_status_stream_end_wait: |
| if (!instance->fifo) { |
| PERROR_CRITICAL |
| ("Streaming on single device! This feature is not implemented in this version!\n"); |
| instance->status = ao_status_stream_error; |
| // Signal the end. |
| signaling = 1; |
| break; |
| } |
| |
| if (!(status & ME4600_AO_STATUS_BIT_FSM)) { // State machine is not working. Waiting for stop finish. |
| instance->status = ao_status_stream_end; |
| signaling = 1; |
| } |
| // State machine is working. |
| reschedule = 1; |
| break; |
| |
| default: |
| PERROR_CRITICAL("Status is in wrong state (%d)!\n", |
| instance->status); |
| instance->status = ao_status_stream_error; |
| // Signal the end. |
| signaling = 1; |
| break; |
| |
| } |
| |
| if (signaling) { //Signal it. |
| wake_up_interruptible_all(&instance->wait_queue); |
| } |
| |
| if (instance->ao_control_task_flag && reschedule) { // Reschedule task |
| queue_delayed_work(instance->me4600_workqueue, |
| &instance->ao_control_task, 1); |
| } else { |
| PINFO("<%s> Ending control task.\n", __func__); |
| } |
| |
| } |
| #else |
| /// @note SPECIAL BUILD FOR BOSCH |
| /// @author Guenter Gebhardt |
| static int me4600_ao_io_reset_subdevice(me_subdevice_t * subdevice, |
| struct file *filep, int flags) |
| { |
| me4600_ao_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| uint32_t tmp; |
| unsigned long status; |
| |
| PDEBUG("executed.\n"); |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| ME_SUBDEVICE_ENTER spin_lock_irqsave(&instance->subdevice_lock, status); |
| spin_lock(instance->preload_reg_lock); |
| tmp = inl(instance->preload_reg); |
| tmp &= ~(0x10001 << instance->ao_idx); |
| outl(tmp, instance->preload_reg); |
| *instance->preload_flags &= ~(0x1 << instance->ao_idx); |
| spin_unlock(instance->preload_reg_lock); |
| |
| tmp = inl(instance->ctrl_reg); |
| tmp |= ME4600_AO_CTRL_BIT_STOP | ME4600_AO_CTRL_BIT_IMMEDIATE_STOP; |
| outl(tmp, instance->ctrl_reg); |
| |
| while (inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM) ; |
| |
| outl(ME4600_AO_CTRL_BIT_STOP | ME4600_AO_CTRL_BIT_IMMEDIATE_STOP, |
| instance->ctrl_reg); |
| |
| outl(0x8000, instance->single_reg); |
| |
| instance->single_value = 0x8000; |
| instance->circ_buf.head = 0; |
| instance->circ_buf.tail = 0; |
| |
| spin_unlock_irqrestore(&instance->subdevice_lock, status); |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| static int me4600_ao_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_ao_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| uint32_t tmp; |
| unsigned long cpu_flags; |
| |
| PDEBUG("executed.\n"); |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| ME_SUBDEVICE_ENTER |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| |
| if (inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM) { |
| PERROR("Subdevice is busy.\n"); |
| err = ME_ERRNO_SUBDEVICE_BUSY; |
| goto ERROR; |
| } |
| |
| if (channel == 0) { |
| if (single_config == 0) { |
| if (ref == ME_REF_AO_GROUND) { |
| if (trig_chan == ME_TRIG_CHAN_DEFAULT) { |
| if (trig_type == ME_TRIG_TYPE_SW) { |
| tmp = inl(instance->ctrl_reg); |
| tmp |= |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP; |
| outl(tmp, instance->ctrl_reg); |
| tmp = |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP; |
| outl(tmp, instance->ctrl_reg); |
| |
| spin_lock(instance-> |
| preload_reg_lock); |
| tmp = |
| inl(instance->preload_reg); |
| tmp &= |
| ~(0x10001 << instance-> |
| ao_idx); |
| outl(tmp, |
| instance->preload_reg); |
| *instance->preload_flags &= |
| ~(0x1 << instance->ao_idx); |
| spin_unlock(instance-> |
| preload_reg_lock); |
| } else if (trig_type == |
| ME_TRIG_TYPE_EXT_DIGITAL) { |
| if (trig_edge == |
| ME_TRIG_EDGE_RISING) { |
| tmp = |
| inl(instance-> |
| ctrl_reg); |
| tmp |= |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP; |
| outl(tmp, |
| instance-> |
| ctrl_reg); |
| tmp = |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP |
| | |
| ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG; |
| outl(tmp, |
| instance-> |
| ctrl_reg); |
| } else if (trig_edge == |
| ME_TRIG_EDGE_FALLING) |
| { |
| tmp = |
| inl(instance-> |
| ctrl_reg); |
| tmp |= |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP; |
| outl(tmp, |
| instance-> |
| ctrl_reg); |
| tmp = |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP |
| | |
| ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG |
| | |
| ME4600_AO_CTRL_BIT_EX_TRIG_EDGE; |
| outl(tmp, |
| instance-> |
| ctrl_reg); |
| } else if (trig_edge == |
| ME_TRIG_EDGE_ANY) { |
| tmp = |
| inl(instance-> |
| ctrl_reg); |
| tmp |= |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP; |
| outl(tmp, |
| instance-> |
| ctrl_reg); |
| tmp = |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP |
| | |
| ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG |
| | |
| ME4600_AO_CTRL_BIT_EX_TRIG_EDGE |
| | |
| ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH; |
| outl(tmp, |
| instance-> |
| ctrl_reg); |
| } else { |
| PERROR |
| ("Invalid trigger edge.\n"); |
| err = |
| ME_ERRNO_INVALID_TRIG_EDGE; |
| goto ERROR; |
| } |
| |
| spin_lock(instance-> |
| preload_reg_lock); |
| |
| tmp = |
| inl(instance->preload_reg); |
| tmp &= |
| ~(0x10001 << instance-> |
| ao_idx); |
| tmp |= 0x1 << instance->ao_idx; |
| outl(tmp, |
| instance->preload_reg); |
| *instance->preload_flags &= |
| ~(0x1 << instance->ao_idx); |
| spin_unlock(instance-> |
| preload_reg_lock); |
| } else { |
| PERROR |
| ("Invalid trigger type.\n"); |
| err = |
| ME_ERRNO_INVALID_TRIG_TYPE; |
| goto ERROR; |
| } |
| } else if (trig_chan == |
| ME_TRIG_CHAN_SYNCHRONOUS) { |
| if (trig_type == ME_TRIG_TYPE_SW) { |
| tmp = inl(instance->ctrl_reg); |
| tmp |= |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP; |
| outl(tmp, instance->ctrl_reg); |
| tmp = |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP; |
| outl(tmp, instance->ctrl_reg); |
| |
| spin_lock(instance-> |
| preload_reg_lock); |
| tmp = |
| inl(instance->preload_reg); |
| tmp &= |
| ~(0x10001 << instance-> |
| ao_idx); |
| tmp |= 0x1 << instance->ao_idx; |
| outl(tmp, |
| instance->preload_reg); |
| *instance->preload_flags |= |
| 0x1 << instance->ao_idx; |
| spin_unlock(instance-> |
| preload_reg_lock); |
| } else if (trig_type == |
| ME_TRIG_TYPE_EXT_DIGITAL) { |
| if (trig_edge == |
| ME_TRIG_EDGE_RISING) { |
| tmp = |
| inl(instance-> |
| ctrl_reg); |
| tmp |= |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP; |
| outl(tmp, |
| instance-> |
| ctrl_reg); |
| tmp = |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP; |
| outl(tmp, |
| instance-> |
| ctrl_reg); |
| } else if (trig_edge == |
| ME_TRIG_EDGE_FALLING) |
| { |
| tmp = |
| inl(instance-> |
| ctrl_reg); |
| tmp |= |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP; |
| outl(tmp, |
| instance-> |
| ctrl_reg); |
| tmp = |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP |
| | |
| ME4600_AO_CTRL_BIT_EX_TRIG_EDGE; |
| outl(tmp, |
| instance-> |
| ctrl_reg); |
| } else if (trig_edge == |
| ME_TRIG_EDGE_ANY) { |
| tmp = |
| inl(instance-> |
| ctrl_reg); |
| tmp |= |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP; |
| outl(tmp, |
| instance-> |
| ctrl_reg); |
| tmp = |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP |
| | |
| ME4600_AO_CTRL_BIT_EX_TRIG_EDGE |
| | |
| ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH; |
| outl(tmp, |
| instance-> |
| ctrl_reg); |
| } else { |
| PERROR |
| ("Invalid trigger edge.\n"); |
| err = |
| ME_ERRNO_INVALID_TRIG_EDGE; |
| goto ERROR; |
| } |
| |
| spin_lock(instance-> |
| preload_reg_lock); |
| |
| tmp = |
| inl(instance->preload_reg); |
| tmp |= |
| 0x10001 << instance->ao_idx; |
| outl(tmp, |
| instance->preload_reg); |
| *instance->preload_flags &= |
| ~(0x1 << instance->ao_idx); |
| spin_unlock(instance-> |
| preload_reg_lock); |
| } else { |
| PERROR |
| ("Invalid trigger type.\n"); |
| err = |
| ME_ERRNO_INVALID_TRIG_TYPE; |
| goto ERROR; |
| } |
| } else { |
| PERROR |
| ("Invalid trigger channel specified.\n"); |
| err = ME_ERRNO_INVALID_REF; |
| goto ERROR; |
| } |
| } else { |
| PERROR("Invalid analog reference specified.\n"); |
| err = ME_ERRNO_INVALID_REF; |
| goto ERROR; |
| } |
| } else { |
| PERROR("Invalid single config specified.\n"); |
| err = ME_ERRNO_INVALID_SINGLE_CONFIG; |
| goto ERROR; |
| } |
| } else { |
| PERROR("Invalid channel number specified.\n"); |
| err = ME_ERRNO_INVALID_CHANNEL; |
| goto ERROR; |
| } |
| |
| ERROR: |
| |
| spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags); |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| static int me4600_ao_io_single_read(me_subdevice_t * subdevice, |
| struct file *filep, |
| int channel, |
| int *value, int time_out, int flags) |
| { |
| me4600_ao_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| unsigned long tmp; |
| unsigned long cpu_flags; |
| |
| PDEBUG("executed.\n"); |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| if (channel != 0) { |
| PERROR("Invalid channel number specified.\n"); |
| return ME_ERRNO_INVALID_CHANNEL; |
| } |
| |
| ME_SUBDEVICE_ENTER |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| tmp = inl(instance->ctrl_reg); |
| |
| if (tmp & 0x3) { |
| PERROR("Not in single mode.\n"); |
| err = ME_ERRNO_PREVIOUS_CONFIG; |
| } else { |
| *value = instance->single_value; |
| } |
| |
| spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags); |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| static int me4600_ao_io_single_write(me_subdevice_t * subdevice, |
| struct file *filep, |
| int channel, |
| int value, int time_out, int flags) |
| { |
| me4600_ao_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| unsigned long mask = 0; |
| unsigned long tmp; |
| unsigned long cpu_flags; |
| int i; |
| wait_queue_head_t queue; |
| unsigned long j; |
| unsigned long delay = 0; |
| |
| PDEBUG("executed.\n"); |
| |
| init_waitqueue_head(&queue); |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| if (channel != 0) { |
| PERROR("Invalid channel number specified.\n"); |
| return ME_ERRNO_INVALID_CHANNEL; |
| } |
| |
| 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->subdevice_lock, cpu_flags); |
| |
| tmp = inl(instance->ctrl_reg); |
| |
| if (tmp & 0x3) { |
| spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags); |
| PERROR("Not in single mode.\n"); |
| err = ME_ERRNO_PREVIOUS_CONFIG; |
| goto ERROR; |
| } |
| |
| if (tmp & ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG) { |
| outl(value, instance->single_reg); |
| instance->single_value = value; |
| spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags); |
| |
| if (!(flags & ME_IO_SINGLE_TYPE_WRITE_NONBLOCKING)) { |
| j = jiffies; |
| |
| while (inl(instance->status_reg) & |
| ME4600_AO_STATUS_BIT_FSM) { |
| interruptible_sleep_on_timeout(&queue, 1); |
| |
| if (signal_pending(current)) { |
| PERROR |
| ("Wait on external trigger interrupted by signal.\n"); |
| err = ME_ERRNO_SIGNAL; |
| goto ERROR; |
| } |
| |
| if (delay && ((jiffies - j) > delay)) { |
| PERROR("Timeout reached.\n"); |
| err = ME_ERRNO_TIMEOUT; |
| goto ERROR; |
| } |
| } |
| } |
| } else if ((inl(instance->preload_reg) & (0x10001 << instance->ao_idx)) |
| == (0x10001 << instance->ao_idx)) { |
| if (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) { |
| tmp |= ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG; |
| outl(tmp, instance->ctrl_reg); |
| outl(value, instance->single_reg); |
| instance->single_value = value; |
| spin_unlock_irqrestore(&instance->subdevice_lock, |
| cpu_flags); |
| |
| if (!(flags & ME_IO_SINGLE_TYPE_WRITE_NONBLOCKING)) { |
| j = jiffies; |
| |
| while (inl(instance->status_reg) & |
| ME4600_AO_STATUS_BIT_FSM) { |
| interruptible_sleep_on_timeout(&queue, |
| 1); |
| |
| if (signal_pending(current)) { |
| PERROR |
| ("Wait on external trigger interrupted by signal.\n"); |
| err = ME_ERRNO_SIGNAL; |
| goto ERROR; |
| } |
| |
| if (delay && ((jiffies - j) > delay)) { |
| PERROR("Timeout reached.\n"); |
| err = ME_ERRNO_TIMEOUT; |
| goto ERROR; |
| } |
| } |
| } |
| } else { |
| outl(value, instance->single_reg); |
| instance->single_value = value; |
| spin_unlock_irqrestore(&instance->subdevice_lock, |
| cpu_flags); |
| } |
| } else if ((inl(instance->preload_reg) & (0x10001 << instance->ao_idx)) |
| == (0x1 << instance->ao_idx)) { |
| outl(value, instance->single_reg); |
| instance->single_value = value; |
| |
| PDEBUG("Synchronous SW, flags = 0x%X.\n", flags); |
| |
| if (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) { |
| PDEBUG("Trigger synchronous SW.\n"); |
| spin_lock(instance->preload_reg_lock); |
| tmp = inl(instance->preload_reg); |
| |
| for (i = 0; i < ME4600_AO_MAX_SUBDEVICES; i++) { |
| if ((*instance->preload_flags & (0x1 << i))) { |
| if ((tmp & (0x10001 << i)) == |
| (0x1 << i)) { |
| mask |= 0x1 << i; |
| } |
| } |
| } |
| |
| tmp &= ~(mask); |
| |
| outl(tmp, instance->preload_reg); |
| spin_unlock(instance->preload_reg_lock); |
| } |
| |
| spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags); |
| } else { |
| outl(value, instance->single_reg); |
| instance->single_value = value; |
| spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags); |
| } |
| |
| ERROR: |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| static int me4600_ao_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_ao_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| unsigned long ctrl; |
| unsigned long tmp; |
| unsigned long cpu_flags; |
| uint64_t conv_ticks; |
| unsigned int conv_start_ticks_low = trigger->iConvStartTicksLow; |
| unsigned int conv_start_ticks_high = trigger->iConvStartTicksHigh; |
| |
| PDEBUG("executed.\n"); |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| conv_ticks = |
| (uint64_t) conv_start_ticks_low + |
| ((uint64_t) conv_start_ticks_high << 32); |
| |
| if (!instance->fifo) { |
| PERROR("Not a streaming ao.\n"); |
| return ME_ERRNO_NOT_SUPPORTED; |
| } |
| |
| ME_SUBDEVICE_ENTER |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| |
| if ((inl(instance->status_reg)) & ME4600_AO_STATUS_BIT_FSM) { |
| PERROR("Subdevice is busy.\n"); |
| err = ME_ERRNO_SUBDEVICE_BUSY; |
| goto ERROR; |
| } |
| |
| ctrl = inl(instance->ctrl_reg); |
| ctrl |= ME4600_AO_CTRL_BIT_IMMEDIATE_STOP; |
| outl(ctrl, instance->ctrl_reg); |
| ctrl = ME4600_AO_CTRL_BIT_IMMEDIATE_STOP; |
| outl(ctrl, instance->ctrl_reg); |
| |
| if (count != 1) { |
| PERROR("Invalid stream configuration list count specified.\n"); |
| err = ME_ERRNO_INVALID_CONFIG_LIST_COUNT; |
| goto ERROR; |
| } |
| |
| if (config_list[0].iChannel != 0) { |
| PERROR("Invalid channel number specified.\n"); |
| err = ME_ERRNO_INVALID_CHANNEL; |
| goto ERROR; |
| } |
| |
| if (config_list[0].iStreamConfig != 0) { |
| PERROR("Invalid stream config specified.\n"); |
| err = ME_ERRNO_INVALID_STREAM_CONFIG; |
| goto ERROR; |
| } |
| |
| if (config_list[0].iRef != ME_REF_AO_GROUND) { |
| PERROR("Invalid analog reference.\n"); |
| err = ME_ERRNO_INVALID_REF; |
| goto ERROR; |
| } |
| |
| if ((trigger->iAcqStartTicksLow != 0) |
| || (trigger->iAcqStartTicksHigh != 0)) { |
| PERROR |
| ("Invalid acquisition start trigger argument specified.\n"); |
| err = ME_ERRNO_INVALID_ACQ_START_ARG; |
| goto ERROR; |
| } |
| |
| switch (trigger->iAcqStartTrigType) { |
| |
| case ME_TRIG_TYPE_SW: |
| break; |
| |
| case ME_TRIG_TYPE_EXT_DIGITAL: |
| ctrl |= ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG; |
| |
| switch (trigger->iAcqStartTrigEdge) { |
| |
| case ME_TRIG_EDGE_RISING: |
| break; |
| |
| case ME_TRIG_EDGE_FALLING: |
| ctrl |= ME4600_AO_CTRL_BIT_EX_TRIG_EDGE; |
| |
| break; |
| |
| case ME_TRIG_EDGE_ANY: |
| ctrl |= |
| ME4600_AO_CTRL_BIT_EX_TRIG_EDGE | |
| ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH; |
| |
| break; |
| |
| default: |
| PERROR |
| ("Invalid acquisition start trigger edge specified.\n"); |
| |
| err = ME_ERRNO_INVALID_ACQ_START_TRIG_EDGE; |
| |
| goto ERROR; |
| |
| break; |
| } |
| |
| break; |
| |
| default: |
| PERROR("Invalid acquisition start trigger type specified.\n"); |
| |
| err = ME_ERRNO_INVALID_ACQ_START_TRIG_TYPE; |
| |
| goto ERROR; |
| |
| break; |
| } |
| |
| switch (trigger->iScanStartTrigType) { |
| |
| 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_AO_MIN_CHAN_TICKS) |
| || (conv_ticks > ME4600_AO_MAX_CHAN_TICKS)) { |
| PERROR |
| ("Invalid conv start trigger argument specified.\n"); |
| err = ME_ERRNO_INVALID_CONV_START_ARG; |
| goto ERROR; |
| } |
| |
| break; |
| |
| default: |
| PERROR("Invalid conv start trigger type specified.\n"); |
| |
| err = ME_ERRNO_INVALID_CONV_START_TRIG_TYPE; |
| |
| goto ERROR; |
| |
| break; |
| } |
| |
| /* Preset to hardware wraparound mode */ |
| instance->flags &= ~(ME4600_AO_FLAGS_SW_WRAP_MODE_MASK); |
| |
| switch (trigger->iScanStopTrigType) { |
| |
| case ME_TRIG_TYPE_NONE: |
| if (flags & ME_IO_STREAM_CONFIG_WRAPAROUND) { |
| /* Set flags to indicate usage of software mode. */ |
| instance->flags |= ME4600_AO_FLAGS_SW_WRAP_MODE_INF; |
| instance->wrap_count = 0; |
| instance->wrap_remaining = 0; |
| } |
| |
| break; |
| |
| case ME_TRIG_TYPE_COUNT: |
| if (flags & ME_IO_STREAM_CONFIG_WRAPAROUND) { |
| if (trigger->iScanStopCount <= 0) { |
| PERROR("Invalid scan stop count specified.\n"); |
| err = ME_ERRNO_INVALID_SCAN_STOP_ARG; |
| goto ERROR; |
| } |
| |
| /* Set flags to indicate usage of software mode. */ |
| instance->flags |= ME4600_AO_FLAGS_SW_WRAP_MODE_FIN; |
| instance->wrap_count = trigger->iScanStopCount; |
| instance->wrap_remaining = trigger->iScanStopCount; |
| } else { |
| PERROR("Invalid scan stop trigger type specified.\n"); |
| err = ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE; |
| 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: |
| 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 (flags & ME_IO_STREAM_CONFIG_WRAPAROUND) { |
| if (trigger->iAcqStopCount <= 0) { |
| PERROR("Invalid acq stop count specified.\n"); |
| err = ME_ERRNO_INVALID_ACQ_STOP_ARG; |
| goto ERROR; |
| } |
| |
| /* Set flags to indicate usage of software mode. */ |
| instance->flags |= ME4600_AO_FLAGS_SW_WRAP_MODE_FIN; |
| instance->wrap_count = trigger->iAcqStopCount; |
| instance->wrap_remaining = trigger->iAcqStopCount; |
| } else { |
| PERROR("Invalid acp stop trigger type specified.\n"); |
| err = ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE; |
| goto ERROR; |
| } |
| |
| break; |
| |
| default: |
| PERROR("Invalid acq stop trigger type specified.\n"); |
| err = ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE; |
| goto ERROR; |
| break; |
| } |
| |
| switch (trigger->iAcqStartTrigChan) { |
| |
| case ME_TRIG_CHAN_DEFAULT: |
| spin_lock(instance->preload_reg_lock); |
| tmp = inl(instance->preload_reg); |
| tmp &= ~(0x10001 << instance->ao_idx); |
| outl(tmp, instance->preload_reg); |
| spin_unlock(instance->preload_reg_lock); |
| |
| break; |
| |
| case ME_TRIG_CHAN_SYNCHRONOUS: |
| if (trigger->iAcqStartTrigType == ME_TRIG_TYPE_SW) { |
| spin_lock(instance->preload_reg_lock); |
| tmp = inl(instance->preload_reg); |
| tmp &= ~(0x10001 << instance->ao_idx); |
| outl(tmp, instance->preload_reg); |
| tmp |= 0x1 << instance->ao_idx; |
| outl(tmp, instance->preload_reg); |
| spin_unlock(instance->preload_reg_lock); |
| } else { |
| ctrl &= ~(ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG); |
| spin_lock(instance->preload_reg_lock); |
| tmp = inl(instance->preload_reg); |
| tmp &= ~(0x10001 << instance->ao_idx); |
| outl(tmp, instance->preload_reg); |
| tmp |= 0x10000 << instance->ao_idx; |
| outl(tmp, instance->preload_reg); |
| spin_unlock(instance->preload_reg_lock); |
| } |
| |
| break; |
| |
| default: |
| PERROR("Invalid acq start trigger channel specified.\n"); |
| err = ME_ERRNO_INVALID_ACQ_START_TRIG_CHAN; |
| goto ERROR; |
| |
| break; |
| } |
| |
| outl(conv_ticks - 2, instance->timer_reg); |
| |
| if (flags & ME_IO_STREAM_CONFIG_BIT_PATTERN) { |
| if (instance->ao_idx == 3) { |
| ctrl |= ME4600_AO_CTRL_BIT_ENABLE_DO; |
| } else { |
| err = ME_ERRNO_INVALID_FLAGS; |
| goto ERROR; |
| } |
| } else { |
| if (instance->ao_idx == 3) { |
| ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_DO; |
| } |
| } |
| |
| /* Set hardware mode. */ |
| if (flags & ME_IO_STREAM_CONFIG_WRAPAROUND) { |
| ctrl |= ME4600_AO_CTRL_BIT_MODE_0; |
| } else { |
| ctrl |= ME4600_AO_CTRL_BIT_MODE_1; |
| } |
| |
| PDEBUG("Preload word = 0x%X.\n", inl(instance->preload_reg)); |
| |
| PDEBUG("Ctrl word = 0x%lX.\n", ctrl); |
| outl(ctrl, instance->ctrl_reg); // Write the control word |
| |
| ERROR: |
| |
| spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags); |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| static int me4600_ao_io_stream_new_values(me_subdevice_t * subdevice, |
| struct file *filep, |
| int time_out, int *count, int flags) |
| { |
| me4600_ao_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| long t = 0; |
| long j; |
| |
| PDEBUG("executed.\n"); |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| if (!instance->fifo) { |
| PERROR("Not a streaming ao.\n"); |
| return ME_ERRNO_NOT_SUPPORTED; |
| } |
| |
| 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; |
| } |
| |
| *count = 0; |
| |
| ME_SUBDEVICE_ENTER; |
| |
| if (t) { |
| j = jiffies; |
| wait_event_interruptible_timeout(instance->wait_queue, |
| ((me_circ_buf_space |
| (&instance->circ_buf)) |
| || !(inl(instance->status_reg) |
| & |
| ME4600_AO_STATUS_BIT_FSM)), |
| t); |
| |
| if (!(inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM)) { |
| PERROR("AO subdevice is not running.\n"); |
| err = ME_ERRNO_SUBDEVICE_NOT_RUNNING; |
| } else if (signal_pending(current)) { |
| PERROR("Wait on values interrupted from signal.\n"); |
| err = ME_ERRNO_SIGNAL; |
| } else if ((jiffies - j) >= t) { |
| PERROR("Wait on values timed out.\n"); |
| err = ME_ERRNO_TIMEOUT; |
| } else { |
| *count = me_circ_buf_space(&instance->circ_buf); |
| } |
| } else { |
| wait_event_interruptible(instance->wait_queue, |
| ((me_circ_buf_space |
| (&instance->circ_buf)) |
| || !(inl(instance->status_reg) & |
| ME4600_AO_STATUS_BIT_FSM))); |
| |
| if (!(inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM)) { |
| PERROR("AO subdevice is not running.\n"); |
| err = ME_ERRNO_SUBDEVICE_NOT_RUNNING; |
| } else if (signal_pending(current)) { |
| PERROR("Wait on values interrupted from signal.\n"); |
| err = ME_ERRNO_SIGNAL; |
| } else { |
| *count = me_circ_buf_space(&instance->circ_buf); |
| } |
| } |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| static void stop_immediately(me4600_ao_subdevice_t * instance) |
| { |
| unsigned long cpu_flags; |
| uint32_t tmp; |
| |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| tmp = inl(instance->ctrl_reg); |
| tmp |= ME4600_AO_CTRL_BIT_STOP | ME4600_AO_CTRL_BIT_IMMEDIATE_STOP; |
| outl(tmp, instance->ctrl_reg); |
| |
| while (inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM) ; |
| |
| spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags); |
| } |
| |
| static int me4600_ao_io_stream_start(me_subdevice_t * subdevice, |
| struct file *filep, |
| int start_mode, int time_out, int flags) |
| { |
| me4600_ao_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| unsigned long cpu_flags = 0; |
| unsigned long ref; |
| unsigned long tmp; |
| unsigned long delay = 0; |
| wait_queue_head_t queue; |
| |
| PDEBUG("executed.\n"); |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| init_waitqueue_head(&queue); |
| |
| 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; |
| } |
| |
| if (!instance->fifo) { |
| PERROR("Not a streaming ao.\n"); |
| return ME_ERRNO_NOT_SUPPORTED; |
| } |
| |
| ME_SUBDEVICE_ENTER |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| |
| tmp = inl(instance->ctrl_reg); |
| |
| switch (tmp & (ME4600_AO_CTRL_MASK_MODE)) { |
| |
| case 0: // Single mode |
| spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags); |
| PERROR("Subdevice is configured in single mode.\n"); |
| err = ME_ERRNO_PREVIOUS_CONFIG; |
| goto ERROR; |
| |
| case 1: // Wraparound mode |
| if (tmp & ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG) { // Normal wraparound with external trigger |
| |
| if ((inl(instance->status_reg) & |
| ME4600_AO_STATUS_BIT_FSM)) { |
| spin_unlock_irqrestore(&instance-> |
| subdevice_lock, |
| cpu_flags); |
| PERROR("Conversion is already running.\n"); |
| err = ME_ERRNO_SUBDEVICE_BUSY; |
| goto ERROR; |
| } |
| |
| tmp &= |
| ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ | |
| ME4600_AO_CTRL_BIT_STOP | |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP); |
| |
| outl(tmp, instance->ctrl_reg); |
| spin_unlock_irqrestore(&instance->subdevice_lock, |
| cpu_flags); |
| |
| if (start_mode == ME_START_MODE_BLOCKING) { |
| init_waitqueue_head(&queue); |
| |
| if (delay) { |
| ref = jiffies; |
| |
| while (! |
| (inl(instance->status_reg) & |
| ME4600_AO_STATUS_BIT_FSM)) { |
| interruptible_sleep_on_timeout |
| (&queue, 1); |
| |
| if (signal_pending(current)) { |
| PERROR |
| ("Wait on start of state machine interrupted.\n"); |
| stop_immediately |
| (instance); |
| err = ME_ERRNO_SIGNAL; |
| goto ERROR; |
| } |
| |
| if (((jiffies - ref) >= delay)) { |
| PERROR |
| ("Timeout reached.\n"); |
| stop_immediately |
| (instance); |
| err = ME_ERRNO_TIMEOUT; |
| goto ERROR; |
| } |
| } |
| } else { |
| while (! |
| (inl(instance->status_reg) & |
| ME4600_AO_STATUS_BIT_FSM)) { |
| interruptible_sleep_on_timeout |
| (&queue, 1); |
| |
| if (signal_pending(current)) { |
| PERROR |
| ("Wait on start of state machine interrupted.\n"); |
| stop_immediately |
| (instance); |
| err = ME_ERRNO_SIGNAL; |
| goto ERROR; |
| } |
| } |
| } |
| } else if (start_mode == ME_START_MODE_NONBLOCKING) { |
| } else { |
| PERROR("Invalid start mode specified.\n"); |
| err = ME_ERRNO_INVALID_START_MODE; |
| goto ERROR; |
| } |
| } else if ((inl(instance->preload_reg) & (0x10001 << instance->ao_idx)) == (0x10000 << instance->ao_idx)) { // Synchronous with external trigger |
| |
| if ((inl(instance->status_reg) & |
| ME4600_AO_STATUS_BIT_FSM)) { |
| spin_unlock_irqrestore(&instance-> |
| subdevice_lock, |
| cpu_flags); |
| PERROR("Conversion is already running.\n"); |
| err = ME_ERRNO_SUBDEVICE_BUSY; |
| goto ERROR; |
| } |
| |
| if (flags & ME_IO_STREAM_START_TYPE_TRIG_SYNCHRONOUS) { |
| tmp |= ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG; |
| tmp &= |
| ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ | |
| ME4600_AO_CTRL_BIT_STOP | |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP); |
| outl(tmp, instance->ctrl_reg); |
| spin_unlock_irqrestore(&instance-> |
| subdevice_lock, |
| cpu_flags); |
| |
| if (start_mode == ME_START_MODE_BLOCKING) { |
| init_waitqueue_head(&queue); |
| |
| if (delay) { |
| ref = jiffies; |
| |
| while (! |
| (inl |
| (instance-> |
| status_reg) & |
| ME4600_AO_STATUS_BIT_FSM)) |
| { |
| interruptible_sleep_on_timeout |
| (&queue, 1); |
| |
| if (signal_pending |
| (current)) { |
| PERROR |
| ("Wait on start of state machine interrupted.\n"); |
| stop_immediately |
| (instance); |
| err = |
| ME_ERRNO_SIGNAL; |
| goto ERROR; |
| } |
| |
| if (((jiffies - ref) >= |
| delay)) { |
| PERROR |
| ("Timeout reached.\n"); |
| stop_immediately |
| (instance); |
| err = |
| ME_ERRNO_TIMEOUT; |
| goto ERROR; |
| } |
| } |
| } else { |
| while (! |
| (inl |
| (instance-> |
| status_reg) & |
| ME4600_AO_STATUS_BIT_FSM)) |
| { |
| interruptible_sleep_on_timeout |
| (&queue, 1); |
| |
| if (signal_pending |
| (current)) { |
| PERROR |
| ("Wait on start of state machine interrupted.\n"); |
| stop_immediately |
| (instance); |
| err = |
| ME_ERRNO_SIGNAL; |
| goto ERROR; |
| } |
| } |
| } |
| } else if (start_mode == |
| ME_START_MODE_NONBLOCKING) { |
| } else { |
| PERROR |
| ("Invalid start mode specified.\n"); |
| err = ME_ERRNO_INVALID_START_MODE; |
| goto ERROR; |
| } |
| } else { |
| tmp &= |
| ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ | |
| ME4600_AO_CTRL_BIT_STOP | |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP); |
| outl(tmp, instance->ctrl_reg); |
| spin_unlock_irqrestore(&instance-> |
| subdevice_lock, |
| cpu_flags); |
| } |
| } else if ((inl(instance->preload_reg) & (0x10001 << instance->ao_idx)) == (0x1 << instance->ao_idx)) { // Synchronous wraparound with sw trigger |
| |
| if ((inl(instance->status_reg) & |
| ME4600_AO_STATUS_BIT_FSM)) { |
| spin_unlock_irqrestore(&instance-> |
| subdevice_lock, |
| cpu_flags); |
| PERROR("Conversion is already running.\n"); |
| err = ME_ERRNO_SUBDEVICE_BUSY; |
| goto ERROR; |
| } |
| |
| tmp &= |
| ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ | |
| ME4600_AO_CTRL_BIT_STOP | |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP); |
| |
| outl(tmp, instance->ctrl_reg); |
| |
| if (flags & ME_IO_STREAM_START_TYPE_TRIG_SYNCHRONOUS) { |
| outl(0x8000, instance->single_reg); |
| instance->single_value = 0x8000; |
| } |
| |
| spin_unlock_irqrestore(&instance->subdevice_lock, |
| cpu_flags); |
| } else { // Software start |
| |
| if ((inl(instance->status_reg) & |
| ME4600_AO_STATUS_BIT_FSM)) { |
| spin_unlock_irqrestore(&instance-> |
| subdevice_lock, |
| cpu_flags); |
| PERROR("Conversion is already running.\n"); |
| err = ME_ERRNO_SUBDEVICE_BUSY; |
| goto ERROR; |
| } |
| |
| tmp &= |
| ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ | |
| ME4600_AO_CTRL_BIT_STOP | |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP); |
| |
| outl(tmp, instance->ctrl_reg); |
| |
| outl(0x8000, instance->single_reg); |
| instance->single_value = 0x8000; |
| |
| spin_unlock_irqrestore(&instance->subdevice_lock, |
| cpu_flags); |
| } |
| |
| break; |
| |
| case 2: // Continuous mode |
| if (tmp & ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG) { // Externally triggered |
| |
| if ((inl(instance->status_reg) & |
| ME4600_AO_STATUS_BIT_FSM)) { |
| spin_unlock_irqrestore(&instance-> |
| subdevice_lock, |
| cpu_flags); |
| PERROR("Conversion is already running.\n"); |
| err = ME_ERRNO_SUBDEVICE_BUSY; |
| goto ERROR; |
| } |
| |
| tmp &= |
| ~(ME4600_AO_CTRL_BIT_STOP | |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP); |
| tmp |= ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| outl(tmp, instance->ctrl_reg); |
| instance->wrap_remaining = instance->wrap_count; |
| instance->circ_buf.tail = 0; |
| spin_unlock_irqrestore(&instance->subdevice_lock, |
| cpu_flags); |
| |
| if (start_mode == ME_START_MODE_BLOCKING) { |
| init_waitqueue_head(&queue); |
| |
| if (delay) { |
| ref = jiffies; |
| |
| while (! |
| (inl(instance->status_reg) & |
| ME4600_AO_STATUS_BIT_FSM)) { |
| interruptible_sleep_on_timeout |
| (&queue, 1); |
| |
| if (signal_pending(current)) { |
| PERROR |
| ("Wait on start of state machine interrupted.\n"); |
| stop_immediately |
| (instance); |
| err = ME_ERRNO_SIGNAL; |
| goto ERROR; |
| } |
| |
| if (((jiffies - ref) >= delay)) { |
| PERROR |
| ("Timeout reached.\n"); |
| stop_immediately |
| (instance); |
| err = ME_ERRNO_TIMEOUT; |
| goto ERROR; |
| } |
| } |
| } else { |
| while (! |
| (inl(instance->status_reg) & |
| ME4600_AO_STATUS_BIT_FSM)) { |
| interruptible_sleep_on_timeout |
| (&queue, 1); |
| |
| if (signal_pending(current)) { |
| PERROR |
| ("Wait on start of state machine interrupted.\n"); |
| stop_immediately |
| (instance); |
| err = ME_ERRNO_SIGNAL; |
| goto ERROR; |
| } |
| } |
| } |
| } else if (start_mode == ME_START_MODE_NONBLOCKING) { |
| /* Do nothing */ |
| } else { |
| PERROR("Invalid start mode specified.\n"); |
| stop_immediately(instance); |
| err = ME_ERRNO_INVALID_START_MODE; |
| goto ERROR; |
| } |
| } else if ((inl(instance->preload_reg) & (0x10001 << instance->ao_idx)) == (0x10000 << instance->ao_idx)) { // Synchronous with external trigger |
| |
| if ((inl(instance->status_reg) & |
| ME4600_AO_STATUS_BIT_FSM)) { |
| spin_unlock_irqrestore(&instance-> |
| subdevice_lock, |
| cpu_flags); |
| PERROR("Conversion is already running.\n"); |
| err = ME_ERRNO_SUBDEVICE_BUSY; |
| goto ERROR; |
| } |
| |
| if (flags & ME_IO_STREAM_START_TYPE_TRIG_SYNCHRONOUS) { |
| tmp |= |
| ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG | |
| ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| tmp &= |
| ~(ME4600_AO_CTRL_BIT_STOP | |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP); |
| outl(tmp, instance->ctrl_reg); |
| instance->wrap_remaining = instance->wrap_count; |
| instance->circ_buf.tail = 0; |
| |
| spin_unlock_irqrestore(&instance-> |
| subdevice_lock, |
| cpu_flags); |
| |
| if (start_mode == ME_START_MODE_BLOCKING) { |
| init_waitqueue_head(&queue); |
| |
| if (delay) { |
| ref = jiffies; |
| |
| while (! |
| (inl |
| (instance-> |
| status_reg) & |
| ME4600_AO_STATUS_BIT_FSM)) |
| { |
| interruptible_sleep_on_timeout |
| (&queue, 1); |
| |
| if (signal_pending |
| (current)) { |
| PERROR |
| ("Wait on start of state machine interrupted.\n"); |
| stop_immediately |
| (instance); |
| err = |
| ME_ERRNO_SIGNAL; |
| goto ERROR; |
| } |
| |
| if (((jiffies - ref) >= |
| delay)) { |
| PERROR |
| ("Timeout reached.\n"); |
| stop_immediately |
| (instance); |
| err = |
| ME_ERRNO_TIMEOUT; |
| goto ERROR; |
| } |
| } |
| } else { |
| while (! |
| (inl |
| (instance-> |
| status_reg) & |
| ME4600_AO_STATUS_BIT_FSM)) |
| { |
| interruptible_sleep_on_timeout |
| (&queue, 1); |
| |
| if (signal_pending |
| (current)) { |
| PERROR |
| ("Wait on start of state machine interrupted.\n"); |
| stop_immediately |
| (instance); |
| err = |
| ME_ERRNO_SIGNAL; |
| goto ERROR; |
| } |
| } |
| } |
| } else if (start_mode == |
| ME_START_MODE_NONBLOCKING) { |
| } else { |
| PERROR |
| ("Invalid start mode specified.\n"); |
| stop_immediately(instance); |
| err = ME_ERRNO_INVALID_START_MODE; |
| goto ERROR; |
| } |
| } else { |
| tmp |= ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| tmp &= |
| ~(ME4600_AO_CTRL_BIT_STOP | |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP); |
| outl(tmp, instance->ctrl_reg); |
| instance->wrap_remaining = instance->wrap_count; |
| instance->circ_buf.tail = 0; |
| spin_unlock_irqrestore(&instance-> |
| subdevice_lock, |
| cpu_flags); |
| } |
| } else if ((inl(instance->preload_reg) & (0x10001 << instance->ao_idx)) == (0x1 << instance->ao_idx)) { // Synchronous wraparound with sw trigger |
| |
| if ((inl(instance->status_reg) & |
| ME4600_AO_STATUS_BIT_FSM)) { |
| spin_unlock_irqrestore(&instance-> |
| subdevice_lock, |
| cpu_flags); |
| PERROR("Conversion is already running.\n"); |
| err = ME_ERRNO_SUBDEVICE_BUSY; |
| goto ERROR; |
| } |
| |
| tmp &= |
| ~(ME4600_AO_CTRL_BIT_STOP | |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP); |
| tmp |= ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| instance->wrap_remaining = instance->wrap_count; |
| instance->circ_buf.tail = 0; |
| PDEBUG("CTRL Reg = 0x%X.\n", inl(instance->ctrl_reg)); |
| outl(tmp, instance->ctrl_reg); |
| |
| if (flags & ME_IO_STREAM_START_TYPE_TRIG_SYNCHRONOUS) { |
| outl(0x8000, instance->single_reg); |
| instance->single_value = 0x8000; |
| } |
| |
| spin_unlock_irqrestore(&instance->subdevice_lock, |
| cpu_flags); |
| } else { // Software start |
| |
| if ((inl(instance->status_reg) & |
| ME4600_AO_STATUS_BIT_FSM)) { |
| spin_unlock_irqrestore(&instance-> |
| subdevice_lock, |
| cpu_flags); |
| PERROR("Conversion is already running.\n"); |
| err = ME_ERRNO_SUBDEVICE_BUSY; |
| goto ERROR; |
| } |
| |
| tmp &= |
| ~(ME4600_AO_CTRL_BIT_STOP | |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP); |
| |
| tmp |= ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| outl(tmp, instance->ctrl_reg); |
| outl(0x8000, instance->single_reg); |
| instance->single_value = 0x8000; |
| instance->wrap_remaining = instance->wrap_count; |
| instance->circ_buf.tail = 0; |
| spin_unlock_irqrestore(&instance->subdevice_lock, |
| cpu_flags); |
| } |
| |
| break; |
| |
| default: |
| spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags); |
| PERROR("Invalid mode configured.\n"); |
| err = ME_ERRNO_INTERNAL; |
| goto ERROR; |
| } |
| |
| ERROR: |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| static int me4600_ao_io_stream_status(me_subdevice_t * subdevice, |
| struct file *filep, |
| int wait, |
| int *status, int *values, int flags) |
| { |
| me4600_ao_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| wait_queue_head_t queue; |
| |
| PDEBUG("executed.\n"); |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| init_waitqueue_head(&queue); |
| |
| if (!instance->fifo) { |
| PERROR("Not a streaming ao.\n"); |
| return ME_ERRNO_NOT_SUPPORTED; |
| } |
| |
| ME_SUBDEVICE_ENTER; |
| |
| if (wait == ME_WAIT_NONE) { |
| *status = |
| (inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM) ? |
| ME_STATUS_BUSY : ME_STATUS_IDLE; |
| *values = me_circ_buf_space(&instance->circ_buf); |
| } else if (wait == ME_WAIT_IDLE) { |
| while (inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM) { |
| interruptible_sleep_on_timeout(&queue, 1); |
| |
| if (instance->flags & ME4600_AO_FLAGS_BROKEN_PIPE) { |
| PERROR("Output stream was interrupted.\n"); |
| *status = ME_STATUS_ERROR; |
| err = ME_ERRNO_SUCCESS; |
| goto ERROR; |
| } |
| |
| if (signal_pending(current)) { |
| PERROR |
| ("Wait on state machine interrupted by signal.\n"); |
| *status = ME_STATUS_INVALID; |
| err = ME_ERRNO_SIGNAL; |
| goto ERROR; |
| } |
| } |
| |
| *status = ME_STATUS_IDLE; |
| |
| *values = me_circ_buf_space(&instance->circ_buf); |
| } else { |
| PERROR("Invalid wait argument specified.\n"); |
| *status = ME_STATUS_INVALID; |
| err = ME_ERRNO_INVALID_WAIT; |
| goto ERROR; |
| } |
| |
| ERROR: |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| static int me4600_ao_io_stream_stop(me_subdevice_t * subdevice, |
| struct file *filep, |
| int stop_mode, int flags) |
| { |
| int err = ME_ERRNO_SUCCESS; |
| me4600_ao_subdevice_t *instance; |
| unsigned long cpu_flags; |
| unsigned long tmp; |
| |
| PDEBUG("executed.\n"); |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| if (!instance->fifo) { |
| PERROR("Not a streaming ao.\n"); |
| return ME_ERRNO_NOT_SUPPORTED; |
| } |
| |
| ME_SUBDEVICE_ENTER; |
| |
| if (stop_mode == ME_STOP_MODE_IMMEDIATE) { |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| tmp = inl(instance->ctrl_reg); |
| tmp |= |
| ME4600_AO_CTRL_BIT_STOP | ME4600_AO_CTRL_BIT_IMMEDIATE_STOP; |
| outl(tmp, instance->ctrl_reg); |
| |
| while (inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM) ; |
| |
| spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags); |
| } else if (stop_mode == ME_STOP_MODE_LAST_VALUE) { |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| tmp = inl(instance->ctrl_reg); |
| tmp |= ME4600_AO_CTRL_BIT_STOP; |
| outl(tmp, instance->ctrl_reg); |
| spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags); |
| } else { |
| PERROR("Invalid stop mode specified.\n"); |
| err = ME_ERRNO_INVALID_STOP_MODE; |
| goto ERROR; |
| } |
| |
| ERROR: |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| static int me4600_ao_io_stream_write(me_subdevice_t * subdevice, |
| struct file *filep, |
| int write_mode, |
| int *values, int *count, int flags) |
| { |
| int err = ME_ERRNO_SUCCESS; |
| me4600_ao_subdevice_t *instance; |
| unsigned long tmp; |
| int i; |
| int value; |
| int cnt = *count; |
| int c; |
| int k; |
| int ret = 0; |
| unsigned long cpu_flags = 0; |
| |
| PDEBUG("executed.\n"); |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| if (!instance->fifo) { |
| PERROR("Not a streaming ao.\n"); |
| return ME_ERRNO_NOT_SUPPORTED; |
| } |
| |
| ME_SUBDEVICE_ENTER; |
| |
| if (*count <= 0) { |
| PERROR("Invalid count of values specified.\n"); |
| err = ME_ERRNO_INVALID_VALUE_COUNT; |
| goto ERROR; |
| } |
| |
| spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); |
| |
| tmp = inl(instance->ctrl_reg); |
| |
| switch (tmp & 0x3) { |
| |
| case 1: // Wraparound mode |
| if (instance->bosch_fw) { // Bosch firmware |
| spin_unlock_irqrestore(&instance->subdevice_lock, |
| cpu_flags); |
| |
| if (cnt != 7) { |
| PERROR |
| ("Invalid count of values specified. 7 expected.\n"); |
| err = ME_ERRNO_INVALID_VALUE_COUNT; |
| goto ERROR; |
| } |
| |
| for (i = 0; i < 7; i++) { |
| if (get_user(value, values)) { |
| PERROR |
| ("Can't copy value from user space.\n"); |
| err = ME_ERRNO_INTERNAL; |
| goto ERROR; |
| } |
| |
| if (i == 0) { |
| /* Maximum voltage */ |
| value <<= 16; |
| value |= |
| inl(instance->reg_base + |
| 0xD4) & 0xFFFF; |
| outl(value, instance->reg_base + 0xD4); |
| } else if (i == 1) { |
| /* Minimum voltage */ |
| value &= 0xFFFF; |
| value |= |
| inl(instance->reg_base + |
| 0xD4) & 0xFFFF0000; |
| outl(value, instance->reg_base + 0xD4); |
| } else if (i == 2) { |
| /* Delta up */ |
| value <<= 16; |
| value |= |
| inl(instance->reg_base + |
| 0xD8) & 0xFFFF; |
| outl(value, instance->reg_base + 0xD8); |
| } else if (i == 3) { |
| /* Delta down */ |
| value &= 0xFFFF; |
| value |= |
| inl(instance->reg_base + |
| 0xD8) & 0xFFFF0000; |
| outl(value, instance->reg_base + 0xD8); |
| } else if (i == 4) { |
| /* Start value */ |
| outl(value, instance->reg_base + 0xDC); |
| } else if (i == 5) { |
| /* Invert */ |
| if (value) { |
| value = inl(instance->ctrl_reg); |
| value |= 0x100; |
| outl(value, instance->ctrl_reg); |
| } else { |
| value = inl(instance->ctrl_reg); |
| value &= ~0x100; |
| outl(value, instance->ctrl_reg); |
| } |
| } else if (i == 6) { |
| /* Timer for positive ramp */ |
| outl(value, instance->reg_base + 0xE0); |
| } |
| |
| values++; |
| } |
| } else { // Normal firmware |
| PDEBUG("Write for wraparound mode.\n"); |
| |
| if (inl(instance->status_reg) & |
| ME4600_AO_STATUS_BIT_FSM) { |
| spin_unlock_irqrestore(&instance-> |
| subdevice_lock, |
| cpu_flags); |
| PERROR |
| ("There is already a conversion running.\n"); |
| err = ME_ERRNO_SUBDEVICE_BUSY; |
| goto ERROR; |
| } |
| |
| tmp |= ME4600_AO_CTRL_BIT_IMMEDIATE_STOP; |
| tmp &= ~ME4600_AO_CTRL_BIT_ENABLE_FIFO; |
| outl(tmp, instance->ctrl_reg); |
| tmp |= ME4600_AO_CTRL_BIT_ENABLE_FIFO; |
| |
| if ((*count > ME4600_AO_FIFO_COUNT) || |
| ((instance-> |
| flags & ME4600_AO_FLAGS_SW_WRAP_MODE_MASK) == |
| ME4600_AO_FLAGS_SW_WRAP_MODE_FIN)) { |
| tmp &= |
| ~(ME4600_AO_CTRL_BIT_MODE_0 | |
| ME4600_AO_CTRL_BIT_MODE_1); |
| tmp |= ME4600_AO_CTRL_BIT_MODE_1; |
| } |
| |
| outl(tmp, instance->ctrl_reg); |
| spin_unlock_irqrestore(&instance->subdevice_lock, |
| cpu_flags); |
| |
| if ((*count <= ME4600_AO_FIFO_COUNT) && |
| ((instance-> |
| flags & ME4600_AO_FLAGS_SW_WRAP_MODE_MASK) == |
| ME4600_AO_FLAGS_SW_WRAP_MODE_INF)) { |
| for (i = 0; i < *count; i++) { |
| if (get_user(value, values + i)) { |
| PERROR |
| ("Cannot copy value from user space.\n"); |
| err = ME_ERRNO_INTERNAL; |
| goto ERROR; |
| } |
| |
| if (instance->ao_idx & 0x1) |
| value <<= 16; |
| |
| outl(value, instance->fifo_reg); |
| } |
| } else if ((*count <= ME4600_AO_CIRC_BUF_COUNT) && |
| ((instance-> |
| flags & ME4600_AO_FLAGS_SW_WRAP_MODE_MASK) |
| == ME4600_AO_FLAGS_SW_WRAP_MODE_INF)) { |
| for (i = 0; i < *count; i++) { |
| if (get_user(value, values + i)) { |
| PERROR |
| ("Cannot copy value from user space.\n"); |
| err = ME_ERRNO_INTERNAL; |
| goto ERROR; |
| } |
| |
| instance->circ_buf.buf[i] = value; /* Used to hold the values. */ |
| } |
| |
| instance->circ_buf.tail = 0; /* Used as the current read position. */ |
| instance->circ_buf.head = *count; /* Used as the buffer size. */ |
| |
| /* Preload the FIFO. */ |
| |
| for (i = 0; i < ME4600_AO_FIFO_COUNT; |
| i++, instance->circ_buf.tail++) { |
| if (instance->circ_buf.tail >= |
| instance->circ_buf.head) |
| instance->circ_buf.tail = 0; |
| |
| if (instance->ao_idx & 0x1) |
| outl(instance->circ_buf. |
| buf[instance->circ_buf. |
| tail] << 16, |
| instance->fifo_reg); |
| else |
| outl(instance->circ_buf. |
| buf[instance->circ_buf. |
| tail], |
| instance->fifo_reg); |
| } |
| } else if ((*count <= ME4600_AO_CIRC_BUF_COUNT) && |
| ((instance-> |
| flags & ME4600_AO_FLAGS_SW_WRAP_MODE_MASK) |
| == ME4600_AO_FLAGS_SW_WRAP_MODE_FIN)) { |
| unsigned int preload_count; |
| |
| for (i = 0; i < *count; i++) { |
| if (get_user(value, values + i)) { |
| PERROR |
| ("Cannot copy value from user space.\n"); |
| err = ME_ERRNO_INTERNAL; |
| goto ERROR; |
| } |
| |
| instance->circ_buf.buf[i] = value; /* Used to hold the values. */ |
| } |
| |
| instance->circ_buf.tail = 0; /* Used as the current read position. */ |
| instance->circ_buf.head = *count; /* Used as the buffer size. */ |
| |
| /* Try to preload the whole FIFO. */ |
| preload_count = ME4600_AO_FIFO_COUNT; |
| |
| if (preload_count > instance->wrap_count) |
| preload_count = instance->wrap_count; |
| |
| /* Preload the FIFO. */ |
| for (i = 0; i < preload_count; |
| i++, instance->circ_buf.tail++) { |
| if (instance->circ_buf.tail >= |
| instance->circ_buf.head) |
| instance->circ_buf.tail = 0; |
| |
| if (instance->ao_idx & 0x1) |
| outl(instance->circ_buf. |
| buf[instance->circ_buf. |
| tail] << 16, |
| instance->fifo_reg); |
| else |
| outl(instance->circ_buf. |
| buf[instance->circ_buf. |
| tail], |
| instance->fifo_reg); |
| } |
| |
| instance->wrap_remaining = |
| instance->wrap_count - preload_count; |
| } else { |
| PERROR("To many values written.\n"); |
| err = ME_ERRNO_INVALID_VALUE_COUNT; |
| goto ERROR; |
| } |
| } |
| |
| break; |
| |
| case 2: // Continuous mode |
| /* Check if in SW wrapround mode */ |
| if (instance->flags & ME4600_AO_FLAGS_SW_WRAP_MODE_MASK) { |
| spin_unlock_irqrestore(&instance->subdevice_lock, |
| cpu_flags); |
| PERROR("Subdevice is configured SW wrapround mode.\n"); |
| err = ME_ERRNO_PREVIOUS_CONFIG; |
| goto ERROR; |
| } |
| |
| switch (write_mode) { |
| |
| case ME_WRITE_MODE_BLOCKING: |
| spin_unlock_irqrestore(&instance->subdevice_lock, |
| cpu_flags); |
| |
| PDEBUG("Write for blocking continuous mode.\n"); |
| |
| while (cnt > 0) { |
| wait_event_interruptible(instance->wait_queue, |
| (c = |
| me_circ_buf_space_to_end |
| (&instance-> |
| circ_buf))); |
| |
| if (instance-> |
| flags & ME4600_AO_FLAGS_BROKEN_PIPE) { |
| PERROR |
| ("Broken pipe in blocking write.\n"); |
| err = ME_ERRNO_SUBDEVICE_NOT_RUNNING; |
| goto ERROR; |
| } else if (signal_pending(current)) { |
| PERROR |
| ("Wait for free buffer interrupted from signal.\n"); |
| err = ME_ERRNO_SIGNAL; |
| goto ERROR; |
| } |
| |
| PDEBUG("Space to end = %d.\n", c); |
| |
| /* Only able to write size of free buffer or size of count */ |
| |
| if (cnt < c) |
| c = cnt; |
| k = sizeof(int) * c; |
| k -= copy_from_user(instance->circ_buf.buf + |
| instance->circ_buf.head, |
| values, k); |
| c = k / sizeof(int); |
| |
| PDEBUG("Copy %d values from user space.\n", c); |
| |
| if (!c) { |
| PERROR |
| ("Cannot copy values from user space.\n"); |
| err = ME_ERRNO_INTERNAL; |
| goto ERROR; |
| } |
| |
| instance->circ_buf.head = |
| (instance->circ_buf.head + |
| c) & (instance->circ_buf.mask); |
| |
| values += c; |
| cnt -= c; |
| ret += c; |
| |
| /* Values are now available so enable interrupts */ |
| spin_lock_irqsave(&instance->subdevice_lock, |
| cpu_flags); |
| |
| if (me_circ_buf_space(&instance->circ_buf)) { |
| tmp = inl(instance->ctrl_reg); |
| tmp |= ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| outl(tmp, instance->ctrl_reg); |
| } |
| |
| spin_unlock_irqrestore(&instance-> |
| subdevice_lock, |
| cpu_flags); |
| } |
| |
| *count = ret; |
| |
| break; |
| |
| case ME_WRITE_MODE_NONBLOCKING: |
| spin_unlock_irqrestore(&instance->subdevice_lock, |
| cpu_flags); |
| |
| PDEBUG("Write for non blocking continuous mode.\n"); |
| |
| while (cnt > 0) { |
| if (instance-> |
| flags & ME4600_AO_FLAGS_BROKEN_PIPE) { |
| PERROR |
| ("ME4600:Broken pipe in nonblocking write.\n"); |
| err = ME_ERRNO_SUBDEVICE_NOT_RUNNING; |
| goto ERROR; |
| } |
| |
| c = me_circ_buf_space_to_end(&instance-> |
| circ_buf); |
| |
| if (!c) { |
| PDEBUG |
| ("Returning from nonblocking write.\n"); |
| break; |
| } |
| |
| PDEBUG("Space to end = %d.\n", c); |
| |
| /* Only able to write size of free buffer or size of count */ |
| |
| if (cnt < c) |
| c = cnt; |
| k = sizeof(int) * c; |
| k -= copy_from_user(instance->circ_buf.buf + |
| instance->circ_buf.head, |
| values, k); |
| c = k / sizeof(int); |
| |
| PDEBUG("Copy %d values from user space.\n", c); |
| |
| if (!c) { |
| PERROR |
| ("Cannot copy values from user space.\n"); |
| err = ME_ERRNO_INTERNAL; |
| goto ERROR; |
| } |
| |
| instance->circ_buf.head = |
| (instance->circ_buf.head + |
| c) & (instance->circ_buf.mask); |
| |
| values += c; |
| cnt -= c; |
| ret += c; |
| |
| /* Values are now available so enable interrupts */ |
| spin_lock_irqsave(&instance->subdevice_lock, |
| cpu_flags); |
| |
| if (me_circ_buf_space(&instance->circ_buf)) { |
| tmp = inl(instance->ctrl_reg); |
| tmp |= ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| outl(tmp, instance->ctrl_reg); |
| } |
| |
| spin_unlock_irqrestore(&instance-> |
| subdevice_lock, |
| cpu_flags); |
| } |
| |
| *count = ret; |
| |
| break; |
| |
| case ME_WRITE_MODE_PRELOAD: |
| PDEBUG("Write for preload continuous mode.\n"); |
| |
| if ((inl(instance->status_reg) & |
| ME4600_AO_STATUS_BIT_FSM)) { |
| spin_unlock_irqrestore(&instance-> |
| subdevice_lock, |
| cpu_flags); |
| PERROR |
| ("Can't Preload DAC FIFO while conversion is running.\n"); |
| err = ME_ERRNO_SUBDEVICE_BUSY; |
| goto ERROR; |
| } |
| |
| tmp = inl(instance->ctrl_reg); |
| |
| tmp |= |
| ME4600_AO_CTRL_BIT_STOP | |
| ME4600_AO_CTRL_BIT_IMMEDIATE_STOP; |
| outl(tmp, instance->ctrl_reg); |
| tmp &= |
| ~(ME4600_AO_CTRL_BIT_ENABLE_FIFO | |
| ME4600_AO_CTRL_BIT_ENABLE_IRQ); |
| outl(tmp, instance->ctrl_reg); |
| tmp |= ME4600_AO_CTRL_BIT_ENABLE_FIFO; |
| outl(tmp, instance->ctrl_reg); |
| |
| instance->circ_buf.head = 0; |
| instance->circ_buf.tail = 0; |
| instance->flags &= ~ME4600_AO_FLAGS_BROKEN_PIPE; |
| |
| spin_unlock_irqrestore(&instance->subdevice_lock, |
| cpu_flags); |
| |
| c = ME4600_AO_FIFO_COUNT; |
| |
| if (cnt < c) |
| c = cnt; |
| |
| for (i = 0; i < c; i++) { |
| if (get_user(value, values)) { |
| PERROR |
| ("Can't copy value from user space.\n"); |
| err = ME_ERRNO_INTERNAL; |
| goto ERROR; |
| } |
| |
| if (instance->ao_idx & 0x1) |
| value <<= 16; |
| |
| outl(value, instance->fifo_reg); |
| |
| values++; |
| } |
| |
| cnt -= c; |
| |
| ret += c; |
| |
| PDEBUG("Wrote %d values to fifo.\n", c); |
| |
| while (1) { |
| c = me_circ_buf_space_to_end(&instance-> |
| circ_buf); |
| |
| if (c == 0) |
| break; |
| |
| if (cnt < c) |
| c = cnt; |
| |
| if (c <= 0) |
| break; |
| |
| k = sizeof(int) * c; |
| |
| k -= copy_from_user(instance->circ_buf.buf + |
| instance->circ_buf.head, |
| values, k); |
| |
| c = k / sizeof(int); |
| |
| PDEBUG("Wrote %d values to circular buffer.\n", |
| c); |
| |
| if (!c) { |
| PERROR |
| ("Can't copy values from user space.\n"); |
| err = ME_ERRNO_INTERNAL; |
| goto ERROR; |
| } |
| |
| instance->circ_buf.head = |
| (instance->circ_buf.head + |
| c) & (instance->circ_buf.mask); |
| |
| values += c; |
| cnt -= c; |
| ret += c; |
| } |
| |
| *count = ret; |
| |
| break; |
| |
| default: |
| spin_unlock_irqrestore(&instance->subdevice_lock, |
| cpu_flags); |
| |
| PERROR("Invalid write mode specified.\n"); |
| |
| err = ME_ERRNO_INVALID_WRITE_MODE; |
| |
| goto ERROR; |
| } |
| |
| break; |
| |
| default: // Single mode of invalid |
| spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags); |
| PERROR("Subdevice is configured in single mode.\n"); |
| err = ME_ERRNO_PREVIOUS_CONFIG; |
| goto ERROR; |
| } |
| |
| ERROR: |
| |
| ME_SUBDEVICE_EXIT; |
| |
| return err; |
| } |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) |
| static irqreturn_t me4600_ao_isr(int irq, void *dev_id) |
| #else |
| static irqreturn_t me4600_ao_isr(int irq, void *dev_id, struct pt_regs *regs) |
| #endif |
| { |
| unsigned long tmp; |
| int value; |
| me4600_ao_subdevice_t *instance = dev_id; |
| int i; |
| int c = 0; |
| int c1 = 0; |
| |
| if (irq != instance->irq) { |
| PDEBUG("Incorrect interrupt num: %d.\n", irq); |
| return IRQ_NONE; |
| } |
| |
| if (!((0x1 << (instance->ao_idx + 3)) & inl(instance->irq_status_reg))) { |
| return IRQ_NONE; |
| } |
| |
| PDEBUG("executed.\n"); |
| |
| tmp = inl(instance->status_reg); |
| |
| if (!(tmp & ME4600_AO_STATUS_BIT_EF) && |
| (tmp & ME4600_AO_STATUS_BIT_HF) && |
| (tmp & ME4600_AO_STATUS_BIT_HF)) { |
| c = ME4600_AO_FIFO_COUNT; |
| PDEBUG("Fifo empty.\n"); |
| } else if ((tmp & ME4600_AO_STATUS_BIT_EF) && |
| (tmp & ME4600_AO_STATUS_BIT_HF) && |
| (tmp & ME4600_AO_STATUS_BIT_HF)) { |
| c = ME4600_AO_FIFO_COUNT / 2; |
| PDEBUG("Fifo under half full.\n"); |
| } else { |
| c = 0; |
| PDEBUG("Fifo full.\n"); |
| } |
| |
| PDEBUG("Try to write 0x%04X values.\n", c); |
| |
| if ((instance->flags & ME4600_AO_FLAGS_SW_WRAP_MODE_MASK) == |
| ME4600_AO_FLAGS_SW_WRAP_MODE_INF) { |
| while (c) { |
| c1 = c; |
| |
| if (c1 > (instance->circ_buf.head - instance->circ_buf.tail)) /* Only up to the end of the buffer */ |
| c1 = (instance->circ_buf.head - |
| instance->circ_buf.tail); |
| |
| /* Write the values to the FIFO */ |
| for (i = 0; i < c1; i++, instance->circ_buf.tail++, c--) { |
| if (instance->ao_idx & 0x1) |
| outl(instance->circ_buf. |
| buf[instance->circ_buf.tail] << 16, |
| instance->fifo_reg); |
| else |
| outl(instance->circ_buf. |
| buf[instance->circ_buf.tail], |
| instance->fifo_reg); |
| } |
| |
| if (instance->circ_buf.tail >= instance->circ_buf.head) /* Start from beginning */ |
| instance->circ_buf.tail = 0; |
| } |
| |
| spin_lock(&instance->subdevice_lock); |
| |
| tmp = inl(instance->ctrl_reg); |
| tmp |= ME4600_AO_CTRL_BIT_RESET_IRQ; |
| outl(tmp, instance->ctrl_reg); |
| tmp &= ~ME4600_AO_CTRL_BIT_RESET_IRQ; |
| outl(tmp, instance->ctrl_reg); |
| |
| if (!(inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM)) { |
| PERROR("Broken pipe.\n"); |
| instance->flags |= ME4600_AO_FLAGS_BROKEN_PIPE; |
| tmp &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| outl(tmp, instance->ctrl_reg); |
| } |
| |
| spin_unlock(&instance->subdevice_lock); |
| } else if ((instance->flags & ME4600_AO_FLAGS_SW_WRAP_MODE_MASK) == |
| ME4600_AO_FLAGS_SW_WRAP_MODE_FIN) { |
| while (c && instance->wrap_remaining) { |
| c1 = c; |
| |
| if (c1 > (instance->circ_buf.head - instance->circ_buf.tail)) /* Only up to the end of the buffer */ |
| c1 = (instance->circ_buf.head - |
| instance->circ_buf.tail); |
| |
| if (c1 > instance->wrap_remaining) /* Only up to count of user defined number of values */ |
| c1 = instance->wrap_remaining; |
| |
| /* Write the values to the FIFO */ |
| for (i = 0; i < c1; |
| i++, instance->circ_buf.tail++, c--, |
| instance->wrap_remaining--) { |
| if (instance->ao_idx & 0x1) |
| outl(instance->circ_buf. |
| buf[instance->circ_buf.tail] << 16, |
| instance->fifo_reg); |
| else |
| outl(instance->circ_buf. |
| buf[instance->circ_buf.tail], |
| instance->fifo_reg); |
| } |
| |
| if (instance->circ_buf.tail >= instance->circ_buf.head) /* Start from beginning */ |
| instance->circ_buf.tail = 0; |
| } |
| |
| spin_lock(&instance->subdevice_lock); |
| |
| tmp = inl(instance->ctrl_reg); |
| |
| if (!instance->wrap_remaining) { |
| PDEBUG("Finite SW wraparound done.\n"); |
| tmp &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| } |
| |
| tmp |= ME4600_AO_CTRL_BIT_RESET_IRQ; |
| |
| outl(tmp, instance->ctrl_reg); |
| tmp &= ~ME4600_AO_CTRL_BIT_RESET_IRQ; |
| outl(tmp, instance->ctrl_reg); |
| |
| if (!(inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM)) { |
| PERROR("Broken pipe.\n"); |
| instance->flags |= ME4600_AO_FLAGS_BROKEN_PIPE; |
| tmp &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| outl(tmp, instance->ctrl_reg); |
| } |
| |
| spin_unlock(&instance->subdevice_lock); |
| |
| } else { /* Regular continuous mode */ |
| |
| while (1) { |
| c1 = me_circ_buf_values_to_end(&instance->circ_buf); |
| PDEBUG("Values to end = %d.\n", c1); |
| |
| if (c1 > c) |
| c1 = c; |
| |
| if (c1 <= 0) { |
| PDEBUG("Work done or buffer empty.\n"); |
| break; |
| } |
| |
| if (instance->ao_idx & 0x1) { |
| for (i = 0; i < c1; i++) { |
| value = |
| *(instance->circ_buf.buf + |
| instance->circ_buf.tail + |
| i) << 16; |
| outl(value, instance->fifo_reg); |
| } |
| } else |
| outsl(instance->fifo_reg, |
| instance->circ_buf.buf + |
| instance->circ_buf.tail, c1); |
| |
| instance->circ_buf.tail = |
| (instance->circ_buf.tail + |
| c1) & (instance->circ_buf.mask); |
| |
| PDEBUG("%d values wrote to port 0x%04X.\n", c1, |
| instance->fifo_reg); |
| |
| c -= c1; |
| } |
| |
| spin_lock(&instance->subdevice_lock); |
| |
| tmp = inl(instance->ctrl_reg); |
| |
| if (!me_circ_buf_values(&instance->circ_buf)) { |
| PDEBUG |
| ("Disable Interrupt because no values left in buffer.\n"); |
| tmp &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| } |
| |
| tmp |= ME4600_AO_CTRL_BIT_RESET_IRQ; |
| |
| outl(tmp, instance->ctrl_reg); |
| tmp &= ~ME4600_AO_CTRL_BIT_RESET_IRQ; |
| outl(tmp, instance->ctrl_reg); |
| |
| if (!(inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM)) { |
| PDEBUG("Broken pipe in me4600_ao_isr.\n"); |
| instance->flags |= ME4600_AO_FLAGS_BROKEN_PIPE; |
| tmp &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ; |
| outl(tmp, instance->ctrl_reg); |
| } |
| |
| spin_unlock(&instance->subdevice_lock); |
| |
| wake_up_interruptible(&instance->wait_queue); |
| } |
| |
| return IRQ_HANDLED; |
| } |
| |
| static void me4600_ao_destructor(struct me_subdevice *subdevice) |
| { |
| me4600_ao_subdevice_t *instance; |
| |
| PDEBUG("executed.\n"); |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| free_irq(instance->irq, instance); |
| kfree(instance->circ_buf.buf); |
| me_subdevice_deinit(&instance->base); |
| kfree(instance); |
| } |
| |
| me4600_ao_subdevice_t *me4600_ao_constructor(uint32_t reg_base, |
| spinlock_t * preload_reg_lock, |
| uint32_t * preload_flags, |
| int ao_idx, int fifo, int irq) |
| { |
| me4600_ao_subdevice_t *subdevice; |
| int err; |
| |
| PDEBUG("executed.\n"); |
| |
| /* Allocate memory for subdevice instance */ |
| subdevice = kmalloc(sizeof(me4600_ao_subdevice_t), GFP_KERNEL); |
| |
| if (!subdevice) { |
| PERROR("Cannot get memory for subdevice instance.\n"); |
| return NULL; |
| } |
| |
| memset(subdevice, 0, sizeof(me4600_ao_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->preload_reg_lock = preload_reg_lock; |
| subdevice->preload_flags = preload_flags; |
| |
| /* Allocate and initialize circular buffer */ |
| subdevice->circ_buf.mask = ME4600_AO_CIRC_BUF_COUNT - 1; |
| subdevice->circ_buf.buf = kmalloc(ME4600_AO_CIRC_BUF_SIZE, GFP_KERNEL); |
| |
| if (!subdevice->circ_buf.buf) { |
| PERROR("Cannot initialize subdevice base class instance.\n"); |
| me_subdevice_deinit((me_subdevice_t *) subdevice); |
| kfree(subdevice); |
| return NULL; |
| } |
| |
| memset(subdevice->circ_buf.buf, 0, ME4600_AO_CIRC_BUF_SIZE); |
| |
| subdevice->circ_buf.head = 0; |
| subdevice->circ_buf.tail = 0; |
| |
| /* Initialize wait queue */ |
| init_waitqueue_head(&subdevice->wait_queue); |
| |
| /* Initialize single value to 0V */ |
| subdevice->single_value = 0x8000; |
| |
| /* Store analog output index */ |
| subdevice->ao_idx = ao_idx; |
| |
| /* Store if analog output has fifo */ |
| subdevice->fifo = fifo; |
| |
| /* Initialize registers */ |
| |
| if (ao_idx == 0) { |
| subdevice->ctrl_reg = reg_base + ME4600_AO_00_CTRL_REG; |
| subdevice->status_reg = reg_base + ME4600_AO_00_STATUS_REG; |
| subdevice->fifo_reg = reg_base + ME4600_AO_00_FIFO_REG; |
| subdevice->single_reg = reg_base + ME4600_AO_00_SINGLE_REG; |
| subdevice->timer_reg = reg_base + ME4600_AO_00_TIMER_REG; |
| subdevice->reg_base = reg_base; |
| |
| if (inl(subdevice->reg_base + ME4600_AO_BOSCH_REG) == 0x20000) { |
| PINFO("Bosch firmware in use for channel 0.\n"); |
| subdevice->bosch_fw = 1; |
| } else { |
| subdevice->bosch_fw = 0; |
| } |
| } else if (ao_idx == 1) { |
| subdevice->ctrl_reg = reg_base + ME4600_AO_01_CTRL_REG; |
| subdevice->status_reg = reg_base + ME4600_AO_01_STATUS_REG; |
| subdevice->fifo_reg = reg_base + ME4600_AO_01_FIFO_REG; |
| subdevice->single_reg = reg_base + ME4600_AO_01_SINGLE_REG; |
| subdevice->timer_reg = reg_base + ME4600_AO_01_TIMER_REG; |
| subdevice->reg_base = reg_base; |
| subdevice->bosch_fw = 0; |
| } else if (ao_idx == 2) { |
| subdevice->ctrl_reg = reg_base + ME4600_AO_02_CTRL_REG; |
| subdevice->status_reg = reg_base + ME4600_AO_02_STATUS_REG; |
| subdevice->fifo_reg = reg_base + ME4600_AO_02_FIFO_REG; |
| subdevice->single_reg = reg_base + ME4600_AO_02_SINGLE_REG; |
| subdevice->timer_reg = reg_base + ME4600_AO_02_TIMER_REG; |
| subdevice->reg_base = reg_base; |
| subdevice->bosch_fw = 0; |
| } else { |
| subdevice->ctrl_reg = reg_base + ME4600_AO_03_CTRL_REG; |
| subdevice->status_reg = reg_base + ME4600_AO_03_STATUS_REG; |
| subdevice->fifo_reg = reg_base + ME4600_AO_03_FIFO_REG; |
| subdevice->single_reg = reg_base + ME4600_AO_03_SINGLE_REG; |
| subdevice->timer_reg = reg_base + ME4600_AO_03_TIMER_REG; |
| subdevice->reg_base = reg_base; |
| subdevice->bosch_fw = 0; |
| } |
| |
| subdevice->irq_status_reg = reg_base + ME4600_IRQ_STATUS_REG; |
| subdevice->preload_reg = reg_base + ME4600_AO_LOADSETREG_XX; |
| |
| /* Register interrupt service routine */ |
| subdevice->irq = irq; |
| |
| if (request_irq |
| (subdevice->irq, me4600_ao_isr, SA_INTERRUPT | SA_SHIRQ, |
| ME4600_NAME, subdevice)) { |
| PERROR("Cannot get interrupt line.\n"); |
| me_subdevice_deinit((me_subdevice_t *) subdevice); |
| kfree(subdevice->circ_buf.buf); |
| kfree(subdevice); |
| return NULL; |
| } |
| |
| /* Override base class methods. */ |
| subdevice->base.me_subdevice_destructor = me4600_ao_destructor; |
| subdevice->base.me_subdevice_io_reset_subdevice = |
| me4600_ao_io_reset_subdevice; |
| subdevice->base.me_subdevice_io_single_config = |
| me4600_ao_io_single_config; |
| subdevice->base.me_subdevice_io_single_read = me4600_ao_io_single_read; |
| subdevice->base.me_subdevice_io_single_write = |
| me4600_ao_io_single_write; |
| subdevice->base.me_subdevice_io_stream_config = |
| me4600_ao_io_stream_config; |
| subdevice->base.me_subdevice_io_stream_new_values = |
| me4600_ao_io_stream_new_values; |
| subdevice->base.me_subdevice_io_stream_write = |
| me4600_ao_io_stream_write; |
| subdevice->base.me_subdevice_io_stream_start = |
| me4600_ao_io_stream_start; |
| subdevice->base.me_subdevice_io_stream_status = |
| me4600_ao_io_stream_status; |
| subdevice->base.me_subdevice_io_stream_stop = me4600_ao_io_stream_stop; |
| subdevice->base.me_subdevice_query_number_channels = |
| me4600_ao_query_number_channels; |
| subdevice->base.me_subdevice_query_subdevice_type = |
| me4600_ao_query_subdevice_type; |
| subdevice->base.me_subdevice_query_subdevice_caps = |
| me4600_ao_query_subdevice_caps; |
| subdevice->base.me_subdevice_query_subdevice_caps_args = |
| me4600_ao_query_subdevice_caps_args; |
| subdevice->base.me_subdevice_query_range_by_min_max = |
| me4600_ao_query_range_by_min_max; |
| subdevice->base.me_subdevice_query_number_ranges = |
| me4600_ao_query_number_ranges; |
| subdevice->base.me_subdevice_query_range_info = |
| me4600_ao_query_range_info; |
| subdevice->base.me_subdevice_query_timer = me4600_ao_query_timer; |
| |
| return subdevice; |
| } |
| |
| #endif // BOSCH |
| |
| /* Common functions |
| */ |
| |
| static int me4600_ao_query_range_by_min_max(me_subdevice_t * subdevice, |
| int unit, |
| int *min, |
| int *max, int *maxdata, int *range) |
| { |
| me4600_ao_subdevice_t *instance; |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| PDEBUG("executed. idx=%d\n", instance->ao_idx); |
| |
| 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)) { |
| if ((*max <= (ME4600_AO_MAX_RANGE + 1000)) |
| && (*min >= ME4600_AO_MIN_RANGE)) { |
| *min = ME4600_AO_MIN_RANGE; |
| *max = ME4600_AO_MAX_RANGE; |
| *maxdata = ME4600_AO_MAX_DATA; |
| *range = 0; |
| } else { |
| PERROR("No matching range available.\n"); |
| return ME_ERRNO_NO_RANGE; |
| } |
| } else { |
| PERROR("Invalid physical unit specified.\n"); |
| return ME_ERRNO_INVALID_UNIT; |
| } |
| |
| return ME_ERRNO_SUCCESS; |
| } |
| |
| static int me4600_ao_query_number_ranges(me_subdevice_t * subdevice, |
| int unit, int *count) |
| { |
| me4600_ao_subdevice_t *instance; |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| PDEBUG("executed. idx=%d\n", instance->ao_idx); |
| |
| if ((unit == ME_UNIT_VOLT) || (unit == ME_UNIT_ANY)) { |
| *count = 1; |
| } else { |
| *count = 0; |
| } |
| |
| return ME_ERRNO_SUCCESS; |
| } |
| |
| static int me4600_ao_query_range_info(me_subdevice_t * subdevice, |
| int range, |
| int *unit, |
| int *min, int *max, int *maxdata) |
| { |
| me4600_ao_subdevice_t *instance; |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| PDEBUG("executed. idx=%d\n", instance->ao_idx); |
| |
| if (range == 0) { |
| *unit = ME_UNIT_VOLT; |
| *min = ME4600_AO_MIN_RANGE; |
| *max = ME4600_AO_MAX_RANGE; |
| *maxdata = ME4600_AO_MAX_DATA; |
| } else { |
| PERROR("Invalid range number specified.\n"); |
| return ME_ERRNO_INVALID_RANGE; |
| } |
| |
| return ME_ERRNO_SUCCESS; |
| } |
| |
| static int me4600_ao_query_timer(me_subdevice_t * subdevice, |
| int timer, |
| int *base_frequency, |
| long long *min_ticks, long long *max_ticks) |
| { |
| me4600_ao_subdevice_t *instance; |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| PDEBUG("executed. idx=%d\n", instance->ao_idx); |
| |
| if ((timer != ME_TIMER_ACQ_START) && (timer != ME_TIMER_CONV_START)) { |
| PERROR("Invalid timer specified.\n"); |
| return ME_ERRNO_INVALID_TIMER; |
| } |
| |
| if (instance->fifo) { //Streaming device. |
| *base_frequency = ME4600_AO_BASE_FREQUENCY; |
| if (timer == ME_TIMER_ACQ_START) { |
| *min_ticks = ME4600_AO_MIN_ACQ_TICKS; |
| *max_ticks = ME4600_AO_MAX_ACQ_TICKS; |
| } else if (timer == ME_TIMER_CONV_START) { |
| *min_ticks = ME4600_AO_MIN_CHAN_TICKS; |
| *max_ticks = ME4600_AO_MAX_CHAN_TICKS; |
| } |
| } else { //Not streaming device! |
| *base_frequency = 0; |
| *min_ticks = 0; |
| *max_ticks = 0; |
| } |
| |
| return ME_ERRNO_SUCCESS; |
| } |
| |
| static int me4600_ao_query_number_channels(me_subdevice_t * subdevice, |
| int *number) |
| { |
| me4600_ao_subdevice_t *instance; |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| PDEBUG("executed. idx=%d\n", instance->ao_idx); |
| |
| *number = 1; |
| |
| return ME_ERRNO_SUCCESS; |
| } |
| |
| static int me4600_ao_query_subdevice_type(me_subdevice_t * subdevice, |
| int *type, int *subtype) |
| { |
| me4600_ao_subdevice_t *instance; |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| PDEBUG("executed. idx=%d\n", instance->ao_idx); |
| |
| *type = ME_TYPE_AO; |
| *subtype = (instance->fifo) ? ME_SUBTYPE_STREAMING : ME_SUBTYPE_SINGLE; |
| |
| return ME_ERRNO_SUCCESS; |
| } |
| |
| static int me4600_ao_query_subdevice_caps(me_subdevice_t * subdevice, int *caps) |
| { |
| me4600_ao_subdevice_t *instance; |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| PDEBUG("executed. idx=%d\n", instance->ao_idx); |
| |
| *caps = |
| ME_CAPS_AO_TRIG_SYNCHRONOUS | ((instance->fifo) ? ME_CAPS_AO_FIFO : |
| ME_CAPS_NONE); |
| |
| return ME_ERRNO_SUCCESS; |
| } |
| |
| static int me4600_ao_query_subdevice_caps_args(struct me_subdevice *subdevice, |
| int cap, int *args, int count) |
| { |
| me4600_ao_subdevice_t *instance; |
| int err = ME_ERRNO_SUCCESS; |
| |
| instance = (me4600_ao_subdevice_t *) subdevice; |
| |
| PDEBUG("executed. idx=%d\n", instance->ao_idx); |
| |
| 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] = (instance->fifo) ? ME4600_AO_FIFO_COUNT : 0; |
| break; |
| |
| case ME_CAP_AI_BUFFER_SIZE: |
| args[0] = |
| (instance->circ_buf.buf) ? ME4600_AO_CIRC_BUF_COUNT : 0; |
| break; |
| |
| default: |
| PERROR("Invalid capability.\n"); |
| err = ME_ERRNO_INVALID_CAP; |
| args[0] = 0; |
| } |
| |
| return err; |
| } |