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