2008-10-31 23:39:12 +00:00
|
|
|
/**
|
|
|
|
* @file me1600_ao.c
|
|
|
|
*
|
|
|
|
* @brief ME-1600 analog output subdevice instance.
|
|
|
|
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
|
|
|
|
* @author Guenter Gebhardt
|
|
|
|
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
|
|
|
|
*
|
|
|
|
* This file is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef __KERNEL__
|
|
|
|
# define __KERNEL__
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Includes
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/spinlock.h>
|
2009-03-16 16:45:13 +00:00
|
|
|
#include <linux/io.h>
|
2008-10-31 23:39:12 +00:00
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
|
|
|
|
#include <linux/workqueue.h>
|
|
|
|
|
|
|
|
#include "medefines.h"
|
|
|
|
#include "meinternal.h"
|
|
|
|
#include "meerror.h"
|
|
|
|
#include "medebug.h"
|
|
|
|
|
|
|
|
#include "me1600_ao_reg.h"
|
|
|
|
#include "me1600_ao.h"
|
|
|
|
|
|
|
|
/* Defines
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void me1600_ao_destructor(struct me_subdevice *subdevice);
|
|
|
|
|
|
|
|
static void me1600_ao_work_control_task(struct work_struct *work);
|
|
|
|
|
2009-03-16 16:45:13 +00:00
|
|
|
static int me1600_ao_io_reset_subdevice(me_subdevice_t *subdevice,
|
2008-10-31 23:39:12 +00:00
|
|
|
struct file *filep, int flags);
|
2009-03-16 16:45:13 +00:00
|
|
|
static int me1600_ao_io_single_config(me_subdevice_t *subdevice,
|
2008-10-31 23:39:12 +00:00
|
|
|
struct file *filep, int channel,
|
|
|
|
int single_config, int ref, int trig_chan,
|
|
|
|
int trig_type, int trig_edge, int flags);
|
2009-03-16 16:45:13 +00:00
|
|
|
static int me1600_ao_io_single_read(me_subdevice_t *subdevice,
|
2008-10-31 23:39:12 +00:00
|
|
|
struct file *filep, int channel, int *value,
|
|
|
|
int time_out, int flags);
|
2009-03-16 16:45:13 +00:00
|
|
|
static int me1600_ao_io_single_write(me_subdevice_t *subdevice,
|
2008-10-31 23:39:12 +00:00
|
|
|
struct file *filep, int channel, int value,
|
|
|
|
int time_out, int flags);
|
2009-03-16 16:45:13 +00:00
|
|
|
static int me1600_ao_query_number_channels(me_subdevice_t *subdevice,
|
2008-10-31 23:39:12 +00:00
|
|
|
int *number);
|
2009-03-16 16:45:13 +00:00
|
|
|
static int me1600_ao_query_subdevice_type(me_subdevice_t *subdevice, int *type,
|
2008-10-31 23:39:12 +00:00
|
|
|
int *subtype);
|
2009-03-16 16:45:13 +00:00
|
|
|
static int me1600_ao_query_subdevice_caps(me_subdevice_t *subdevice,
|
2008-10-31 23:39:12 +00:00
|
|
|
int *caps);
|
2009-03-16 16:45:13 +00:00
|
|
|
static int me1600_ao_query_range_by_min_max(me_subdevice_t *subdevice,
|
2008-10-31 23:39:12 +00:00
|
|
|
int unit, int *min, int *max,
|
|
|
|
int *maxdata, int *range);
|
2009-03-16 16:45:13 +00:00
|
|
|
static int me1600_ao_query_number_ranges(me_subdevice_t *subdevice, int unit,
|
2008-10-31 23:39:12 +00:00
|
|
|
int *count);
|
2009-03-16 16:45:13 +00:00
|
|
|
static int me1600_ao_query_range_info(me_subdevice_t *subdevice, int range,
|
2008-10-31 23:39:12 +00:00
|
|
|
int *unit, int *min, int *max,
|
|
|
|
int *maxdata);
|
|
|
|
|
|
|
|
/* Functions
|
|
|
|
*/
|
|
|
|
|
|
|
|
me1600_ao_subdevice_t *me1600_ao_constructor(uint32_t reg_base,
|
|
|
|
unsigned int ao_idx,
|
|
|
|
int curr,
|
2009-03-16 16:45:13 +00:00
|
|
|
spinlock_t *config_regs_lock,
|
|
|
|
spinlock_t *ao_shadows_lock,
|
|
|
|
me1600_ao_shadow_t *ao_regs_shadows,
|
2008-10-31 23:39:12 +00:00
|
|
|
struct workqueue_struct *me1600_wq)
|
|
|
|
{
|
|
|
|
me1600_ao_subdevice_t *subdevice;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
PDEBUG("executed. idx=%d\n", ao_idx);
|
|
|
|
|
|
|
|
// Allocate memory for subdevice instance.
|
|
|
|
subdevice = kmalloc(sizeof(me1600_ao_subdevice_t), GFP_KERNEL);
|
|
|
|
|
|
|
|
if (!subdevice) {
|
|
|
|
PERROR
|
|
|
|
("Cannot get memory for analog output subdevice instance.\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(subdevice, 0, sizeof(me1600_ao_subdevice_t));
|
|
|
|
|
|
|
|
// Initialize subdevice base class.
|
|
|
|
err = me_subdevice_init(&subdevice->base);
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
PERROR("Cannot initialize subdevice base class instance.\n");
|
|
|
|
kfree(subdevice);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
// Initialize spin locks.
|
|
|
|
spin_lock_init(&subdevice->subdevice_lock);
|
|
|
|
subdevice->config_regs_lock = config_regs_lock;
|
|
|
|
subdevice->ao_shadows_lock = ao_shadows_lock;
|
|
|
|
|
|
|
|
// Save the subdevice index.
|
|
|
|
subdevice->ao_idx = ao_idx;
|
|
|
|
|
|
|
|
// Initialize range lists.
|
|
|
|
subdevice->u_ranges_count = 2;
|
|
|
|
|
|
|
|
subdevice->u_ranges[0].min = 0; //0V
|
|
|
|
subdevice->u_ranges[0].max = 9997558; //10V
|
|
|
|
|
|
|
|
subdevice->u_ranges[1].min = -10E6; //-10V
|
|
|
|
subdevice->u_ranges[1].max = 9995117; //10V
|
|
|
|
|
|
|
|
if (curr) { // This is version with current outputs.
|
|
|
|
subdevice->i_ranges_count = 2;
|
|
|
|
|
|
|
|
subdevice->i_ranges[0].min = 0; //0mA
|
|
|
|
subdevice->i_ranges[0].max = 19995117; //20mA
|
|
|
|
|
|
|
|
subdevice->i_ranges[1].min = 4E3; //4mA
|
|
|
|
subdevice->i_ranges[1].max = 19995118; //20mA
|
|
|
|
} else { // This is version without current outputs.
|
|
|
|
subdevice->i_ranges_count = 0;
|
|
|
|
|
|
|
|
subdevice->i_ranges[0].min = 0; //0mA
|
|
|
|
subdevice->i_ranges[0].max = 0; //0mA
|
|
|
|
|
|
|
|
subdevice->i_ranges[1].min = 0; //0mA
|
|
|
|
subdevice->i_ranges[1].max = 0; //0mA
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize registers.
|
|
|
|
subdevice->uni_bi_reg = reg_base + ME1600_UNI_BI_REG;
|
|
|
|
subdevice->i_range_reg = reg_base + ME1600_020_420_REG;
|
|
|
|
subdevice->sim_output_reg = reg_base + ME1600_SIM_OUTPUT_REG;
|
|
|
|
subdevice->current_on_reg = reg_base + ME1600_CURRENT_ON_REG;
|
|
|
|
#ifdef MEDEBUG_DEBUG_REG
|
|
|
|
subdevice->reg_base = reg_base;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Initialize shadow structure.
|
|
|
|
subdevice->ao_regs_shadows = ao_regs_shadows;
|
|
|
|
|
|
|
|
// Override base class methods.
|
|
|
|
subdevice->base.me_subdevice_destructor = me1600_ao_destructor;
|
|
|
|
subdevice->base.me_subdevice_io_reset_subdevice =
|
|
|
|
me1600_ao_io_reset_subdevice;
|
|
|
|
subdevice->base.me_subdevice_io_single_config =
|
|
|
|
me1600_ao_io_single_config;
|
|
|
|
subdevice->base.me_subdevice_io_single_read = me1600_ao_io_single_read;
|
|
|
|
subdevice->base.me_subdevice_io_single_write =
|
|
|
|
me1600_ao_io_single_write;
|
|
|
|
subdevice->base.me_subdevice_query_number_channels =
|
|
|
|
me1600_ao_query_number_channels;
|
|
|
|
subdevice->base.me_subdevice_query_subdevice_type =
|
|
|
|
me1600_ao_query_subdevice_type;
|
|
|
|
subdevice->base.me_subdevice_query_subdevice_caps =
|
|
|
|
me1600_ao_query_subdevice_caps;
|
|
|
|
subdevice->base.me_subdevice_query_range_by_min_max =
|
|
|
|
me1600_ao_query_range_by_min_max;
|
|
|
|
subdevice->base.me_subdevice_query_number_ranges =
|
|
|
|
me1600_ao_query_number_ranges;
|
|
|
|
subdevice->base.me_subdevice_query_range_info =
|
|
|
|
me1600_ao_query_range_info;
|
|
|
|
|
|
|
|
// Initialize wait queue.
|
|
|
|
init_waitqueue_head(&subdevice->wait_queue);
|
|
|
|
|
|
|
|
// Prepare work queue.
|
|
|
|
subdevice->me1600_workqueue = me1600_wq;
|
|
|
|
|
|
|
|
/* workqueue API changed in kernel 2.6.20 */
|
|
|
|
INIT_DELAYED_WORK(&subdevice->ao_control_task,
|
|
|
|
me1600_ao_work_control_task);
|
|
|
|
return subdevice;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void me1600_ao_destructor(struct me_subdevice *subdevice)
|
|
|
|
{
|
|
|
|
me1600_ao_subdevice_t *instance;
|
|
|
|
|
|
|
|
instance = (me1600_ao_subdevice_t *) subdevice;
|
|
|
|
|
|
|
|
PDEBUG("executed. idx=%d\n", instance->ao_idx);
|
|
|
|
|
|
|
|
instance->ao_control_task_flag = 0;
|
|
|
|
|
|
|
|
// Reset subdevice to asure clean exit.
|
|
|
|
me1600_ao_io_reset_subdevice(subdevice, NULL,
|
|
|
|
ME_IO_RESET_SUBDEVICE_NO_FLAGS);
|
|
|
|
|
|
|
|
// Remove any tasks from work queue. This is paranoic because it was done allready in reset().
|
|
|
|
if (!cancel_delayed_work(&instance->ao_control_task)) { //Wait 2 ticks to be sure that control task is removed from queue.
|
|
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
|
|
|
schedule_timeout(2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-16 16:45:13 +00:00
|
|
|
static int me1600_ao_io_reset_subdevice(me_subdevice_t *subdevice,
|
2008-10-31 23:39:12 +00:00
|
|
|
struct file *filep, int flags)
|
|
|
|
{
|
|
|
|
me1600_ao_subdevice_t *instance;
|
|
|
|
uint16_t tmp;
|
|
|
|
|
|
|
|
instance = (me1600_ao_subdevice_t *) subdevice;
|
|
|
|
|
|
|
|
PDEBUG("executed. idx=%d\n", instance->ao_idx);
|
|
|
|
|
|
|
|
if (flags) {
|
|
|
|
PERROR("Invalid flag specified.\n");
|
|
|
|
return ME_ERRNO_INVALID_FLAGS;
|
|
|
|
}
|
|
|
|
|
|
|
|
ME_SUBDEVICE_ENTER;
|
|
|
|
|
|
|
|
//Cancel control task
|
|
|
|
PDEBUG("Cancel control task. idx=%d\n", instance->ao_idx);
|
|
|
|
instance->ao_control_task_flag = 0;
|
|
|
|
cancel_delayed_work(&instance->ao_control_task);
|
|
|
|
(instance->ao_regs_shadows)->trigger &= ~(0x1 << instance->ao_idx); //Cancell waiting for trigger.
|
|
|
|
|
|
|
|
// Reset all settings.
|
|
|
|
spin_lock(&instance->subdevice_lock);
|
|
|
|
spin_lock(instance->ao_shadows_lock);
|
|
|
|
(instance->ao_regs_shadows)->shadow[instance->ao_idx] = 0;
|
|
|
|
(instance->ao_regs_shadows)->mirror[instance->ao_idx] = 0;
|
|
|
|
(instance->ao_regs_shadows)->trigger &= ~(0x1 << instance->ao_idx); //Not waiting for triggering.
|
|
|
|
(instance->ao_regs_shadows)->synchronous &= ~(0x1 << instance->ao_idx); //Individual triggering.
|
|
|
|
|
|
|
|
// Set output to default (safe) state.
|
|
|
|
spin_lock(instance->config_regs_lock);
|
|
|
|
tmp = inw(instance->uni_bi_reg); // unipolar
|
|
|
|
tmp |= (0x1 << instance->ao_idx);
|
|
|
|
outw(tmp, instance->uni_bi_reg);
|
|
|
|
PDEBUG_REG("uni_bi_reg outw(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
|
|
|
|
instance->uni_bi_reg - instance->reg_base, tmp);
|
|
|
|
|
|
|
|
tmp = inw(instance->current_on_reg); // Volts only!
|
|
|
|
tmp &= ~(0x1 << instance->ao_idx);
|
|
|
|
tmp &= 0x00FF;
|
|
|
|
outw(tmp, instance->current_on_reg);
|
|
|
|
PDEBUG_REG("current_on_reg outl(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
instance->current_on_reg - instance->reg_base, tmp);
|
|
|
|
|
|
|
|
tmp = inw(instance->i_range_reg); // 0..20mA <= If exists.
|
|
|
|
tmp &= ~(0x1 << instance->ao_idx);
|
|
|
|
outw(tmp, instance->i_range_reg);
|
|
|
|
PDEBUG_REG("i_range_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
|
|
|
|
instance->i_range_reg - instance->reg_base, tmp);
|
|
|
|
|
|
|
|
outw(0, (instance->ao_regs_shadows)->registry[instance->ao_idx]);
|
|
|
|
PDEBUG_REG("channel_reg outw(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
|
|
|
|
(instance->ao_regs_shadows)->registry[instance->ao_idx] -
|
|
|
|
instance->reg_base, 0);
|
|
|
|
|
|
|
|
// Trigger output.
|
|
|
|
outw(0x0000, instance->sim_output_reg);
|
|
|
|
PDEBUG_REG("sim_output_reg outl(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
instance->sim_output_reg - instance->reg_base, 0x0000);
|
|
|
|
outw(0xFFFF, instance->sim_output_reg);
|
|
|
|
PDEBUG_REG("sim_output_reg outl(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
instance->sim_output_reg - instance->reg_base, 0xFFFF);
|
|
|
|
spin_unlock(instance->config_regs_lock);
|
|
|
|
spin_unlock(instance->ao_shadows_lock);
|
|
|
|
|
|
|
|
// Set status to 'none'
|
|
|
|
instance->status = ao_status_none;
|
|
|
|
spin_unlock(&instance->subdevice_lock);
|
|
|
|
|
|
|
|
//Signal reset if user is on wait.
|
|
|
|
wake_up_interruptible_all(&instance->wait_queue);
|
|
|
|
|
|
|
|
ME_SUBDEVICE_EXIT;
|
|
|
|
|
|
|
|
return ME_ERRNO_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2009-03-16 16:45:13 +00:00
|
|
|
static int me1600_ao_io_single_config(me_subdevice_t *subdevice,
|
2008-10-31 23:39:12 +00:00
|
|
|
struct file *filep,
|
|
|
|
int channel,
|
|
|
|
int single_config,
|
|
|
|
int ref,
|
|
|
|
int trig_chan,
|
|
|
|
int trig_type, int trig_edge, int flags)
|
|
|
|
{
|
|
|
|
me1600_ao_subdevice_t *instance;
|
|
|
|
uint16_t tmp;
|
|
|
|
|
|
|
|
instance = (me1600_ao_subdevice_t *) subdevice;
|
|
|
|
|
|
|
|
PDEBUG("executed. idx=%d\n", instance->ao_idx);
|
|
|
|
|
|
|
|
// Checking parameters.
|
|
|
|
if (flags) {
|
|
|
|
PERROR
|
|
|
|
("Invalid flag specified. Must be ME_IO_SINGLE_CONFIG_NO_FLAGS.\n");
|
|
|
|
return ME_ERRNO_INVALID_FLAGS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (trig_edge != ME_TRIG_EDGE_NONE) {
|
|
|
|
PERROR
|
|
|
|
("Invalid trigger edge. Software trigger has not edge. Must be ME_TRIG_EDGE_NONE\n");
|
|
|
|
return ME_ERRNO_INVALID_TRIG_EDGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (trig_type != ME_TRIG_TYPE_SW) {
|
|
|
|
PERROR("Invalid trigger edge. Must be ME_TRIG_TYPE_SW.\n");
|
|
|
|
return ME_ERRNO_INVALID_TRIG_TYPE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((trig_chan != ME_TRIG_CHAN_DEFAULT)
|
|
|
|
&& (trig_chan != ME_TRIG_CHAN_SYNCHRONOUS)) {
|
|
|
|
PERROR("Invalid trigger channel specified.\n");
|
|
|
|
return ME_ERRNO_INVALID_TRIG_CHAN;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ref != ME_REF_AO_GROUND) {
|
|
|
|
PERROR
|
|
|
|
("Invalid reference. Analog outputs have to have got REF_AO_GROUND.\n");
|
|
|
|
return ME_ERRNO_INVALID_REF;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (((single_config + 1) >
|
|
|
|
(instance->u_ranges_count + instance->i_ranges_count))
|
|
|
|
|| (single_config < 0)) {
|
|
|
|
PERROR("Invalid range specified.\n");
|
|
|
|
return ME_ERRNO_INVALID_SINGLE_CONFIG;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (channel) {
|
|
|
|
PERROR("Invalid channel specified.\n");
|
|
|
|
return ME_ERRNO_INVALID_CHANNEL;
|
|
|
|
}
|
|
|
|
// Checking parameters - done. All is fine. Do config.
|
|
|
|
|
|
|
|
ME_SUBDEVICE_ENTER;
|
|
|
|
|
|
|
|
//Cancel control task
|
|
|
|
PDEBUG("Cancel control task. idx=%d\n", instance->ao_idx);
|
|
|
|
instance->ao_control_task_flag = 0;
|
|
|
|
cancel_delayed_work(&instance->ao_control_task);
|
|
|
|
|
|
|
|
spin_lock(&instance->subdevice_lock);
|
|
|
|
spin_lock(instance->ao_shadows_lock);
|
|
|
|
(instance->ao_regs_shadows)->trigger &= ~(0x1 << instance->ao_idx); //Cancell waiting for trigger.
|
|
|
|
(instance->ao_regs_shadows)->shadow[instance->ao_idx] = 0;
|
|
|
|
(instance->ao_regs_shadows)->mirror[instance->ao_idx] = 0;
|
|
|
|
|
|
|
|
spin_lock(instance->config_regs_lock);
|
|
|
|
switch (single_config) {
|
|
|
|
case 0: // 0V 10V
|
|
|
|
tmp = inw(instance->current_on_reg); // Volts
|
|
|
|
tmp &= ~(0x1 << instance->ao_idx);
|
|
|
|
outw(tmp, instance->current_on_reg);
|
|
|
|
PDEBUG_REG("current_on_reg outw(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
instance->current_on_reg - instance->reg_base, tmp);
|
|
|
|
|
|
|
|
// 0V
|
|
|
|
outw(0,
|
|
|
|
(instance->ao_regs_shadows)->registry[instance->ao_idx]);
|
|
|
|
PDEBUG_REG("channel_reg outw(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
(instance->ao_regs_shadows)->registry[instance->
|
|
|
|
ao_idx] -
|
|
|
|
instance->reg_base, 0);
|
|
|
|
|
|
|
|
tmp = inw(instance->uni_bi_reg); // unipolar
|
|
|
|
tmp |= (0x1 << instance->ao_idx);
|
|
|
|
outw(tmp, instance->uni_bi_reg);
|
|
|
|
PDEBUG_REG("uni_bi_reg outw(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
instance->uni_bi_reg - instance->reg_base, tmp);
|
|
|
|
|
|
|
|
tmp = inw(instance->i_range_reg); // 0..20mA <= If exists.
|
|
|
|
tmp &= ~(0x1 << instance->ao_idx);
|
|
|
|
outw(tmp, instance->i_range_reg);
|
|
|
|
PDEBUG_REG("i_range_reg outl(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
instance->i_range_reg - instance->reg_base, tmp);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1: // -10V 10V
|
|
|
|
tmp = inw(instance->current_on_reg); // Volts
|
|
|
|
tmp &= ~(0x1 << instance->ao_idx);
|
|
|
|
outw(tmp, instance->current_on_reg);
|
|
|
|
PDEBUG_REG("current_on_reg outw(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
instance->current_on_reg - instance->reg_base, tmp);
|
|
|
|
|
|
|
|
// 0V
|
|
|
|
outw(0x0800,
|
|
|
|
(instance->ao_regs_shadows)->registry[instance->ao_idx]);
|
|
|
|
PDEBUG_REG("channel_reg outw(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
(instance->ao_regs_shadows)->registry[instance->
|
|
|
|
ao_idx] -
|
|
|
|
instance->reg_base, 0x0800);
|
|
|
|
|
|
|
|
tmp = inw(instance->uni_bi_reg); // bipolar
|
|
|
|
tmp &= ~(0x1 << instance->ao_idx);
|
|
|
|
outw(tmp, instance->uni_bi_reg);
|
|
|
|
PDEBUG_REG("uni_bi_reg outw(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
instance->uni_bi_reg - instance->reg_base, tmp);
|
|
|
|
|
|
|
|
tmp = inw(instance->i_range_reg); // 0..20mA <= If exists.
|
|
|
|
tmp &= ~(0x1 << instance->ao_idx);
|
|
|
|
outw(tmp, instance->i_range_reg);
|
|
|
|
PDEBUG_REG("i_range_reg outl(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
instance->i_range_reg - instance->reg_base, tmp);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2: // 0mA 20mA
|
|
|
|
tmp = inw(instance->current_on_reg); // mAmpers
|
|
|
|
tmp |= (0x1 << instance->ao_idx);
|
|
|
|
outw(tmp, instance->current_on_reg);
|
|
|
|
PDEBUG_REG("current_on_reg outw(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
instance->current_on_reg - instance->reg_base, tmp);
|
|
|
|
|
|
|
|
tmp = inw(instance->i_range_reg); // 0..20mA
|
|
|
|
tmp &= ~(0x1 << instance->ao_idx);
|
|
|
|
outw(tmp, instance->i_range_reg);
|
|
|
|
PDEBUG_REG("i_range_reg outl(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
instance->i_range_reg - instance->reg_base, tmp);
|
|
|
|
|
|
|
|
// 0mA
|
|
|
|
outw(0,
|
|
|
|
(instance->ao_regs_shadows)->registry[instance->ao_idx]);
|
|
|
|
PDEBUG_REG("channel_reg outw(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
(instance->ao_regs_shadows)->registry[instance->
|
|
|
|
ao_idx] -
|
|
|
|
instance->reg_base, 0);
|
|
|
|
|
|
|
|
tmp = inw(instance->uni_bi_reg); // unipolar
|
|
|
|
tmp |= (0x1 << instance->ao_idx);
|
|
|
|
outw(tmp, instance->uni_bi_reg);
|
|
|
|
PDEBUG_REG("uni_bi_reg outw(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
instance->uni_bi_reg - instance->reg_base, tmp);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3: // 4mA 20mA
|
|
|
|
tmp = inw(instance->current_on_reg); // mAmpers
|
|
|
|
tmp |= (0x1 << instance->ao_idx);
|
|
|
|
outw(tmp, instance->current_on_reg);
|
|
|
|
PDEBUG_REG("current_on_reg outw(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
instance->current_on_reg - instance->reg_base, tmp);
|
|
|
|
|
|
|
|
tmp = inw(instance->i_range_reg); // 4..20mA
|
|
|
|
tmp |= (0x1 << instance->ao_idx);
|
|
|
|
outw(tmp, instance->i_range_reg);
|
|
|
|
PDEBUG_REG("i_range_reg outl(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
instance->i_range_reg - instance->reg_base, tmp);
|
|
|
|
|
|
|
|
// 4mA
|
|
|
|
outw(0,
|
|
|
|
(instance->ao_regs_shadows)->registry[instance->ao_idx]);
|
|
|
|
PDEBUG_REG("channel_reg outw(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
(instance->ao_regs_shadows)->registry[instance->
|
|
|
|
ao_idx] -
|
|
|
|
instance->reg_base, 0);
|
|
|
|
|
|
|
|
tmp = inw(instance->uni_bi_reg); // unipolar
|
|
|
|
tmp |= (0x1 << instance->ao_idx);
|
|
|
|
outw(tmp, instance->uni_bi_reg);
|
|
|
|
PDEBUG_REG("uni_bi_reg outw(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
instance->uni_bi_reg - instance->reg_base, tmp);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Trigger output.
|
|
|
|
outw(0x0000, instance->sim_output_reg);
|
|
|
|
PDEBUG_REG("sim_output_reg outl(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
instance->sim_output_reg - instance->reg_base, 0x0000);
|
|
|
|
outw(0xFFFF, instance->sim_output_reg);
|
|
|
|
PDEBUG_REG("sim_output_reg outl(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
instance->sim_output_reg - instance->reg_base, 0xFFFF);
|
|
|
|
|
|
|
|
if (trig_chan == ME_TRIG_CHAN_DEFAULT) { // Individual triggering.
|
|
|
|
(instance->ao_regs_shadows)->synchronous &=
|
|
|
|
~(0x1 << instance->ao_idx);
|
|
|
|
PDEBUG("Individual triggering.\n");
|
|
|
|
} else if (trig_chan == ME_TRIG_CHAN_SYNCHRONOUS) { // Synchronous triggering.
|
|
|
|
(instance->ao_regs_shadows)->synchronous |=
|
|
|
|
(0x1 << instance->ao_idx);
|
|
|
|
PDEBUG("Synchronous triggering.\n");
|
|
|
|
}
|
|
|
|
spin_unlock(instance->config_regs_lock);
|
|
|
|
spin_unlock(instance->ao_shadows_lock);
|
|
|
|
|
|
|
|
instance->status = ao_status_single_configured;
|
|
|
|
spin_unlock(&instance->subdevice_lock);
|
|
|
|
|
|
|
|
ME_SUBDEVICE_EXIT;
|
|
|
|
|
|
|
|
return ME_ERRNO_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2009-03-16 16:45:13 +00:00
|
|
|
static int me1600_ao_io_single_read(me_subdevice_t *subdevice,
|
2008-10-31 23:39:12 +00:00
|
|
|
struct file *filep,
|
|
|
|
int channel,
|
|
|
|
int *value, int time_out, int flags)
|
|
|
|
{
|
|
|
|
me1600_ao_subdevice_t *instance;
|
|
|
|
unsigned long delay = 0;
|
|
|
|
unsigned long j = 0;
|
|
|
|
int err = ME_ERRNO_SUCCESS;
|
|
|
|
|
|
|
|
instance = (me1600_ao_subdevice_t *) subdevice;
|
|
|
|
|
|
|
|
PDEBUG("executed. idx=%d\n", instance->ao_idx);
|
|
|
|
|
|
|
|
if (flags & ~ME_IO_SINGLE_NONBLOCKING) {
|
|
|
|
PERROR("Invalid flag specified. %d\n", flags);
|
|
|
|
return ME_ERRNO_INVALID_FLAGS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (time_out < 0) {
|
|
|
|
PERROR("Invalid timeout specified.\n");
|
|
|
|
return ME_ERRNO_INVALID_TIMEOUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (channel) {
|
|
|
|
PERROR("Invalid channel specified.\n");
|
|
|
|
return ME_ERRNO_INVALID_CHANNEL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((!flags) && ((instance->ao_regs_shadows)->trigger & instance->ao_idx)) { //Blocking mode. Wait for software trigger.
|
|
|
|
if (time_out) {
|
|
|
|
delay = (time_out * HZ) / 1000;
|
|
|
|
if (delay == 0)
|
|
|
|
delay = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
j = jiffies;
|
|
|
|
|
|
|
|
//Only runing process will interrupt this call. Events are signaled when status change. This procedure has own timeout.
|
|
|
|
wait_event_interruptible_timeout(instance->wait_queue,
|
|
|
|
(!((instance->
|
|
|
|
ao_regs_shadows)->
|
|
|
|
trigger & instance->
|
|
|
|
ao_idx)),
|
|
|
|
(delay) ? delay : LONG_MAX);
|
|
|
|
|
|
|
|
if (instance == ao_status_none) { // Reset was called.
|
|
|
|
PDEBUG("Single canceled.\n");
|
|
|
|
err = ME_ERRNO_CANCELLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (signal_pending(current)) {
|
|
|
|
PERROR("Wait on start of state machine interrupted.\n");
|
|
|
|
err = ME_ERRNO_SIGNAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((delay) && ((jiffies - j) >= delay)) {
|
|
|
|
PDEBUG("Timeout reached.\n");
|
|
|
|
err = ME_ERRNO_TIMEOUT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*value = (instance->ao_regs_shadows)->mirror[instance->ao_idx];
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2009-03-16 16:45:13 +00:00
|
|
|
static int me1600_ao_io_single_write(me_subdevice_t *subdevice,
|
2008-10-31 23:39:12 +00:00
|
|
|
struct file *filep,
|
|
|
|
int channel,
|
|
|
|
int value, int time_out, int flags)
|
|
|
|
{
|
|
|
|
me1600_ao_subdevice_t *instance;
|
|
|
|
int err = ME_ERRNO_SUCCESS;
|
|
|
|
unsigned long delay = 0;
|
|
|
|
int i;
|
|
|
|
unsigned long j = 0;
|
|
|
|
|
|
|
|
instance = (me1600_ao_subdevice_t *) subdevice;
|
|
|
|
|
|
|
|
PDEBUG("executed. idx=%d\n", instance->ao_idx);
|
|
|
|
|
|
|
|
if (flags &
|
|
|
|
~(ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS |
|
|
|
|
ME_IO_SINGLE_TYPE_WRITE_NONBLOCKING)) {
|
|
|
|
PERROR("Invalid flag specified.\n");
|
|
|
|
return ME_ERRNO_INVALID_FLAGS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (time_out < 0) {
|
|
|
|
PERROR("Invalid timeout specified.\n");
|
|
|
|
return ME_ERRNO_INVALID_TIMEOUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (value & ~ME1600_AO_MAX_DATA) {
|
|
|
|
PERROR("Invalid value provided.\n");
|
|
|
|
return ME_ERRNO_VALUE_OUT_OF_RANGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (channel) {
|
|
|
|
PERROR("Invalid channel specified.\n");
|
|
|
|
return ME_ERRNO_INVALID_CHANNEL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ME_SUBDEVICE_ENTER;
|
|
|
|
|
|
|
|
//Cancel control task
|
|
|
|
PDEBUG("Cancel control task. idx=%d\n", instance->ao_idx);
|
|
|
|
instance->ao_control_task_flag = 0;
|
|
|
|
cancel_delayed_work(&instance->ao_control_task);
|
|
|
|
(instance->ao_regs_shadows)->trigger &= ~(0x1 << instance->ao_idx); //Cancell waiting for trigger.
|
|
|
|
|
|
|
|
if (time_out) {
|
|
|
|
delay = (time_out * HZ) / 1000;
|
|
|
|
|
|
|
|
if (delay == 0)
|
|
|
|
delay = 1;
|
|
|
|
}
|
|
|
|
//Write value.
|
|
|
|
spin_lock(instance->ao_shadows_lock);
|
|
|
|
(instance->ao_regs_shadows)->shadow[instance->ao_idx] =
|
|
|
|
(uint16_t) value;
|
|
|
|
|
|
|
|
if (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) { // Trigger all outputs from synchronous list.
|
|
|
|
for (i = 0; i < (instance->ao_regs_shadows)->count; i++) {
|
|
|
|
if (((instance->ao_regs_shadows)->synchronous & (0x1 << i)) || (i == instance->ao_idx)) { // Set all from synchronous list to correct state.
|
|
|
|
PDEBUG
|
|
|
|
("Synchronous triggering: output %d. idx=%d\n",
|
|
|
|
i, instance->ao_idx);
|
|
|
|
(instance->ao_regs_shadows)->mirror[i] =
|
|
|
|
(instance->ao_regs_shadows)->shadow[i];
|
|
|
|
|
|
|
|
outw((instance->ao_regs_shadows)->shadow[i],
|
|
|
|
(instance->ao_regs_shadows)->registry[i]);
|
|
|
|
PDEBUG_REG
|
|
|
|
("channel_reg outw(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
(instance->ao_regs_shadows)->registry[i] -
|
|
|
|
instance->reg_base,
|
|
|
|
(instance->ao_regs_shadows)->shadow[i]);
|
|
|
|
|
|
|
|
(instance->ao_regs_shadows)->trigger &=
|
|
|
|
~(0x1 << i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Trigger output.
|
|
|
|
outw(0x0000, instance->sim_output_reg);
|
|
|
|
PDEBUG_REG("sim_output_reg outl(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
instance->sim_output_reg - instance->reg_base, 0);
|
|
|
|
outw(0xFFFF, instance->sim_output_reg);
|
|
|
|
PDEBUG_REG("sim_output_reg outl(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
instance->sim_output_reg - instance->reg_base,
|
|
|
|
0xFFFF);
|
|
|
|
instance->status = ao_status_single_end;
|
|
|
|
} else { // Individual mode.
|
|
|
|
if ((instance->ao_regs_shadows)->synchronous & (0x1 << instance->ao_idx)) { // Put on synchronous start list. Set output as waiting for trigger.
|
|
|
|
PDEBUG("Add to synchronous list. idx=%d\n",
|
|
|
|
instance->ao_idx);
|
|
|
|
(instance->ao_regs_shadows)->trigger |=
|
|
|
|
(0x1 << instance->ao_idx);
|
|
|
|
instance->status = ao_status_single_run;
|
|
|
|
PDEBUG("Synchronous list: 0x%x.\n",
|
|
|
|
(instance->ao_regs_shadows)->synchronous);
|
|
|
|
} else { // Fired this one.
|
|
|
|
PDEBUG("Triggering. idx=%d\n", instance->ao_idx);
|
|
|
|
(instance->ao_regs_shadows)->mirror[instance->ao_idx] =
|
|
|
|
(instance->ao_regs_shadows)->shadow[instance->
|
|
|
|
ao_idx];
|
|
|
|
|
|
|
|
outw((instance->ao_regs_shadows)->
|
|
|
|
shadow[instance->ao_idx],
|
|
|
|
(instance->ao_regs_shadows)->registry[instance->
|
|
|
|
ao_idx]);
|
|
|
|
PDEBUG_REG("channel_reg outw(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
(instance->ao_regs_shadows)->
|
|
|
|
registry[instance->ao_idx] -
|
|
|
|
instance->reg_base,
|
|
|
|
(instance->ao_regs_shadows)->
|
|
|
|
shadow[instance->ao_idx]);
|
|
|
|
|
|
|
|
// Set output as triggered.
|
|
|
|
(instance->ao_regs_shadows)->trigger &=
|
|
|
|
~(0x1 << instance->ao_idx);
|
|
|
|
|
|
|
|
// Trigger output.
|
|
|
|
outw(0x0000, instance->sim_output_reg);
|
|
|
|
PDEBUG_REG("sim_output_reg outl(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
instance->sim_output_reg -
|
|
|
|
instance->reg_base, 0);
|
|
|
|
outw(0xFFFF, instance->sim_output_reg);
|
|
|
|
PDEBUG_REG("sim_output_reg outl(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
instance->sim_output_reg -
|
|
|
|
instance->reg_base, 0xFFFF);
|
|
|
|
instance->status = ao_status_single_end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock(instance->ao_shadows_lock);
|
|
|
|
|
|
|
|
//Init control task
|
|
|
|
instance->timeout.delay = delay;
|
|
|
|
instance->timeout.start_time = jiffies;
|
|
|
|
instance->ao_control_task_flag = 1;
|
|
|
|
queue_delayed_work(instance->me1600_workqueue,
|
|
|
|
&instance->ao_control_task, 1);
|
|
|
|
|
2008-12-24 15:23:37 +00:00
|
|
|
if ((!(flags & ME_IO_SINGLE_TYPE_WRITE_NONBLOCKING)) &&
|
|
|
|
((instance->ao_regs_shadows)->trigger & instance->ao_idx)) {
|
|
|
|
/* Blocking mode. Wait for software trigger. */
|
2008-10-31 23:39:12 +00:00
|
|
|
if (time_out) {
|
|
|
|
delay = (time_out * HZ) / 1000;
|
|
|
|
if (delay == 0)
|
|
|
|
delay = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
j = jiffies;
|
|
|
|
|
|
|
|
//Only runing process will interrupt this call. Events are signaled when status change. This procedure has own timeout.
|
|
|
|
wait_event_interruptible_timeout(instance->wait_queue,
|
|
|
|
(!((instance->
|
|
|
|
ao_regs_shadows)->
|
|
|
|
trigger & instance->
|
|
|
|
ao_idx)),
|
|
|
|
(delay) ? delay : LONG_MAX);
|
|
|
|
|
|
|
|
if (instance == ao_status_none) {
|
|
|
|
PDEBUG("Single canceled.\n");
|
|
|
|
err = ME_ERRNO_CANCELLED;
|
|
|
|
}
|
|
|
|
if (signal_pending(current)) {
|
|
|
|
PERROR("Wait on start of state machine interrupted.\n");
|
|
|
|
err = ME_ERRNO_SIGNAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((delay) && ((jiffies - j) >= delay)) {
|
|
|
|
PDEBUG("Timeout reached.\n");
|
|
|
|
err = ME_ERRNO_TIMEOUT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ME_SUBDEVICE_EXIT;
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2009-03-16 16:45:13 +00:00
|
|
|
static int me1600_ao_query_number_channels(me_subdevice_t *subdevice,
|
2008-10-31 23:39:12 +00:00
|
|
|
int *number)
|
|
|
|
{
|
|
|
|
me1600_ao_subdevice_t *instance;
|
|
|
|
instance = (me1600_ao_subdevice_t *) subdevice;
|
|
|
|
|
|
|
|
PDEBUG("executed. idx=%d\n", instance->ao_idx);
|
|
|
|
|
|
|
|
*number = 1; //Every subdevice has only 1 channel.
|
|
|
|
return ME_ERRNO_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2009-03-16 16:45:13 +00:00
|
|
|
static int me1600_ao_query_subdevice_type(me_subdevice_t *subdevice, int *type,
|
2008-10-31 23:39:12 +00:00
|
|
|
int *subtype)
|
|
|
|
{
|
|
|
|
me1600_ao_subdevice_t *instance;
|
|
|
|
instance = (me1600_ao_subdevice_t *) subdevice;
|
|
|
|
|
|
|
|
PDEBUG("executed. idx=%d\n", instance->ao_idx);
|
|
|
|
|
|
|
|
*type = ME_TYPE_AO;
|
|
|
|
*subtype = ME_SUBTYPE_SINGLE;
|
|
|
|
return ME_ERRNO_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2009-03-16 16:45:13 +00:00
|
|
|
static int me1600_ao_query_subdevice_caps(me_subdevice_t *subdevice, int *caps)
|
2008-10-31 23:39:12 +00:00
|
|
|
{
|
|
|
|
PDEBUG("executed.\n");
|
|
|
|
*caps = ME_CAPS_AO_TRIG_SYNCHRONOUS;
|
|
|
|
return ME_ERRNO_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2009-03-16 16:45:13 +00:00
|
|
|
static int me1600_ao_query_range_by_min_max(me_subdevice_t *subdevice,
|
2008-10-31 23:39:12 +00:00
|
|
|
int unit,
|
|
|
|
int *min,
|
|
|
|
int *max, int *maxdata, int *range)
|
|
|
|
{
|
|
|
|
me1600_ao_subdevice_t *instance;
|
|
|
|
int i;
|
|
|
|
int r = -1;
|
|
|
|
int diff = 21E6;
|
|
|
|
|
|
|
|
instance = (me1600_ao_subdevice_t *) subdevice;
|
|
|
|
|
|
|
|
PDEBUG("executed. idx=%d\n", instance->ao_idx);
|
|
|
|
|
|
|
|
if ((*max - *min) < 0) {
|
|
|
|
PERROR("Invalid minimum and maximum values specified.\n");
|
|
|
|
return ME_ERRNO_INVALID_MIN_MAX;
|
|
|
|
}
|
|
|
|
// Maximum ranges are slightly less then 10V or 20mA. For convenient we accepted this value as valid one.
|
|
|
|
if (unit == ME_UNIT_VOLT) {
|
|
|
|
for (i = 0; i < instance->u_ranges_count; i++) {
|
|
|
|
if ((instance->u_ranges[i].min <= *min)
|
|
|
|
&& ((instance->u_ranges[i].max + 5000) >= *max)) {
|
|
|
|
if ((instance->u_ranges[i].max -
|
|
|
|
instance->u_ranges[i].min) - (*max -
|
|
|
|
*min) <
|
|
|
|
diff) {
|
|
|
|
r = i;
|
|
|
|
diff =
|
|
|
|
(instance->u_ranges[i].max -
|
|
|
|
instance->u_ranges[i].min) -
|
|
|
|
(*max - *min);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r < 0) {
|
|
|
|
PERROR("No matching range found.\n");
|
|
|
|
return ME_ERRNO_NO_RANGE;
|
|
|
|
} else {
|
|
|
|
*min = instance->u_ranges[r].min;
|
|
|
|
*max = instance->u_ranges[r].max;
|
|
|
|
*range = r;
|
|
|
|
}
|
|
|
|
} else if (unit == ME_UNIT_AMPERE) {
|
|
|
|
for (i = 0; i < instance->i_ranges_count; i++) {
|
|
|
|
if ((instance->i_ranges[i].min <= *min)
|
|
|
|
&& (instance->i_ranges[i].max + 5000 >= *max)) {
|
|
|
|
if ((instance->i_ranges[i].max -
|
|
|
|
instance->i_ranges[i].min) - (*max -
|
|
|
|
*min) <
|
|
|
|
diff) {
|
|
|
|
r = i;
|
|
|
|
diff =
|
|
|
|
(instance->i_ranges[i].max -
|
|
|
|
instance->i_ranges[i].min) -
|
|
|
|
(*max - *min);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r < 0) {
|
|
|
|
PERROR("No matching range found.\n");
|
|
|
|
return ME_ERRNO_NO_RANGE;
|
|
|
|
} else {
|
|
|
|
*min = instance->i_ranges[r].min;
|
|
|
|
*max = instance->i_ranges[r].max;
|
|
|
|
*range = r + instance->u_ranges_count;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
PERROR("Invalid physical unit specified.\n");
|
|
|
|
return ME_ERRNO_INVALID_UNIT;
|
|
|
|
}
|
|
|
|
*maxdata = ME1600_AO_MAX_DATA;
|
|
|
|
|
|
|
|
return ME_ERRNO_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2009-03-16 16:45:13 +00:00
|
|
|
static int me1600_ao_query_number_ranges(me_subdevice_t *subdevice,
|
2008-10-31 23:39:12 +00:00
|
|
|
int unit, int *count)
|
|
|
|
{
|
|
|
|
me1600_ao_subdevice_t *instance;
|
|
|
|
|
|
|
|
PDEBUG("executed.\n");
|
|
|
|
|
|
|
|
instance = (me1600_ao_subdevice_t *) subdevice;
|
|
|
|
switch (unit) {
|
|
|
|
case ME_UNIT_VOLT:
|
|
|
|
*count = instance->u_ranges_count;
|
|
|
|
break;
|
|
|
|
case ME_UNIT_AMPERE:
|
|
|
|
*count = instance->i_ranges_count;
|
|
|
|
break;
|
|
|
|
case ME_UNIT_ANY:
|
|
|
|
*count = instance->u_ranges_count + instance->i_ranges_count;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
*count = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ME_ERRNO_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2009-03-16 16:45:13 +00:00
|
|
|
static int me1600_ao_query_range_info(me_subdevice_t *subdevice,
|
2008-10-31 23:39:12 +00:00
|
|
|
int range,
|
|
|
|
int *unit,
|
|
|
|
int *min, int *max, int *maxdata)
|
|
|
|
{
|
|
|
|
me1600_ao_subdevice_t *instance;
|
|
|
|
|
|
|
|
PDEBUG("executed.\n");
|
|
|
|
|
|
|
|
instance = (me1600_ao_subdevice_t *) subdevice;
|
|
|
|
|
|
|
|
if (((range + 1) >
|
|
|
|
(instance->u_ranges_count + instance->i_ranges_count))
|
|
|
|
|| (range < 0)) {
|
|
|
|
PERROR("Invalid range number specified.\n");
|
|
|
|
return ME_ERRNO_INVALID_RANGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (range < instance->u_ranges_count) {
|
|
|
|
*unit = ME_UNIT_VOLT;
|
|
|
|
*min = instance->u_ranges[range].min;
|
|
|
|
*max = instance->u_ranges[range].max;
|
|
|
|
} else if (range < instance->u_ranges_count + instance->i_ranges_count) {
|
|
|
|
*unit = ME_UNIT_AMPERE;
|
|
|
|
*min = instance->i_ranges[range - instance->u_ranges_count].min;
|
|
|
|
*max = instance->i_ranges[range - instance->u_ranges_count].max;
|
|
|
|
}
|
|
|
|
*maxdata = ME1600_AO_MAX_DATA;
|
|
|
|
|
|
|
|
return ME_ERRNO_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void me1600_ao_work_control_task(struct work_struct *work)
|
|
|
|
{
|
|
|
|
me1600_ao_subdevice_t *instance;
|
|
|
|
int reschedule = 1;
|
|
|
|
int signaling = 0;
|
|
|
|
|
|
|
|
instance =
|
|
|
|
container_of((void *)work, me1600_ao_subdevice_t, ao_control_task);
|
|
|
|
|
2009-01-07 22:31:57 +00:00
|
|
|
PINFO("<%s: %ld> executed. idx=%d\n", __func__, jiffies,
|
2008-10-31 23:39:12 +00:00
|
|
|
instance->ao_idx);
|
|
|
|
|
|
|
|
if (!((instance->ao_regs_shadows)->trigger & instance->ao_idx)) { // Output was triggerd.
|
|
|
|
// Signal the end.
|
|
|
|
signaling = 1;
|
|
|
|
reschedule = 0;
|
|
|
|
if (instance->status == ao_status_single_run) {
|
|
|
|
instance->status = ao_status_single_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if ((instance->timeout.delay) && ((jiffies - instance->timeout.start_time) >= instance->timeout.delay)) { // Timeout
|
|
|
|
PDEBUG("Timeout reached.\n");
|
|
|
|
spin_lock(instance->ao_shadows_lock);
|
|
|
|
// Restore old settings.
|
|
|
|
PDEBUG("Write old value back to register.\n");
|
|
|
|
(instance->ao_regs_shadows)->shadow[instance->ao_idx] =
|
|
|
|
(instance->ao_regs_shadows)->mirror[instance->ao_idx];
|
|
|
|
|
|
|
|
outw((instance->ao_regs_shadows)->mirror[instance->ao_idx],
|
|
|
|
(instance->ao_regs_shadows)->registry[instance->ao_idx]);
|
|
|
|
PDEBUG_REG("channel_reg outw(0x%lX+0x%lX)=0x%x\n",
|
|
|
|
instance->reg_base,
|
|
|
|
(instance->ao_regs_shadows)->registry[instance->
|
|
|
|
ao_idx] -
|
|
|
|
instance->reg_base,
|
|
|
|
(instance->ao_regs_shadows)->mirror[instance->
|
|
|
|
ao_idx]);
|
|
|
|
|
|
|
|
//Remove from synchronous strt list.
|
|
|
|
(instance->ao_regs_shadows)->trigger &=
|
|
|
|
~(0x1 << instance->ao_idx);
|
|
|
|
if (instance->status == ao_status_none) {
|
|
|
|
instance->status = ao_status_single_end;
|
|
|
|
}
|
|
|
|
spin_unlock(instance->ao_shadows_lock);
|
|
|
|
|
|
|
|
// Signal the end.
|
|
|
|
signaling = 1;
|
|
|
|
reschedule = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (signaling) { //Signal it.
|
|
|
|
wake_up_interruptible_all(&instance->wait_queue);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (instance->ao_control_task_flag && reschedule) { // Reschedule task
|
|
|
|
queue_delayed_work(instance->me1600_workqueue,
|
|
|
|
&instance->ao_control_task, 1);
|
|
|
|
} else {
|
2009-01-07 22:31:57 +00:00
|
|
|
PINFO("<%s> Ending control task.\n", __func__);
|
2008-10-31 23:39:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|