/** * @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 #include #include #include #include #include #include #include #include #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 /* 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); /** Copy data from circular buffer to fifo (fast) in wraparound mode. */ inline int ao_write_data_wraparound(me4600_ao_subdevice_t *instance, int count, int start_pos); /** Copy data from circular buffer to fifo (fast). */ inline int ao_write_data(me4600_ao_subdevice_t *instance, int count, int start_pos); /** Copy data from circular buffer to fifo (slow). */ inline int ao_write_data_pooling(me4600_ao_subdevice_t *instance, int count, int start_pos); /** Copy data from user space to circular buffer. */ inline int ao_get_data_from_user(me4600_ao_subdevice_t *instance, int count, int *user_values); /** Stop presentation. Preserve FIFOs. */ inline int ao_stop_immediately(me4600_ao_subdevice_t *instance); /** Task for asynchronical state verifying. */ static void me4600_ao_work_control_task(struct work_struct *work); /* 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) { 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 */ INIT_DELAYED_WORK(&subdevice->ao_control_task, me4600_ao_work_control_task); 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). */ inline int 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. */ inline int 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. */ inline int 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. */ inline int 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. */ inline int 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(struct work_struct *work) { 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; instance = container_of((void *)work, me4600_ao_subdevice_t, ao_control_task); 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; } static irqreturn_t me4600_ao_isr(int irq, void *dev_id) { 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; }