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