| /* Device driver for Meilhaus ME-4000 board family. |
| * ================================================ |
| * |
| * Copyright (C) 2003 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. |
| * |
| * Author: Guenter Gebhardt <g.gebhardt@meilhaus.de> |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/fs.h> |
| #include <linux/sched.h> |
| #include <linux/interrupt.h> |
| #include <linux/pci.h> |
| #include <linux/errno.h> |
| #include <linux/delay.h> |
| #include <linux/mm.h> |
| #include <linux/unistd.h> |
| #include <linux/list.h> |
| #include <linux/proc_fs.h> |
| #include <linux/types.h> |
| #include <linux/poll.h> |
| #include <linux/vmalloc.h> |
| #include <linux/slab.h> |
| #include <asm/pgtable.h> |
| #include <asm/uaccess.h> |
| #include <asm/io.h> |
| #include <asm/system.h> |
| |
| /* Include-File for the Meilhaus ME-4000 I/O board */ |
| #include "me4000.h" |
| #include "me4000_firmware.h" |
| #include "me4610_firmware.h" |
| |
| /* Administrative stuff for modinfo */ |
| MODULE_AUTHOR("Guenter Gebhardt <g.gebhardt@meilhaus.de>"); |
| MODULE_DESCRIPTION |
| ("Device Driver Module for Meilhaus ME-4000 boards version 1.0.5"); |
| MODULE_SUPPORTED_DEVICE("Meilhaus ME-4000 Multi I/O boards"); |
| MODULE_LICENSE("GPL"); |
| |
| /* Board specific data are kept in a global list */ |
| static LIST_HEAD(me4000_board_info_list); |
| |
| /* Major Device Numbers. 0 means to get it automatically from the System */ |
| static int me4000_ao_major_driver_no; |
| static int me4000_ai_major_driver_no; |
| static int me4000_dio_major_driver_no; |
| static int me4000_cnt_major_driver_no; |
| static int me4000_ext_int_major_driver_no; |
| |
| /* Let the user specify a custom major driver number */ |
| module_param(me4000_ao_major_driver_no, int, 0); |
| MODULE_PARM_DESC(me4000_ao_major_driver_no, |
| "Major driver number for analog output (default 0)"); |
| |
| module_param(me4000_ai_major_driver_no, int, 0); |
| MODULE_PARM_DESC(me4000_ai_major_driver_no, |
| "Major driver number for analog input (default 0)"); |
| |
| module_param(me4000_dio_major_driver_no, int, 0); |
| MODULE_PARM_DESC(me4000_dio_major_driver_no, |
| "Major driver number digital I/O (default 0)"); |
| |
| module_param(me4000_cnt_major_driver_no, int, 0); |
| MODULE_PARM_DESC(me4000_cnt_major_driver_no, |
| "Major driver number for counter (default 0)"); |
| |
| module_param(me4000_ext_int_major_driver_no, int, 0); |
| MODULE_PARM_DESC(me4000_ext_int_major_driver_no, |
| "Major driver number for external interrupt (default 0)"); |
| |
| /*----------------------------------------------------------------------------- |
| Board detection and initialization |
| ---------------------------------------------------------------------------*/ |
| static int me4000_probe(struct pci_dev *dev, const struct pci_device_id *id); |
| static int me4000_xilinx_download(struct me4000_info *); |
| static int me4000_reset_board(struct me4000_info *); |
| |
| static void clear_board_info_list(void); |
| static void release_ao_contexts(struct me4000_info *board_info); |
| /*----------------------------------------------------------------------------- |
| Stuff used by all device parts |
| ---------------------------------------------------------------------------*/ |
| static int me4000_open(struct inode *, struct file *); |
| static int me4000_release(struct inode *, struct file *); |
| |
| static int me4000_get_user_info(struct me4000_user_info *, |
| struct me4000_info *board_info); |
| static int me4000_read_procmem(char *, char **, off_t, int, int *, void *); |
| |
| /*----------------------------------------------------------------------------- |
| Analog output stuff |
| ---------------------------------------------------------------------------*/ |
| static ssize_t me4000_ao_write_sing(struct file *, const char *, size_t, |
| loff_t *); |
| static ssize_t me4000_ao_write_wrap(struct file *, const char *, size_t, |
| loff_t *); |
| static ssize_t me4000_ao_write_cont(struct file *, const char *, size_t, |
| loff_t *); |
| |
| static int me4000_ao_ioctl_sing(struct inode *, struct file *, unsigned int, |
| unsigned long); |
| static int me4000_ao_ioctl_wrap(struct inode *, struct file *, unsigned int, |
| unsigned long); |
| static int me4000_ao_ioctl_cont(struct inode *, struct file *, unsigned int, |
| unsigned long); |
| |
| static unsigned int me4000_ao_poll_cont(struct file *, poll_table *); |
| static int me4000_ao_fsync_cont(struct file *, struct dentry *, int); |
| |
| static int me4000_ao_start(unsigned long *, struct me4000_ao_context *); |
| static int me4000_ao_stop(struct me4000_ao_context *); |
| static int me4000_ao_immediate_stop(struct me4000_ao_context *); |
| static int me4000_ao_timer_set_divisor(u32 *, struct me4000_ao_context *); |
| static int me4000_ao_preload(struct me4000_ao_context *); |
| static int me4000_ao_preload_update(struct me4000_ao_context *); |
| static int me4000_ao_ex_trig_set_edge(int *, struct me4000_ao_context *); |
| static int me4000_ao_ex_trig_enable(struct me4000_ao_context *); |
| static int me4000_ao_ex_trig_disable(struct me4000_ao_context *); |
| static int me4000_ao_prepare(struct me4000_ao_context *ao_info); |
| static int me4000_ao_reset(struct me4000_ao_context *ao_info); |
| static int me4000_ao_enable_do(struct me4000_ao_context *); |
| static int me4000_ao_disable_do(struct me4000_ao_context *); |
| static int me4000_ao_fsm_state(int *, struct me4000_ao_context *); |
| |
| static int me4000_ao_simultaneous_ex_trig(struct me4000_ao_context *ao_context); |
| static int me4000_ao_simultaneous_sw(struct me4000_ao_context *ao_context); |
| static int me4000_ao_simultaneous_disable(struct me4000_ao_context *ao_context); |
| static int me4000_ao_simultaneous_update( |
| struct me4000_ao_channel_list *channels, |
| struct me4000_ao_context *ao_context); |
| |
| static int me4000_ao_synchronous_ex_trig(struct me4000_ao_context *ao_context); |
| static int me4000_ao_synchronous_sw(struct me4000_ao_context *ao_context); |
| static int me4000_ao_synchronous_disable(struct me4000_ao_context *ao_context); |
| |
| static int me4000_ao_ex_trig_timeout(unsigned long *arg, |
| struct me4000_ao_context *ao_context); |
| static int me4000_ao_get_free_buffer(unsigned long *arg, |
| struct me4000_ao_context *ao_context); |
| |
| /*----------------------------------------------------------------------------- |
| Analog input stuff |
| ---------------------------------------------------------------------------*/ |
| static int me4000_ai_single(struct me4000_ai_single *, |
| struct me4000_ai_context *); |
| static int me4000_ai_ioctl_sing(struct inode *, struct file *, unsigned int, |
| unsigned long); |
| |
| static ssize_t me4000_ai_read(struct file *, char *, size_t, loff_t *); |
| static int me4000_ai_ioctl_sw(struct inode *, struct file *, unsigned int, |
| unsigned long); |
| static unsigned int me4000_ai_poll(struct file *, poll_table *); |
| static int me4000_ai_fasync(int fd, struct file *file_p, int mode); |
| |
| static int me4000_ai_ioctl_ext(struct inode *, struct file *, unsigned int, |
| unsigned long); |
| |
| static int me4000_ai_prepare(struct me4000_ai_context *ai_context); |
| static int me4000_ai_reset(struct me4000_ai_context *ai_context); |
| static int me4000_ai_config(struct me4000_ai_config *, |
| struct me4000_ai_context *); |
| static int me4000_ai_start(struct me4000_ai_context *); |
| static int me4000_ai_start_ex(unsigned long *, struct me4000_ai_context *); |
| static int me4000_ai_stop(struct me4000_ai_context *); |
| static int me4000_ai_immediate_stop(struct me4000_ai_context *); |
| static int me4000_ai_ex_trig_enable(struct me4000_ai_context *); |
| static int me4000_ai_ex_trig_disable(struct me4000_ai_context *); |
| static int me4000_ai_ex_trig_setup(struct me4000_ai_trigger *, |
| struct me4000_ai_context *); |
| static int me4000_ai_sc_setup(struct me4000_ai_sc *arg, |
| struct me4000_ai_context *ai_context); |
| static int me4000_ai_offset_enable(struct me4000_ai_context *ai_context); |
| static int me4000_ai_offset_disable(struct me4000_ai_context *ai_context); |
| static int me4000_ai_fullscale_enable(struct me4000_ai_context *ai_context); |
| static int me4000_ai_fullscale_disable(struct me4000_ai_context *ai_context); |
| static int me4000_ai_fsm_state(int *arg, struct me4000_ai_context *ai_context); |
| static int me4000_ai_get_count_buffer(unsigned long *arg, |
| struct me4000_ai_context *ai_context); |
| |
| /*----------------------------------------------------------------------------- |
| EEPROM stuff |
| ---------------------------------------------------------------------------*/ |
| static int me4000_eeprom_read(struct me4000_eeprom *arg, |
| struct me4000_ai_context *ai_context); |
| static int me4000_eeprom_write(struct me4000_eeprom *arg, |
| struct me4000_ai_context *ai_context); |
| |
| /*----------------------------------------------------------------------------- |
| Digital I/O stuff |
| ---------------------------------------------------------------------------*/ |
| static int me4000_dio_ioctl(struct inode *, struct file *, unsigned int, |
| unsigned long); |
| static int me4000_dio_config(struct me4000_dio_config *, |
| struct me4000_dio_context *); |
| static int me4000_dio_get_byte(struct me4000_dio_byte *, |
| struct me4000_dio_context *); |
| static int me4000_dio_set_byte(struct me4000_dio_byte *, |
| struct me4000_dio_context *); |
| static int me4000_dio_reset(struct me4000_dio_context *); |
| |
| /*----------------------------------------------------------------------------- |
| Counter stuff |
| ---------------------------------------------------------------------------*/ |
| static int me4000_cnt_ioctl(struct inode *, struct file *, unsigned int, |
| unsigned long); |
| static int me4000_cnt_config(struct me4000_cnt_config *, |
| struct me4000_cnt_context *); |
| static int me4000_cnt_read(struct me4000_cnt *, struct me4000_cnt_context *); |
| static int me4000_cnt_write(struct me4000_cnt *, struct me4000_cnt_context *); |
| static int me4000_cnt_reset(struct me4000_cnt_context *); |
| |
| /*----------------------------------------------------------------------------- |
| External interrupt routines |
| ---------------------------------------------------------------------------*/ |
| static int me4000_ext_int_ioctl(struct inode *, struct file *, unsigned int, |
| unsigned long); |
| static int me4000_ext_int_enable(struct me4000_ext_int_context *); |
| static int me4000_ext_int_disable(struct me4000_ext_int_context *); |
| static int me4000_ext_int_count(unsigned long *arg, |
| struct me4000_ext_int_context *ext_int_context); |
| static int me4000_ext_int_fasync(int fd, struct file *file_ptr, int mode); |
| |
| /*----------------------------------------------------------------------------- |
| The interrupt service routines |
| ---------------------------------------------------------------------------*/ |
| static irqreturn_t me4000_ao_isr(int, void *); |
| static irqreturn_t me4000_ai_isr(int, void *); |
| static irqreturn_t me4000_ext_int_isr(int, void *); |
| |
| /*----------------------------------------------------------------------------- |
| Inline functions |
| ---------------------------------------------------------------------------*/ |
| |
| static int inline me4000_buf_count(struct me4000_circ_buf buf, int size) |
| { |
| return ((buf.head - buf.tail) & (size - 1)); |
| } |
| |
| static int inline me4000_buf_space(struct me4000_circ_buf buf, int size) |
| { |
| return ((buf.tail - (buf.head + 1)) & (size - 1)); |
| } |
| |
| static int inline me4000_values_to_end(struct me4000_circ_buf buf, int size) |
| { |
| int end; |
| int n; |
| end = size - buf.tail; |
| n = (buf.head + end) & (size - 1); |
| return (n < end) ? n : end; |
| } |
| |
| static int inline me4000_space_to_end(struct me4000_circ_buf buf, int size) |
| { |
| int end; |
| int n; |
| |
| end = size - 1 - buf.head; |
| n = (end + buf.tail) & (size - 1); |
| return (n <= end) ? n : (end + 1); |
| } |
| |
| static void inline me4000_outb(unsigned char value, unsigned long port) |
| { |
| PORT_PDEBUG("--> 0x%02X port 0x%04lX\n", value, port); |
| outb(value, port); |
| } |
| |
| static void inline me4000_outl(unsigned long value, unsigned long port) |
| { |
| PORT_PDEBUG("--> 0x%08lX port 0x%04lX\n", value, port); |
| outl(value, port); |
| } |
| |
| static unsigned long inline me4000_inl(unsigned long port) |
| { |
| unsigned long value; |
| value = inl(port); |
| PORT_PDEBUG("<-- 0x%08lX port 0x%04lX\n", value, port); |
| return value; |
| } |
| |
| static unsigned char inline me4000_inb(unsigned long port) |
| { |
| unsigned char value; |
| value = inb(port); |
| PORT_PDEBUG("<-- 0x%08X port 0x%04lX\n", value, port); |
| return value; |
| } |
| |
| static struct pci_driver me4000_driver = { |
| .name = ME4000_NAME, |
| .id_table = me4000_pci_table, |
| .probe = me4000_probe |
| }; |
| |
| static struct file_operations me4000_ao_fops_sing = { |
| .owner = THIS_MODULE, |
| .write = me4000_ao_write_sing, |
| .ioctl = me4000_ao_ioctl_sing, |
| .open = me4000_open, |
| .release = me4000_release, |
| }; |
| |
| static struct file_operations me4000_ao_fops_wrap = { |
| .owner = THIS_MODULE, |
| .write = me4000_ao_write_wrap, |
| .ioctl = me4000_ao_ioctl_wrap, |
| .open = me4000_open, |
| .release = me4000_release, |
| }; |
| |
| static struct file_operations me4000_ao_fops_cont = { |
| .owner = THIS_MODULE, |
| .write = me4000_ao_write_cont, |
| .poll = me4000_ao_poll_cont, |
| .ioctl = me4000_ao_ioctl_cont, |
| .open = me4000_open, |
| .release = me4000_release, |
| .fsync = me4000_ao_fsync_cont, |
| }; |
| |
| static struct file_operations me4000_ai_fops_sing = { |
| .owner = THIS_MODULE, |
| .ioctl = me4000_ai_ioctl_sing, |
| .open = me4000_open, |
| .release = me4000_release, |
| }; |
| |
| static struct file_operations me4000_ai_fops_cont_sw = { |
| .owner = THIS_MODULE, |
| .read = me4000_ai_read, |
| .poll = me4000_ai_poll, |
| .ioctl = me4000_ai_ioctl_sw, |
| .open = me4000_open, |
| .release = me4000_release, |
| .fasync = me4000_ai_fasync, |
| }; |
| |
| static struct file_operations me4000_ai_fops_cont_et = { |
| .owner = THIS_MODULE, |
| .read = me4000_ai_read, |
| .poll = me4000_ai_poll, |
| .ioctl = me4000_ai_ioctl_ext, |
| .open = me4000_open, |
| .release = me4000_release, |
| }; |
| |
| static struct file_operations me4000_ai_fops_cont_et_value = { |
| .owner = THIS_MODULE, |
| .read = me4000_ai_read, |
| .poll = me4000_ai_poll, |
| .ioctl = me4000_ai_ioctl_ext, |
| .open = me4000_open, |
| .release = me4000_release, |
| }; |
| |
| static struct file_operations me4000_ai_fops_cont_et_chanlist = { |
| .owner = THIS_MODULE, |
| .read = me4000_ai_read, |
| .poll = me4000_ai_poll, |
| .ioctl = me4000_ai_ioctl_ext, |
| .open = me4000_open, |
| .release = me4000_release, |
| }; |
| |
| static struct file_operations me4000_dio_fops = { |
| .owner = THIS_MODULE, |
| .ioctl = me4000_dio_ioctl, |
| .open = me4000_open, |
| .release = me4000_release, |
| }; |
| |
| static struct file_operations me4000_cnt_fops = { |
| .owner = THIS_MODULE, |
| .ioctl = me4000_cnt_ioctl, |
| .open = me4000_open, |
| .release = me4000_release, |
| }; |
| |
| static struct file_operations me4000_ext_int_fops = { |
| .owner = THIS_MODULE, |
| .ioctl = me4000_ext_int_ioctl, |
| .open = me4000_open, |
| .release = me4000_release, |
| .fasync = me4000_ext_int_fasync, |
| }; |
| |
| static struct file_operations *me4000_ao_fops_array[] = { |
| &me4000_ao_fops_sing, // single operations |
| &me4000_ao_fops_wrap, // wraparound operations |
| &me4000_ao_fops_cont, // continous operations |
| }; |
| |
| static struct file_operations *me4000_ai_fops_array[] = { |
| &me4000_ai_fops_sing, // single operations |
| &me4000_ai_fops_cont_sw, // continuous operations with software start |
| &me4000_ai_fops_cont_et, // continous operations with external trigger |
| &me4000_ai_fops_cont_et_value, // sample values by external trigger |
| &me4000_ai_fops_cont_et_chanlist, // work through one channel list by external trigger |
| }; |
| |
| static int __init me4000_init_module(void) |
| { |
| int result; |
| |
| CALL_PDEBUG("init_module() is executed\n"); |
| |
| /* Register driver capabilities */ |
| result = pci_register_driver(&me4000_driver); |
| PDEBUG("init_module():%d devices detected\n", result); |
| if (result < 0) { |
| printk(KERN_ERR "ME4000:init_module():Can't register driver\n"); |
| goto INIT_ERROR_1; |
| } |
| |
| /* Allocate major number for analog output */ |
| result = |
| register_chrdev(me4000_ao_major_driver_no, ME4000_AO_NAME, |
| &me4000_ao_fops_sing); |
| if (result < 0) { |
| printk(KERN_ERR "ME4000:init_module():Can't get AO major no\n"); |
| goto INIT_ERROR_2; |
| } else { |
| me4000_ao_major_driver_no = result; |
| } |
| PDEBUG("init_module():Major driver number for AO = %ld\n", |
| me4000_ao_major_driver_no); |
| |
| /* Allocate major number for analog input */ |
| result = |
| register_chrdev(me4000_ai_major_driver_no, ME4000_AI_NAME, |
| &me4000_ai_fops_sing); |
| if (result < 0) { |
| printk(KERN_ERR "ME4000:init_module():Can't get AI major no\n"); |
| goto INIT_ERROR_3; |
| } else { |
| me4000_ai_major_driver_no = result; |
| } |
| PDEBUG("init_module():Major driver number for AI = %ld\n", |
| me4000_ai_major_driver_no); |
| |
| /* Allocate major number for digital I/O */ |
| result = |
| register_chrdev(me4000_dio_major_driver_no, ME4000_DIO_NAME, |
| &me4000_dio_fops); |
| if (result < 0) { |
| printk(KERN_ERR |
| "ME4000:init_module():Can't get DIO major no\n"); |
| goto INIT_ERROR_4; |
| } else { |
| me4000_dio_major_driver_no = result; |
| } |
| PDEBUG("init_module():Major driver number for DIO = %ld\n", |
| me4000_dio_major_driver_no); |
| |
| /* Allocate major number for counter */ |
| result = |
| register_chrdev(me4000_cnt_major_driver_no, ME4000_CNT_NAME, |
| &me4000_cnt_fops); |
| if (result < 0) { |
| printk(KERN_ERR |
| "ME4000:init_module():Can't get CNT major no\n"); |
| goto INIT_ERROR_5; |
| } else { |
| me4000_cnt_major_driver_no = result; |
| } |
| PDEBUG("init_module():Major driver number for CNT = %ld\n", |
| me4000_cnt_major_driver_no); |
| |
| /* Allocate major number for external interrupt */ |
| result = |
| register_chrdev(me4000_ext_int_major_driver_no, ME4000_EXT_INT_NAME, |
| &me4000_ext_int_fops); |
| if (result < 0) { |
| printk(KERN_ERR |
| "ME4000:init_module():Can't get major no for external interrupt\n"); |
| goto INIT_ERROR_6; |
| } else { |
| me4000_ext_int_major_driver_no = result; |
| } |
| PDEBUG |
| ("init_module():Major driver number for external interrupt = %ld\n", |
| me4000_ext_int_major_driver_no); |
| |
| /* Create the /proc/me4000 entry */ |
| if (!create_proc_read_entry |
| ("me4000", 0, NULL, me4000_read_procmem, NULL)) { |
| result = -ENODEV; |
| printk(KERN_ERR |
| "ME4000:init_module():Can't create proc entry\n"); |
| goto INIT_ERROR_7; |
| } |
| |
| return 0; |
| |
| INIT_ERROR_7: |
| unregister_chrdev(me4000_ext_int_major_driver_no, ME4000_EXT_INT_NAME); |
| |
| INIT_ERROR_6: |
| unregister_chrdev(me4000_cnt_major_driver_no, ME4000_CNT_NAME); |
| |
| INIT_ERROR_5: |
| unregister_chrdev(me4000_dio_major_driver_no, ME4000_DIO_NAME); |
| |
| INIT_ERROR_4: |
| unregister_chrdev(me4000_ai_major_driver_no, ME4000_AI_NAME); |
| |
| INIT_ERROR_3: |
| unregister_chrdev(me4000_ao_major_driver_no, ME4000_AO_NAME); |
| |
| INIT_ERROR_2: |
| pci_unregister_driver(&me4000_driver); |
| clear_board_info_list(); |
| |
| INIT_ERROR_1: |
| return result; |
| } |
| |
| module_init(me4000_init_module); |
| |
| static void clear_board_info_list(void) |
| { |
| struct list_head *board_p; |
| struct list_head *dac_p; |
| struct me4000_info *board_info; |
| struct me4000_ao_context *ao_context; |
| |
| /* Clear context lists */ |
| for (board_p = me4000_board_info_list.next; |
| board_p != &me4000_board_info_list; board_p = board_p->next) { |
| board_info = list_entry(board_p, struct me4000_info, list); |
| /* Clear analog output context list */ |
| while (!list_empty(&board_info->ao_context_list)) { |
| dac_p = board_info->ao_context_list.next; |
| ao_context = |
| list_entry(dac_p, struct me4000_ao_context, list); |
| me4000_ao_reset(ao_context); |
| free_irq(ao_context->irq, ao_context); |
| if (ao_context->circ_buf.buf) |
| kfree(ao_context->circ_buf.buf); |
| list_del(dac_p); |
| kfree(ao_context); |
| } |
| |
| /* Clear analog input context */ |
| if (board_info->ai_context->circ_buf.buf) |
| kfree(board_info->ai_context->circ_buf.buf); |
| kfree(board_info->ai_context); |
| |
| /* Clear digital I/O context */ |
| kfree(board_info->dio_context); |
| |
| /* Clear counter context */ |
| kfree(board_info->cnt_context); |
| |
| /* Clear external interrupt context */ |
| kfree(board_info->ext_int_context); |
| } |
| |
| /* Clear the board info list */ |
| while (!list_empty(&me4000_board_info_list)) { |
| board_p = me4000_board_info_list.next; |
| board_info = list_entry(board_p, struct me4000_info, list); |
| pci_release_regions(board_info->pci_dev_p); |
| list_del(board_p); |
| kfree(board_info); |
| } |
| } |
| |
| static int get_registers(struct pci_dev *dev, struct me4000_info *board_info) |
| { |
| |
| /*--------------------------- plx regbase ---------------------------------*/ |
| |
| board_info->plx_regbase = pci_resource_start(dev, 1); |
| if (board_info->plx_regbase == 0) { |
| printk(KERN_ERR |
| "ME4000:get_registers():PCI base address 1 is not available\n"); |
| return -ENODEV; |
| } |
| board_info->plx_regbase_size = pci_resource_len(dev, 1); |
| |
| PDEBUG |
| ("get_registers():PLX configuration registers at address 0x%4lX [0x%4lX]\n", |
| board_info->plx_regbase, board_info->plx_regbase_size); |
| |
| /*--------------------------- me4000 regbase ------------------------------*/ |
| |
| board_info->me4000_regbase = pci_resource_start(dev, 2); |
| if (board_info->me4000_regbase == 0) { |
| printk(KERN_ERR |
| "ME4000:get_registers():PCI base address 2 is not available\n"); |
| return -ENODEV; |
| } |
| board_info->me4000_regbase_size = pci_resource_len(dev, 2); |
| |
| PDEBUG("get_registers():ME4000 registers at address 0x%4lX [0x%4lX]\n", |
| board_info->me4000_regbase, board_info->me4000_regbase_size); |
| |
| /*--------------------------- timer regbase ------------------------------*/ |
| |
| board_info->timer_regbase = pci_resource_start(dev, 3); |
| if (board_info->timer_regbase == 0) { |
| printk(KERN_ERR |
| "ME4000:get_registers():PCI base address 3 is not available\n"); |
| return -ENODEV; |
| } |
| board_info->timer_regbase_size = pci_resource_len(dev, 3); |
| |
| PDEBUG("get_registers():Timer registers at address 0x%4lX [0x%4lX]\n", |
| board_info->timer_regbase, board_info->timer_regbase_size); |
| |
| /*--------------------------- program regbase ------------------------------*/ |
| |
| board_info->program_regbase = pci_resource_start(dev, 5); |
| if (board_info->program_regbase == 0) { |
| printk(KERN_ERR |
| "get_registers():ME4000:PCI base address 5 is not available\n"); |
| return -ENODEV; |
| } |
| board_info->program_regbase_size = pci_resource_len(dev, 5); |
| |
| PDEBUG("get_registers():Program registers at address 0x%4lX [0x%4lX]\n", |
| board_info->program_regbase, board_info->program_regbase_size); |
| |
| return 0; |
| } |
| |
| static int init_board_info(struct pci_dev *pci_dev_p, |
| struct me4000_info *board_info) |
| { |
| int i; |
| int result; |
| struct list_head *board_p; |
| board_info->pci_dev_p = pci_dev_p; |
| |
| for (i = 0; i < ARRAY_SIZE(me4000_boards); i++) { |
| if (me4000_boards[i].device_id == pci_dev_p->device) { |
| board_info->board_p = &me4000_boards[i]; |
| break; |
| } |
| } |
| if (i == ARRAY_SIZE(me4000_boards)) { |
| printk(KERN_ERR |
| "ME4000:init_board_info():Device ID not valid\n"); |
| return -ENODEV; |
| } |
| |
| /* Get the index of the board in the global list */ |
| for (board_p = me4000_board_info_list.next, i = 0; |
| board_p != &me4000_board_info_list; board_p = board_p->next, i++) { |
| if (board_p == &board_info->list) { |
| board_info->board_count = i; |
| break; |
| } |
| } |
| if (board_p == &me4000_board_info_list) { |
| printk(KERN_ERR |
| "ME4000:init_board_info():Cannot get index of baord\n"); |
| return -ENODEV; |
| } |
| |
| /* Init list head for analog output contexts */ |
| INIT_LIST_HEAD(&board_info->ao_context_list); |
| |
| /* Init spin locks */ |
| spin_lock_init(&board_info->preload_lock); |
| spin_lock_init(&board_info->ai_ctrl_lock); |
| |
| /* Get the serial number */ |
| result = pci_read_config_dword(pci_dev_p, 0x2C, &board_info->serial_no); |
| if (result != PCIBIOS_SUCCESSFUL) { |
| printk(KERN_WARNING |
| "ME4000:init_board_info: Can't get serial_no\n"); |
| return result; |
| } |
| PDEBUG("init_board_info():serial_no = 0x%x\n", board_info->serial_no); |
| |
| /* Get the hardware revision */ |
| result = |
| pci_read_config_byte(pci_dev_p, 0x08, &board_info->hw_revision); |
| if (result != PCIBIOS_SUCCESSFUL) { |
| printk(KERN_WARNING |
| "ME4000:init_board_info():Can't get hw_revision\n"); |
| return result; |
| } |
| PDEBUG("init_board_info():hw_revision = 0x%x\n", |
| board_info->hw_revision); |
| |
| /* Get the vendor id */ |
| board_info->vendor_id = pci_dev_p->vendor; |
| PDEBUG("init_board_info():vendor_id = 0x%x\n", board_info->vendor_id); |
| |
| /* Get the device id */ |
| board_info->device_id = pci_dev_p->device; |
| PDEBUG("init_board_info():device_id = 0x%x\n", board_info->device_id); |
| |
| /* Get the pci device number */ |
| board_info->pci_dev_no = PCI_FUNC(pci_dev_p->devfn); |
| PDEBUG("init_board_info():pci_func_no = 0x%x\n", |
| board_info->pci_func_no); |
| |
| /* Get the pci slot number */ |
| board_info->pci_dev_no = PCI_SLOT(pci_dev_p->devfn); |
| PDEBUG("init_board_info():pci_dev_no = 0x%x\n", board_info->pci_dev_no); |
| |
| /* Get the pci bus number */ |
| board_info->pci_bus_no = pci_dev_p->bus->number; |
| PDEBUG("init_board_info():pci_bus_no = 0x%x\n", board_info->pci_bus_no); |
| |
| /* Get the irq assigned to the board */ |
| board_info->irq = pci_dev_p->irq; |
| PDEBUG("init_board_info():irq = %d\n", board_info->irq); |
| |
| return 0; |
| } |
| |
| static int alloc_ao_contexts(struct me4000_info *info) |
| { |
| int i; |
| int err; |
| struct me4000_ao_context *ao_context; |
| |
| for (i = 0; i < info->board_p->ao.count; i++) { |
| ao_context = kzalloc(sizeof(struct me4000_ao_context), |
| GFP_KERNEL); |
| if (!ao_context) { |
| printk(KERN_ERR |
| "alloc_ao_contexts():Can't get memory for ao context\n"); |
| release_ao_contexts(info); |
| return -ENOMEM; |
| } |
| |
| spin_lock_init(&ao_context->use_lock); |
| spin_lock_init(&ao_context->int_lock); |
| ao_context->irq = info->irq; |
| init_waitqueue_head(&ao_context->wait_queue); |
| ao_context->board_info = info; |
| |
| if (info->board_p->ao.fifo_count) { |
| /* Allocate circular buffer */ |
| ao_context->circ_buf.buf = |
| kzalloc(ME4000_AO_BUFFER_SIZE, GFP_KERNEL); |
| if (!ao_context->circ_buf.buf) { |
| printk(KERN_ERR |
| "alloc_ao_contexts():Can't get circular buffer\n"); |
| release_ao_contexts(info); |
| return -ENOMEM; |
| } |
| |
| /* Clear the circular buffer */ |
| ao_context->circ_buf.head = 0; |
| ao_context->circ_buf.tail = 0; |
| } |
| |
| switch (i) { |
| case 0: |
| ao_context->ctrl_reg = |
| info->me4000_regbase + ME4000_AO_00_CTRL_REG; |
| ao_context->status_reg = |
| info->me4000_regbase + ME4000_AO_00_STATUS_REG; |
| ao_context->fifo_reg = |
| info->me4000_regbase + ME4000_AO_00_FIFO_REG; |
| ao_context->single_reg = |
| info->me4000_regbase + ME4000_AO_00_SINGLE_REG; |
| ao_context->timer_reg = |
| info->me4000_regbase + ME4000_AO_00_TIMER_REG; |
| ao_context->irq_status_reg = |
| info->me4000_regbase + ME4000_IRQ_STATUS_REG; |
| ao_context->preload_reg = |
| info->me4000_regbase + ME4000_AO_LOADSETREG_XX; |
| break; |
| case 1: |
| ao_context->ctrl_reg = |
| info->me4000_regbase + ME4000_AO_01_CTRL_REG; |
| ao_context->status_reg = |
| info->me4000_regbase + ME4000_AO_01_STATUS_REG; |
| ao_context->fifo_reg = |
| info->me4000_regbase + ME4000_AO_01_FIFO_REG; |
| ao_context->single_reg = |
| info->me4000_regbase + ME4000_AO_01_SINGLE_REG; |
| ao_context->timer_reg = |
| info->me4000_regbase + ME4000_AO_01_TIMER_REG; |
| ao_context->irq_status_reg = |
| info->me4000_regbase + ME4000_IRQ_STATUS_REG; |
| ao_context->preload_reg = |
| info->me4000_regbase + ME4000_AO_LOADSETREG_XX; |
| break; |
| case 2: |
| ao_context->ctrl_reg = |
| info->me4000_regbase + ME4000_AO_02_CTRL_REG; |
| ao_context->status_reg = |
| info->me4000_regbase + ME4000_AO_02_STATUS_REG; |
| ao_context->fifo_reg = |
| info->me4000_regbase + ME4000_AO_02_FIFO_REG; |
| ao_context->single_reg = |
| info->me4000_regbase + ME4000_AO_02_SINGLE_REG; |
| ao_context->timer_reg = |
| info->me4000_regbase + ME4000_AO_02_TIMER_REG; |
| ao_context->irq_status_reg = |
| info->me4000_regbase + ME4000_IRQ_STATUS_REG; |
| ao_context->preload_reg = |
| info->me4000_regbase + ME4000_AO_LOADSETREG_XX; |
| break; |
| case 3: |
| ao_context->ctrl_reg = |
| info->me4000_regbase + ME4000_AO_03_CTRL_REG; |
| ao_context->status_reg = |
| info->me4000_regbase + ME4000_AO_03_STATUS_REG; |
| ao_context->fifo_reg = |
| info->me4000_regbase + ME4000_AO_03_FIFO_REG; |
| ao_context->single_reg = |
| info->me4000_regbase + ME4000_AO_03_SINGLE_REG; |
| ao_context->timer_reg = |
| info->me4000_regbase + ME4000_AO_03_TIMER_REG; |
| ao_context->irq_status_reg = |
| info->me4000_regbase + ME4000_IRQ_STATUS_REG; |
| ao_context->preload_reg = |
| info->me4000_regbase + ME4000_AO_LOADSETREG_XX; |
| break; |
| default: |
| break; |
| } |
| |
| if (info->board_p->ao.fifo_count) { |
| /* Request the interrupt line */ |
| err = |
| request_irq(ao_context->irq, me4000_ao_isr, |
| IRQF_DISABLED | IRQF_SHARED, |
| ME4000_NAME, ao_context); |
| if (err) { |
| printk(KERN_ERR |
| "%s:Can't get interrupt line", __func__); |
| kfree(ao_context->circ_buf.buf); |
| kfree(ao_context); |
| release_ao_contexts(info); |
| return -ENODEV; |
| } |
| } |
| |
| list_add_tail(&ao_context->list, &info->ao_context_list); |
| ao_context->index = i; |
| } |
| |
| return 0; |
| } |
| |
| static void release_ao_contexts(struct me4000_info *board_info) |
| { |
| struct list_head *dac_p; |
| struct me4000_ao_context *ao_context; |
| |
| /* Clear analog output context list */ |
| while (!list_empty(&board_info->ao_context_list)) { |
| dac_p = board_info->ao_context_list.next; |
| ao_context = list_entry(dac_p, struct me4000_ao_context, list); |
| free_irq(ao_context->irq, ao_context); |
| kfree(ao_context->circ_buf.buf); |
| list_del(dac_p); |
| kfree(ao_context); |
| } |
| } |
| |
| static int alloc_ai_context(struct me4000_info *info) |
| { |
| struct me4000_ai_context *ai_context; |
| |
| if (info->board_p->ai.count) { |
| ai_context = kzalloc(sizeof(struct me4000_ai_context), |
| GFP_KERNEL); |
| if (!ai_context) { |
| printk(KERN_ERR |
| "ME4000:alloc_ai_context():Can't get memory for ai context\n"); |
| return -ENOMEM; |
| } |
| |
| info->ai_context = ai_context; |
| |
| spin_lock_init(&ai_context->use_lock); |
| spin_lock_init(&ai_context->int_lock); |
| ai_context->number = 0; |
| ai_context->irq = info->irq; |
| init_waitqueue_head(&ai_context->wait_queue); |
| ai_context->board_info = info; |
| |
| ai_context->ctrl_reg = |
| info->me4000_regbase + ME4000_AI_CTRL_REG; |
| ai_context->status_reg = |
| info->me4000_regbase + ME4000_AI_STATUS_REG; |
| ai_context->channel_list_reg = |
| info->me4000_regbase + ME4000_AI_CHANNEL_LIST_REG; |
| ai_context->data_reg = |
| info->me4000_regbase + ME4000_AI_DATA_REG; |
| ai_context->chan_timer_reg = |
| info->me4000_regbase + ME4000_AI_CHAN_TIMER_REG; |
| ai_context->chan_pre_timer_reg = |
| info->me4000_regbase + ME4000_AI_CHAN_PRE_TIMER_REG; |
| ai_context->scan_timer_low_reg = |
| info->me4000_regbase + ME4000_AI_SCAN_TIMER_LOW_REG; |
| ai_context->scan_timer_high_reg = |
| info->me4000_regbase + ME4000_AI_SCAN_TIMER_HIGH_REG; |
| ai_context->scan_pre_timer_low_reg = |
| info->me4000_regbase + ME4000_AI_SCAN_PRE_TIMER_LOW_REG; |
| ai_context->scan_pre_timer_high_reg = |
| info->me4000_regbase + ME4000_AI_SCAN_PRE_TIMER_HIGH_REG; |
| ai_context->start_reg = |
| info->me4000_regbase + ME4000_AI_START_REG; |
| ai_context->irq_status_reg = |
| info->me4000_regbase + ME4000_IRQ_STATUS_REG; |
| ai_context->sample_counter_reg = |
| info->me4000_regbase + ME4000_AI_SAMPLE_COUNTER_REG; |
| } |
| |
| return 0; |
| } |
| |
| static int alloc_dio_context(struct me4000_info *info) |
| { |
| struct me4000_dio_context *dio_context; |
| |
| if (info->board_p->dio.count) { |
| dio_context = kzalloc(sizeof(struct me4000_dio_context), |
| GFP_KERNEL); |
| if (!dio_context) { |
| printk(KERN_ERR |
| "ME4000:alloc_dio_context():Can't get memory for dio context\n"); |
| return -ENOMEM; |
| } |
| |
| info->dio_context = dio_context; |
| |
| spin_lock_init(&dio_context->use_lock); |
| dio_context->board_info = info; |
| |
| dio_context->dio_count = info->board_p->dio.count; |
| |
| dio_context->dir_reg = |
| info->me4000_regbase + ME4000_DIO_DIR_REG; |
| dio_context->ctrl_reg = |
| info->me4000_regbase + ME4000_DIO_CTRL_REG; |
| dio_context->port_0_reg = |
| info->me4000_regbase + ME4000_DIO_PORT_0_REG; |
| dio_context->port_1_reg = |
| info->me4000_regbase + ME4000_DIO_PORT_1_REG; |
| dio_context->port_2_reg = |
| info->me4000_regbase + ME4000_DIO_PORT_2_REG; |
| dio_context->port_3_reg = |
| info->me4000_regbase + ME4000_DIO_PORT_3_REG; |
| } |
| |
| return 0; |
| } |
| |
| static int alloc_cnt_context(struct me4000_info *info) |
| { |
| struct me4000_cnt_context *cnt_context; |
| |
| if (info->board_p->cnt.count) { |
| cnt_context = kzalloc(sizeof(struct me4000_cnt_context), |
| GFP_KERNEL); |
| if (!cnt_context) { |
| printk(KERN_ERR |
| "ME4000:alloc_cnt_context():Can't get memory for cnt context\n"); |
| return -ENOMEM; |
| } |
| |
| info->cnt_context = cnt_context; |
| |
| spin_lock_init(&cnt_context->use_lock); |
| cnt_context->board_info = info; |
| |
| cnt_context->ctrl_reg = |
| info->timer_regbase + ME4000_CNT_CTRL_REG; |
| cnt_context->counter_0_reg = |
| info->timer_regbase + ME4000_CNT_COUNTER_0_REG; |
| cnt_context->counter_1_reg = |
| info->timer_regbase + ME4000_CNT_COUNTER_1_REG; |
| cnt_context->counter_2_reg = |
| info->timer_regbase + ME4000_CNT_COUNTER_2_REG; |
| } |
| |
| return 0; |
| } |
| |
| static int alloc_ext_int_context(struct me4000_info *info) |
| { |
| struct me4000_ext_int_context *ext_int_context; |
| |
| if (info->board_p->cnt.count) { |
| ext_int_context = |
| kzalloc(sizeof(struct me4000_ext_int_context), GFP_KERNEL); |
| if (!ext_int_context) { |
| printk(KERN_ERR |
| "ME4000:alloc_ext_int_context():Can't get memory for cnt context\n"); |
| return -ENOMEM; |
| } |
| |
| info->ext_int_context = ext_int_context; |
| |
| spin_lock_init(&ext_int_context->use_lock); |
| ext_int_context->board_info = info; |
| |
| ext_int_context->fasync_ptr = NULL; |
| ext_int_context->irq = info->irq; |
| |
| ext_int_context->ctrl_reg = |
| info->me4000_regbase + ME4000_AI_CTRL_REG; |
| ext_int_context->irq_status_reg = |
| info->me4000_regbase + ME4000_IRQ_STATUS_REG; |
| } |
| |
| return 0; |
| } |
| |
| static int me4000_probe(struct pci_dev *dev, const struct pci_device_id *id) |
| { |
| int result = 0; |
| struct me4000_info *board_info; |
| |
| CALL_PDEBUG("me4000_probe() is executed\n"); |
| |
| /* Allocate structure for board context */ |
| board_info = kzalloc(sizeof(struct me4000_info), GFP_KERNEL); |
| if (!board_info) { |
| printk(KERN_ERR |
| "ME4000:Can't get memory for board info structure\n"); |
| result = -ENOMEM; |
| goto PROBE_ERROR_1; |
| } |
| |
| /* Add to global linked list */ |
| list_add_tail(&board_info->list, &me4000_board_info_list); |
| |
| /* Get the PCI base registers */ |
| result = get_registers(dev, board_info); |
| if (result) { |
| printk(KERN_ERR "%s:Cannot get registers\n", __func__); |
| goto PROBE_ERROR_2; |
| } |
| |
| /* Enable the device */ |
| result = pci_enable_device(dev); |
| if (result < 0) { |
| printk(KERN_ERR "%s:Cannot enable PCI device\n", __func__); |
| goto PROBE_ERROR_2; |
| } |
| |
| /* Request the PCI register regions */ |
| result = pci_request_regions(dev, ME4000_NAME); |
| if (result < 0) { |
| printk(KERN_ERR "%s:Cannot request I/O regions\n", __func__); |
| goto PROBE_ERROR_2; |
| } |
| |
| /* Initialize board info */ |
| result = init_board_info(dev, board_info); |
| if (result) { |
| printk(KERN_ERR "%s:Cannot init baord info\n", __func__); |
| goto PROBE_ERROR_3; |
| } |
| |
| /* Download the xilinx firmware */ |
| result = me4000_xilinx_download(board_info); |
| if (result) { |
| printk(KERN_ERR "%s:Can't download firmware\n", __func__); |
| goto PROBE_ERROR_3; |
| } |
| |
| /* Make a hardware reset */ |
| result = me4000_reset_board(board_info); |
| if (result) { |
| printk(KERN_ERR "%s :Can't reset board\n", __func__); |
| goto PROBE_ERROR_3; |
| } |
| |
| /* Allocate analog output context structures */ |
| result = alloc_ao_contexts(board_info); |
| if (result) { |
| printk(KERN_ERR "%s:Cannot allocate ao contexts\n", __func__); |
| goto PROBE_ERROR_3; |
| } |
| |
| /* Allocate analog input context */ |
| result = alloc_ai_context(board_info); |
| if (result) { |
| printk(KERN_ERR "%s:Cannot allocate ai context\n", __func__); |
| goto PROBE_ERROR_4; |
| } |
| |
| /* Allocate digital I/O context */ |
| result = alloc_dio_context(board_info); |
| if (result) { |
| printk(KERN_ERR "%s:Cannot allocate dio context\n", __func__); |
| goto PROBE_ERROR_5; |
| } |
| |
| /* Allocate counter context */ |
| result = alloc_cnt_context(board_info); |
| if (result) { |
| printk(KERN_ERR "%s:Cannot allocate cnt context\n", __func__); |
| goto PROBE_ERROR_6; |
| } |
| |
| /* Allocate external interrupt context */ |
| result = alloc_ext_int_context(board_info); |
| if (result) { |
| printk(KERN_ERR |
| "%s:Cannot allocate ext_int context\n", __func__); |
| goto PROBE_ERROR_7; |
| } |
| |
| return 0; |
| |
| PROBE_ERROR_7: |
| kfree(board_info->cnt_context); |
| |
| PROBE_ERROR_6: |
| kfree(board_info->dio_context); |
| |
| PROBE_ERROR_5: |
| kfree(board_info->ai_context); |
| |
| PROBE_ERROR_4: |
| release_ao_contexts(board_info); |
| |
| PROBE_ERROR_3: |
| pci_release_regions(dev); |
| |
| PROBE_ERROR_2: |
| list_del(&board_info->list); |
| kfree(board_info); |
| |
| PROBE_ERROR_1: |
| return result; |
| } |
| |
| static int me4000_xilinx_download(struct me4000_info *info) |
| { |
| int size = 0; |
| u32 value = 0; |
| int idx = 0; |
| unsigned char *firm; |
| wait_queue_head_t queue; |
| |
| CALL_PDEBUG("me4000_xilinx_download() is executed\n"); |
| |
| init_waitqueue_head(&queue); |
| |
| firm = (info->device_id == 0x4610) ? xilinx_firm_4610 : xilinx_firm; |
| |
| /* |
| * Set PLX local interrupt 2 polarity to high. |
| * Interrupt is thrown by init pin of xilinx. |
| */ |
| outl(0x10, info->plx_regbase + PLX_INTCSR); |
| |
| /* Set /CS and /WRITE of the Xilinx */ |
| value = inl(info->plx_regbase + PLX_ICR); |
| value |= 0x100; |
| outl(value, info->plx_regbase + PLX_ICR); |
| |
| /* Init Xilinx with CS1 */ |
| inb(info->program_regbase + 0xC8); |
| |
| /* Wait until /INIT pin is set */ |
| udelay(20); |
| if (!inl(info->plx_regbase + PLX_INTCSR) & 0x20) { |
| printk(KERN_ERR "%s:Can't init Xilinx\n", __func__); |
| return -EIO; |
| } |
| |
| /* Reset /CS and /WRITE of the Xilinx */ |
| value = inl(info->plx_regbase + PLX_ICR); |
| value &= ~0x100; |
| outl(value, info->plx_regbase + PLX_ICR); |
| |
| /* Download Xilinx firmware */ |
| size = (firm[0] << 24) + (firm[1] << 16) + (firm[2] << 8) + firm[3]; |
| udelay(10); |
| |
| for (idx = 0; idx < size; idx++) { |
| outb(firm[16 + idx], info->program_regbase); |
| |
| udelay(10); |
| |
| /* Check if BUSY flag is low */ |
| if (inl(info->plx_regbase + PLX_ICR) & 0x20) { |
| printk(KERN_ERR |
| "%s:Xilinx is still busy (idx = %d)\n", __func__, |
| idx); |
| return -EIO; |
| } |
| } |
| |
| PDEBUG("me4000_xilinx_download():%d bytes written\n", idx); |
| |
| /* If done flag is high download was successful */ |
| if (inl(info->plx_regbase + PLX_ICR) & 0x4) { |
| PDEBUG("me4000_xilinx_download():Done flag is set\n"); |
| PDEBUG("me4000_xilinx_download():Download was successful\n"); |
| } else { |
| printk(KERN_ERR |
| "ME4000:%s:DONE flag is not set\n", __func__); |
| printk(KERN_ERR |
| "ME4000:%s:Download not succesful\n", __func__); |
| return -EIO; |
| } |
| |
| /* Set /CS and /WRITE */ |
| value = inl(info->plx_regbase + PLX_ICR); |
| value |= 0x100; |
| outl(value, info->plx_regbase + PLX_ICR); |
| |
| return 0; |
| } |
| |
| static int me4000_reset_board(struct me4000_info *info) |
| { |
| unsigned long icr; |
| |
| CALL_PDEBUG("me4000_reset_board() is executed\n"); |
| |
| /* Make a hardware reset */ |
| icr = me4000_inl(info->plx_regbase + PLX_ICR); |
| icr |= 0x40000000; |
| me4000_outl(icr, info->plx_regbase + PLX_ICR); |
| icr &= ~0x40000000; |
| me4000_outl(icr, info->plx_regbase + PLX_ICR); |
| |
| /* Set both stop bits in the analog input control register */ |
| me4000_outl(ME4000_AI_CTRL_BIT_IMMEDIATE_STOP | ME4000_AI_CTRL_BIT_STOP, |
| info->me4000_regbase + ME4000_AI_CTRL_REG); |
| |
| /* Set both stop bits in the analog output control register */ |
| me4000_outl(ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP, |
| info->me4000_regbase + ME4000_AO_00_CTRL_REG); |
| me4000_outl(ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP, |
| info->me4000_regbase + ME4000_AO_01_CTRL_REG); |
| me4000_outl(ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP, |
| info->me4000_regbase + ME4000_AO_02_CTRL_REG); |
| me4000_outl(ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP, |
| info->me4000_regbase + ME4000_AO_03_CTRL_REG); |
| |
| /* 0x8000 to the DACs means an output voltage of 0V */ |
| me4000_outl(0x8000, info->me4000_regbase + ME4000_AO_00_SINGLE_REG); |
| me4000_outl(0x8000, info->me4000_regbase + ME4000_AO_01_SINGLE_REG); |
| me4000_outl(0x8000, info->me4000_regbase + ME4000_AO_02_SINGLE_REG); |
| me4000_outl(0x8000, info->me4000_regbase + ME4000_AO_03_SINGLE_REG); |
| |
| /* Enable interrupts on the PLX */ |
| me4000_outl(0x43, info->plx_regbase + PLX_INTCSR); |
| |
| /* Set the adustment register for AO demux */ |
| me4000_outl(ME4000_AO_DEMUX_ADJUST_VALUE, |
| info->me4000_regbase + ME4000_AO_DEMUX_ADJUST_REG); |
| |
| /* Set digital I/O direction for port 0 to output on isolated versions */ |
| if (!(me4000_inl(info->me4000_regbase + ME4000_DIO_DIR_REG) & 0x1)) { |
| me4000_outl(0x1, info->me4000_regbase + ME4000_DIO_CTRL_REG); |
| } |
| |
| return 0; |
| } |
| |
| static int me4000_open(struct inode *inode_p, struct file *file_p) |
| { |
| int board, dev, mode; |
| int err = 0; |
| int i; |
| struct list_head *ptr; |
| struct me4000_info *board_info = NULL; |
| struct me4000_ao_context *ao_context = NULL; |
| struct me4000_ai_context *ai_context = NULL; |
| struct me4000_dio_context *dio_context = NULL; |
| struct me4000_cnt_context *cnt_context = NULL; |
| struct me4000_ext_int_context *ext_int_context = NULL; |
| |
| CALL_PDEBUG("me4000_open() is executed\n"); |
| |
| /* Analog output */ |
| if (MAJOR(inode_p->i_rdev) == me4000_ao_major_driver_no) { |
| board = AO_BOARD(inode_p->i_rdev); |
| dev = AO_PORT(inode_p->i_rdev); |
| mode = AO_MODE(inode_p->i_rdev); |
| |
| PDEBUG("me4000_open():board = %d ao = %d mode = %d\n", board, |
| dev, mode); |
| |
| /* Search for the board context */ |
| for (ptr = me4000_board_info_list.next, i = 0; |
| ptr != &me4000_board_info_list; ptr = ptr->next, i++) { |
| board_info = list_entry(ptr, struct me4000_info, list); |
| if (i == board) |
| break; |
| } |
| |
| if (ptr == &me4000_board_info_list) { |
| printk(KERN_ERR |
| "ME4000:me4000_open():Board %d not in device list\n", |
| board); |
| return -ENODEV; |
| } |
| |
| /* Search for the dac context */ |
| for (ptr = board_info->ao_context_list.next, i = 0; |
| ptr != &board_info->ao_context_list; |
| ptr = ptr->next, i++) { |
| ao_context = list_entry(ptr, struct me4000_ao_context, |
| list); |
| if (i == dev) |
| break; |
| } |
| |
| if (ptr == &board_info->ao_context_list) { |
| printk(KERN_ERR |
| "ME4000:me4000_open():Device %d not in device list\n", |
| dev); |
| return -ENODEV; |
| } |
| |
| /* Check if mode is valid */ |
| if (mode > 2) { |
| printk(KERN_ERR |
| "ME4000:me4000_open():Mode is not valid\n"); |
| return -ENODEV; |
| } |
| |
| /* Check if mode is valid for this AO */ |
| if ((mode != ME4000_AO_CONV_MODE_SINGLE) |
| && (dev >= board_info->board_p->ao.fifo_count)) { |
| printk(KERN_ERR |
| "ME4000:me4000_open():AO %d only in single mode available\n", |
| dev); |
| return -ENODEV; |
| } |
| |
| /* Check if already opened */ |
| spin_lock(&ao_context->use_lock); |
| if (ao_context->dac_in_use) { |
| printk(KERN_ERR |
| "ME4000:me4000_open():AO %d already in use\n", |
| dev); |
| spin_unlock(&ao_context->use_lock); |
| return -EBUSY; |
| } |
| ao_context->dac_in_use = 1; |
| spin_unlock(&ao_context->use_lock); |
| |
| ao_context->mode = mode; |
| |
| /* Hold the context in private data */ |
| file_p->private_data = ao_context; |
| |
| /* Set file operations pointer */ |
| file_p->f_op = me4000_ao_fops_array[mode]; |
| |
| err = me4000_ao_prepare(ao_context); |
| if (err) { |
| ao_context->dac_in_use = 0; |
| return 1; |
| } |
| } |
| /* Analog input */ |
| else if (MAJOR(inode_p->i_rdev) == me4000_ai_major_driver_no) { |
| board = AI_BOARD(inode_p->i_rdev); |
| mode = AI_MODE(inode_p->i_rdev); |
| |
| PDEBUG("me4000_open():ai board = %d mode = %d\n", board, mode); |
| |
| /* Search for the board context */ |
| for (ptr = me4000_board_info_list.next, i = 0; |
| ptr != &me4000_board_info_list; ptr = ptr->next, i++) { |
| board_info = list_entry(ptr, struct me4000_info, list); |
| if (i == board) |
| break; |
| } |
| |
| if (ptr == &me4000_board_info_list) { |
| printk(KERN_ERR |
| "ME4000:me4000_open():Board %d not in device list\n", |
| board); |
| return -ENODEV; |
| } |
| |
| ai_context = board_info->ai_context; |
| |
| /* Check if mode is valid */ |
| if (mode > 5) { |
| printk(KERN_ERR |
| "ME4000:me4000_open():Mode is not valid\n"); |
| return -EINVAL; |
| } |
| |
| /* Check if already opened */ |
| spin_lock(&ai_context->use_lock); |
| if (ai_context->in_use) { |
| printk(KERN_ERR |
| "ME4000:me4000_open():AI already in use\n"); |
| spin_unlock(&ai_context->use_lock); |
| return -EBUSY; |
| } |
| ai_context->in_use = 1; |
| spin_unlock(&ai_context->use_lock); |
| |
| ai_context->mode = mode; |
| |
| /* Hold the context in private data */ |
| file_p->private_data = ai_context; |
| |
| /* Set file operations pointer */ |
| file_p->f_op = me4000_ai_fops_array[mode]; |
| |
| /* Prepare analog input */ |
| me4000_ai_prepare(ai_context); |
| } |
| /* Digital I/O */ |
| else if (MAJOR(inode_p->i_rdev) == me4000_dio_major_driver_no) { |
| board = DIO_BOARD(inode_p->i_rdev); |
| dev = 0; |
| mode = 0; |
| |
| PDEBUG("me4000_open():board = %d\n", board); |
| |
| /* Search for the board context */ |
| for (ptr = me4000_board_info_list.next; |
| ptr != &me4000_board_info_list; ptr = ptr->next) { |
| board_info = list_entry(ptr, struct me4000_info, list); |
| if (board_info->board_count == board) |
| break; |
| } |
| |
| if (ptr == &me4000_board_info_list) { |
| printk(KERN_ERR |
| "ME4000:me4000_open():Board %d not in device list\n", |
| board); |
| return -ENODEV; |
| } |
| |
| /* Search for the dio context */ |
| dio_context = board_info->dio_context; |
| |
| /* Check if already opened */ |
| spin_lock(&dio_context->use_lock); |
| if (dio_context->in_use) { |
| printk(KERN_ERR |
| "ME4000:me4000_open():DIO already in use\n"); |
| spin_unlock(&dio_context->use_lock); |
| return -EBUSY; |
| } |
| dio_context->in_use = 1; |
| spin_unlock(&dio_context->use_lock); |
| |
| /* Hold the context in private data */ |
| file_p->private_data = dio_context; |
| |
| /* Set file operations pointer to single functions */ |
| file_p->f_op = &me4000_dio_fops; |
| |
| //me4000_dio_reset(dio_context); |
| } |
| /* Counters */ |
| else if (MAJOR(inode_p->i_rdev) == me4000_cnt_major_driver_no) { |
| board = CNT_BOARD(inode_p->i_rdev); |
| dev = 0; |
| mode = 0; |
| |
| PDEBUG("me4000_open():board = %d\n", board); |
| |
| /* Search for the board context */ |
| for (ptr = me4000_board_info_list.next; |
| ptr != &me4000_board_info_list; ptr = ptr->next) { |
| board_info = list_entry(ptr, struct me4000_info, list); |
| if (board_info->board_count == board) |
| break; |
| } |
| |
| if (ptr == &me4000_board_info_list) { |
| printk(KERN_ERR |
| "ME4000:me4000_open():Board %d not in device list\n", |
| board); |
| return -ENODEV; |
| } |
| |
| /* Get the cnt context */ |
| cnt_context = board_info->cnt_context; |
| |
| /* Check if already opened */ |
| spin_lock(&cnt_context->use_lock); |
| if (cnt_context->in_use) { |
| printk(KERN_ERR |
| "ME4000:me4000_open():CNT already in use\n"); |
| spin_unlock(&cnt_context->use_lock); |
| return -EBUSY; |
| } |
| cnt_context->in_use = 1; |
| spin_unlock(&cnt_context->use_lock); |
| |
| /* Hold the context in private data */ |
| file_p->private_data = cnt_context; |
| |
| /* Set file operations pointer to single functions */ |
| file_p->f_op = &me4000_cnt_fops; |
| } |
| /* External Interrupt */ |
| else if (MAJOR(inode_p->i_rdev) == me4000_ext_int_major_driver_no) { |
| board = EXT_INT_BOARD(inode_p->i_rdev); |
| dev = 0; |
| mode = 0; |
| |
| PDEBUG("me4000_open():board = %d\n", board); |
| |
| /* Search for the board context */ |
| for (ptr = me4000_board_info_list.next; |
| ptr != &me4000_board_info_list; ptr = ptr->next) { |
| board_info = list_entry(ptr, struct me4000_info, list); |
| if (board_info->board_count == board) |
| break; |
| } |
| |
| if (ptr == &me4000_board_info_list) { |
| printk(KERN_ERR |
| "ME4000:me4000_open():Board %d not in device list\n", |
| board); |
| return -ENODEV; |
| } |
| |
| /* Get the external interrupt context */ |
| ext_int_context = board_info->ext_int_context; |
| |
| /* Check if already opened */ |
| spin_lock(&cnt_context->use_lock); |
| if (ext_int_context->in_use) { |
| printk(KERN_ERR |
| "ME4000:me4000_open():External interrupt already in use\n"); |
| spin_unlock(&ext_int_context->use_lock); |
| return -EBUSY; |
| } |
| ext_int_context->in_use = 1; |
| spin_unlock(&ext_int_context->use_lock); |
| |
| /* Hold the context in private data */ |
| file_p->private_data = ext_int_context; |
| |
| /* Set file operations pointer to single functions */ |
| file_p->f_op = &me4000_ext_int_fops; |
| |
| /* Request the interrupt line */ |
| err = |
| request_irq(ext_int_context->irq, me4000_ext_int_isr, |
| IRQF_DISABLED | IRQF_SHARED, ME4000_NAME, |
| ext_int_context); |
| if (err) { |
| printk(KERN_ERR |
| "ME4000:me4000_open():Can't get interrupt line"); |
| ext_int_context->in_use = 0; |
| return -ENODEV; |
| } |
| |
| /* Reset the counter */ |
| me4000_ext_int_disable(ext_int_context); |
| } else { |
| printk(KERN_ERR "ME4000:me4000_open():Major number unknown\n"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int me4000_release(struct inode *inode_p, struct file *file_p) |
| { |
| struct me4000_ao_context *ao_context; |
| struct me4000_ai_context *ai_context; |
| struct me4000_dio_context *dio_context; |
| struct me4000_cnt_context *cnt_context; |
| struct me4000_ext_int_context *ext_int_context; |
| |
| CALL_PDEBUG("me4000_release() is executed\n"); |
| |
| if (MAJOR(inode_p->i_rdev) == me4000_ao_major_driver_no) { |
| ao_context = file_p->private_data; |
| |
| /* Mark DAC as unused */ |
| ao_context->dac_in_use = 0; |
| } else if (MAJOR(inode_p->i_rdev) == me4000_ai_major_driver_no) { |
| ai_context = file_p->private_data; |
| |
| /* Reset the analog input */ |
| me4000_ai_reset(ai_context); |
| |
| /* Free the interrupt and the circular buffer */ |
| if (ai_context->mode) { |
| free_irq(ai_context->irq, ai_context); |
| kfree(ai_context->circ_buf.buf); |
| ai_context->circ_buf.buf = NULL; |
| ai_context->circ_buf.head = 0; |
| ai_context->circ_buf.tail = 0; |
| } |
| |
| /* Mark AI as unused */ |
| ai_context->in_use = 0; |
| } else if (MAJOR(inode_p->i_rdev) == me4000_dio_major_driver_no) { |
| dio_context = file_p->private_data; |
| |
| /* Mark digital I/O as unused */ |
| dio_context->in_use = 0; |
| } else if (MAJOR(inode_p->i_rdev) == me4000_cnt_major_driver_no) { |
| cnt_context = file_p->private_data; |
| |
| /* Mark counters as unused */ |
| cnt_context->in_use = 0; |
| } else if (MAJOR(inode_p->i_rdev) == me4000_ext_int_major_driver_no) { |
| ext_int_context = file_p->private_data; |
| |
| /* Disable the externel interrupt */ |
| me4000_ext_int_disable(ext_int_context); |
| |
| free_irq(ext_int_context->irq, ext_int_context); |
| |
| /* Mark as unused */ |
| ext_int_context->in_use = 0; |
| } else { |
| printk(KERN_ERR |
| "ME4000:me4000_release():Major number unknown\n"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /*------------------------------- Analog output stuff --------------------------------------*/ |
| |
| static int me4000_ao_prepare(struct me4000_ao_context *ao_context) |
| { |
| unsigned long flags; |
| |
| CALL_PDEBUG("me4000_ao_prepare() is executed\n"); |
| |
| if (ao_context->mode == ME4000_AO_CONV_MODE_CONTINUOUS) { |
| /* Only do anything if not already in the correct mode */ |
| unsigned long mode = me4000_inl(ao_context->ctrl_reg); |
| if ((mode & ME4000_AO_CONV_MODE_CONTINUOUS) |
| && (mode & ME4000_AO_CTRL_BIT_ENABLE_FIFO)) { |
| return 0; |
| } |
| |
| /* Stop any conversion */ |
| me4000_ao_immediate_stop(ao_context); |
| |
| /* Set the control register to default state */ |
| spin_lock_irqsave(&ao_context->int_lock, flags); |
| me4000_outl(ME4000_AO_CONV_MODE_CONTINUOUS | |
| ME4000_AO_CTRL_BIT_ENABLE_FIFO | |
| ME4000_AO_CTRL_BIT_STOP | |
| ME4000_AO_CTRL_BIT_IMMEDIATE_STOP, |
| ao_context->ctrl_reg); |
| spin_unlock_irqrestore(&ao_context->int_lock, flags); |
| |
| /* Set to fastest sample rate */ |
| me4000_outl(65, ao_context->timer_reg); |
| } else if (ao_context->mode == ME4000_AO_CONV_MODE_WRAPAROUND) { |
| /* Only do anything if not already in the correct mode */ |
| unsigned long mode = me4000_inl(ao_context->ctrl_reg); |
| if ((mode & ME4000_AO_CONV_MODE_WRAPAROUND) |
| && (mode & ME4000_AO_CTRL_BIT_ENABLE_FIFO)) { |
| return 0; |
| } |
| |
| /* Stop any conversion */ |
| me4000_ao_immediate_stop(ao_context); |
| |
| /* Set the control register to default state */ |
| spin_lock_irqsave(&ao_context->int_lock, flags); |
| me4000_outl(ME4000_AO_CONV_MODE_WRAPAROUND | |
| ME4000_AO_CTRL_BIT_ENABLE_FIFO | |
| ME4000_AO_CTRL_BIT_STOP | |
| ME4000_AO_CTRL_BIT_IMMEDIATE_STOP, |
| ao_context->ctrl_reg); |
| spin_unlock_irqrestore(&ao_context->int_lock, flags); |
| |
| /* Set to fastest sample rate */ |
| me4000_outl(65, ao_context->timer_reg); |
| } else if (ao_context->mode == ME4000_AO_CONV_MODE_SINGLE) { |
| /* Only do anything if not already in the correct mode */ |
| unsigned long mode = me4000_inl(ao_context->ctrl_reg); |
| if (! |
| (mode & |
| (ME4000_AO_CONV_MODE_WRAPAROUND | |
| ME4000_AO_CONV_MODE_CONTINUOUS))) { |
| return 0; |
| } |
| |
| /* Stop any conversion */ |
| me4000_ao_immediate_stop(ao_context); |
| |
| /* Clear the control register */ |
| spin_lock_irqsave(&ao_context->int_lock, flags); |
| me4000_outl(0x0, ao_context->ctrl_reg); |
| spin_unlock_irqrestore(&ao_context->int_lock, flags); |
| |
| /* Set voltage to 0V */ |
| me4000_outl(0x8000, ao_context->single_reg); |
| } else { |
| printk(KERN_ERR |
| "ME4000:me4000_ao_prepare():Invalid mode specified\n"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int me4000_ao_reset(struct me4000_ao_context *ao_context) |
| { |
| u32 tmp; |
| wait_queue_head_t queue; |
| unsigned long flags; |
| |
| CALL_PDEBUG("me4000_ao_reset() is executed\n"); |
| |
| init_waitqueue_head(&queue); |
| |
| if (ao_context->mode == ME4000_AO_CONV_MODE_WRAPAROUND) { |
| /* |
| * First stop conversion of the DAC before reconfigure. |
| * This is essantial, cause of the state machine. |
| * If not stopped before configuring mode, it could |
| * walk in a undefined state. |
| */ |
| tmp = me4000_inl(ao_context->ctrl_reg); |
| tmp |= ME4000_AO_CTRL_BIT_IMMEDIATE_STOP; |
| me4000_outl(tmp, ao_context->ctrl_reg); |
| |
| wait_event_timeout(queue, |
| (inl(ao_context->status_reg) & |
| ME4000_AO_STATUS_BIT_FSM) == 0, |
| 1); |
| |
| /* Set to transparent mode */ |
| me4000_ao_simultaneous_disable(ao_context); |
| |
| /* Set to single mode in order to set default voltage */ |
| me4000_outl(0x0, ao_context->ctrl_reg); |
| |
| /* Set voltage to 0V */ |
| me4000_outl(0x8000, ao_context->single_reg); |
| |
| /* Set to fastest sample rate */ |
| me4000_outl(65, ao_context->timer_reg); |
| |
| /* Set the original mode and enable FIFO */ |
| me4000_outl(ME4000_AO_CONV_MODE_WRAPAROUND | |
| ME4000_AO_CTRL_BIT_ENABLE_FIFO | |
| ME4000_AO_CTRL_BIT_STOP | |
| ME4000_AO_CTRL_BIT_IMMEDIATE_STOP, |
| ao_context->ctrl_reg); |
| } else if (ao_context->mode == ME4000_AO_CONV_MODE_CONTINUOUS) { |
| /* |
| * First stop conversion of the DAC before reconfigure. |
| * This is essantial, cause of the state machine. |
| * If not stopped before configuring mode, it could |
| * walk in a undefined state. |
| */ |
| spin_lock_irqsave(&ao_context->int_lock, flags); |
| tmp = me4000_inl(ao_context->ctrl_reg); |
| tmp |= ME4000_AO_CTRL_BIT_STOP; |
| me4000_outl(tmp, ao_context->ctrl_reg); |
| spin_unlock_irqrestore(&ao_context->int_lock, flags); |
| |
| wait_event_timeout(queue, |
| (inl(ao_context->status_reg) & |
| ME4000_AO_STATUS_BIT_FSM) == 0, |
| 1); |
| |
| /* Clear the circular buffer */ |
| ao_context->circ_buf.head = 0; |
| ao_context->circ_buf.tail = 0; |
| |
| /* Set to transparent mode */ |
| me4000_ao_simultaneous_disable(ao_context); |
| |
| /* Set to single mode in order to set default voltage */ |
| spin_lock_irqsave(&ao_context->int_lock, flags); |
| tmp = me4000_inl(ao_context->ctrl_reg); |
| me4000_outl(0x0, ao_context->ctrl_reg); |
| |
| /* Set voltage to 0V */ |
| me4000_outl(0x8000, ao_context->single_reg); |
| |
| /* Set to fastest sample rate */ |
| me4000_outl(65, ao_context->timer_reg); |
| |
| /* Set the original mode and enable FIFO */ |
| me4000_outl(ME4000_AO_CONV_MODE_CONTINUOUS | |
| ME4000_AO_CTRL_BIT_ENABLE_FIFO | |
| ME4000_AO_CTRL_BIT_STOP | |
| ME4000_AO_CTRL_BIT_IMMEDIATE_STOP, |
| ao_context->ctrl_reg); |
| spin_unlock_irqrestore(&ao_context->int_lock, flags); |
| } else { |
| /* Set to transparent mode */ |
| me4000_ao_simultaneous_disable(ao_context); |
| |
| /* Set voltage to 0V */ |
| me4000_outl(0x8000, ao_context->single_reg); |
| } |
| |
| return 0; |
| } |
| |
| static ssize_t me4000_ao_write_sing(struct file *filep, const char *buff, |
| size_t cnt, loff_t *offp) |
| { |
| struct me4000_ao_context *ao_context = filep->private_data; |
| u32 value; |
| const u16 *buffer = (const u16 *)buff; |
| |
| CALL_PDEBUG("me4000_ao_write_sing() is executed\n"); |
| |
| if (cnt != 2) { |
| printk(KERN_ERR |
| "%s:Write count is not 2\n", __func__); |
| return -EINVAL; |
| } |
| |
| if (get_user(value, buffer)) { |
| printk(KERN_ERR |
| "%s:Cannot copy data from user\n", __func__); |
| return -EFAULT; |
| } |
| |
| me4000_outl(value, ao_context->single_reg); |
| |
| return 2; |
| } |
| |
| static ssize_t me4000_ao_write_wrap(struct file *filep, const char *buff, |
| size_t cnt, loff_t *offp) |
| { |
| struct me4000_ao_context *ao_context = filep->private_data; |
| size_t i; |
| u32 value; |
| u32 tmp; |
| const u16 *buffer = (const u16 *)buff; |
| size_t count = cnt / 2; |
| |
| CALL_PDEBUG("me4000_ao_write_wrap() is executed\n"); |
| |
| /* Check if a conversion is already running */ |
| if (inl(ao_context->status_reg) & ME4000_AO_STATUS_BIT_FSM) { |
| printk(KERN_ERR |
| "%s:There is already a conversion running\n", __func__); |
| return -EBUSY; |
| } |
| |
| if (count > ME4000_AO_FIFO_COUNT) { |
| printk(KERN_ERR |
| "%s:Can't load more than %d values\n", __func__, |
| ME4000_AO_FIFO_COUNT); |
| return -ENOSPC; |
| } |
| |
| /* Reset the FIFO */ |
| tmp = inl(ao_context->ctrl_reg); |
| tmp &= ~ME4000_AO_CTRL_BIT_ENABLE_FIFO; |
| outl(tmp, ao_context->ctrl_reg); |
| tmp |= ME4000_AO_CTRL_BIT_ENABLE_FIFO; |
| outl(tmp, ao_context->ctrl_reg); |
| |
| for (i = 0; i < count; i++) { |
| if (get_user(value, buffer + i)) { |
| printk(KERN_ERR |
| "%s:Cannot copy data from user\n", __func__); |
| return -EFAULT; |
| } |
| if (((ao_context->fifo_reg & 0xFF) == ME4000_AO_01_FIFO_REG) |
| || ((ao_context->fifo_reg & 0xFF) == ME4000_AO_03_FIFO_REG)) |
| value = value << 16; |
| outl(value, ao_context->fifo_reg); |
| } |
| CALL_PDEBUG("me4000_ao_write_wrap() is leaved with %d\n", i * 2); |
| |
| return i * 2; |
| } |
| |
| static ssize_t me4000_ao_write_cont(struct file *filep, const char *buff, |
| size_t cnt, loff_t *offp) |
| { |
| struct me4000_ao_context *ao_context = filep->private_data; |
| const u16 *buffer = (const u16 *)buff; |
| size_t count = cnt / 2; |
| unsigned long flags; |
| u32 tmp; |
| int c = 0; |
| int k = 0; |
| int ret = 0; |
| u16 svalue; |
| u32 lvalue; |
| int i; |
| wait_queue_head_t queue; |
| |
| CALL_PDEBUG("me4000_ao_write_cont() is executed\n"); |
| |
| init_waitqueue_head(&queue); |
| |
| /* Check count */ |
| if (count <= 0) { |
| PDEBUG("me4000_ao_write_cont():Count is 0\n"); |
| return 0; |
| } |
| |
| if (filep->f_flags & O_APPEND) { |
| PDEBUG("me4000_ao_write_cont():Append data to data stream\n"); |
| while (count > 0) { |
| if (filep->f_flags & O_NONBLOCK) { |
| if (ao_context->pipe_flag) { |
| printk(KERN_ERR |
| "ME4000:me4000_ao_write_cont():Broken pipe in nonblocking write\n"); |
| return -EPIPE; |
| } |
| c = me4000_space_to_end(ao_context->circ_buf, |
| ME4000_AO_BUFFER_COUNT); |
| if (!c) { |
| PDEBUG |
| ("me4000_ao_write_cont():Returning from nonblocking write\n"); |
| break; |
| } |
| } else { |
| wait_event_interruptible(ao_context->wait_queue, |
| (c = |
| me4000_space_to_end |
| (ao_context->circ_buf, |
| ME4000_AO_BUFFER_COUNT))); |
| if (ao_context->pipe_flag) { |
| printk(KERN_ERR |
| "me4000_ao_write_cont():Broken pipe in blocking write\n"); |
| return -EPIPE; |
| } |
| if (signal_pending(current)) { |
| printk(KERN_ERR |
| "me4000_ao_write_cont():Wait for free buffer interrupted from signal\n"); |
| return -EINTR; |
| } |
| } |
| |
| PDEBUG("me4000_ao_write_cont():Space to end = %d\n", c); |
| |
| /* Only able to write size of free buffer or size of count */ |
| if (count < c) |
| c = count; |
| |
| k = 2 * c; |
| k -= copy_from_user(ao_context->circ_buf.buf + |
| ao_context->circ_buf.head, buffer, |
| k); |
| c = k / 2; |
| PDEBUG |
| ("me4000_ao_write_cont():Copy %d values from user space\n", |
| c); |
| |
| if (!c) |
| return -EFAULT; |
| |
| ao_context->circ_buf.head = |
| (ao_context->circ_buf.head + |
| c) & (ME4000_AO_BUFFER_COUNT - 1); |
| buffer += c; |
| count -= c; |
| ret += c; |
| |
| /* Values are now available so enable interrupts */ |
| spin_lock_irqsave(&ao_context->int_lock, flags); |
| if (me4000_buf_count |
| (ao_context->circ_buf, ME4000_AO_BUFFER_COUNT)) { |
| tmp = me4000_inl(ao_context->ctrl_reg); |
| tmp |= ME4000_AO_CTRL_BIT_ENABLE_IRQ; |
| me4000_outl(tmp, ao_context->ctrl_reg); |
| } |
| spin_unlock_irqrestore(&ao_context->int_lock, flags); |
| } |
| |
| /* Wait until the state machine is stopped if O_SYNC is set */ |
| if (filep->f_flags & O_SYNC) { |
| while (inl(ao_context->status_reg) & |
| ME4000_AO_STATUS_BIT_FSM) { |
| interruptible_sleep_on_timeout(&queue, 1); |
| if (ao_context->pipe_flag) { |
| PDEBUG |
| ("me4000_ao_write_cont():Broken pipe detected after sync\n"); |
| return -EPIPE; |
| } |
| if (signal_pending(current)) { |
| printk(KERN_ERR |
| "me4000_ao_write_cont():Wait on state machine after sync interrupted\n"); |
| return -EINTR; |
| } |
| } |
| } |
| } else { |
| PDEBUG("me4000_ao_write_cont():Preload DAC FIFO\n"); |
| if ((me4000_inl(ao_context->status_reg) & |
| ME4000_AO_STATUS_BIT_FSM)) { |
| printk(KERN_ERR |
| "me4000_ao_write_cont():Can't Preload DAC FIFO while conversion is running\n"); |
| return -EBUSY; |
| } |
| |
| /* Clear the FIFO */ |
| spin_lock_irqsave(&ao_context->int_lock, flags); |
| tmp = me4000_inl(ao_context->ctrl_reg); |
| tmp &= |
| ~(ME4000_AO_CTRL_BIT_ENABLE_FIFO | |
| ME4000_AO_CTRL_BIT_ENABLE_IRQ); |
| me4000_outl(tmp, ao_context->ctrl_reg); |
| tmp |= ME4000_AO_CTRL_BIT_ENABLE_FIFO; |
| me4000_outl(tmp, ao_context->ctrl_reg); |
| spin_unlock_irqrestore(&ao_context->int_lock, flags); |
| |
| /* Clear the circular buffer */ |
| ao_context->circ_buf.head = 0; |
| ao_context->circ_buf.tail = 0; |
| |
| /* Reset the broken pipe flag */ |
| ao_context->pipe_flag = 0; |
| |
| /* Only able to write size of fifo or count */ |
| c = ME4000_AO_FIFO_COUNT; |
| if (count < c) |
| c = count; |
| |
| PDEBUG |
| ("me4000_ao_write_cont():Write %d values to DAC on 0x%lX\n", |
| c, ao_context->fifo_reg); |
| |
| /* Write values to the fifo */ |
| for (i = 0; i < c; i++) { |
| if (get_user(svalue, buffer)) |
| return -EFAULT; |
| |
| if (((ao_context->fifo_reg & 0xFF) == |
| ME4000_AO_01_FIFO_REG) |
| || ((ao_context->fifo_reg & 0xFF) == |
| ME4000_AO_03_FIFO_REG)) { |
| lvalue = ((u32) svalue) << 16; |
| } else |
| lvalue = (u32) svalue; |
| |
| outl(lvalue, ao_context->fifo_reg); |
| buffer++; |
| } |
| count -= c; |
| ret += c; |
| |
| while (1) { |
| /* Get free buffer */ |
| c = me4000_space_to_end(ao_context->circ_buf, |
| ME4000_AO_BUFFER_COUNT); |
| |
| if (c == 0) |
| return (2 * ret); |
| |
| /* Only able to write size of free buffer or size of count */ |
| if (count < c) |
| c = count; |
| |
| /* If count = 0 return to user */ |
| if (c <= 0) { |
| PDEBUG |
| ("me4000_ao_write_cont():Count reached 0\n"); |
| break; |
| } |
| |
| k = 2 * c; |
| k -= copy_from_user(ao_context->circ_buf.buf + |
| ao_context->circ_buf.head, buffer, |
| k); |
| c = k / 2; |
| PDEBUG |
| ("me4000_ao_write_cont():Wrote %d values to buffer\n", |
| c); |
| |
| if (!c) |
| return -EFAULT; |
| |
| ao_context->circ_buf.head = |
| (ao_context->circ_buf.head + |
| c) & (ME4000_AO_BUFFER_COUNT - 1); |
| buffer += c; |
| count -= c; |
| ret += c; |
| |
| /* If values in the buffer are available so enable interrupts */ |
| spin_lock_irqsave(&ao_context->int_lock, flags); |
| if (me4000_buf_count |
| (ao_context->circ_buf, ME4000_AO_BUFFER_COUNT)) { |
| PDEBUG |
| ("me4000_ao_write_cont():Enable Interrupts\n"); |
| tmp = me4000_inl(ao_context->ctrl_reg); |
| tmp |= ME4000_AO_CTRL_BIT_ENABLE_IRQ; |
| me4000_outl(tmp, ao_context->ctrl_reg); |
| } |
| spin_unlock_irqrestore(&ao_context->int_lock, flags); |
| } |
| } |
| |
| if (filep->f_flags & O_NONBLOCK) { |
| return (ret == 0) ? -EAGAIN : 2 * ret; |
| } |
| |
| return 2 * ret; |
| } |
| |
| static unsigned int me4000_ao_poll_cont(struct file *file_p, poll_table *wait) |
| { |
| struct me4000_ao_context *ao_context; |
| unsigned long mask = 0; |
| |
| CALL_PDEBUG("me4000_ao_poll_cont() is executed\n"); |
| |
| ao_context = file_p->private_data; |
| |
| poll_wait(file_p, &ao_context->wait_queue, wait); |
| |
| /* Get free buffer */ |
| if (me4000_space_to_end(ao_context->circ_buf, ME4000_AO_BUFFER_COUNT)) |
| mask |= POLLOUT | POLLWRNORM; |
| |
| CALL_PDEBUG("me4000_ao_poll_cont():Return mask %lX\n", mask); |
| |
| return mask; |
| } |
| |
| static int me4000_ao_fsync_cont(struct file *file_p, struct dentry *dentry_p, |
| int datasync) |
| { |
| struct me4000_ao_context *ao_context; |
| wait_queue_head_t queue; |
| |
| CALL_PDEBUG("me4000_ao_fsync_cont() is executed\n"); |
| |
| ao_context = file_p->private_data; |
| init_waitqueue_head(&queue); |
| |
| while (inl(ao_context->status_reg) & ME4000_AO_STATUS_BIT_FSM) { |
| interruptible_sleep_on_timeout(&queue, 1); |
| wait_event_interruptible_timeout(queue, |
| !(inl(ao_context->status_reg) & ME4000_AO_STATUS_BIT_FSM), |
| 1); |
| if (ao_context->pipe_flag) { |
| printk(KERN_ERR |
| "%s:Broken pipe detected\n", __func__); |
| return -EPIPE; |
| } |
| |
| if (signal_pending(current)) { |
| printk(KERN_ERR |
| "%s:Wait on state machine interrupted\n", |
| __func__); |
| return -EINTR; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int me4000_ao_ioctl_sing(struct inode *inode_p, struct file *file_p, |
| unsigned int service, unsigned long arg) |
| { |
| struct me4000_ao_context *ao_context; |
| |
| CALL_PDEBUG("me4000_ao_ioctl_sing() is executed\n"); |
| |
| ao_context = file_p->private_data; |
| |
| if (_IOC_TYPE(service) != ME4000_MAGIC) { |
| return -ENOTTY; |
| PDEBUG("me4000_ao_ioctl_sing():Wrong magic number\n"); |
| } |
| |
| switch (service) { |
| case ME4000_AO_EX_TRIG_SETUP: |
| return me4000_ao_ex_trig_set_edge((int *)arg, ao_context); |
| case ME4000_AO_EX_TRIG_ENABLE: |
| return me4000_ao_ex_trig_enable(ao_context); |
| case ME4000_AO_EX_TRIG_DISABLE: |
| return me4000_ao_ex_trig_disable(ao_context); |
| case ME4000_AO_PRELOAD: |
| return me4000_ao_preload(ao_context); |
| case ME4000_AO_PRELOAD_UPDATE: |
| return me4000_ao_preload_update(ao_context); |
| case ME4000_GET_USER_INFO: |
| return me4000_get_user_info((struct me4000_user_info *)arg, |
| ao_context->board_info); |
| case ME4000_AO_SIMULTANEOUS_EX_TRIG: |
| return me4000_ao_simultaneous_ex_trig(ao_context); |
| case ME4000_AO_SIMULTANEOUS_SW: |
| return me4000_ao_simultaneous_sw(ao_context); |
| case ME4000_AO_SIMULTANEOUS_DISABLE: |
| return me4000_ao_simultaneous_disable(ao_context); |
| case ME4000_AO_SIMULTANEOUS_UPDATE: |
| return |
| me4000_ao_simultaneous_update( |
| (struct me4000_ao_channel_list *)arg, |
| ao_context); |
| case ME4000_AO_EX_TRIG_TIMEOUT: |
| return me4000_ao_ex_trig_timeout((unsigned long *)arg, |
| ao_context); |
| case ME4000_AO_DISABLE_DO: |
| return me4000_ao_disable_do(ao_context); |
| default: |
| printk(KERN_ERR |
| "me4000_ao_ioctl_sing():Service number invalid\n"); |
| return -ENOTTY; |
| } |
| |
| return 0; |
| } |
| |
| static int me4000_ao_ioctl_wrap(struct inode *inode_p, struct file *file_p, |
| unsigned int service, unsigned long arg) |
| { |
| struct me4000_ao_context *ao_context; |
| |
| CALL_PDEBUG("me4000_ao_ioctl_wrap() is executed\n"); |
| |
| ao_context = file_p->private_data; |
| |
| if (_IOC_TYPE(service) != ME4000_MAGIC) { |
| return -ENOTTY; |
| PDEBUG("me4000_ao_ioctl_wrap():Wrong magic number\n"); |
| } |
| |
| switch (service) { |
| case ME4000_AO_START: |
| return me4000_ao_start((unsigned long *)arg, ao_context); |
| case ME4000_AO_STOP: |
| return me4000_ao_stop(ao_context); |
| case ME4000_AO_IMMEDIATE_STOP: |
| return me4000_ao_immediate_stop(ao_context); |
| case ME4000_AO_RESET: |
| return me4000_ao_reset(ao_context); |
| case ME4000_AO_TIMER_SET_DIVISOR: |
| return me4000_ao_timer_set_divisor((u32 *) arg, ao_context); |
| case ME4000_AO_EX_TRIG_SETUP: |
| return me4000_ao_ex_trig_set_edge((int *)arg, ao_context); |
| case ME4000_AO_EX_TRIG_ENABLE: |
| return me4000_ao_ex_trig_enable(ao_context); |
| case ME4000_AO_EX_TRIG_DISABLE: |
| return me4000_ao_ex_trig_disable(ao_context); |
| case ME4000_GET_USER_INFO: |
| return me4000_get_user_info((struct me4000_user_info *)arg, |
| ao_context->board_info); |
| case ME4000_AO_FSM_STATE: |
| return me4000_ao_fsm_state((int *)arg, ao_context); |
| case ME4000_AO_ENABLE_DO: |
| return me4000_ao_enable_do(ao_context); |
| case ME4000_AO_DISABLE_DO: |
| return me4000_ao_disable_do(ao_context); |
| case ME4000_AO_SYNCHRONOUS_EX_TRIG: |
| return me4000_ao_synchronous_ex_trig(ao_context); |
| case ME4000_AO_SYNCHRONOUS_SW: |
| return me4000_ao_synchronous_sw(ao_context); |
| case ME4000_AO_SYNCHRONOUS_DISABLE: |
| return me4000_ao_synchronous_disable(ao_context); |
| default: |
| return -ENOTTY; |
| } |
| return 0; |
| } |
| |
| static int me4000_ao_ioctl_cont(struct inode *inode_p, struct file *file_p, |
| unsigned int service, unsigned long arg) |
| { |
| struct me4000_ao_context *ao_context; |
| |
| CALL_PDEBUG("me4000_ao_ioctl_cont() is executed\n"); |
| |
| ao_context = file_p->private_data; |
| |
| if (_IOC_TYPE(service) != ME4000_MAGIC) { |
| return -ENOTTY; |
| PDEBUG("me4000_ao_ioctl_cont():Wrong magic number\n"); |
| } |
| |
| switch (service) { |
| case ME4000_AO_START: |
| return me4000_ao_start((unsigned long *)arg, ao_context); |
| case ME4000_AO_STOP: |
| return me4000_ao_stop(ao_context); |
| case ME4000_AO_IMMEDIATE_STOP: |
| return me4000_ao_immediate_stop(ao_context); |
| case ME4000_AO_RESET: |
| return me4000_ao_reset(ao_context); |
| case ME4000_AO_TIMER_SET_DIVISOR: |
| return me4000_ao_timer_set_divisor((u32 *) arg, ao_context); |
| case ME4000_AO_EX_TRIG_SETUP: |
| return me4000_ao_ex_trig_set_edge((int *)arg, ao_context); |
| case ME4000_AO_EX_TRIG_ENABLE: |
| return me4000_ao_ex_trig_enable(ao_context); |
| case ME4000_AO_EX_TRIG_DISABLE: |
| return me4000_ao_ex_trig_disable(ao_context); |
| case ME4000_AO_ENABLE_DO: |
| return me4000_ao_enable_do(ao_context); |
| case ME4000_AO_DISABLE_DO: |
| return me4000_ao_disable_do(ao_context); |
| case ME4000_AO_FSM_STATE: |
| return me4000_ao_fsm_state((int *)arg, ao_context); |
| case ME4000_GET_USER_INFO: |
| return me4000_get_user_info((struct me4000_user_info *)arg, |
| ao_context->board_info); |
| case ME4000_AO_SYNCHRONOUS_EX_TRIG: |
| return me4000_ao_synchronous_ex_trig(ao_context); |
| case ME4000_AO_SYNCHRONOUS_SW: |
| return me4000_ao_synchronous_sw(ao_context); |
| case ME4000_AO_SYNCHRONOUS_DISABLE: |
| return me4000_ao_synchronous_disable(ao_context); |
| case ME4000_AO_GET_FREE_BUFFER: |
| return me4000_ao_get_free_buffer((unsigned long *)arg, |
| ao_context); |
| default: |
| return -ENOTTY; |
| } |
| return 0; |
| } |
| |
| static int me4000_ao_start(unsigned long *arg, |
| struct me4000_ao_context *ao_context) |
| { |
| u32 tmp; |
| wait_queue_head_t queue; |
| unsigned long ref; |
| unsigned long timeout; |
| unsigned long flags; |
| |
| CALL_PDEBUG("me4000_ao_start() is executed\n"); |
| |
| if (get_user(timeout, arg)) { |
| printk(KERN_ERR |
| "me4000_ao_start():Cannot copy data from user\n"); |
| return -EFAULT; |
| } |
| |
| init_waitqueue_head(&queue); |
| |
| spin_lock_irqsave(&ao_context->int_lock, flags); |
| tmp = inl(ao_context->ctrl_reg); |
| tmp &= ~(ME4000_AO_CTRL_BIT_STOP | ME4000_AO_CTRL_BIT_IMMEDIATE_STOP); |
| me4000_outl(tmp, ao_context->ctrl_reg); |
| spin_unlock_irqrestore(&ao_context->int_lock, flags); |
| |
| if ((tmp & ME4000_AO_CTRL_BIT_ENABLE_EX_TRIG)) { |
| if (timeout) { |
| ref = jiffies; |
| while (! |
| (inl(ao_context->status_reg) & |
| ME4000_AO_STATUS_BIT_FSM)) { |
| interruptible_sleep_on_timeout(&queue, 1); |
| if (signal_pending(current)) { |
| printk(KERN_ERR |
| "ME4000:me4000_ao_start():Wait on start of state machine interrupted\n"); |
| return -EINTR; |
| } |
| if (((jiffies - ref) > (timeout * HZ / USER_HZ))) { // 2.6 has diffrent definitions for HZ in user and kernel space |
| printk(KERN_ERR |
| "ME4000:me4000_ao_start():Timeout reached\n"); |
| return -EIO; |
| } |
| } |
| } |
| } else { |
| me4000_outl(0x8000, ao_context->single_reg); |
| } |
| |
| return 0; |
| } |
| |
| static int me4000_ao_stop(struct me4000_ao_context *ao_context) |
| { |
| u32 tmp; |
| wait_queue_head_t queue; |
| unsigned long flags; |
| |
| init_waitqueue_head(&queue); |
| |
| CALL_PDEBUG("me4000_ao_stop() is executed\n"); |
| |
| /* Set the stop bit */ |
| spin_lock_irqsave(&ao_context->int_lock, flags); |
| tmp = inl(ao_context->ctrl_reg); |
| tmp |= ME4000_AO_CTRL_BIT_STOP; |
| me4000_outl(tmp, ao_context->ctrl_reg); |
| spin_unlock_irqrestore(&ao_context->int_lock, flags); |
| |
| while (inl(ao_context->status_reg) & ME4000_AO_STATUS_BIT_FSM) { |
| interruptible_sleep_on_timeout(&queue, 1); |
| if (signal_pending(current)) { |
| printk(KERN_ERR |
| "me4000_ao_stop():Wait on state machine after stop interrupted\n"); |
| return -EINTR; |
| } |
| } |
| |
| /* Clear the stop bit */ |
| //tmp &= ~ME4000_AO_CTRL_BIT_STOP; |
| //me4000_outl(tmp, ao_context->ctrl_reg); |
| |
| return 0; |
| } |
| |
| static int me4000_ao_immediate_stop(struct me4000_ao_context *ao_context) |
| { |
| u32 tmp; |
| wait_queue_head_t queue; |
| unsigned long flags; |
| |
| init_waitqueue_head(&queue); |
| |
| CALL_PDEBUG("me4000_ao_immediate_stop() is executed\n"); |
| |
| spin_lock_irqsave(&ao_context->int_lock, flags); |
| tmp = inl(ao_context->ctrl_reg); |
| tmp |= ME4000_AO_CTRL_BIT_STOP | ME4000_AO_CTRL_BIT_IMMEDIATE_STOP; |
| me4000_outl(tmp, ao_context->ctrl_reg); |
| spin_unlock_irqrestore(&ao_context->int_lock, flags); |
| |
| while (inl(ao_context->status_reg) & ME4000_AO_STATUS_BIT_FSM) { |
| interruptible_sleep_on_timeout(&queue, 1); |
| if (signal_pending(current)) { |
| printk(KERN_ERR |
| "me4000_ao_immediate_stop():Wait on state machine after stop interrupted\n"); |
| return -EINTR; |
| } |
| } |
| |
| /* Clear the stop bits */ |
| //tmp &= ~(ME4000_AO_CTRL_BIT_STOP | ME4000_AO_CTRL_BIT_IMMEDIATE_STOP); |
| //me4000_outl(tmp, ao_context->ctrl_reg); |
| |
| return 0; |
| } |
| |
| static int me4000_ao_timer_set_divisor(u32 *arg, |
| struct me4000_ao_context *ao_context) |
| { |
| u32 divisor; |
| u32 tmp; |
| |
| CALL_PDEBUG("me4000_ao_timer set_divisor() is executed\n"); |
| |
| if (get_user(divisor, arg)) |
| return -EFAULT; |
| |
| /* Check if the state machine is stopped */ |
| tmp = me4000_inl(ao_context->status_reg); |
| if (tmp & ME4000_AO_STATUS_BIT_FSM) { |
| printk(KERN_ERR |
| "me4000_ao_timer_set_divisor():Can't set timer while DAC is running\n"); |
| return -EBUSY; |
| } |
| |
| PDEBUG("me4000_ao_timer set_divisor():Divisor from user = %d\n", |
| divisor); |
| |
| /* Check if the divisor is right. ME4000_AO_MIN_TICKS is the lowest */ |
| if (divisor < ME4000_AO_MIN_TICKS) { |
| printk(KERN_ERR |
| "ME4000:me4000_ao_timer set_divisor():Divisor to low\n"); |
| return -EINVAL; |
| } |
| |
| /* Fix bug in Firmware */ |
| divisor -= 2; |
| |
| PDEBUG("me4000_ao_timer set_divisor():Divisor to HW = %d\n", divisor); |
| |
| /* Write the divisor */ |
| me4000_outl(divisor, ao_context->timer_reg); |
| |
| return 0; |
| } |
| |
| static int me4000_ao_ex_trig_set_edge(int *arg, |
| struct me4000_ao_context *ao_context) |
| { |
| int mode; |
| u32 tmp; |
| unsigned long flags; |
| |
| CALL_PDEBUG("me4000_ao_ex_trig_set_edge() is executed\n"); |
| |
| if (get_user(mode, arg)) |
| return -EFAULT; |
| |
| /* Check if the state machine is stopped */ |
| tmp = me4000_inl(ao_context->status_reg); |
| if (tmp & ME4000_AO_STATUS_BIT_FSM) { |
| printk(KERN_ERR |
| "me4000_ao_ex_trig_set_edge():Can't set trigger while DAC is running\n"); |
| return -EBUSY; |
| } |
| |
| if (mode == ME4000_AO_TRIGGER_EXT_EDGE_RISING) { |
| spin_lock_irqsave(&ao_context->int_lock, flags); |
| tmp = me4000_inl(ao_context->ctrl_reg); |
| tmp &= |
| ~(ME4000_AO_CTRL_BIT_EX_TRIG_EDGE | |
| ME4000_AO_CTRL_BIT_EX_TRIG_BOTH); |
| me4000_outl(tmp, ao_context->ctrl_reg); |
| spin_unlock_irqrestore(&ao_context->int_lock, flags); |
| } else if (mode == ME4000_AO_TRIGGER_EXT_EDGE_FALLING) { |
| spin_lock_irqsave(&ao_context->int_lock, flags); |
| tmp = me4000_inl(ao_context->ctrl_reg); |
| tmp &= ~ME4000_AO_CTRL_BIT_EX_TRIG_BOTH; |
| tmp |= ME4000_AO_CTRL_BIT_EX_TRIG_EDGE; |
| me4000_outl(tmp, ao_context->ctrl_reg); |
| spin_unlock_irqrestore(&ao_context->int_lock, flags); |
| } else if (mode == ME4000_AO_TRIGGER_EXT_EDGE_BOTH) { |
| spin_lock_irqsave(&ao_context->int_lock, flags); |
| tmp = me4000_inl(ao_context->ctrl_reg); |
| tmp |= |
| ME4000_AO_CTRL_BIT_EX_TRIG_EDGE | |
| ME4000_AO_CTRL_BIT_EX_TRIG_BOTH; |
| me4000_outl(tmp, ao_context->ctrl_reg); |
| spin_unlock_irqrestore(&ao_context->int_lock, flags); |
| } else { |
| printk(KERN_ERR |
| "me4000_ao_ex_trig_set_edge():Invalid trigger mode\n"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int me4000_ao_ex_trig_enable(struct me4000_ao_context *ao_context) |
| { |
| u32 tmp; |
| unsigned long flags; |
| |
| CALL_PDEBUG("me4000_ao_ex_trig_enable() is executed\n"); |
| |
| /* Check if the state machine is stopped */ |
| tmp = me4000_inl(ao_context->status_reg); |
| if (tmp & ME4000_AO_STATUS_BIT_FSM) { |
| printk(KERN_ERR |
| "me4000_ao_ex_trig_enable():Can't enable trigger while DAC is running\n"); |
| return -EBUSY; |
| } |
| |
| spin_lock_irqsave(&ao_context->int_lock, flags); |
| tmp = me4000_inl(ao_context->ctrl_reg); |
| tmp |= ME4000_AO_CTRL_BIT_ENABLE_EX_TRIG; |
| me4000_outl(tmp, ao_context->ctrl_reg); |
| spin_unlock_irqrestore(&ao_context->int_lock, flags); |
| |
| return 0; |
| } |
| |
| static int me4000_ao_ex_trig_disable(struct me4000_ao_context *ao_context) |
| { |
| u32 tmp; |
| unsigned long flags; |
| |
| CALL_PDEBUG("me4000_ao_ex_trig_disable() is executed\n"); |
| |
| /* Check if the state machine is stopped */ |
| tmp = me4000_inl(ao_context->status_reg); |
| if (tmp & ME4000_AO_STATUS_BIT_FSM) { |
| printk(KERN_ERR |
| "me4000_ao_ex_trig_disable():Can't disable trigger while DAC is running\n"); |
| return -EBUSY; |
| } |
| |
| spin_lock_irqsave(&ao_context->int_lock, flags); |
| tmp = me4000_inl(ao_context->ctrl_reg); |
| tmp &= ~ME4000_AO_CTRL_BIT_ENABLE_EX_TRIG; |
| me4000_outl(tmp, ao_context->ctrl_reg); |
| spin_unlock_irqrestore(&ao_context->int_lock, flags); |
| |
| return 0; |
| } |
| |
| static int me4000_ao_simultaneous_disable(struct me4000_ao_context *ao_context) |
| { |
| u32 tmp; |
| |
| CALL_PDEBUG("me4000_ao_simultaneous_disable() is executed\n"); |
| |
| /* Check if the state machine is stopped */ |
| /* Be careful here because this function is called from |
| me4000_ao_synchronous disable */ |
| tmp = me4000_inl(ao_context->status_reg); |
| if (tmp & ME4000_AO_STATUS_BIT_FSM) { |
| printk(KERN_ERR |
| "me4000_ao_simultaneous_disable():Can't disable while DAC is running\n"); |
| return -EBUSY; |
| } |
| |
| spin_lock(&ao_context->board_info->preload_lock); |
| tmp = me4000_inl(ao_context->preload_reg); |
| tmp &= ~(0x1 << ao_context->index); // Disable preload bit |
| tmp &= ~(0x1 << (ao_context->index + 16)); // Disable hw simultaneous bit |
| me4000_outl(tmp, ao_context->preload_reg); |
| spin_unlock(&ao_context->board_info->preload_lock); |
| |
| return 0; |
| } |
| |
| static int me4000_ao_simultaneous_ex_trig(struct me4000_ao_context *ao_context) |
| { |
| u32 tmp; |
| |
| CALL_PDEBUG("me4000_ao_simultaneous_ex_trig() is executed\n"); |
| |
| spin_lock(&ao_context->board_info->preload_lock); |
| tmp = me4000_inl(ao_context->preload_reg); |
| tmp |= (0x1 << ao_context->index); // Enable preload bit |
| tmp |= (0x1 << (ao_context->index + 16)); // Enable hw simultaneous bit |
| me4000_outl(tmp, ao_context->preload_reg); |
| spin_unlock(&ao_context->board_info->preload_lock); |
| |
| return 0; |
| } |
| |
| static int me4000_ao_simultaneous_sw(struct me4000_ao_context *ao_context) |
| { |
| u32 tmp; |
| |
| CALL_PDEBUG("me4000_ao_simultaneous_sw() is executed\n"); |
| |
| spin_lock(&ao_context->board_info->preload_lock); |
| tmp = me4000_inl(ao_context->preload_reg); |
| tmp |= (0x1 << ao_context->index); // Enable preload bit |
| tmp &= ~(0x1 << (ao_context->index + 16)); // Disable hw simultaneous bit |
| me4000_outl(tmp, ao_context->preload_reg); |
| spin_unlock(&ao_context->board_info->preload_lock); |
| |
| return 0; |
| } |
| |
| static int me4000_ao_preload(struct me4000_ao_context *ao_context) |
| { |
| CALL_PDEBUG("me4000_ao_preload() is executed\n"); |
| return me4000_ao_simultaneous_sw(ao_context); |
| } |
| |
| static int me4000_ao_preload_update(struct me4000_ao_context *ao_context) |
| { |
| u32 tmp; |
| u32 ctrl; |
| struct list_head *entry; |
| |
| CALL_PDEBUG("me4000_ao_preload_update() is executed\n"); |
| |
| spin_lock(&ao_context->board_info->preload_lock); |
| tmp = me4000_inl(ao_context->preload_reg); |
| list_for_each(entry, &ao_context->board_info->ao_context_list) { |
| /* The channels we update must be in the following state : |
| - Mode A |
| - Hardware trigger is disabled |
| - Corresponding simultaneous bit is reset |
| */ |
| ctrl = me4000_inl(ao_context->ctrl_reg); |
| if (! |
| (ctrl & |
| (ME4000_AO_CTRL_BIT_MODE_0 | ME4000_AO_CTRL_BIT_MODE_1 | |
| ME4000_AO_CTRL_BIT_ENABLE_EX_TRIG))) { |
| if (! |
| (tmp & |
| (0x1 << |
| (((struct me4000_ao_context *)entry)->index |
| + 16)))) { |
| tmp &= |
| ~(0x1 << |
| (((struct me4000_ao_context *)entry)-> |
| index)); |
| } |
| } |
| } |
| me4000_outl(tmp, ao_context->preload_reg); |
| spin_unlock(&ao_context->board_info->preload_lock); |
| |
| return 0; |
| } |
| |
| static int me4000_ao_simultaneous_update(struct me4000_ao_channel_list *arg, |
| struct me4000_ao_context *ao_context) |
| { |
| int err; |
| int i; |
| u32 tmp; |
| struct me4000_ao_channel_list channels; |
| |
| CALL_PDEBUG("me4000_ao_simultaneous_update() is executed\n"); |
| |
| /* Copy data from user */ |
| err = copy_from_user(&channels, arg, |
| sizeof(struct me4000_ao_channel_list)); |
| if (err) { |
| printk(KERN_ERR |
| "ME4000:me4000_ao_simultaneous_update():Can't copy command\n"); |
| return -EFAULT; |
| } |
| |
| channels.list = |
| kzalloc(sizeof(unsigned long) * channels.count, GFP_KERNEL); |
| if (!channels.list) { |
| printk(KERN_ERR |
| "ME4000:me4000_ao_simultaneous_update():Can't get buffer\n"); |
| return -ENOMEM; |
| } |
| |
| /* Copy channel list from user */ |
| err = |
| copy_from_user(channels.list, arg->list, |
| sizeof(unsigned long) * channels.count); |
| if (err) { |
| printk(KERN_ERR |
| "ME4000:me4000_ao_simultaneous_update():Can't copy list\n"); |
| kfree(channels.list); |
| return -EFAULT; |
| } |
| |
| spin_lock(&ao_context->board_info->preload_lock); |
| tmp = me4000_inl(ao_context->preload_reg); |
| for (i = 0; i < channels.count; i++) { |
| if (channels.list[i] > |
| ao_context->board_info->board_p->ao.count) { |
| spin_unlock(&ao_context->board_info->preload_lock); |
| kfree(channels.list); |
| printk(KERN_ERR |
| "ME4000:me4000_ao_simultaneous_update():Invalid board number specified\n"); |
| return -EFAULT; |
| } |
| tmp &= ~(0x1 << channels.list[i]); // Clear the preload bit |
| tmp &= ~(0x1 << (channels.list[i] + 16)); // Clear the hw simultaneous bit |
| } |
| me4000_outl(tmp, ao_context->preload_reg); |
| spin_unlock(&ao_context->board_info->preload_lock); |
| kfree(channels.list); |
| |
| return 0; |
| } |
| |
| static int me4000_ao_synchronous_ex_trig(struct me4000_ao_context *ao_context) |
| { |
| u32 tmp; |
| unsigned long flags; |
| |
| CALL_PDEBUG("me4000_ao_synchronous_ex_trig() is executed\n"); |
| |
| /* Check if the state machine is stopped */ |
| tmp = me4000_inl(ao_context->status_reg); |
| if (tmp & ME4000_AO_STATUS_BIT_FSM) { |
| printk(KERN_ERR |
| "me4000_ao_synchronous_ex_trig(): DAC is running\n"); |
| return -EBUSY; |
| } |
| |
| spin_lock(&ao_context->board_info->preload_lock); |
| tmp = me4000_inl(ao_context->preload_reg); |
| tmp &= ~(0x1 << ao_context->index); // Disable synchronous sw bit |
| tmp |= 0x1 << (ao_context->index + 16); // Enable synchronous hw bit |
| me4000_outl(tmp, ao_context->preload_reg); |
| spin_unlock(&ao_context->board_info->preload_lock); |
| |
| /* Make runnable */ |
| spin_lock_irqsave(&ao_context->int_lock, flags); |
| tmp = me4000_inl(ao_context->ctrl_reg); |
| if (tmp & (ME4000_AO_CTRL_BIT_MODE_0 | ME4000_AO_CTRL_BIT_MODE_1)) { |
| tmp &= |
| ~(ME4000_AO_CTRL_BIT_STOP | |
| ME4000_AO_CTRL_BIT_IMMEDIATE_STOP); |
| me4000_outl(tmp, ao_context->ctrl_reg); |
| } |
| spin_unlock_irqrestore(&ao_context->int_lock, flags); |
| |
| return 0; |
| } |
| |
| static int me4000_ao_synchronous_sw(struct me4000_ao_context *ao_context) |
| { |
| u32 tmp; |
| unsigned long flags; |
| |
| CALL_PDEBUG("me4000_ao_synchronous_sw() is executed\n"); |
| |
| /* Check if the state machine is stopped */ |
| tmp = me4000_inl(ao_context->status_reg); |
| if (tmp & ME4000_AO_STATUS_BIT_FSM) { |
| printk(KERN_ERR "me4000_ao_synchronous_sw(): DAC is running\n"); |
| return -EBUSY; |
| } |
| |
| spin_lock(&ao_context->board_info->preload_lock); |
| tmp = me4000_inl(ao_context->preload_reg); |
| tmp |= 0x1 << ao_context->index; // Enable synchronous sw bit |
| tmp &= ~(0x1 << (ao_context->index + 16)); // Disable synchronous hw bit |
| me4000_outl(tmp, ao_context->preload_reg); |
| spin_unlock(&ao_context->board_info->preload_lock); |
| |
| /* Make runnable */ |
| spin_lock_irqsave(&ao_context->int_lock, flags); |
| tmp = me4000_inl(ao_context->ctrl_reg); |
| if (tmp & (ME4000_AO_CTRL_BIT_MODE_0 | ME4000_AO_CTRL_BIT_MODE_1)) { |
| tmp &= |
| ~(ME4000_AO_CTRL_BIT_STOP | |
| ME4000_AO_CTRL_BIT_IMMEDIATE_STOP); |
| me4000_outl(tmp, ao_context->ctrl_reg); |
| } |
| spin_unlock_irqrestore(&ao_context->int_lock, flags); |
| |
| return 0; |
| } |
| |
| static int me4000_ao_synchronous_disable(struct me4000_ao_context *ao_context) |
| { |
| return me4000_ao_simultaneous_disable(ao_context); |
| } |
| |
| static int me4000_ao_get_free_buffer(unsigned long *arg, |
| struct me4000_ao_context *ao_context) |
| { |
| unsigned long c; |
| int err; |
| |
| c = me4000_buf_space(ao_context->circ_buf, ME4000_AO_BUFFER_COUNT); |
| |
| err = copy_to_user(arg, &c, sizeof(unsigned long)); |
| if (err) { |
| printk(KERN_ERR |
| "%s:Can't copy to user space\n", __func__); |
| return -EFAULT; |
| } |
| |
| return 0; |
| } |
| |
| static int me4000_ao_ex_trig_timeout(unsigned long *arg, |
| struct me4000_ao_context *ao_context) |
| { |
| u32 tmp; |
| wait_queue_head_t queue; |
| unsigned long ref; |
| unsigned long timeout; |
| |
| CALL_PDEBUG("me4000_ao_ex_trig_timeout() is executed\n"); |
| |
| if (get_user(timeout, arg)) { |
| printk(KERN_ERR |
| "me4000_ao_ex_trig_timeout():Cannot copy data from user\n"); |
| return -EFAULT; |
| } |
| |
| init_waitqueue_head(&queue); |
| |
| tmp = inl(ao_context->ctrl_reg); |
| |
| if ((tmp & ME4000_AO_CTRL_BIT_ENABLE_EX_TRIG)) { |
| if (timeout) { |
| ref = jiffies; |
| while ((inl(ao_context->status_reg) & |
| ME4000_AO_STATUS_BIT_FSM)) { |
| interruptible_sleep_on_timeout(&queue, 1); |
| if (signal_pending(current)) { |
| printk(KERN_ERR |
| "ME4000:me4000_ao_ex_trig_timeout():Wait on start of state machine interrupted\n"); |
| return -EINTR; |
| } |
| if (((jiffies - ref) > (timeout * HZ / USER_HZ))) { // 2.6 has diffrent definitions for HZ in user and kernel space |
| printk(KERN_ERR |
| "ME4000:me4000_ao_ex_trig_timeout():Timeout reached\n"); |
| return -EIO; |
| } |
| } |
| } else { |
| while ((inl(ao_context->status_reg) & |
| ME4000_AO_STATUS_BIT_FSM)) { |
| interruptible_sleep_on_timeout(&queue, 1); |
| if (signal_pending(current)) { |
| printk(KERN_ERR |
| "ME4000:me4000_ao_ex_trig_timeout():Wait on start of state machine interrupted\n"); |
| return -EINTR; |
| } |
| } |
| } |
| } else { |
| printk(KERN_ERR |
| "ME4000:me4000_ao_ex_trig_timeout():External Trigger is not enabled\n"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int me4000_ao_enable_do(struct me4000_ao_context *ao_context) |
| { |
| u32 tmp; |
| unsigned long flags; |
| |
| CALL_PDEBUG("me4000_ao_enable_do() is executed\n"); |
| |
| /* Only available for analog output 3 */ |
| if (ao_context->index != 3) { |
| printk(KERN_ERR |
| "me4000_ao_enable_do():Only available for analog output 3\n"); |
| return -ENOTTY; |
| } |
| |
| /* Check if the state machine is stopped */ |
| tmp = me4000_inl(ao_context->status_reg); |
| if (tmp & ME4000_AO_STATUS_BIT_FSM) { |
| printk(KERN_ERR "me4000_ao_enable_do(): DAC is running\n"); |
| return -EBUSY; |
| } |
| |
| /* Set the stop bit */ |
| spin_lock_irqsave(&ao_context->int_lock, flags); |
| tmp = inl(ao_context->ctrl_reg); |
| tmp |= ME4000_AO_CTRL_BIT_ENABLE_DO; |
| me4000_outl(tmp, ao_context->ctrl_reg); |
| spin_unlock_irqrestore(&ao_context->int_lock, flags); |
| |
| return 0; |
| } |
| |
| static int me4000_ao_disable_do(struct me4000_ao_context *ao_context) |
| { |
| u32 tmp; |
| unsigned long flags; |
| |
| CALL_PDEBUG("me4000_ao_disable_do() is executed\n"); |
| |
| /* Only available for analog output 3 */ |
| if (ao_context->index != 3) { |
| printk(KERN_ERR |
| "me4000_ao_disable():Only available for analog output 3\n"); |
| return -ENOTTY; |
| } |
| |
| /* Check if the state machine is stopped */ |
| tmp = me4000_inl(ao_context->status_reg); |
| if (tmp & ME4000_AO_STATUS_BIT_FSM) { |
| printk(KERN_ERR "me4000_ao_disable_do(): DAC is running\n"); |
| return -EBUSY; |
| } |
| |
| spin_lock_irqsave(&ao_context->int_lock, flags); |
| tmp = inl(ao_context->ctrl_reg); |
| tmp &= ~(ME4000_AO_CTRL_BIT_ENABLE_DO); |
| me4000_outl(tmp, ao_context->ctrl_reg); |
| spin_unlock_irqrestore(&ao_context->int_lock, flags); |
| |
| return 0; |
| } |
| |
| static int me4000_ao_fsm_state(int *arg, struct me4000_ao_context *ao_context) |
| { |
| unsigned long tmp; |
| |
| CALL_PDEBUG("me4000_ao_fsm_state() is executed\n"); |
| |
| tmp = |
| (me4000_inl(ao_context->status_reg) & ME4000_AO_STATUS_BIT_FSM) ? 1 |
| : 0; |
| |
| if (ao_context->pipe_flag) { |
| printk(KERN_ERR "me4000_ao_fsm_state():Broken pipe detected\n"); |
| return -EPIPE; |
| } |
| |
| if (put_user(tmp, arg)) { |
| printk(KERN_ERR "me4000_ao_fsm_state():Cannot copy to user\n"); |
| return -EFAULT; |
| } |
| |
| return 0; |
| } |
| |
| /*------------------------- Analog input stuff -------------------------------*/ |
| |
| static int me4000_ai_prepare(struct me4000_ai_context *ai_context) |
| { |
| wait_queue_head_t queue; |
| int err; |
| |
| CALL_PDEBUG("me4000_ai_prepare() is executed\n"); |
| |
| init_waitqueue_head(&queue); |
| |
| /* Set the new mode and stop bits */ |
| me4000_outl(ai_context-> |
| mode | ME4000_AI_CTRL_BIT_STOP | |
| ME4000_AI_CTRL_BIT_IMMEDIATE_STOP, ai_context->ctrl_reg); |
| |
| /* Set the timer registers */ |
| ai_context->chan_timer = 66; |
| ai_context->chan_pre_timer = 66; |
| ai_context->scan_timer_low = 0; |
| ai_context->scan_timer_high = 0; |
| |
| me4000_outl(65, ai_context->chan_timer_reg); |
| me4000_outl(65, ai_context->chan_pre_timer_reg); |
| me4000_outl(0, ai_context->scan_timer_low_reg); |
| me4000_outl(0, ai_context->scan_timer_high_reg); |
| me4000_outl(0, ai_context->scan_pre_timer_low_reg); |
| me4000_outl(0, ai_context->scan_pre_timer_high_reg); |
| |
| ai_context->channel_list_count = 0; |
| |
| if (ai_context->mode) { |
| /* Request the interrupt line */ |
| err = |
| request_irq(ai_context->irq, me4000_ai_isr, |
| IRQF_DISABLED | IRQF_SHARED, ME4000_NAME, |
| ai_context); |
| if (err) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_prepare():Can't get interrupt line"); |
| return -ENODEV; |
| } |
| |
| /* Allocate circular buffer */ |
| ai_context->circ_buf.buf = |
| kzalloc(ME4000_AI_BUFFER_SIZE, GFP_KERNEL); |
| if (!ai_context->circ_buf.buf) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_prepare():Can't get circular buffer\n"); |
| free_irq(ai_context->irq, ai_context); |
| return -ENOMEM; |
| } |
| |
| /* Clear the circular buffer */ |
| ai_context->circ_buf.head = 0; |
| ai_context->circ_buf.tail = 0; |
| } |
| |
| return 0; |
| } |
| |
| static int me4000_ai_reset(struct me4000_ai_context *ai_context) |
| { |
| wait_queue_head_t queue; |
| u32 tmp; |
| unsigned long flags; |
| |
| CALL_PDEBUG("me4000_ai_reset() is executed\n"); |
| |
| init_waitqueue_head(&queue); |
| |
| /* |
| * First stop conversion of the state machine before reconfigure. |
| * If not stopped before configuring mode, it could |
| * walk in a undefined state. |
| */ |
| spin_lock_irqsave(&ai_context->int_lock, flags); |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP; |
| me4000_outl(tmp, ai_context->ctrl_reg); |
| spin_unlock_irqrestore(&ai_context->int_lock, flags); |
| |
| while (inl(ai_context->status_reg) & ME4000_AI_STATUS_BIT_FSM) { |
| interruptible_sleep_on_timeout(&queue, 1); |
| if (signal_pending(current)) { |
| printk(KERN_ERR |
| "me4000_ai_reset():Wait on state machine after stop interrupted\n"); |
| return -EINTR; |
| } |
| } |
| |
| /* Clear the control register and set the stop bits */ |
| spin_lock_irqsave(&ai_context->int_lock, flags); |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| me4000_outl(ME4000_AI_CTRL_BIT_IMMEDIATE_STOP | ME4000_AI_CTRL_BIT_STOP, |
| ai_context->ctrl_reg); |
| spin_unlock_irqrestore(&ai_context->int_lock, flags); |
| |
| /* Reset timer registers */ |
| ai_context->chan_timer = 66; |
| ai_context->chan_pre_timer = 66; |
| ai_context->scan_timer_low = 0; |
| ai_context->scan_timer_high = 0; |
| ai_context->sample_counter = 0; |
| ai_context->sample_counter_reload = 0; |
| |
| me4000_outl(65, ai_context->chan_timer_reg); |
| me4000_outl(65, ai_context->chan_pre_timer_reg); |
| me4000_outl(0, ai_context->scan_timer_low_reg); |
| me4000_outl(0, ai_context->scan_timer_high_reg); |
| me4000_outl(0, ai_context->scan_pre_timer_low_reg); |
| me4000_outl(0, ai_context->scan_pre_timer_high_reg); |
| me4000_outl(0, ai_context->sample_counter_reg); |
| |
| ai_context->channel_list_count = 0; |
| |
| /* Clear the circular buffer */ |
| ai_context->circ_buf.head = 0; |
| ai_context->circ_buf.tail = 0; |
| |
| return 0; |
| } |
| |
| static int me4000_ai_ioctl_sing(struct inode *inode_p, struct file *file_p, |
| unsigned int service, unsigned long arg) |
| { |
| struct me4000_ai_context *ai_context; |
| |
| CALL_PDEBUG("me4000_ai_ioctl_sing() is executed\n"); |
| |
| ai_context = file_p->private_data; |
| |
| if (_IOC_TYPE(service) != ME4000_MAGIC) { |
| printk(KERN_ERR "me4000_ai_ioctl_sing():Wrong magic number\n"); |
| return -ENOTTY; |
| } |
| if (_IOC_NR(service) > ME4000_IOCTL_MAXNR) { |
| printk(KERN_ERR |
| "me4000_ai_ioctl_sing():Service number to high\n"); |
| return -ENOTTY; |
| } |
| |
| switch (service) { |
| case ME4000_AI_SINGLE: |
| return me4000_ai_single((struct me4000_ai_single *)arg, |
| ai_context); |
| case ME4000_AI_EX_TRIG_ENABLE: |
| return me4000_ai_ex_trig_enable(ai_context); |
| case ME4000_AI_EX_TRIG_DISABLE: |
| return me4000_ai_ex_trig_disable(ai_context); |
| case ME4000_AI_EX_TRIG_SETUP: |
| return me4000_ai_ex_trig_setup((struct me4000_ai_trigger *)arg, |
| ai_context); |
| case ME4000_GET_USER_INFO: |
| return me4000_get_user_info((struct me4000_user_info *)arg, |
| ai_context->board_info); |
| case ME4000_AI_OFFSET_ENABLE: |
| return me4000_ai_offset_enable(ai_context); |
| case ME4000_AI_OFFSET_DISABLE: |
| return me4000_ai_offset_disable(ai_context); |
| case ME4000_AI_FULLSCALE_ENABLE: |
| return me4000_ai_fullscale_enable(ai_context); |
| case ME4000_AI_FULLSCALE_DISABLE: |
| return me4000_ai_fullscale_disable(ai_context); |
| case ME4000_AI_EEPROM_READ: |
| return me4000_eeprom_read((struct me4000_eeprom *)arg, |
| ai_context); |
| case ME4000_AI_EEPROM_WRITE: |
| return me4000_eeprom_write((struct me4000_eeprom *)arg, |
| ai_context); |
| default: |
| printk(KERN_ERR |
| "me4000_ai_ioctl_sing():Invalid service number\n"); |
| return -ENOTTY; |
| } |
| return 0; |
| } |
| |
| static int me4000_ai_single(struct me4000_ai_single *arg, |
| struct me4000_ai_context *ai_context) |
| { |
| struct me4000_ai_single cmd; |
| int err; |
| u32 tmp; |
| wait_queue_head_t queue; |
| unsigned long jiffy; |
| |
| CALL_PDEBUG("me4000_ai_single() is executed\n"); |
| |
| init_waitqueue_head(&queue); |
| |
| /* Copy data from user */ |
| err = copy_from_user(&cmd, arg, sizeof(struct me4000_ai_single)); |
| if (err) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_single():Can't copy from user space\n"); |
| return -EFAULT; |
| } |
| |
| /* Check range parameter */ |
| switch (cmd.range) { |
| case ME4000_AI_LIST_RANGE_BIPOLAR_10: |
| case ME4000_AI_LIST_RANGE_BIPOLAR_2_5: |
| case ME4000_AI_LIST_RANGE_UNIPOLAR_10: |
| case ME4000_AI_LIST_RANGE_UNIPOLAR_2_5: |
| break; |
| default: |
| printk(KERN_ERR |
| "ME4000:me4000_ai_single():Invalid range specified\n"); |
| return -EINVAL; |
| } |
| |
| /* Check mode and channel number */ |
| switch (cmd.mode) { |
| case ME4000_AI_LIST_INPUT_SINGLE_ENDED: |
| if (cmd.channel >= ai_context->board_info->board_p->ai.count) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_single():Analog input is not available\n"); |
| return -EINVAL; |
| } |
| break; |
| case ME4000_AI_LIST_INPUT_DIFFERENTIAL: |
| if (cmd.channel >= |
| ai_context->board_info->board_p->ai.diff_count) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_single():Analog input is not available in differential mode\n"); |
| return -EINVAL; |
| } |
| break; |
| default: |
| printk(KERN_ERR |
| "ME4000:me4000_ai_single():Invalid mode specified\n"); |
| return -EINVAL; |
| } |
| |
| /* Clear channel list, data fifo and both stop bits */ |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| tmp &= |
| ~(ME4000_AI_CTRL_BIT_CHANNEL_FIFO | ME4000_AI_CTRL_BIT_DATA_FIFO | |
| ME4000_AI_CTRL_BIT_STOP | ME4000_AI_CTRL_BIT_IMMEDIATE_STOP); |
| me4000_outl(tmp, ai_context->ctrl_reg); |
| |
| /* Enable channel list and data fifo */ |
| tmp |= ME4000_AI_CTRL_BIT_CHANNEL_FIFO | ME4000_AI_CTRL_BIT_DATA_FIFO; |
| me4000_outl(tmp, ai_context->ctrl_reg); |
| |
| /* Generate channel list entry */ |
| me4000_outl(cmd.channel | cmd.range | cmd. |
| mode | ME4000_AI_LIST_LAST_ENTRY, |
| ai_context->channel_list_reg); |
| |
| /* Set the timer to maximum */ |
| me4000_outl(66, ai_context->chan_timer_reg); |
| me4000_outl(66, ai_context->chan_pre_timer_reg); |
| |
| if (tmp & ME4000_AI_CTRL_BIT_EX_TRIG) { |
| jiffy = jiffies; |
| while (! |
| (me4000_inl(ai_context->status_reg) & |
| ME4000_AI_STATUS_BIT_EF_DATA)) { |
| interruptible_sleep_on_timeout(&queue, 1); |
| if (signal_pending(current)) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_single():Wait on start of state machine interrupted\n"); |
| return -EINTR; |
| } |
| if (((jiffies - jiffy) > (cmd.timeout * HZ / USER_HZ)) && cmd.timeout) { // 2.6 has diffrent definitions for HZ in user and kernel space |
| printk(KERN_ERR |
| "ME4000:me4000_ai_single():Timeout reached\n"); |
| return -EIO; |
| } |
| } |
| } else { |
| /* Start conversion */ |
| me4000_inl(ai_context->start_reg); |
| |
| /* Wait until ready */ |
| udelay(10); |
| if (! |
| (me4000_inl(ai_context->status_reg) & |
| ME4000_AI_STATUS_BIT_EF_DATA)) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_single():Value not available after wait\n"); |
| return -EIO; |
| } |
| } |
| |
| /* Read value from data fifo */ |
| cmd.value = me4000_inl(ai_context->data_reg) & 0xFFFF; |
| |
| /* Copy result back to user */ |
| err = copy_to_user(arg, &cmd, sizeof(struct me4000_ai_single)); |
| if (err) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_single():Can't copy to user space\n"); |
| return -EFAULT; |
| } |
| |
| return 0; |
| } |
| |
| static int me4000_ai_ioctl_sw(struct inode *inode_p, struct file *file_p, |
| unsigned int service, unsigned long arg) |
| { |
| struct me4000_ai_context *ai_context; |
| |
| CALL_PDEBUG("me4000_ai_ioctl_sw() is executed\n"); |
| |
| ai_context = file_p->private_data; |
| |
| if (_IOC_TYPE(service) != ME4000_MAGIC) { |
| printk(KERN_ERR "me4000_ai_ioctl_sw():Wrong magic number\n"); |
| return -ENOTTY; |
| } |
| if (_IOC_NR(service) > ME4000_IOCTL_MAXNR) { |
| printk(KERN_ERR |
| "me4000_ai_ioctl_sw():Service number to high\n"); |
| return -ENOTTY; |
| } |
| |
| switch (service) { |
| case ME4000_AI_SC_SETUP: |
| return me4000_ai_sc_setup((struct me4000_ai_sc *)arg, |
| ai_context); |
| case ME4000_AI_CONFIG: |
| return me4000_ai_config((struct me4000_ai_config *)arg, |
| ai_context); |
| case ME4000_AI_START: |
| return me4000_ai_start(ai_context); |
| case ME4000_AI_STOP: |
| return me4000_ai_stop(ai_context); |
| case ME4000_AI_IMMEDIATE_STOP: |
| return me4000_ai_immediate_stop(ai_context); |
| case ME4000_AI_FSM_STATE: |
| return me4000_ai_fsm_state((int *)arg, ai_context); |
| case ME4000_GET_USER_INFO: |
| return me4000_get_user_info((struct me4000_user_info *)arg, |
| ai_context->board_info); |
| case ME4000_AI_EEPROM_READ: |
| return me4000_eeprom_read((struct me4000_eeprom *)arg, |
| ai_context); |
| case ME4000_AI_EEPROM_WRITE: |
| return me4000_eeprom_write((struct me4000_eeprom *)arg, |
| ai_context); |
| case ME4000_AI_GET_COUNT_BUFFER: |
| return me4000_ai_get_count_buffer((unsigned long *)arg, |
| ai_context); |
| default: |
| printk(KERN_ERR |
| "%s:Invalid service number %d\n", __func__, service); |
| return -ENOTTY; |
| } |
| return 0; |
| } |
| |
| static int me4000_ai_ioctl_ext(struct inode *inode_p, struct file *file_p, |
| unsigned int service, unsigned long arg) |
| { |
| struct me4000_ai_context *ai_context; |
| |
| CALL_PDEBUG("me4000_ai_ioctl_ext() is executed\n"); |
| |
| ai_context = file_p->private_data; |
| |
| if (_IOC_TYPE(service) != ME4000_MAGIC) { |
| printk(KERN_ERR "me4000_ai_ioctl_ext():Wrong magic number\n"); |
| return -ENOTTY; |
| } |
| if (_IOC_NR(service) > ME4000_IOCTL_MAXNR) { |
| printk(KERN_ERR |
| "me4000_ai_ioctl_ext():Service number to high\n"); |
| return -ENOTTY; |
| } |
| |
| switch (service) { |
| case ME4000_AI_SC_SETUP: |
| return me4000_ai_sc_setup((struct me4000_ai_sc *)arg, |
| ai_context); |
| case ME4000_AI_CONFIG: |
| return me4000_ai_config((struct me4000_ai_config *)arg, |
| ai_context); |
| case ME4000_AI_START: |
| return me4000_ai_start_ex((unsigned long *)arg, ai_context); |
| case ME4000_AI_STOP: |
| return me4000_ai_stop(ai_context); |
| case ME4000_AI_IMMEDIATE_STOP: |
| return me4000_ai_immediate_stop(ai_context); |
| case ME4000_AI_EX_TRIG_ENABLE: |
| return me4000_ai_ex_trig_enable(ai_context); |
| case ME4000_AI_EX_TRIG_DISABLE: |
| return me4000_ai_ex_trig_disable(ai_context); |
| case ME4000_AI_EX_TRIG_SETUP: |
| return me4000_ai_ex_trig_setup((struct me4000_ai_trigger *)arg, |
| ai_context); |
| case ME4000_AI_FSM_STATE: |
| return me4000_ai_fsm_state((int *)arg, ai_context); |
| case ME4000_GET_USER_INFO: |
| return me4000_get_user_info((struct me4000_user_info *)arg, |
| ai_context->board_info); |
| case ME4000_AI_GET_COUNT_BUFFER: |
| return me4000_ai_get_count_buffer((unsigned long *)arg, |
| ai_context); |
| default: |
| printk(KERN_ERR |
| "%s:Invalid service number %d\n", __func__ , service); |
| return -ENOTTY; |
| } |
| return 0; |
| } |
| |
| static int me4000_ai_fasync(int fd, struct file *file_p, int mode) |
| { |
| struct me4000_ai_context *ai_context; |
| |
| CALL_PDEBUG("me4000_ao_fasync_cont() is executed\n"); |
| |
| ai_context = file_p->private_data; |
| return fasync_helper(fd, file_p, mode, &ai_context->fasync_p); |
| } |
| |
| static int me4000_ai_config(struct me4000_ai_config *arg, |
| struct me4000_ai_context *ai_context) |
| { |
| struct me4000_ai_config cmd; |
| u32 *list = NULL; |
| u32 mode; |
| int i; |
| int err; |
| wait_queue_head_t queue; |
| u64 scan; |
| u32 tmp; |
| |
| CALL_PDEBUG("me4000_ai_config() is executed\n"); |
| |
| init_waitqueue_head(&queue); |
| |
| /* Check if conversion is stopped */ |
| if (inl(ai_context->ctrl_reg) & ME4000_AI_STATUS_BIT_FSM) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_config():Conversion is not stopped\n"); |
| err = -EBUSY; |
| goto AI_CONFIG_ERR; |
| } |
| |
| /* Copy data from user */ |
| err = copy_from_user(&cmd, arg, sizeof(struct me4000_ai_config)); |
| if (err) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_config():Can't copy from user space\n"); |
| err = -EFAULT; |
| goto AI_CONFIG_ERR; |
| } |
| |
| PDEBUG |
| ("me4000_ai_config():chan = %ld, pre_chan = %ld, scan_low = %ld, scan_high = %ld, count = %ld\n", |
| cmd.timer.chan, cmd.timer.pre_chan, cmd.timer.scan_low, |
| cmd.timer.scan_high, cmd.channel_list.count); |
| |
| /* Check whether sample and hold is available for this board */ |
| if (cmd.sh) { |
| if (!ai_context->board_info->board_p->ai.sh_count) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_config():Sample and Hold is not available for this board\n"); |
| err = -ENODEV; |
| goto AI_CONFIG_ERR; |
| } |
| } |
| |
| /* Check the channel list size */ |
| if (cmd.channel_list.count > ME4000_AI_CHANNEL_LIST_COUNT) { |
| printk(KERN_ERR |
| "me4000_ai_config():Channel list is to large\n"); |
| err = -EINVAL; |
| goto AI_CONFIG_ERR; |
| } |
| |
| /* Copy channel list from user */ |
| list = kmalloc(sizeof(u32) * cmd.channel_list.count, GFP_KERNEL); |
| if (!list) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_config():Can't get memory for channel list\n"); |
| err = -ENOMEM; |
| goto AI_CONFIG_ERR; |
| } |
| err = |
| copy_from_user(list, cmd.channel_list.list, |
| sizeof(u32) * cmd.channel_list.count); |
| if (err) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_config():Can't copy from user space\n"); |
| err = -EFAULT; |
| goto AI_CONFIG_ERR; |
| } |
| |
| /* Check if last entry bit is set */ |
| if (!(list[cmd.channel_list.count - 1] & ME4000_AI_LIST_LAST_ENTRY)) { |
| printk(KERN_WARNING |
| "me4000_ai_config():Last entry bit is not set\n"); |
| list[cmd.channel_list.count - 1] |= ME4000_AI_LIST_LAST_ENTRY; |
| } |
| |
| /* Check whether mode is equal for all entries */ |
| mode = list[0] & 0x20; |
| for (i = 0; i < cmd.channel_list.count; i++) { |
| if ((list[i] & 0x20) != mode) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_config():Mode is not equal for all entries\n"); |
| err = -EINVAL; |
| goto AI_CONFIG_ERR; |
| } |
| } |
| |
| /* Check whether channels are available for this mode */ |
| if (mode == ME4000_AI_LIST_INPUT_SINGLE_ENDED) { |
| for (i = 0; i < cmd.channel_list.count; i++) { |
| if ((list[i] & 0x1F) >= |
| ai_context->board_info->board_p->ai.count) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_config():Channel is not available for single ended\n"); |
| err = -EINVAL; |
| goto AI_CONFIG_ERR; |
| } |
| } |
| } else if (mode == ME4000_AI_LIST_INPUT_DIFFERENTIAL) { |
| for (i = 0; i < cmd.channel_list.count; i++) { |
| if ((list[i] & 0x1F) >= |
| ai_context->board_info->board_p->ai.diff_count) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_config():Channel is not available for differential\n"); |
| err = -EINVAL; |
| goto AI_CONFIG_ERR; |
| } |
| } |
| } |
| |
| /* Check if bipolar is set for all entries when in differential mode */ |
| if (mode == ME4000_AI_LIST_INPUT_DIFFERENTIAL) { |
| for (i = 0; i < cmd.channel_list.count; i++) { |
| if ((list[i] & 0xC0) != ME4000_AI_LIST_RANGE_BIPOLAR_10 |
| && (list[i] & 0xC0) != |
| ME4000_AI_LIST_RANGE_BIPOLAR_2_5) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_config():Bipolar is not selected in differential mode\n"); |
| err = -EINVAL; |
| goto AI_CONFIG_ERR; |
| } |
| } |
| } |
| |
| if (ai_context->mode != ME4000_AI_ACQ_MODE_EXT_SINGLE_VALUE) { |
| /* Check for minimum channel divisor */ |
| if (cmd.timer.chan < ME4000_AI_MIN_TICKS) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_config():Channel timer divisor is to low\n"); |
| err = -EINVAL; |
| goto AI_CONFIG_ERR; |
| } |
| |
| /* Check if minimum channel divisor is adjusted when sample and hold is activated */ |
| if ((cmd.sh) && (cmd.timer.chan != ME4000_AI_MIN_TICKS)) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_config():Channel timer divisor must be at minimum when sample and hold is activated\n"); |
| err = -EINVAL; |
| goto AI_CONFIG_ERR; |
| } |
| |
| /* Check for minimum channel pre divisor */ |
| if (cmd.timer.pre_chan < ME4000_AI_MIN_TICKS) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_config():Channel pre timer divisor is to low\n"); |
| err = -EINVAL; |
| goto AI_CONFIG_ERR; |
| } |
| |
| /* Write the channel timers */ |
| me4000_outl(cmd.timer.chan - 1, ai_context->chan_timer_reg); |
| me4000_outl(cmd.timer.pre_chan - 1, |
| ai_context->chan_pre_timer_reg); |
| |
| /* Save the timer values in the board context */ |
| ai_context->chan_timer = cmd.timer.chan; |
| ai_context->chan_pre_timer = cmd.timer.pre_chan; |
| |
| if (ai_context->mode != ME4000_AI_ACQ_MODE_EXT_SINGLE_CHANLIST) { |
| /* Check for scan timer divisor */ |
| scan = |
| (u64) cmd.timer.scan_low | ((u64) cmd.timer. |
| scan_high << 32); |
| if (scan != 0) { |
| if (scan < |
| cmd.channel_list.count * cmd.timer.chan + |
| 1) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_config():Scan timer divisor is to low\n"); |
| err = -EINVAL; |
| goto AI_CONFIG_ERR; |
| } |
| } |
| |
| /* Write the scan timers */ |
| if (scan != 0) { |
| scan--; |
| tmp = (u32) (scan & 0xFFFFFFFF); |
| me4000_outl(tmp, |
| ai_context->scan_timer_low_reg); |
| tmp = (u32) ((scan >> 32) & 0xFFFFFFFF); |
| me4000_outl(tmp, |
| ai_context->scan_timer_high_reg); |
| |
| scan = |
| scan - (cmd.timer.chan - 1) + |
| (cmd.timer.pre_chan - 1); |
| tmp = (u32) (scan & 0xFFFFFFFF); |
| me4000_outl(tmp, |
| ai_context->scan_pre_timer_low_reg); |
| tmp = (u32) ((scan >> 32) & 0xFFFFFFFF); |
| me4000_outl(tmp, |
| ai_context-> |
| scan_pre_timer_high_reg); |
| } else { |
| me4000_outl(0x0, |
| ai_context->scan_timer_low_reg); |
| me4000_outl(0x0, |
| ai_context->scan_timer_high_reg); |
| |
| me4000_outl(0x0, |
| ai_context->scan_pre_timer_low_reg); |
| me4000_outl(0x0, |
| ai_context-> |
| scan_pre_timer_high_reg); |
| } |
| |
| ai_context->scan_timer_low = cmd.timer.scan_low; |
| ai_context->scan_timer_high = cmd.timer.scan_high; |
| } |
| } |
| |
| /* Clear the channel list */ |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| tmp &= ~ME4000_AI_CTRL_BIT_CHANNEL_FIFO; |
| me4000_outl(tmp, ai_context->ctrl_reg); |
| tmp |= ME4000_AI_CTRL_BIT_CHANNEL_FIFO; |
| me4000_outl(tmp, ai_context->ctrl_reg); |
| |
| /* Write the channel list */ |
| for (i = 0; i < cmd.channel_list.count; i++) { |
| me4000_outl(list[i], ai_context->channel_list_reg); |
| } |
| |
| /* Setup sample and hold */ |
| if (cmd.sh) { |
| tmp |= ME4000_AI_CTRL_BIT_SAMPLE_HOLD; |
| me4000_outl(tmp, ai_context->ctrl_reg); |
| } else { |
| tmp &= ~ME4000_AI_CTRL_BIT_SAMPLE_HOLD; |
| me4000_outl(tmp, ai_context->ctrl_reg); |
| } |
| |
| /* Save the channel list size in the board context */ |
| ai_context->channel_list_count = cmd.channel_list.count; |
| |
| kfree(list); |
| |
| return 0; |
| |
| AI_CONFIG_ERR: |
| |
| /* Reset the timers */ |
| ai_context->chan_timer = 66; |
| ai_context->chan_pre_timer = 66; |
| ai_context->scan_timer_low = 0; |
| ai_context->scan_timer_high = 0; |
| |
| me4000_outl(65, ai_context->chan_timer_reg); |
| me4000_outl(65, ai_context->chan_pre_timer_reg); |
| me4000_outl(0, ai_context->scan_timer_high_reg); |
| me4000_outl(0, ai_context->scan_timer_low_reg); |
| me4000_outl(0, ai_context->scan_pre_timer_high_reg); |
| me4000_outl(0, ai_context->scan_pre_timer_low_reg); |
| |
| ai_context->channel_list_count = 0; |
| |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| tmp &= |
| ~(ME4000_AI_CTRL_BIT_CHANNEL_FIFO | ME4000_AI_CTRL_BIT_SAMPLE_HOLD); |
| |
| if (list) |
| kfree(list); |
| |
| return err; |
| |
| } |
| |
| static int ai_common_start(struct me4000_ai_context *ai_context) |
| { |
| u32 tmp; |
| CALL_PDEBUG("ai_common_start() is executed\n"); |
| |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| |
| /* Check if conversion is stopped */ |
| if (tmp & ME4000_AI_STATUS_BIT_FSM) { |
| printk(KERN_ERR |
| "ME4000:ai_common_start():Conversion is not stopped\n"); |
| return -EBUSY; |
| } |
| |
| /* Clear data fifo, disable all interrupts, clear sample counter reload */ |
| tmp &= ~(ME4000_AI_CTRL_BIT_DATA_FIFO | ME4000_AI_CTRL_BIT_LE_IRQ | |
| ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ | |
| ME4000_AI_CTRL_BIT_SC_RELOAD); |
| |
| me4000_outl(tmp, ai_context->ctrl_reg); |
| |
| /* Clear circular buffer */ |
| ai_context->circ_buf.head = 0; |
| ai_context->circ_buf.tail = 0; |
| |
| /* Enable data fifo */ |
| tmp |= ME4000_AI_CTRL_BIT_DATA_FIFO; |
| |
| /* Determine interrupt setup */ |
| if (ai_context->sample_counter && !ai_context->sample_counter_reload) { |
| /* Enable Half Full Interrupt and Sample Counter Interrupt */ |
| tmp |= ME4000_AI_CTRL_BIT_SC_IRQ | ME4000_AI_CTRL_BIT_HF_IRQ; |
| } else if (ai_context->sample_counter |
| && ai_context->sample_counter_reload) { |
| if (ai_context->sample_counter <= ME4000_AI_FIFO_COUNT / 2) { |
| /* Enable only Sample Counter Interrupt */ |
| tmp |= |
| ME4000_AI_CTRL_BIT_SC_IRQ | |
| ME4000_AI_CTRL_BIT_SC_RELOAD; |
| } else { |
| /* Enable Half Full Interrupt and Sample Counter Interrupt */ |
| tmp |= |
| ME4000_AI_CTRL_BIT_SC_IRQ | |
| ME4000_AI_CTRL_BIT_HF_IRQ | |
| ME4000_AI_CTRL_BIT_SC_RELOAD; |
| } |
| } else { |
| /* Enable only Half Full Interrupt */ |
| tmp |= ME4000_AI_CTRL_BIT_HF_IRQ; |
| } |
| |
| /* Clear the stop bits */ |
| tmp &= ~(ME4000_AI_CTRL_BIT_STOP | ME4000_AI_CTRL_BIT_IMMEDIATE_STOP); |
| |
| /* Write setup to hardware */ |
| me4000_outl(tmp, ai_context->ctrl_reg); |
| |
| /* Write sample counter */ |
| me4000_outl(ai_context->sample_counter, ai_context->sample_counter_reg); |
| |
| return 0; |
| } |
| |
| static int me4000_ai_start(struct me4000_ai_context *ai_context) |
| { |
| int err; |
| CALL_PDEBUG("me4000_ai_start() is executed\n"); |
| |
| /* Prepare Hardware */ |
| err = ai_common_start(ai_context); |
| if (err) |
| return err; |
| |
| /* Start conversion by dummy read */ |
| me4000_inl(ai_context->start_reg); |
| |
| return 0; |
| } |
| |
| static int me4000_ai_start_ex(unsigned long *arg, |
| struct me4000_ai_context *ai_context) |
| { |
| int err; |
| wait_queue_head_t queue; |
| unsigned long ref; |
| unsigned long timeout; |
| |
| CALL_PDEBUG("me4000_ai_start_ex() is executed\n"); |
| |
| if (get_user(timeout, arg)) { |
| printk(KERN_ERR |
| "me4000_ai_start_ex():Cannot copy data from user\n"); |
| return -EFAULT; |
| } |
| |
| init_waitqueue_head(&queue); |
| |
| /* Prepare Hardware */ |
| err = ai_common_start(ai_context); |
| if (err) |
| return err; |
| |
| if (timeout) { |
| ref = jiffies; |
| while (! |
| (inl(ai_context->status_reg) & ME4000_AI_STATUS_BIT_FSM)) |
| { |
| interruptible_sleep_on_timeout(&queue, 1); |
| if (signal_pending(current)) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_start_ex():Wait on start of state machine interrupted\n"); |
| return -EINTR; |
| } |
| if (((jiffies - ref) > (timeout * HZ / USER_HZ))) { // 2.6 has diffrent definitions for HZ in user and kernel space |
| printk(KERN_ERR |
| "ME4000:me4000_ai_start_ex():Timeout reached\n"); |
| return -EIO; |
| } |
| } |
| } else { |
| while (! |
| (inl(ai_context->status_reg) & ME4000_AI_STATUS_BIT_FSM)) |
| { |
| interruptible_sleep_on_timeout(&queue, 1); |
| if (signal_pending(current)) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_start_ex():Wait on start of state machine interrupted\n"); |
| return -EINTR; |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int me4000_ai_stop(struct me4000_ai_context *ai_context) |
| { |
| wait_queue_head_t queue; |
| u32 tmp; |
| unsigned long flags; |
| |
| CALL_PDEBUG("me4000_ai_stop() is executed\n"); |
| |
| init_waitqueue_head(&queue); |
| |
| /* Disable irqs and clear data fifo */ |
| spin_lock_irqsave(&ai_context->int_lock, flags); |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| tmp &= |
| ~(ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ | |
| ME4000_AI_CTRL_BIT_DATA_FIFO); |
| /* Stop conversion of the state machine */ |
| tmp |= ME4000_AI_CTRL_BIT_STOP; |
| me4000_outl(tmp, ai_context->ctrl_reg); |
| spin_unlock_irqrestore(&ai_context->int_lock, flags); |
| |
| /* Clear circular buffer */ |
| ai_context->circ_buf.head = 0; |
| ai_context->circ_buf.tail = 0; |
| |
| while (inl(ai_context->status_reg) & ME4000_AI_STATUS_BIT_FSM) { |
| interruptible_sleep_on_timeout(&queue, 1); |
| if (signal_pending(current)) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_stop():Wait on state machine after stop interrupted\n"); |
| return -EINTR; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int me4000_ai_immediate_stop(struct me4000_ai_context *ai_context) |
| { |
| wait_queue_head_t queue; |
| u32 tmp; |
| unsigned long flags; |
| |
| CALL_PDEBUG("me4000_ai_stop() is executed\n"); |
| |
| init_waitqueue_head(&queue); |
| |
| /* Disable irqs and clear data fifo */ |
| spin_lock_irqsave(&ai_context->int_lock, flags); |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| tmp &= |
| ~(ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ | |
| ME4000_AI_CTRL_BIT_DATA_FIFO); |
| /* Stop conversion of the state machine */ |
| tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP; |
| me4000_outl(tmp, ai_context->ctrl_reg); |
| spin_unlock_irqrestore(&ai_context->int_lock, flags); |
| |
| /* Clear circular buffer */ |
| ai_context->circ_buf.head = 0; |
| ai_context->circ_buf.tail = 0; |
| |
| while (inl(ai_context->status_reg) & ME4000_AI_STATUS_BIT_FSM) { |
| interruptible_sleep_on_timeout(&queue, 1); |
| if (signal_pending(current)) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_stop():Wait on state machine after stop interrupted\n"); |
| return -EINTR; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int me4000_ai_ex_trig_enable(struct me4000_ai_context *ai_context) |
| { |
| u32 tmp; |
| unsigned long flags; |
| |
| CALL_PDEBUG("me4000_ai_ex_trig_enable() is executed\n"); |
| |
| spin_lock_irqsave(&ai_context->int_lock, flags); |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| tmp |= ME4000_AI_CTRL_BIT_EX_TRIG; |
| me4000_outl(tmp, ai_context->ctrl_reg); |
| spin_unlock_irqrestore(&ai_context->int_lock, flags); |
| |
| return 0; |
| } |
| |
| static int me4000_ai_ex_trig_disable(struct me4000_ai_context *ai_context) |
| { |
| u32 tmp; |
| unsigned long flags; |
| |
| CALL_PDEBUG("me4000_ai_ex_trig_disable() is executed\n"); |
| |
| spin_lock_irqsave(&ai_context->int_lock, flags); |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| tmp &= ~ME4000_AI_CTRL_BIT_EX_TRIG; |
| me4000_outl(tmp, ai_context->ctrl_reg); |
| spin_unlock_irqrestore(&ai_context->int_lock, flags); |
| |
| return 0; |
| } |
| |
| static int me4000_ai_ex_trig_setup(struct me4000_ai_trigger *arg, |
| struct me4000_ai_context *ai_context) |
| { |
| struct me4000_ai_trigger cmd; |
| int err; |
| u32 tmp; |
| unsigned long flags; |
| |
| CALL_PDEBUG("me4000_ai_ex_trig_setup() is executed\n"); |
| |
| /* Copy data from user */ |
| err = copy_from_user(&cmd, arg, sizeof(struct me4000_ai_trigger)); |
| if (err) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_ex_trig_setup():Can't copy from user space\n"); |
| return -EFAULT; |
| } |
| |
| spin_lock_irqsave(&ai_context->int_lock, flags); |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| |
| if (cmd.mode == ME4000_AI_TRIGGER_EXT_DIGITAL) { |
| tmp &= ~ME4000_AI_CTRL_BIT_EX_TRIG_ANALOG; |
| } else if (cmd.mode == ME4000_AI_TRIGGER_EXT_ANALOG) { |
| if (!ai_context->board_info->board_p->ai.ex_trig_analog) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_ex_trig_setup():No analog trigger available\n"); |
| return -EINVAL; |
| } |
| tmp |= ME4000_AI_CTRL_BIT_EX_TRIG_ANALOG; |
| } else { |
| spin_unlock_irqrestore(&ai_context->int_lock, flags); |
| printk(KERN_ERR |
| "ME4000:me4000_ai_ex_trig_setup():Invalid trigger mode specified\n"); |
| return -EINVAL; |
| } |
| |
| if (cmd.edge == ME4000_AI_TRIGGER_EXT_EDGE_RISING) { |
| tmp &= |
| ~(ME4000_AI_CTRL_BIT_EX_TRIG_BOTH | |
| ME4000_AI_CTRL_BIT_EX_TRIG_FALLING); |
| } else if (cmd.edge == ME4000_AI_TRIGGER_EXT_EDGE_FALLING) { |
| tmp |= ME4000_AI_CTRL_BIT_EX_TRIG_FALLING; |
| tmp &= ~ME4000_AI_CTRL_BIT_EX_TRIG_BOTH; |
| } else if (cmd.edge == ME4000_AI_TRIGGER_EXT_EDGE_BOTH) { |
| tmp |= |
| ME4000_AI_CTRL_BIT_EX_TRIG_BOTH | |
| ME4000_AI_CTRL_BIT_EX_TRIG_FALLING; |
| } else { |
| spin_unlock_irqrestore(&ai_context->int_lock, flags); |
| printk(KERN_ERR |
| "ME4000:me4000_ai_ex_trig_setup():Invalid trigger edge specified\n"); |
| return -EINVAL; |
| } |
| |
| me4000_outl(tmp, ai_context->ctrl_reg); |
| spin_unlock_irqrestore(&ai_context->int_lock, flags); |
| return 0; |
| } |
| |
| static int me4000_ai_sc_setup(struct me4000_ai_sc *arg, |
| struct me4000_ai_context *ai_context) |
| { |
| struct me4000_ai_sc cmd; |
| int err; |
| |
| CALL_PDEBUG("me4000_ai_sc_setup() is executed\n"); |
| |
| /* Copy data from user */ |
| err = copy_from_user(&cmd, arg, sizeof(struct me4000_ai_sc)); |
| if (err) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_sc_setup():Can't copy from user space\n"); |
| return -EFAULT; |
| } |
| |
| ai_context->sample_counter = cmd.value; |
| ai_context->sample_counter_reload = cmd.reload; |
| |
| return 0; |
| } |
| |
| static ssize_t me4000_ai_read(struct file *filep, char *buff, size_t cnt, |
| loff_t *offp) |
| { |
| struct me4000_ai_context *ai_context = filep->private_data; |
| s16 *buffer = (s16 *) buff; |
| size_t count = cnt / 2; |
| unsigned long flags; |
| int tmp; |
| int c = 0; |
| int k = 0; |
| int ret = 0; |
| wait_queue_t wait; |
| |
| CALL_PDEBUG("me4000_ai_read() is executed\n"); |
| |
| init_waitqueue_entry(&wait, current); |
| |
| /* Check count */ |
| if (count <= 0) { |
| PDEBUG("me4000_ai_read():Count is 0\n"); |
| return 0; |
| } |
| |
| while (count > 0) { |
| if (filep->f_flags & O_NONBLOCK) { |
| c = me4000_values_to_end(ai_context->circ_buf, |
| ME4000_AI_BUFFER_COUNT); |
| if (!c) { |
| PDEBUG |
| ("me4000_ai_read():Returning from nonblocking read\n"); |
| break; |
| } |
| } else { |
| /* Check if conversion is still running */ |
| if (! |
| (me4000_inl(ai_context->status_reg) & |
| ME4000_AI_STATUS_BIT_FSM)) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_read():Conversion interrupted\n"); |
| return -EPIPE; |
| } |
| |
| wait_event_interruptible(ai_context->wait_queue, |
| (me4000_values_to_end |
| (ai_context->circ_buf, |
| ME4000_AI_BUFFER_COUNT))); |
| if (signal_pending(current)) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_read():Wait on values interrupted from signal\n"); |
| return -EINTR; |
| } |
| } |
| |
| /* Only read count values or as much as available */ |
| c = me4000_values_to_end(ai_context->circ_buf, |
| ME4000_AI_BUFFER_COUNT); |
| PDEBUG("me4000_ai_read():%d values to end\n", c); |
| if (count < c) |
| c = count; |
| |
| PDEBUG("me4000_ai_read():Copy %d values to user space\n", c); |
| k = 2 * c; |
| k -= copy_to_user(buffer, |
| ai_context->circ_buf.buf + |
| ai_context->circ_buf.tail, k); |
| c = k / 2; |
| if (!c) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_read():Cannot copy new values to user\n"); |
| return -EFAULT; |
| } |
| |
| ai_context->circ_buf.tail = |
| (ai_context->circ_buf.tail + c) & (ME4000_AI_BUFFER_COUNT - |
| 1); |
| buffer += c; |
| count -= c; |
| ret += c; |
| |
| spin_lock_irqsave(&ai_context->int_lock, flags); |
| if (me4000_buf_space |
| (ai_context->circ_buf, ME4000_AI_BUFFER_COUNT)) { |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| |
| /* Determine interrupt setup */ |
| if (ai_context->sample_counter |
| && !ai_context->sample_counter_reload) { |
| /* Enable Half Full Interrupt and Sample Counter Interrupt */ |
| tmp |= |
| ME4000_AI_CTRL_BIT_SC_IRQ | |
| ME4000_AI_CTRL_BIT_HF_IRQ; |
| } else if (ai_context->sample_counter |
| && ai_context->sample_counter_reload) { |
| if (ai_context->sample_counter < |
| ME4000_AI_FIFO_COUNT / 2) { |
| /* Enable only Sample Counter Interrupt */ |
| tmp |= ME4000_AI_CTRL_BIT_SC_IRQ; |
| } else { |
| /* Enable Half Full Interrupt and Sample Counter Interrupt */ |
| tmp |= |
| ME4000_AI_CTRL_BIT_SC_IRQ | |
| ME4000_AI_CTRL_BIT_HF_IRQ; |
| } |
| } else { |
| /* Enable only Half Full Interrupt */ |
| tmp |= ME4000_AI_CTRL_BIT_HF_IRQ; |
| } |
| |
| me4000_outl(tmp, ai_context->ctrl_reg); |
| } |
| spin_unlock_irqrestore(&ai_context->int_lock, flags); |
| } |
| |
| /* Check if conversion is still running */ |
| if (!(me4000_inl(ai_context->status_reg) & ME4000_AI_STATUS_BIT_FSM)) { |
| printk(KERN_ERR |
| "ME4000:me4000_ai_read():Conversion not running after complete read\n"); |
| return -EPIPE; |
| } |
| |
| if (filep->f_flags & O_NONBLOCK) { |
| return (k == 0) ? -EAGAIN : 2 * ret; |
| } |
| |
| CALL_PDEBUG("me4000_ai_read() is leaved\n"); |
| return ret * 2; |
| } |
| |
| static unsigned int me4000_ai_poll(struct file *file_p, poll_table *wait) |
| { |
| struct me4000_ai_context *ai_context; |
| unsigned long mask = 0; |
| |
| CALL_PDEBUG("me4000_ai_poll() is executed\n"); |
| |
| ai_context = file_p->private_data; |
| |
| /* Register wait queue */ |
| poll_wait(file_p, &ai_context->wait_queue, wait); |
| |
| /* Get available values */ |
| if (me4000_values_to_end(ai_context->circ_buf, ME4000_AI_BUFFER_COUNT)) |
| mask |= POLLIN | POLLRDNORM; |
| |
| PDEBUG("me4000_ai_poll():Return mask %lX\n", mask); |
| |
| return mask; |
| } |
| |
| static int me4000_ai_offset_enable(struct me4000_ai_context *ai_context) |
| { |
| unsigned long tmp; |
| |
| CALL_PDEBUG("me4000_ai_offset_enable() is executed\n"); |
| |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| tmp |= ME4000_AI_CTRL_BIT_OFFSET; |
| me4000_outl(tmp, ai_context->ctrl_reg); |
| |
| return 0; |
| } |
| |
| static int me4000_ai_offset_disable(struct me4000_ai_context *ai_context) |
| { |
| unsigned long tmp; |
| |
| CALL_PDEBUG("me4000_ai_offset_disable() is executed\n"); |
| |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| tmp &= ~ME4000_AI_CTRL_BIT_OFFSET; |
| me4000_outl(tmp, ai_context->ctrl_reg); |
| |
| return 0; |
| } |
| |
| static int me4000_ai_fullscale_enable(struct me4000_ai_context *ai_context) |
| { |
| unsigned long tmp; |
| |
| CALL_PDEBUG("me4000_ai_fullscale_enable() is executed\n"); |
| |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| tmp |= ME4000_AI_CTRL_BIT_FULLSCALE; |
| me4000_outl(tmp, ai_context->ctrl_reg); |
| |
| return 0; |
| } |
| |
| static int me4000_ai_fullscale_disable(struct me4000_ai_context *ai_context) |
| { |
| unsigned long tmp; |
| |
| CALL_PDEBUG("me4000_ai_fullscale_disable() is executed\n"); |
| |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| tmp &= ~ME4000_AI_CTRL_BIT_FULLSCALE; |
| me4000_outl(tmp, ai_context->ctrl_reg); |
| |
| return 0; |
| } |
| |
| static int me4000_ai_fsm_state(int *arg, struct me4000_ai_context *ai_context) |
| { |
| unsigned long tmp; |
| |
| CALL_PDEBUG("me4000_ai_fsm_state() is executed\n"); |
| |
| tmp = |
| (me4000_inl(ai_context->status_reg) & ME4000_AI_STATUS_BIT_FSM) ? 1 |
| : 0; |
| |
| if (put_user(tmp, arg)) { |
| printk(KERN_ERR "me4000_ai_fsm_state():Cannot copy to user\n"); |
| return -EFAULT; |
| } |
| |
| return 0; |
| } |
| |
| static int me4000_ai_get_count_buffer(unsigned long *arg, |
| struct me4000_ai_context *ai_context) |
| { |
| unsigned long c; |
| int err; |
| |
| c = me4000_buf_count(ai_context->circ_buf, ME4000_AI_BUFFER_COUNT); |
| |
| err = copy_to_user(arg, &c, sizeof(unsigned long)); |
| if (err) { |
| printk(KERN_ERR |
| "%s:Can't copy to user space\n", __func__); |
| return -EFAULT; |
| } |
| |
| return 0; |
| } |
| |
| /*---------------------------------- EEPROM stuff ---------------------------*/ |
| |
| static int eeprom_write_cmd(struct me4000_ai_context *ai_context, unsigned long cmd, |
| int length) |
| { |
| int i; |
| unsigned long value; |
| |
| CALL_PDEBUG("eeprom_write_cmd() is executed\n"); |
| |
| PDEBUG("eeprom_write_cmd():Write command 0x%08lX with length = %d\n", |
| cmd, length); |
| |
| /* Get the ICR register and clear the related bits */ |
| value = me4000_inl(ai_context->board_info->plx_regbase + PLX_ICR); |
| value &= ~(PLX_ICR_MASK_EEPROM); |
| me4000_outl(value, ai_context->board_info->plx_regbase + PLX_ICR); |
| |
| /* Raise the chip select */ |
| value |= PLX_ICR_BIT_EEPROM_CHIP_SELECT; |
| me4000_outl(value, ai_context->board_info->plx_regbase + PLX_ICR); |
| udelay(EEPROM_DELAY); |
| |
| for (i = 0; i < length; i++) { |
| if (cmd & ((0x1 << (length - 1)) >> i)) { |
| value |= PLX_ICR_BIT_EEPROM_WRITE; |
| } else { |
| value &= ~PLX_ICR_BIT_EEPROM_WRITE; |
| } |
| |
| /* Write to EEPROM */ |
| me4000_outl(value, |
| ai_context->board_info->plx_regbase + PLX_ICR); |
| udelay(EEPROM_DELAY); |
| |
| /* Raising edge of the clock */ |
| value |= PLX_ICR_BIT_EEPROM_CLOCK_SET; |
| me4000_outl(value, |
| ai_context->board_info->plx_regbase + PLX_ICR); |
| udelay(EEPROM_DELAY); |
| |
| /* Falling edge of the clock */ |
| value &= ~PLX_ICR_BIT_EEPROM_CLOCK_SET; |
| me4000_outl(value, |
| ai_context->board_info->plx_regbase + PLX_ICR); |
| udelay(EEPROM_DELAY); |
| } |
| |
| /* Clear the chip select */ |
| value &= ~PLX_ICR_BIT_EEPROM_CHIP_SELECT; |
| me4000_outl(value, ai_context->board_info->plx_regbase + PLX_ICR); |
| udelay(EEPROM_DELAY); |
| |
| /* Wait until hardware is ready for sure */ |
| mdelay(10); |
| |
| return 0; |
| } |
| |
| static unsigned short eeprom_read_cmd(struct me4000_ai_context *ai_context, |
| unsigned long cmd, int length) |
| { |
| int i; |
| unsigned long value; |
| unsigned short id = 0; |
| |
| CALL_PDEBUG("eeprom_read_cmd() is executed\n"); |
| |
| PDEBUG("eeprom_read_cmd():Read command 0x%08lX with length = %d\n", cmd, |
| length); |
| |
| /* Get the ICR register and clear the related bits */ |
| value = me4000_inl(ai_context->board_info->plx_regbase + PLX_ICR); |
| value &= ~(PLX_ICR_MASK_EEPROM); |
| |
| me4000_outl(value, ai_context->board_info->plx_regbase + PLX_ICR); |
| |
| /* Raise the chip select */ |
| value |= PLX_ICR_BIT_EEPROM_CHIP_SELECT; |
| me4000_outl(value, ai_context->board_info->plx_regbase + PLX_ICR); |
| udelay(EEPROM_DELAY); |
| |
| /* Write the read command to the eeprom */ |
| for (i = 0; i < length; i++) { |
| if (cmd & ((0x1 << (length - 1)) >> i)) { |
| value |= PLX_ICR_BIT_EEPROM_WRITE; |
| } else { |
| value &= ~PLX_ICR_BIT_EEPROM_WRITE; |
| } |
| me4000_outl(value, |
| ai_context->board_info->plx_regbase + PLX_ICR); |
| udelay(EEPROM_DELAY); |
| |
| /* Raising edge of the clock */ |
| value |= PLX_ICR_BIT_EEPROM_CLOCK_SET; |
| me4000_outl(value, |
| ai_context->board_info->plx_regbase + PLX_ICR); |
| udelay(EEPROM_DELAY); |
| |
| /* Falling edge of the clock */ |
| value &= ~PLX_ICR_BIT_EEPROM_CLOCK_SET; |
| me4000_outl(value, |
| ai_context->board_info->plx_regbase + PLX_ICR); |
| udelay(EEPROM_DELAY); |
| } |
| |
| /* Read the value from the eeprom */ |
| for (i = 0; i < 16; i++) { |
| /* Raising edge of the clock */ |
| value |= PLX_ICR_BIT_EEPROM_CLOCK_SET; |
| me4000_outl(value, |
| ai_context->board_info->plx_regbase + PLX_ICR); |
| udelay(EEPROM_DELAY); |
| |
| if (me4000_inl(ai_context->board_info->plx_regbase + PLX_ICR) & |
| PLX_ICR_BIT_EEPROM_READ) { |
| id |= (0x8000 >> i); |
| PDEBUG("eeprom_read_cmd():OR with 0x%04X\n", |
| (0x8000 >> i)); |
| } else { |
| PDEBUG("eeprom_read_cmd():Dont't OR\n"); |
| } |
| |
| /* Falling edge of the clock */ |
| value &= ~PLX_ICR_BIT_EEPROM_CLOCK_SET; |
| me4000_outl(value, |
| ai_context->board_info->plx_regbase + PLX_ICR); |
| udelay(EEPROM_DELAY); |
| } |
| |
| /* Clear the chip select */ |
| value &= ~PLX_ICR_BIT_EEPROM_CHIP_SELECT; |
| me4000_outl(value, ai_context->board_info->plx_regbase + PLX_ICR); |
| udelay(EEPROM_DELAY); |
| |
| return id; |
| } |
| |
| static int me4000_eeprom_write(struct me4000_eeprom *arg, |
| struct me4000_ai_context *ai_context) |
| { |
| int err; |
| struct me4000_eeprom setup; |
| unsigned long cmd; |
| unsigned long date_high; |
| unsigned long date_low; |
| |
| CALL_PDEBUG("me4000_eeprom_write() is executed\n"); |
| |
| err = copy_from_user(&setup, arg, sizeof(setup)); |
| if (err) { |
| printk(KERN_ERR |
| "ME4000:me4000_eeprom_write():Cannot copy from user\n"); |
| return err; |
| } |
| |
| /* Enable writing */ |
| eeprom_write_cmd(ai_context, ME4000_EEPROM_CMD_WRITE_ENABLE, |
| ME4000_EEPROM_CMD_LENGTH_WRITE_ENABLE); |
| |
| /* Command for date */ |
| date_high = (setup.date & 0xFFFF0000) >> 16; |
| date_low = (setup.date & 0x0000FFFF); |
| |
| cmd = |
| ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_DATE_HIGH << |
| ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & |
| (unsigned |
| long) |
| date_high); |
| err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); |
| if (err) |
| return err; |
| |
| cmd = |
| ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_DATE_LOW << |
| ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & |
| (unsigned |
| long) |
| date_low); |
| err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); |
| if (err) |
| return err; |
| |
| /* Command for unipolar 10V offset */ |
| cmd = |
| ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_1_UNI_OFFSET << |
| ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & |
| (unsigned |
| long) |
| setup. |
| uni_10_offset); |
| err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); |
| if (err) |
| return err; |
| |
| /* Command for unipolar 10V fullscale */ |
| cmd = |
| ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_1_UNI_FULLSCALE << |
| ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & |
| (unsigned |
| long) |
| setup. |
| uni_10_fullscale); |
| err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); |
| if (err) |
| return err; |
| |
| /* Command for unipolar 2,5V offset */ |
| cmd = |
| ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_4_UNI_OFFSET << |
| ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & |
| (unsigned |
| long) |
| setup. |
| uni_2_5_offset); |
| err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); |
| if (err) |
| return err; |
| |
| /* Command for unipolar 2,5V fullscale */ |
| cmd = |
| ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_4_UNI_FULLSCALE << |
| ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & |
| (unsigned |
| long) |
| setup. |
| uni_2_5_fullscale); |
| err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); |
| if (err) |
| return err; |
| |
| /* Command for bipolar 10V offset */ |
| cmd = |
| ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_1_BI_OFFSET << |
| ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & |
| (unsigned |
| long) |
| setup. |
| bi_10_offset); |
| err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); |
| if (err) |
| return err; |
| |
| /* Command for bipolar 10V fullscale */ |
| cmd = |
| ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_1_BI_FULLSCALE << |
| ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & |
| (unsigned |
| long) |
| setup. |
| bi_10_fullscale); |
| err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); |
| if (err) |
| return err; |
| |
| /* Command for bipolar 2,5V offset */ |
| cmd = |
| ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_4_BI_OFFSET << |
| ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & |
| (unsigned |
| long) |
| setup. |
| bi_2_5_offset); |
| err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); |
| if (err) |
| return err; |
| |
| /* Command for bipolar 2,5V fullscale */ |
| cmd = |
| ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_4_BI_FULLSCALE << |
| ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & |
| (unsigned |
| long) |
| setup. |
| bi_2_5_fullscale); |
| err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); |
| if (err) |
| return err; |
| |
| /* Command for differential 10V offset */ |
| cmd = |
| ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_1_DIFF_OFFSET << |
| ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & |
| (unsigned |
| long) |
| setup. |
| diff_10_offset); |
| err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); |
| if (err) |
| return err; |
| |
| /* Command for differential 10V fullscale */ |
| cmd = |
| ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_1_DIFF_FULLSCALE |
| << ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & |
| (unsigned |
| long) |
| setup. |
| diff_10_fullscale); |
| err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); |
| if (err) |
| return err; |
| |
| /* Command for differential 2,5V offset */ |
| cmd = |
| ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_4_DIFF_OFFSET << |
| ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & |
| (unsigned |
| long) |
| setup. |
| diff_2_5_offset); |
| err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); |
| if (err) |
| return err; |
| |
| /* Command for differential 2,5V fullscale */ |
| cmd = |
| ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_4_DIFF_FULLSCALE |
| << ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & |
| (unsigned |
| long) |
| setup. |
| diff_2_5_fullscale); |
| err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); |
| if (err) |
| return err; |
| |
| /* Disable writing */ |
| eeprom_write_cmd(ai_context, ME4000_EEPROM_CMD_WRITE_DISABLE, |
| ME4000_EEPROM_CMD_LENGTH_WRITE_DISABLE); |
| |
| return 0; |
| } |
| |
| static int me4000_eeprom_read(struct me4000_eeprom *arg, |
| struct me4000_ai_context *ai_context) |
| { |
| int err; |
| unsigned long cmd; |
| struct me4000_eeprom setup; |
| |
| CALL_PDEBUG("me4000_eeprom_read() is executed\n"); |
| |
| /* Command for date */ |
| cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_DATE_HIGH; |
| setup.date = |
| eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); |
| setup.date <<= 16; |
| cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_DATE_LOW; |
| setup.date |= |
| eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); |
| |
| /* Command for unipolar 10V offset */ |
| cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_1_UNI_OFFSET; |
| setup.uni_10_offset = |
| eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); |
| |
| /* Command for unipolar 10V fullscale */ |
| cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_1_UNI_FULLSCALE; |
| setup.uni_10_fullscale = |
| eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); |
| |
| /* Command for unipolar 2,5V offset */ |
| cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_4_UNI_OFFSET; |
| setup.uni_2_5_offset = |
| eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); |
| |
| /* Command for unipolar 2,5V fullscale */ |
| cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_4_UNI_FULLSCALE; |
| setup.uni_2_5_fullscale = |
| eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); |
| |
| /* Command for bipolar 10V offset */ |
| cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_1_BI_OFFSET; |
| setup.bi_10_offset = |
| eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); |
| |
| /* Command for bipolar 10V fullscale */ |
| cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_1_BI_FULLSCALE; |
| setup.bi_10_fullscale = |
| eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); |
| |
| /* Command for bipolar 2,5V offset */ |
| cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_4_BI_OFFSET; |
| setup.bi_2_5_offset = |
| eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); |
| |
| /* Command for bipolar 2,5V fullscale */ |
| cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_4_BI_FULLSCALE; |
| setup.bi_2_5_fullscale = |
| eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); |
| |
| /* Command for differntial 10V offset */ |
| cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_1_DIFF_OFFSET; |
| setup.diff_10_offset = |
| eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); |
| |
| /* Command for differential 10V fullscale */ |
| cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_1_DIFF_FULLSCALE; |
| setup.diff_10_fullscale = |
| eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); |
| |
| /* Command for differntial 2,5V offset */ |
| cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_4_DIFF_OFFSET; |
| setup.diff_2_5_offset = |
| eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); |
| |
| /* Command for differential 2,5V fullscale */ |
| cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_4_DIFF_FULLSCALE; |
| setup.diff_2_5_fullscale = |
| eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); |
| |
| err = copy_to_user(arg, &setup, sizeof(setup)); |
| if (err) { |
| printk(KERN_ERR |
| "ME4000:me4000_eeprom_read():Cannot copy to user\n"); |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| /*------------------------------------ DIO stuff ----------------------------------------------*/ |
| |
| static int me4000_dio_ioctl(struct inode *inode_p, struct file *file_p, |
| unsigned int service, unsigned long arg) |
| { |
| struct me4000_dio_context *dio_context; |
| |
| CALL_PDEBUG("me4000_dio_ioctl() is executed\n"); |
| |
| dio_context = file_p->private_data; |
| |
| if (_IOC_TYPE(service) != ME4000_MAGIC) { |
| printk(KERN_ERR "me4000_dio_ioctl():Wrong magic number\n"); |
| return -ENOTTY; |
| } |
| if (_IOC_NR(service) > ME4000_IOCTL_MAXNR) { |
| printk(KERN_ERR "me4000_dio_ioctl():Service number to high\n"); |
| return -ENOTTY; |
| } |
| |
| switch (service) { |
| case ME4000_DIO_CONFIG: |
| return me4000_dio_config((struct me4000_dio_config *)arg, |
| dio_context); |
| case ME4000_DIO_SET_BYTE: |
| return me4000_dio_set_byte((struct me4000_dio_byte *)arg, |
| dio_context); |
| case ME4000_DIO_GET_BYTE: |
| return me4000_dio_get_byte((struct me4000_dio_byte *)arg, |
| dio_context); |
| case ME4000_DIO_RESET: |
| return me4000_dio_reset(dio_context); |
| default: |
| printk(KERN_ERR |
| "ME4000:me4000_dio_ioctl():Invalid service number %d\n", |
| service); |
| return -ENOTTY; |
| } |
| return 0; |
| } |
| |
| static int me4000_dio_config(struct me4000_dio_config *arg, |
| struct me4000_dio_context *dio_context) |
| { |
| struct me4000_dio_config cmd; |
| u32 tmp; |
| int err; |
| |
| CALL_PDEBUG("me4000_dio_config() is executed\n"); |
| |
| /* Copy data from user */ |
| err = copy_from_user(&cmd, arg, sizeof(struct me4000_dio_config)); |
| if (err) { |
| printk(KERN_ERR |
| "ME4000:me4000_dio_config():Can't copy from user space\n"); |
| return -EFAULT; |
| } |
| |
| /* Check port parameter */ |
| if (cmd.port >= dio_context->dio_count) { |
| printk(KERN_ERR |
| "ME4000:me4000_dio_config():Port %d is not available\n", |
| cmd.port); |
| return -EINVAL; |
| } |
| |
| PDEBUG("me4000_dio_config(): port %d, mode %d, function %d\n", cmd.port, |
| cmd.mode, cmd.function); |
| |
| if (cmd.port == ME4000_DIO_PORT_A) { |
| if (cmd.mode == ME4000_DIO_PORT_INPUT) { |
| /* Check if opto isolated version */ |
| if (!(me4000_inl(dio_context->dir_reg) & 0x1)) { |
| printk(KERN_ERR |
| "ME4000:me4000_dio_config():Cannot set to input on opto isolated versions\n"); |
| return -EIO; |
| } |
| |
| tmp = me4000_inl(dio_context->ctrl_reg); |
| tmp &= |
| ~(ME4000_DIO_CTRL_BIT_MODE_0 | |
| ME4000_DIO_CTRL_BIT_MODE_1); |
| me4000_outl(tmp, dio_context->ctrl_reg); |
| } else if (cmd.mode == ME4000_DIO_PORT_OUTPUT) { |
| tmp = me4000_inl(dio_context->ctrl_reg); |
| tmp &= |
| ~(ME4000_DIO_CTRL_BIT_MODE_0 | |
| ME4000_DIO_CTRL_BIT_MODE_1); |
| tmp |= ME4000_DIO_CTRL_BIT_MODE_0; |
| me4000_outl(tmp, dio_context->ctrl_reg); |
| } else if (cmd.mode == ME4000_DIO_FIFO_LOW) { |
| tmp = me4000_inl(dio_context->ctrl_reg); |
| tmp &= |
| ~(ME4000_DIO_CTRL_BIT_MODE_0 | |
| ME4000_DIO_CTRL_BIT_MODE_1 | |
| ME4000_DIO_CTRL_BIT_FIFO_HIGH_0); |
| tmp |= |
| ME4000_DIO_CTRL_BIT_MODE_0 | |
| ME4000_DIO_CTRL_BIT_MODE_1; |
| me4000_outl(tmp, dio_context->ctrl_reg); |
| } else if (cmd.mode == ME4000_DIO_FIFO_HIGH) { |
| tmp = me4000_inl(dio_context->ctrl_reg); |
| tmp |= |
| ME4000_DIO_CTRL_BIT_MODE_0 | |
| ME4000_DIO_CTRL_BIT_MODE_1 | |
| ME4000_DIO_CTRL_BIT_FIFO_HIGH_0; |
| me4000_outl(tmp, dio_context->ctrl_reg); |
| } else { |
| printk(KERN_ERR |
| "ME4000:me4000_dio_config():Mode %d is not available\n", |
| cmd.mode); |
| return -EINVAL; |
| } |
| } else if (cmd.port == ME4000_DIO_PORT_B) { |
| if (cmd.mode == ME4000_DIO_PORT_INPUT) { |
| /* Only do anything when TTL version is installed */ |
| if ((me4000_inl(dio_context->dir_reg) & 0x1)) { |
| tmp = me4000_inl(dio_context->ctrl_reg); |
| tmp &= |
| ~(ME4000_DIO_CTRL_BIT_MODE_2 | |
| ME4000_DIO_CTRL_BIT_MODE_3); |
| me4000_outl(tmp, dio_context->ctrl_reg); |
| } |
| } else if (cmd.mode == ME4000_DIO_PORT_OUTPUT) { |
| /* Check if opto isolated version */ |
| if (!(me4000_inl(dio_context->dir_reg) & 0x1)) { |
| printk(KERN_ERR |
| "ME4000:me4000_dio_config():Cannot set to output on opto isolated versions\n"); |
| return -EIO; |
| } |
| |
| tmp = me4000_inl(dio_context->ctrl_reg); |
| tmp &= |
| ~(ME4000_DIO_CTRL_BIT_MODE_2 | |
| ME4000_DIO_CTRL_BIT_MODE_3); |
| tmp |= ME4000_DIO_CTRL_BIT_MODE_2; |
| me4000_outl(tmp, dio_context->ctrl_reg); |
| } else if (cmd.mode == ME4000_DIO_FIFO_LOW) { |
| /* Check if opto isolated version */ |
| if (!(me4000_inl(dio_context->dir_reg) & 0x1)) { |
| printk(KERN_ERR |
| "ME4000:me4000_dio_config():Cannot set to FIFO low output on opto isolated versions\n"); |
| return -EIO; |
| } |
| |
| tmp = me4000_inl(dio_context->ctrl_reg); |
| tmp &= |
| ~(ME4000_DIO_CTRL_BIT_MODE_2 | |
| ME4000_DIO_CTRL_BIT_MODE_3 | |
| ME4000_DIO_CTRL_BIT_FIFO_HIGH_1); |
| tmp |= |
| ME4000_DIO_CTRL_BIT_MODE_2 | |
| ME4000_DIO_CTRL_BIT_MODE_3; |
| me4000_outl(tmp, dio_context->ctrl_reg); |
| } else if (cmd.mode == ME4000_DIO_FIFO_HIGH) { |
| /* Check if opto isolated version */ |
| if (!(me4000_inl(dio_context->dir_reg) & 0x1)) { |
| printk(KERN_ERR |
| "ME4000:me4000_dio_config():Cannot set to FIFO high output on opto isolated versions\n"); |
| return -EIO; |
| } |
| |
| tmp = me4000_inl(dio_context->ctrl_reg); |
| tmp |= |
| ME4000_DIO_CTRL_BIT_MODE_2 | |
| ME4000_DIO_CTRL_BIT_MODE_3 | |
| ME4000_DIO_CTRL_BIT_FIFO_HIGH_1; |
| me4000_outl(tmp, dio_context->ctrl_reg); |
| } else { |
| printk(KERN_ERR |
| "ME4000:me4000_dio_config():Mode %d is not available\n", |
| cmd.mode); |
| return -EINVAL; |
| } |
| } else if (cmd.port == ME4000_DIO_PORT_C) { |
| if (cmd.mode == ME4000_DIO_PORT_INPUT) { |
| tmp = me4000_inl(dio_context->ctrl_reg); |
| tmp &= |
| ~(ME4000_DIO_CTRL_BIT_MODE_4 | |
| ME4000_DIO_CTRL_BIT_MODE_5); |
| me4000_outl(tmp, dio_context->ctrl_reg); |
| } else if (cmd.mode == ME4000_DIO_PORT_OUTPUT) { |
| tmp = me4000_inl(dio_context->ctrl_reg); |
| tmp &= |
| ~(ME4000_DIO_CTRL_BIT_MODE_4 | |
| ME4000_DIO_CTRL_BIT_MODE_5); |
| tmp |= ME4000_DIO_CTRL_BIT_MODE_4; |
| me4000_outl(tmp, dio_context->ctrl_reg); |
| } else if (cmd.mode == ME4000_DIO_FIFO_LOW) { |
| tmp = me4000_inl(dio_context->ctrl_reg); |
| tmp &= |
| ~(ME4000_DIO_CTRL_BIT_MODE_4 | |
| ME4000_DIO_CTRL_BIT_MODE_5 | |
| ME4000_DIO_CTRL_BIT_FIFO_HIGH_2); |
| tmp |= |
| ME4000_DIO_CTRL_BIT_MODE_4 | |
| ME4000_DIO_CTRL_BIT_MODE_5; |
| me4000_outl(tmp, dio_context->ctrl_reg); |
| } else if (cmd.mode == ME4000_DIO_FIFO_HIGH) { |
| tmp = me4000_inl(dio_context->ctrl_reg); |
| tmp |= |
| ME4000_DIO_CTRL_BIT_MODE_4 | |
| ME4000_DIO_CTRL_BIT_MODE_5 | |
| ME4000_DIO_CTRL_BIT_FIFO_HIGH_2; |
| me4000_outl(tmp, dio_context->ctrl_reg); |
| } else { |
| printk(KERN_ERR |
| "ME4000:me4000_dio_config():Mode %d is not available\n", |
| cmd.mode); |
| return -EINVAL; |
| } |
| } else if (cmd.port == ME4000_DIO_PORT_D) { |
| if (cmd.mode == ME4000_DIO_PORT_INPUT) { |
| tmp = me4000_inl(dio_context->ctrl_reg); |
| tmp &= |
| ~(ME4000_DIO_CTRL_BIT_MODE_6 | |
| ME4000_DIO_CTRL_BIT_MODE_7); |
| me4000_outl(tmp, dio_context->ctrl_reg); |
| } else if (cmd.mode == ME4000_DIO_PORT_OUTPUT) { |
| tmp = me4000_inl(dio_context->ctrl_reg); |
| tmp &= |
| ~(ME4000_DIO_CTRL_BIT_MODE_6 | |
| ME4000_DIO_CTRL_BIT_MODE_7); |
| tmp |= ME4000_DIO_CTRL_BIT_MODE_6; |
| me4000_outl(tmp, dio_context->ctrl_reg); |
| } else if (cmd.mode == ME4000_DIO_FIFO_LOW) { |
| tmp = me4000_inl(dio_context->ctrl_reg); |
| tmp &= |
| ~(ME4000_DIO_CTRL_BIT_MODE_6 | |
| ME4000_DIO_CTRL_BIT_MODE_7 | |
| ME4000_DIO_CTRL_BIT_FIFO_HIGH_3); |
| tmp |= |
| ME4000_DIO_CTRL_BIT_MODE_6 | |
| ME4000_DIO_CTRL_BIT_MODE_7; |
| me4000_outl(tmp, dio_context->ctrl_reg); |
| } else if (cmd.mode == ME4000_DIO_FIFO_HIGH) { |
| tmp = me4000_inl(dio_context->ctrl_reg); |
| tmp |= |
| ME4000_DIO_CTRL_BIT_MODE_6 | |
| ME4000_DIO_CTRL_BIT_MODE_7 | |
| ME4000_DIO_CTRL_BIT_FIFO_HIGH_3; |
| me4000_outl(tmp, dio_context->ctrl_reg); |
| } else { |
| printk(KERN_ERR |
| "ME4000:me4000_dio_config():Mode %d is not available\n", |
| cmd.mode); |
| return -EINVAL; |
| } |
| } else { |
| printk(KERN_ERR |
| "ME4000:me4000_dio_config():Port %d is not available\n", |
| cmd.port); |
| return -EINVAL; |
| } |
| |
| PDEBUG("me4000_dio_config(): port %d, mode %d, function %d\n", cmd.port, |
| cmd.mode, cmd.function); |
| |
| if ((cmd.mode == ME4000_DIO_FIFO_HIGH) |
| || (cmd.mode == ME4000_DIO_FIFO_LOW)) { |
| tmp = me4000_inl(dio_context->ctrl_reg); |
| tmp &= |
| ~(ME4000_DIO_CTRL_BIT_FUNCTION_0 | |
| ME4000_DIO_CTRL_BIT_FUNCTION_1); |
| if (cmd.function == ME4000_DIO_FUNCTION_PATTERN) { |
| me4000_outl(tmp, dio_context->ctrl_reg); |
| } else if (cmd.function == ME4000_DIO_FUNCTION_DEMUX) { |
| tmp |= ME4000_DIO_CTRL_BIT_FUNCTION_0; |
| me4000_outl(tmp, dio_context->ctrl_reg); |
| } else if (cmd.function == ME4000_DIO_FUNCTION_MUX) { |
| tmp |= ME4000_DIO_CTRL_BIT_FUNCTION_1; |
| me4000_outl(tmp, dio_context->ctrl_reg); |
| } else { |
| printk(KERN_ERR |
| "ME4000:me4000_dio_config():Invalid port function specified\n"); |
| return -EINVAL; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int me4000_dio_set_byte(struct me4000_dio_byte *arg, |
| struct me4000_dio_context *dio_context) |
| { |
| struct me4000_dio_byte cmd; |
| int err; |
| |
| CALL_PDEBUG("me4000_dio_set_byte() is executed\n"); |
| |
| /* Copy data from user */ |
| err = copy_from_user(&cmd, arg, sizeof(struct me4000_dio_byte)); |
| if (err) { |
| printk(KERN_ERR |
| "ME4000:me4000_dio_set_byte():Can't copy from user space\n"); |
| return -EFAULT; |
| } |
| |
| /* Check port parameter */ |
| if (cmd.port >= dio_context->dio_count) { |
| printk(KERN_ERR |
| "ME4000:me4000_dio_set_byte():Port %d is not available\n", |
| cmd.port); |
| return -EINVAL; |
| } |
| |
| if (cmd.port == ME4000_DIO_PORT_A) { |
| if ((me4000_inl(dio_context->ctrl_reg) & 0x3) != 0x1) { |
| printk(KERN_ERR |
| "ME4000:me4000_dio_set_byte():Port %d is not in output mode\n", |
| cmd.port); |
| return -EIO; |
| } |
| me4000_outl(cmd.byte, dio_context->port_0_reg); |
| } else if (cmd.port == ME4000_DIO_PORT_B) { |
| if ((me4000_inl(dio_context->ctrl_reg) & 0xC) != 0x4) { |
| printk(KERN_ERR |
| "ME4000:me4000_dio_set_byte():Port %d is not in output mode\n", |
| cmd.port); |
| return -EIO; |
| } |
| me4000_outl(cmd.byte, dio_context->port_1_reg); |
| } else if (cmd.port == ME4000_DIO_PORT_C) { |
| if ((me4000_inl(dio_context->ctrl_reg) & 0x30) != 0x10) { |
| printk(KERN_ERR |
| "ME4000:me4000_dio_set_byte():Port %d is not in output mode\n", |
| cmd.port); |
| return -EIO; |
| } |
| me4000_outl(cmd.byte, dio_context->port_2_reg); |
| } else if (cmd.port == ME4000_DIO_PORT_D) { |
| if ((me4000_inl(dio_context->ctrl_reg) & 0xC0) != 0x40) { |
| printk(KERN_ERR |
| "ME4000:me4000_dio_set_byte():Port %d is not in output mode\n", |
| cmd.port); |
| return -EIO; |
| } |
| me4000_outl(cmd.byte, dio_context->port_3_reg); |
| } else { |
| printk(KERN_ERR |
| "ME4000:me4000_dio_set_byte():Port %d is not available\n", |
| cmd.port); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int me4000_dio_get_byte(struct me4000_dio_byte *arg, |
| struct me4000_dio_context *dio_context) |
| { |
| struct me4000_dio_byte cmd; |
| int err; |
| |
| CALL_PDEBUG("me4000_dio_get_byte() is executed\n"); |
| |
| /* Copy data from user */ |
| err = copy_from_user(&cmd, arg, sizeof(struct me4000_dio_byte)); |
| if (err) { |
| printk(KERN_ERR |
| "ME4000:me4000_dio_get_byte():Can't copy from user space\n"); |
| return -EFAULT; |
| } |
| |
| /* Check port parameter */ |
| if (cmd.port >= dio_context->dio_count) { |
| printk(KERN_ERR |
| "ME4000:me4000_dio_get_byte():Port %d is not available\n", |
| cmd.port); |
| return -EINVAL; |
| } |
| |
| if (cmd.port == ME4000_DIO_PORT_A) { |
| cmd.byte = me4000_inl(dio_context->port_0_reg) & 0xFF; |
| } else if (cmd.port == ME4000_DIO_PORT_B) { |
| cmd.byte = me4000_inl(dio_context->port_1_reg) & 0xFF; |
| } else if (cmd.port == ME4000_DIO_PORT_C) { |
| cmd.byte = me4000_inl(dio_context->port_2_reg) & 0xFF; |
| } else if (cmd.port == ME4000_DIO_PORT_D) { |
| cmd.byte = me4000_inl(dio_context->port_3_reg) & 0xFF; |
| } else { |
| printk(KERN_ERR |
| "ME4000:me4000_dio_get_byte():Port %d is not available\n", |
| cmd.port); |
| return -EINVAL; |
| } |
| |
| /* Copy result back to user */ |
| err = copy_to_user(arg, &cmd, sizeof(struct me4000_dio_byte)); |
| if (err) { |
| printk(KERN_ERR |
| "ME4000:me4000_dio_get_byte():Can't copy to user space\n"); |
| return -EFAULT; |
| } |
| |
| return 0; |
| } |
| |
| static int me4000_dio_reset(struct me4000_dio_context *dio_context) |
| { |
| CALL_PDEBUG("me4000_dio_reset() is executed\n"); |
| |
| /* Clear the control register */ |
| me4000_outl(0, dio_context->ctrl_reg); |
| |
| /* Check for opto isolated version */ |
| if (!(me4000_inl(dio_context->dir_reg) & 0x1)) { |
| me4000_outl(0x1, dio_context->ctrl_reg); |
| me4000_outl(0x0, dio_context->port_0_reg); |
| } |
| |
| return 0; |
| } |
| |
| /*------------------------------------ COUNTER STUFF ------------------------------------*/ |
| |
| static int me4000_cnt_ioctl(struct inode *inode_p, struct file *file_p, |
| unsigned int service, unsigned long arg) |
| { |
| struct me4000_cnt_context *cnt_context; |
| |
| CALL_PDEBUG("me4000_cnt_ioctl() is executed\n"); |
| |
| cnt_context = file_p->private_data; |
| |
| if (_IOC_TYPE(service) != ME4000_MAGIC) { |
| printk(KERN_ERR "me4000_dio_ioctl():Wrong magic number\n"); |
| return -ENOTTY; |
| } |
| if (_IOC_NR(service) > ME4000_IOCTL_MAXNR) { |
| printk(KERN_ERR "me4000_dio_ioctl():Service number to high\n"); |
| return -ENOTTY; |
| } |
| |
| switch (service) { |
| case ME4000_CNT_READ: |
| return me4000_cnt_read((struct me4000_cnt *)arg, cnt_context); |
| case ME4000_CNT_WRITE: |
| return me4000_cnt_write((struct me4000_cnt *)arg, cnt_context); |
| case ME4000_CNT_CONFIG: |
| return me4000_cnt_config((struct me4000_cnt_config *)arg, |
| cnt_context); |
| case ME4000_CNT_RESET: |
| return me4000_cnt_reset(cnt_context); |
| default: |
| printk(KERN_ERR |
| "ME4000:me4000_dio_ioctl():Invalid service number %d\n", |
| service); |
| return -ENOTTY; |
| } |
| return 0; |
| } |
| |
| static int me4000_cnt_config(struct me4000_cnt_config *arg, |
| struct me4000_cnt_context *cnt_context) |
| { |
| struct me4000_cnt_config cmd; |
| u8 counter; |
| u8 mode; |
| int err; |
| |
| CALL_PDEBUG("me4000_cnt_config() is executed\n"); |
| |
| /* Copy data from user */ |
| err = copy_from_user(&cmd, arg, sizeof(struct me4000_cnt_config)); |
| if (err) { |
| printk(KERN_ERR |
| "ME4000:me4000_cnt_config():Can't copy from user space\n"); |
| return -EFAULT; |
| } |
| |
| /* Check counter parameter */ |
| switch (cmd.counter) { |
| case ME4000_CNT_COUNTER_0: |
| counter = ME4000_CNT_CTRL_BIT_COUNTER_0; |
| break; |
| case ME4000_CNT_COUNTER_1: |
| counter = ME4000_CNT_CTRL_BIT_COUNTER_1; |
| break; |
| case ME4000_CNT_COUNTER_2: |
| counter = ME4000_CNT_CTRL_BIT_COUNTER_2; |
| break; |
| default: |
| printk(KERN_ERR |
| "ME4000:me4000_cnt_config():Counter %d is not available\n", |
| cmd.counter); |
| return -EINVAL; |
| } |
| |
| /* Check mode parameter */ |
| switch (cmd.mode) { |
| case ME4000_CNT_MODE_0: |
| mode = ME4000_CNT_CTRL_BIT_MODE_0; |
| break; |
| case ME4000_CNT_MODE_1: |
| mode = ME4000_CNT_CTRL_BIT_MODE_1; |
| break; |
| case ME4000_CNT_MODE_2: |
| mode = ME4000_CNT_CTRL_BIT_MODE_2; |
| break; |
| case ME4000_CNT_MODE_3: |
| mode = ME4000_CNT_CTRL_BIT_MODE_3; |
| break; |
| case ME4000_CNT_MODE_4: |
| mode = ME4000_CNT_CTRL_BIT_MODE_4; |
| break; |
| case ME4000_CNT_MODE_5: |
| mode = ME4000_CNT_CTRL_BIT_MODE_5; |
| break; |
| default: |
| printk(KERN_ERR |
| "ME4000:me4000_cnt_config():Mode %d is not available\n", |
| cmd.mode); |
| return -EINVAL; |
| } |
| |
| /* Write the control word */ |
| me4000_outb((counter | mode | 0x30), cnt_context->ctrl_reg); |
| |
| return 0; |
| } |
| |
| static int me4000_cnt_read(struct me4000_cnt *arg, |
| struct me4000_cnt_context *cnt_context) |
| { |
| struct me4000_cnt cmd; |
| u8 tmp; |
| int err; |
| |
| CALL_PDEBUG("me4000_cnt_read() is executed\n"); |
| |
| /* Copy data from user */ |
| err = copy_from_user(&cmd, arg, sizeof(struct me4000_cnt)); |
| if (err) { |
| printk(KERN_ERR |
| "ME4000:me4000_cnt_read():Can't copy from user space\n"); |
| return -EFAULT; |
| } |
| |
| /* Read counter */ |
| switch (cmd.counter) { |
| case ME4000_CNT_COUNTER_0: |
| tmp = me4000_inb(cnt_context->counter_0_reg); |
| cmd.value = tmp; |
| tmp = me4000_inb(cnt_context->counter_0_reg); |
| cmd.value |= ((u16) tmp) << 8; |
| break; |
| case ME4000_CNT_COUNTER_1: |
| tmp = me4000_inb(cnt_context->counter_1_reg); |
| cmd.value = tmp; |
| tmp = me4000_inb(cnt_context->counter_1_reg); |
| cmd.value |= ((u16) tmp) << 8; |
| break; |
| case ME4000_CNT_COUNTER_2: |
| tmp = me4000_inb(cnt_context->counter_2_reg); |
| cmd.value = tmp; |
| tmp = me4000_inb(cnt_context->counter_2_reg); |
| cmd.value |= ((u16) tmp) << 8; |
| break; |
| default: |
| printk(KERN_ERR |
| "ME4000:me4000_cnt_read():Counter %d is not available\n", |
| cmd.counter); |
| return -EINVAL; |
| } |
| |
| /* Copy result back to user */ |
| err = copy_to_user(arg, &cmd, sizeof(struct me4000_cnt)); |
| if (err) { |
| printk(KERN_ERR |
| "ME4000:me4000_cnt_read():Can't copy to user space\n"); |
| return -EFAULT; |
| } |
| |
| return 0; |
| } |
| |
| static int me4000_cnt_write(struct me4000_cnt *arg, |
| struct me4000_cnt_context *cnt_context) |
| { |
| struct me4000_cnt cmd; |
| u8 tmp; |
| int err; |
| |
| CALL_PDEBUG("me4000_cnt_write() is executed\n"); |
| |
| /* Copy data from user */ |
| err = copy_from_user(&cmd, arg, sizeof(struct me4000_cnt)); |
| if (err) { |
| printk(KERN_ERR |
| "ME4000:me4000_cnt_write():Can't copy from user space\n"); |
| return -EFAULT; |
| } |
| |
| /* Write counter */ |
| switch (cmd.counter) { |
| case ME4000_CNT_COUNTER_0: |
| tmp = cmd.value & 0xFF; |
| me4000_outb(tmp, cnt_context->counter_0_reg); |
| tmp = (cmd.value >> 8) & 0xFF; |
| me4000_outb(tmp, cnt_context->counter_0_reg); |
| break; |
| case ME4000_CNT_COUNTER_1: |
| tmp = cmd.value & 0xFF; |
| me4000_outb(tmp, cnt_context->counter_1_reg); |
| tmp = (cmd.value >> 8) & 0xFF; |
| me4000_outb(tmp, cnt_context->counter_1_reg); |
| break; |
| case ME4000_CNT_COUNTER_2: |
| tmp = cmd.value & 0xFF; |
| me4000_outb(tmp, cnt_context->counter_2_reg); |
| tmp = (cmd.value >> 8) & 0xFF; |
| me4000_outb(tmp, cnt_context->counter_2_reg); |
| break; |
| default: |
| printk(KERN_ERR |
| "ME4000:me4000_cnt_write():Counter %d is not available\n", |
| cmd.counter); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int me4000_cnt_reset(struct me4000_cnt_context *cnt_context) |
| { |
| CALL_PDEBUG("me4000_cnt_reset() is executed\n"); |
| |
| /* Set the mode and value for counter 0 */ |
| me4000_outb(0x30, cnt_context->ctrl_reg); |
| me4000_outb(0x00, cnt_context->counter_0_reg); |
| me4000_outb(0x00, cnt_context->counter_0_reg); |
| |
| /* Set the mode and value for counter 1 */ |
| me4000_outb(0x70, cnt_context->ctrl_reg); |
| me4000_outb(0x00, cnt_context->counter_1_reg); |
| me4000_outb(0x00, cnt_context->counter_1_reg); |
| |
| /* Set the mode and value for counter 2 */ |
| me4000_outb(0xB0, cnt_context->ctrl_reg); |
| me4000_outb(0x00, cnt_context->counter_2_reg); |
| me4000_outb(0x00, cnt_context->counter_2_reg); |
| |
| return 0; |
| } |
| |
| /*------------------------------------ External Interrupt stuff ------------------------------------*/ |
| |
| static int me4000_ext_int_ioctl(struct inode *inode_p, struct file *file_p, |
| unsigned int service, unsigned long arg) |
| { |
| struct me4000_ext_int_context *ext_int_context; |
| |
| CALL_PDEBUG("me4000_ext_int_ioctl() is executed\n"); |
| |
| ext_int_context = file_p->private_data; |
| |
| if (_IOC_TYPE(service) != ME4000_MAGIC) { |
| printk(KERN_ERR "me4000_ext_int_ioctl():Wrong magic number\n"); |
| return -ENOTTY; |
| } |
| if (_IOC_NR(service) > ME4000_IOCTL_MAXNR) { |
| printk(KERN_ERR |
| "me4000_ext_int_ioctl():Service number to high\n"); |
| return -ENOTTY; |
| } |
| |
| switch (service) { |
| case ME4000_EXT_INT_ENABLE: |
| return me4000_ext_int_enable(ext_int_context); |
| case ME4000_EXT_INT_DISABLE: |
| return me4000_ext_int_disable(ext_int_context); |
| case ME4000_EXT_INT_COUNT: |
| return me4000_ext_int_count((unsigned long *)arg, |
| ext_int_context); |
| default: |
| printk(KERN_ERR |
| "ME4000:me4000_ext_int_ioctl():Invalid service number %d\n", |
| service); |
| return -ENOTTY; |
| } |
| return 0; |
| } |
| |
| static int me4000_ext_int_enable(struct me4000_ext_int_context *ext_int_context) |
| { |
| unsigned long tmp; |
| |
| CALL_PDEBUG("me4000_ext_int_enable() is executed\n"); |
| |
| tmp = me4000_inl(ext_int_context->ctrl_reg); |
| tmp |= ME4000_AI_CTRL_BIT_EX_IRQ; |
| me4000_outl(tmp, ext_int_context->ctrl_reg); |
| |
| return 0; |
| } |
| |
| static int me4000_ext_int_disable(struct me4000_ext_int_context *ext_int_context) |
| { |
| unsigned long tmp; |
| |
| CALL_PDEBUG("me4000_ext_int_disable() is executed\n"); |
| |
| tmp = me4000_inl(ext_int_context->ctrl_reg); |
| tmp &= ~ME4000_AI_CTRL_BIT_EX_IRQ; |
| me4000_outl(tmp, ext_int_context->ctrl_reg); |
| |
| return 0; |
| } |
| |
| static int me4000_ext_int_count(unsigned long *arg, |
| struct me4000_ext_int_context *ext_int_context) |
| { |
| |
| CALL_PDEBUG("me4000_ext_int_count() is executed\n"); |
| |
| put_user(ext_int_context->int_count, arg); |
| return 0; |
| } |
| |
| /*------------------------------------ General stuff ------------------------------------*/ |
| |
| static int me4000_get_user_info(struct me4000_user_info *arg, |
| struct me4000_info *board_info) |
| { |
| struct me4000_user_info user_info; |
| |
| CALL_PDEBUG("me4000_get_user_info() is executed\n"); |
| |
| user_info.board_count = board_info->board_count; |
| user_info.plx_regbase = board_info->plx_regbase; |
| user_info.plx_regbase_size = board_info->plx_regbase_size; |
| user_info.me4000_regbase = board_info->me4000_regbase; |
| user_info.me4000_regbase_size = board_info->me4000_regbase_size; |
| user_info.serial_no = board_info->serial_no; |
| user_info.hw_revision = board_info->hw_revision; |
| user_info.vendor_id = board_info->vendor_id; |
| user_info.device_id = board_info->device_id; |
| user_info.pci_bus_no = board_info->pci_bus_no; |
| user_info.pci_dev_no = board_info->pci_dev_no; |
| user_info.pci_func_no = board_info->pci_func_no; |
| user_info.irq = board_info->irq; |
| user_info.irq_count = board_info->irq_count; |
| user_info.driver_version = ME4000_DRIVER_VERSION; |
| user_info.ao_count = board_info->board_p->ao.count; |
| user_info.ao_fifo_count = board_info->board_p->ao.fifo_count; |
| |
| user_info.ai_count = board_info->board_p->ai.count; |
| user_info.ai_sh_count = board_info->board_p->ai.sh_count; |
| user_info.ai_ex_trig_analog = board_info->board_p->ai.ex_trig_analog; |
| |
| user_info.dio_count = board_info->board_p->dio.count; |
| |
| user_info.cnt_count = board_info->board_p->cnt.count; |
| |
| if (copy_to_user(arg, &user_info, sizeof(struct me4000_user_info))) |
| return -EFAULT; |
| |
| return 0; |
| } |
| |
| /*------------------------------------ ISR STUFF ------------------------------------*/ |
| |
| static int me4000_ext_int_fasync(int fd, struct file *file_ptr, int mode) |
| { |
| int result = 0; |
| struct me4000_ext_int_context *ext_int_context; |
| |
| CALL_PDEBUG("me4000_ext_int_fasync() is executed\n"); |
| |
| ext_int_context = file_ptr->private_data; |
| |
| result = |
| fasync_helper(fd, file_ptr, mode, &ext_int_context->fasync_ptr); |
| |
| CALL_PDEBUG("me4000_ext_int_fasync() is leaved\n"); |
| return result; |
| } |
| |
| static irqreturn_t me4000_ao_isr(int irq, void *dev_id) |
| { |
| u32 tmp; |
| u32 value; |
| struct me4000_ao_context *ao_context; |
| int i; |
| int c = 0; |
| int c1 = 0; |
| //unsigned long before; |
| //unsigned long after; |
| |
| ISR_PDEBUG("me4000_ao_isr() is executed\n"); |
| |
| ao_context = dev_id; |
| |
| /* Check if irq number is right */ |
| if (irq != ao_context->irq) { |
| ISR_PDEBUG("me4000_ao_isr():incorrect interrupt num: %d\n", |
| irq); |
| return IRQ_NONE; |
| } |
| |
| /* Check if this DAC rised an interrupt */ |
| if (! |
| ((0x1 << (ao_context->index + 3)) & |
| me4000_inl(ao_context->irq_status_reg))) { |
| ISR_PDEBUG("me4000_ao_isr():Not this DAC\n"); |
| return IRQ_NONE; |
| } |
| |
| /* Read status register to find out what happened */ |
| tmp = me4000_inl(ao_context->status_reg); |
| |
| if (!(tmp & ME4000_AO_STATUS_BIT_EF) && (tmp & ME4000_AO_STATUS_BIT_HF) |
| && (tmp & ME4000_AO_STATUS_BIT_HF)) { |
| c = ME4000_AO_FIFO_COUNT; |
| ISR_PDEBUG("me4000_ao_isr():Fifo empty\n"); |
| } else if ((tmp & ME4000_AO_STATUS_BIT_EF) |
| && (tmp & ME4000_AO_STATUS_BIT_HF) |
| && (tmp & ME4000_AO_STATUS_BIT_HF)) { |
| c = ME4000_AO_FIFO_COUNT / 2; |
| ISR_PDEBUG("me4000_ao_isr():Fifo under half full\n"); |
| } else { |
| c = 0; |
| ISR_PDEBUG("me4000_ao_isr():Fifo full\n"); |
| } |
| |
| ISR_PDEBUG("me4000_ao_isr():Try to write 0x%04X values\n", c); |
| |
| while (1) { |
| c1 = me4000_values_to_end(ao_context->circ_buf, |
| ME4000_AO_BUFFER_COUNT); |
| ISR_PDEBUG("me4000_ao_isr():Values to end = %d\n", c1); |
| if (c1 > c) |
| c1 = c; |
| |
| if (c1 <= 0) { |
| ISR_PDEBUG |
| ("me4000_ao_isr():Work done or buffer empty\n"); |
| break; |
| } |
| //rdtscl(before); |
| if (((ao_context->fifo_reg & 0xFF) == ME4000_AO_01_FIFO_REG) || |
| ((ao_context->fifo_reg & 0xFF) == ME4000_AO_03_FIFO_REG)) { |
| for (i = 0; i < c1; i++) { |
| value = |
| ((u32) |
| (* |
| (ao_context->circ_buf.buf + |
| ao_context->circ_buf.tail + i))) << 16; |
| outl(value, ao_context->fifo_reg); |
| } |
| } else |
| outsw(ao_context->fifo_reg, |
| ao_context->circ_buf.buf + |
| ao_context->circ_buf.tail, c1); |
| |
| //rdtscl(after); |
| //printk(KERN_ERR"ME4000:me4000_ao_isr():Time lapse = %lu\n", after - before); |
| |
| ao_context->circ_buf.tail = |
| (ao_context->circ_buf.tail + c1) & (ME4000_AO_BUFFER_COUNT - |
| 1); |
| ISR_PDEBUG("me4000_ao_isr():%d values wrote to port 0x%04X\n", |
| c1, ao_context->fifo_reg); |
| c -= c1; |
| } |
| |
| /* If there are no values left in the buffer, disable interrupts */ |
| spin_lock(&ao_context->int_lock); |
| if (!me4000_buf_count(ao_context->circ_buf, ME4000_AO_BUFFER_COUNT)) { |
| ISR_PDEBUG |
| ("me4000_ao_isr():Disable Interrupt because no values left in buffer\n"); |
| tmp = me4000_inl(ao_context->ctrl_reg); |
| tmp &= ~ME4000_AO_CTRL_BIT_ENABLE_IRQ; |
| me4000_outl(tmp, ao_context->ctrl_reg); |
| } |
| spin_unlock(&ao_context->int_lock); |
| |
| /* Reset the interrupt */ |
| spin_lock(&ao_context->int_lock); |
| tmp = me4000_inl(ao_context->ctrl_reg); |
| tmp |= ME4000_AO_CTRL_BIT_RESET_IRQ; |
| me4000_outl(tmp, ao_context->ctrl_reg); |
| tmp &= ~ME4000_AO_CTRL_BIT_RESET_IRQ; |
| me4000_outl(tmp, ao_context->ctrl_reg); |
| |
| /* If state machine is stopped, flow was interrupted */ |
| if (!(me4000_inl(ao_context->status_reg) & ME4000_AO_STATUS_BIT_FSM)) { |
| printk(KERN_ERR "ME4000:me4000_ao_isr():Broken pipe\n"); |
| ao_context->pipe_flag = 1; // Set flag in order to inform write routine |
| tmp &= ~ME4000_AO_CTRL_BIT_ENABLE_IRQ; // Disable interrupt |
| } |
| me4000_outl(tmp, ao_context->ctrl_reg); |
| spin_unlock(&ao_context->int_lock); |
| |
| /* Wake up waiting process */ |
| wake_up_interruptible(&(ao_context->wait_queue)); |
| |
| /* Count the interrupt */ |
| ao_context->board_info->irq_count++; |
| |
| return IRQ_HANDLED; |
| } |
| |
| static irqreturn_t me4000_ai_isr(int irq, void *dev_id) |
| { |
| u32 tmp; |
| struct me4000_ai_context *ai_context; |
| int i; |
| int c = 0; |
| int c1 = 0; |
| #ifdef ME4000_ISR_DEBUG |
| unsigned long before; |
| unsigned long after; |
| #endif |
| |
| ISR_PDEBUG("me4000_ai_isr() is executed\n"); |
| |
| #ifdef ME4000_ISR_DEBUG |
| rdtscl(before); |
| #endif |
| |
| ai_context = dev_id; |
| |
| /* Check if irq number is right */ |
| if (irq != ai_context->irq) { |
| ISR_PDEBUG("me4000_ai_isr():incorrect interrupt num: %d\n", |
| irq); |
| return IRQ_NONE; |
| } |
| |
| if (me4000_inl(ai_context->irq_status_reg) & |
| ME4000_IRQ_STATUS_BIT_AI_HF) { |
| ISR_PDEBUG |
| ("me4000_ai_isr():Fifo half full interrupt occured\n"); |
| |
| /* Read status register to find out what happened */ |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| |
| if (!(tmp & ME4000_AI_STATUS_BIT_FF_DATA) && |
| !(tmp & ME4000_AI_STATUS_BIT_HF_DATA) |
| && (tmp & ME4000_AI_STATUS_BIT_EF_DATA)) { |
| ISR_PDEBUG("me4000_ai_isr():Fifo full\n"); |
| c = ME4000_AI_FIFO_COUNT; |
| |
| /* FIFO overflow, so stop conversion and disable all interrupts */ |
| spin_lock(&ai_context->int_lock); |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP; |
| tmp &= |
| ~(ME4000_AI_CTRL_BIT_HF_IRQ | |
| ME4000_AI_CTRL_BIT_SC_IRQ); |
| outl(tmp, ai_context->ctrl_reg); |
| spin_unlock(&ai_context->int_lock); |
| } else if ((tmp & ME4000_AI_STATUS_BIT_FF_DATA) && |
| !(tmp & ME4000_AI_STATUS_BIT_HF_DATA) |
| && (tmp & ME4000_AI_STATUS_BIT_EF_DATA)) { |
| ISR_PDEBUG("me4000_ai_isr():Fifo half full\n"); |
| c = ME4000_AI_FIFO_COUNT / 2; |
| } else { |
| c = 0; |
| ISR_PDEBUG |
| ("me4000_ai_isr():Can't determine state of fifo\n"); |
| } |
| |
| ISR_PDEBUG("me4000_ai_isr():Try to read %d values\n", c); |
| |
| while (1) { |
| c1 = me4000_space_to_end(ai_context->circ_buf, |
| ME4000_AI_BUFFER_COUNT); |
| ISR_PDEBUG("me4000_ai_isr():Space to end = %d\n", c1); |
| if (c1 > c) |
| c1 = c; |
| |
| if (c1 <= 0) { |
| ISR_PDEBUG |
| ("me4000_ai_isr():Work done or buffer full\n"); |
| break; |
| } |
| |
| insw(ai_context->data_reg, |
| ai_context->circ_buf.buf + |
| ai_context->circ_buf.head, c1); |
| ai_context->circ_buf.head = |
| (ai_context->circ_buf.head + |
| c1) & (ME4000_AI_BUFFER_COUNT - 1); |
| c -= c1; |
| } |
| |
| /* Work is done, so reset the interrupt */ |
| ISR_PDEBUG |
| ("me4000_ai_isr():reset interrupt fifo half full interrupt\n"); |
| spin_lock(&ai_context->int_lock); |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| tmp |= ME4000_AI_CTRL_BIT_HF_IRQ_RESET; |
| me4000_outl(tmp, ai_context->ctrl_reg); |
| tmp &= ~ME4000_AI_CTRL_BIT_HF_IRQ_RESET; |
| me4000_outl(tmp, ai_context->ctrl_reg); |
| spin_unlock(&ai_context->int_lock); |
| } |
| |
| if (me4000_inl(ai_context->irq_status_reg) & ME4000_IRQ_STATUS_BIT_SC) { |
| ISR_PDEBUG |
| ("me4000_ai_isr():Sample counter interrupt occured\n"); |
| |
| if (!ai_context->sample_counter_reload) { |
| ISR_PDEBUG |
| ("me4000_ai_isr():Single data block available\n"); |
| |
| /* Poll data until fifo empty */ |
| for (i = 0; |
| (i < ME4000_AI_FIFO_COUNT / 2) |
| && (inl(ai_context->ctrl_reg) & |
| ME4000_AI_STATUS_BIT_EF_DATA); i++) { |
| if (me4000_space_to_end |
| (ai_context->circ_buf, |
| ME4000_AI_BUFFER_COUNT)) { |
| *(ai_context->circ_buf.buf + |
| ai_context->circ_buf.head) = |
| inw(ai_context->data_reg); |
| ai_context->circ_buf.head = |
| (ai_context->circ_buf.head + |
| 1) & (ME4000_AI_BUFFER_COUNT - 1); |
| } else |
| break; |
| } |
| ISR_PDEBUG("me4000_ai_isr():%d values read\n", i); |
| } else { |
| if (ai_context->sample_counter <= |
| ME4000_AI_FIFO_COUNT / 2) { |
| ISR_PDEBUG |
| ("me4000_ai_isr():Interrupt from adjustable half full threshold\n"); |
| |
| /* Read status register to find out what happened */ |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| |
| if (!(tmp & ME4000_AI_STATUS_BIT_FF_DATA) && |
| !(tmp & ME4000_AI_STATUS_BIT_HF_DATA) |
| && (tmp & ME4000_AI_STATUS_BIT_EF_DATA)) { |
| ISR_PDEBUG |
| ("me4000_ai_isr():Fifo full\n"); |
| c = ME4000_AI_FIFO_COUNT; |
| |
| /* FIFO overflow, so stop conversion */ |
| spin_lock(&ai_context->int_lock); |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| tmp |= |
| ME4000_AI_CTRL_BIT_IMMEDIATE_STOP; |
| outl(tmp, ai_context->ctrl_reg); |
| spin_unlock(&ai_context->int_lock); |
| } else if ((tmp & ME4000_AI_STATUS_BIT_FF_DATA) |
| && !(tmp & |
| ME4000_AI_STATUS_BIT_HF_DATA) |
| && (tmp & |
| ME4000_AI_STATUS_BIT_EF_DATA)) { |
| ISR_PDEBUG |
| ("me4000_ai_isr():Fifo half full\n"); |
| c = ME4000_AI_FIFO_COUNT / 2; |
| } else { |
| c = ai_context->sample_counter; |
| ISR_PDEBUG |
| ("me4000_ai_isr():Sample count values\n"); |
| } |
| |
| ISR_PDEBUG |
| ("me4000_ai_isr():Try to read %d values\n", |
| c); |
| |
| while (1) { |
| c1 = me4000_space_to_end(ai_context-> |
| circ_buf, |
| ME4000_AI_BUFFER_COUNT); |
| ISR_PDEBUG |
| ("me4000_ai_isr():Space to end = %d\n", |
| c1); |
| if (c1 > c) |
| c1 = c; |
| |
| if (c1 <= 0) { |
| ISR_PDEBUG |
| ("me4000_ai_isr():Work done or buffer full\n"); |
| break; |
| } |
| |
| insw(ai_context->data_reg, |
| ai_context->circ_buf.buf + |
| ai_context->circ_buf.head, c1); |
| ai_context->circ_buf.head = |
| (ai_context->circ_buf.head + |
| c1) & (ME4000_AI_BUFFER_COUNT - 1); |
| c -= c1; |
| } |
| } else { |
| ISR_PDEBUG |
| ("me4000_ai_isr():Multiple data block available\n"); |
| |
| /* Read status register to find out what happened */ |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| |
| if (!(tmp & ME4000_AI_STATUS_BIT_FF_DATA) && |
| !(tmp & ME4000_AI_STATUS_BIT_HF_DATA) |
| && (tmp & ME4000_AI_STATUS_BIT_EF_DATA)) { |
| ISR_PDEBUG |
| ("me4000_ai_isr():Fifo full\n"); |
| c = ME4000_AI_FIFO_COUNT; |
| |
| /* FIFO overflow, so stop conversion */ |
| spin_lock(&ai_context->int_lock); |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| tmp |= |
| ME4000_AI_CTRL_BIT_IMMEDIATE_STOP; |
| outl(tmp, ai_context->ctrl_reg); |
| spin_unlock(&ai_context->int_lock); |
| |
| while (1) { |
| c1 = me4000_space_to_end |
| (ai_context->circ_buf, |
| ME4000_AI_BUFFER_COUNT); |
| ISR_PDEBUG |
| ("me4000_ai_isr():Space to end = %d\n", |
| c1); |
| if (c1 > c) |
| c1 = c; |
| |
| if (c1 <= 0) { |
| ISR_PDEBUG |
| ("me4000_ai_isr():Work done or buffer full\n"); |
| break; |
| } |
| |
| insw(ai_context->data_reg, |
| ai_context->circ_buf.buf + |
| ai_context->circ_buf.head, |
| c1); |
| ai_context->circ_buf.head = |
| (ai_context->circ_buf.head + |
| c1) & |
| (ME4000_AI_BUFFER_COUNT - |
| 1); |
| c -= c1; |
| } |
| } else if ((tmp & ME4000_AI_STATUS_BIT_FF_DATA) |
| && !(tmp & |
| ME4000_AI_STATUS_BIT_HF_DATA) |
| && (tmp & |
| ME4000_AI_STATUS_BIT_EF_DATA)) { |
| ISR_PDEBUG |
| ("me4000_ai_isr():Fifo half full\n"); |
| c = ME4000_AI_FIFO_COUNT / 2; |
| |
| while (1) { |
| c1 = me4000_space_to_end |
| (ai_context->circ_buf, |
| ME4000_AI_BUFFER_COUNT); |
| ISR_PDEBUG |
| ("me4000_ai_isr():Space to end = %d\n", |
| c1); |
| if (c1 > c) |
| c1 = c; |
| |
| if (c1 <= 0) { |
| ISR_PDEBUG |
| ("me4000_ai_isr():Work done or buffer full\n"); |
| break; |
| } |
| |
| insw(ai_context->data_reg, |
| ai_context->circ_buf.buf + |
| ai_context->circ_buf.head, |
| c1); |
| ai_context->circ_buf.head = |
| (ai_context->circ_buf.head + |
| c1) & |
| (ME4000_AI_BUFFER_COUNT - |
| 1); |
| c -= c1; |
| } |
| } else { |
| /* Poll data until fifo empty */ |
| for (i = 0; |
| (i < ME4000_AI_FIFO_COUNT / 2) |
| && (inl(ai_context->ctrl_reg) & |
| ME4000_AI_STATUS_BIT_EF_DATA); |
| i++) { |
| if (me4000_space_to_end |
| (ai_context->circ_buf, |
| ME4000_AI_BUFFER_COUNT)) { |
| *(ai_context->circ_buf. |
| buf + |
| ai_context->circ_buf. |
| head) = |
| inw(ai_context->data_reg); |
| ai_context->circ_buf. |
| head = |
| (ai_context-> |
| circ_buf.head + |
| 1) & |
| (ME4000_AI_BUFFER_COUNT |
| - 1); |
| } else |
| break; |
| } |
| ISR_PDEBUG |
| ("me4000_ai_isr():%d values read\n", |
| i); |
| } |
| } |
| } |
| |
| /* Work is done, so reset the interrupt */ |
| ISR_PDEBUG |
| ("me4000_ai_isr():reset interrupt from sample counter\n"); |
| spin_lock(&ai_context->int_lock); |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| tmp |= ME4000_AI_CTRL_BIT_SC_IRQ_RESET; |
| me4000_outl(tmp, ai_context->ctrl_reg); |
| tmp &= ~ME4000_AI_CTRL_BIT_SC_IRQ_RESET; |
| me4000_outl(tmp, ai_context->ctrl_reg); |
| spin_unlock(&ai_context->int_lock); |
| } |
| |
| /* Values are now available, so wake up waiting process */ |
| if (me4000_buf_count(ai_context->circ_buf, ME4000_AI_BUFFER_COUNT)) { |
| ISR_PDEBUG("me4000_ai_isr():Wake up waiting process\n"); |
| wake_up_interruptible(&(ai_context->wait_queue)); |
| } |
| |
| /* If there is no space left in the buffer, disable interrupts */ |
| spin_lock(&ai_context->int_lock); |
| if (!me4000_buf_space(ai_context->circ_buf, ME4000_AI_BUFFER_COUNT)) { |
| ISR_PDEBUG |
| ("me4000_ai_isr():Disable Interrupt because no space left in buffer\n"); |
| tmp = me4000_inl(ai_context->ctrl_reg); |
| tmp &= |
| ~(ME4000_AI_CTRL_BIT_SC_IRQ | ME4000_AI_CTRL_BIT_HF_IRQ | |
| ME4000_AI_CTRL_BIT_LE_IRQ); |
| me4000_outl(tmp, ai_context->ctrl_reg); |
| } |
| spin_unlock(&ai_context->int_lock); |
| |
| #ifdef ME4000_ISR_DEBUG |
| rdtscl(after); |
| printk(KERN_ERR "ME4000:me4000_ai_isr():Time lapse = %lu\n", |
| after - before); |
| #endif |
| |
| return IRQ_HANDLED; |
| } |
| |
| static irqreturn_t me4000_ext_int_isr(int irq, void *dev_id) |
| { |
| struct me4000_ext_int_context *ext_int_context; |
| unsigned long tmp; |
| |
| ISR_PDEBUG("me4000_ext_int_isr() is executed\n"); |
| |
| ext_int_context = dev_id; |
| |
| /* Check if irq number is right */ |
| if (irq != ext_int_context->irq) { |
| ISR_PDEBUG("me4000_ext_int_isr():incorrect interrupt num: %d\n", |
| irq); |
| return IRQ_NONE; |
| } |
| |
| if (me4000_inl(ext_int_context->irq_status_reg) & |
| ME4000_IRQ_STATUS_BIT_EX) { |
| ISR_PDEBUG("me4000_ext_int_isr():External interrupt occured\n"); |
| tmp = me4000_inl(ext_int_context->ctrl_reg); |
| tmp |= ME4000_AI_CTRL_BIT_EX_IRQ_RESET; |
| me4000_outl(tmp, ext_int_context->ctrl_reg); |
| tmp &= ~ME4000_AI_CTRL_BIT_EX_IRQ_RESET; |
| me4000_outl(tmp, ext_int_context->ctrl_reg); |
| |
| ext_int_context->int_count++; |
| |
| if (ext_int_context->fasync_ptr) { |
| ISR_PDEBUG |
| ("me2600_ext_int_isr():Send signal to process\n"); |
| kill_fasync(&ext_int_context->fasync_ptr, SIGIO, |
| POLL_IN); |
| } |
| } |
| |
| return IRQ_HANDLED; |
| } |
| |
| static void __exit me4000_module_exit(void) |
| { |
| struct list_head *board_p; |
| struct me4000_info *board_info; |
| |
| CALL_PDEBUG("cleanup_module() is executed\n"); |
| |
| unregister_chrdev(me4000_ext_int_major_driver_no, ME4000_EXT_INT_NAME); |
| |
| unregister_chrdev(me4000_cnt_major_driver_no, ME4000_CNT_NAME); |
| |
| unregister_chrdev(me4000_dio_major_driver_no, ME4000_DIO_NAME); |
| |
| unregister_chrdev(me4000_ai_major_driver_no, ME4000_AI_NAME); |
| |
| unregister_chrdev(me4000_ao_major_driver_no, ME4000_AO_NAME); |
| |
| remove_proc_entry("me4000", NULL); |
| |
| pci_unregister_driver(&me4000_driver); |
| |
| /* Reset the boards */ |
| for (board_p = me4000_board_info_list.next; |
| board_p != &me4000_board_info_list; board_p = board_p->next) { |
| board_info = list_entry(board_p, struct me4000_info, list); |
| me4000_reset_board(board_info); |
| } |
| |
| clear_board_info_list(); |
| } |
| |
| module_exit(me4000_module_exit); |
| |
| static int me4000_read_procmem(char *buf, char **start, off_t offset, int count, |
| int *eof, void *data) |
| { |
| int len = 0; |
| int limit = count - 1000; |
| struct me4000_info *board_info; |
| struct list_head *ptr; |
| |
| len += sprintf(buf + len, "\nME4000 DRIVER VERSION %X.%X.%X\n\n", |
| (ME4000_DRIVER_VERSION & 0xFF0000) >> 16, |
| (ME4000_DRIVER_VERSION & 0xFF00) >> 8, |
| (ME4000_DRIVER_VERSION & 0xFF)); |
| |
| /* Search for the board context */ |
| for (ptr = me4000_board_info_list.next; |
| (ptr != &me4000_board_info_list) && (len < limit); |
| ptr = ptr->next) { |
| board_info = list_entry(ptr, struct me4000_info, list); |
| |
| len += |
| sprintf(buf + len, "Board number %d:\n", |
| board_info->board_count); |
| len += sprintf(buf + len, "---------------\n"); |
| len += |
| sprintf(buf + len, "PLX base register = 0x%lX\n", |
| board_info->plx_regbase); |
| len += |
| sprintf(buf + len, "PLX base register size = 0x%X\n", |
| (unsigned int)board_info->plx_regbase_size); |
| len += |
| sprintf(buf + len, "ME4000 base register = 0x%X\n", |
| (unsigned int)board_info->me4000_regbase); |
| len += |
| sprintf(buf + len, "ME4000 base register size = 0x%X\n", |
| (unsigned int)board_info->me4000_regbase_size); |
| len += |
| sprintf(buf + len, "Serial number = 0x%X\n", |
| board_info->serial_no); |
| len += |
| sprintf(buf + len, "Hardware revision = 0x%X\n", |
| board_info->hw_revision); |
| len += |
| sprintf(buf + len, "Vendor id = 0x%X\n", |
| board_info->vendor_id); |
| len += |
| sprintf(buf + len, "Device id = 0x%X\n", |
| board_info->device_id); |
| len += |
| sprintf(buf + len, "PCI bus number = %d\n", |
| board_info->pci_bus_no); |
| len += |
| sprintf(buf + len, "PCI device number = %d\n", |
| board_info->pci_dev_no); |
| len += |
| sprintf(buf + len, "PCI function number = %d\n", |
| board_info->pci_func_no); |
| len += sprintf(buf + len, "IRQ = %u\n", board_info->irq); |
| len += |
| sprintf(buf + len, |
| "Count of interrupts since module was loaded = %d\n", |
| board_info->irq_count); |
| |
| len += |
| sprintf(buf + len, "Count of analog outputs = %d\n", |
| board_info->board_p->ao.count); |
| len += |
| sprintf(buf + len, "Count of analog output fifos = %d\n", |
| board_info->board_p->ao.fifo_count); |
| |
| len += |
| sprintf(buf + len, "Count of analog inputs = %d\n", |
| board_info->board_p->ai.count); |
| len += |
| sprintf(buf + len, |
| "Count of sample and hold devices for analog input = %d\n", |
| board_info->board_p->ai.sh_count); |
| len += |
| sprintf(buf + len, |
| "Analog external trigger available for analog input = %d\n", |
| board_info->board_p->ai.ex_trig_analog); |
| |
| len += |
| sprintf(buf + len, "Count of digital ports = %d\n", |
| board_info->board_p->dio.count); |
| |
| len += |
| sprintf(buf + len, "Count of counter devices = %d\n", |
| board_info->board_p->cnt.count); |
| len += |
| sprintf(buf + len, "AI control register = 0x%08X\n", |
| inl(board_info->me4000_regbase + |
| ME4000_AI_CTRL_REG)); |
| |
| len += sprintf(buf + len, "AO 0 control register = 0x%08X\n", |
| inl(board_info->me4000_regbase + |
| ME4000_AO_00_CTRL_REG)); |
| len += |
| sprintf(buf + len, "AO 0 status register = 0x%08X\n", |
| inl(board_info->me4000_regbase + |
| ME4000_AO_00_STATUS_REG)); |
| len += |
| sprintf(buf + len, "AO 1 control register = 0x%08X\n", |
| inl(board_info->me4000_regbase + |
| ME4000_AO_01_CTRL_REG)); |
| len += |
| sprintf(buf + len, "AO 1 status register = 0x%08X\n", |
| inl(board_info->me4000_regbase + |
| ME4000_AO_01_STATUS_REG)); |
| len += |
| sprintf(buf + len, "AO 2 control register = 0x%08X\n", |
| inl(board_info->me4000_regbase + |
| ME4000_AO_02_CTRL_REG)); |
| len += |
| sprintf(buf + len, "AO 2 status register = 0x%08X\n", |
| inl(board_info->me4000_regbase + |
| ME4000_AO_02_STATUS_REG)); |
| len += |
| sprintf(buf + len, "AO 3 control register = 0x%08X\n", |
| inl(board_info->me4000_regbase + |
| ME4000_AO_03_CTRL_REG)); |
| len += |
| sprintf(buf + len, "AO 3 status register = 0x%08X\n", |
| inl(board_info->me4000_regbase + |
| ME4000_AO_03_STATUS_REG)); |
| } |
| |
| *eof = 1; |
| return len; |
| } |