162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * comedi/drivers/dt9812.c 462306a36Sopenharmony_ci * COMEDI driver for DataTranslation DT9812 USB module 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2005 Anders Blomdell <anders.blomdell@control.lth.se> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * COMEDI - Linux Control and Measurement Device Interface 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci/* 1262306a36Sopenharmony_ci * Driver: dt9812 1362306a36Sopenharmony_ci * Description: Data Translation DT9812 USB module 1462306a36Sopenharmony_ci * Devices: [Data Translation] DT9812 (dt9812) 1562306a36Sopenharmony_ci * Author: anders.blomdell@control.lth.se (Anders Blomdell) 1662306a36Sopenharmony_ci * Status: in development 1762306a36Sopenharmony_ci * Updated: Sun Nov 20 20:18:34 EST 2005 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * This driver works, but bulk transfers not implemented. Might be a 2062306a36Sopenharmony_ci * starting point for someone else. I found out too late that USB has 2162306a36Sopenharmony_ci * too high latencies (>1 ms) for my needs. 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * Nota Bene: 2662306a36Sopenharmony_ci * 1. All writes to command pipe has to be 32 bytes (ISP1181B SHRTP=0 ?) 2762306a36Sopenharmony_ci * 2. The DDK source (as of sep 2005) is in error regarding the 2862306a36Sopenharmony_ci * input MUX bits (example code says P4, but firmware schematics 2962306a36Sopenharmony_ci * says P1). 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include <linux/kernel.h> 3362306a36Sopenharmony_ci#include <linux/module.h> 3462306a36Sopenharmony_ci#include <linux/errno.h> 3562306a36Sopenharmony_ci#include <linux/slab.h> 3662306a36Sopenharmony_ci#include <linux/uaccess.h> 3762306a36Sopenharmony_ci#include <linux/comedi/comedi_usb.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define DT9812_DIAGS_BOARD_INFO_ADDR 0xFBFF 4062306a36Sopenharmony_ci#define DT9812_MAX_WRITE_CMD_PIPE_SIZE 32 4162306a36Sopenharmony_ci#define DT9812_MAX_READ_CMD_PIPE_SIZE 32 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* usb_bulk_msg() timeout in milliseconds */ 4462306a36Sopenharmony_ci#define DT9812_USB_TIMEOUT 1000 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* 4762306a36Sopenharmony_ci * See Silican Laboratories C8051F020/1/2/3 manual 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ci#define F020_SFR_P4 0x84 5062306a36Sopenharmony_ci#define F020_SFR_P1 0x90 5162306a36Sopenharmony_ci#define F020_SFR_P2 0xa0 5262306a36Sopenharmony_ci#define F020_SFR_P3 0xb0 5362306a36Sopenharmony_ci#define F020_SFR_AMX0CF 0xba 5462306a36Sopenharmony_ci#define F020_SFR_AMX0SL 0xbb 5562306a36Sopenharmony_ci#define F020_SFR_ADC0CF 0xbc 5662306a36Sopenharmony_ci#define F020_SFR_ADC0L 0xbe 5762306a36Sopenharmony_ci#define F020_SFR_ADC0H 0xbf 5862306a36Sopenharmony_ci#define F020_SFR_DAC0L 0xd2 5962306a36Sopenharmony_ci#define F020_SFR_DAC0H 0xd3 6062306a36Sopenharmony_ci#define F020_SFR_DAC0CN 0xd4 6162306a36Sopenharmony_ci#define F020_SFR_DAC1L 0xd5 6262306a36Sopenharmony_ci#define F020_SFR_DAC1H 0xd6 6362306a36Sopenharmony_ci#define F020_SFR_DAC1CN 0xd7 6462306a36Sopenharmony_ci#define F020_SFR_ADC0CN 0xe8 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define F020_MASK_ADC0CF_AMP0GN0 0x01 6762306a36Sopenharmony_ci#define F020_MASK_ADC0CF_AMP0GN1 0x02 6862306a36Sopenharmony_ci#define F020_MASK_ADC0CF_AMP0GN2 0x04 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define F020_MASK_ADC0CN_AD0EN 0x80 7162306a36Sopenharmony_ci#define F020_MASK_ADC0CN_AD0INT 0x20 7262306a36Sopenharmony_ci#define F020_MASK_ADC0CN_AD0BUSY 0x10 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#define F020_MASK_DACXCN_DACXEN 0x80 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cienum { 7762306a36Sopenharmony_ci /* A/D D/A DI DO CT */ 7862306a36Sopenharmony_ci DT9812_DEVID_DT9812_10, /* 8 2 8 8 1 +/- 10V */ 7962306a36Sopenharmony_ci DT9812_DEVID_DT9812_2PT5, /* 8 2 8 8 1 0-2.44V */ 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cienum dt9812_gain { 8362306a36Sopenharmony_ci DT9812_GAIN_0PT25 = 1, 8462306a36Sopenharmony_ci DT9812_GAIN_0PT5 = 2, 8562306a36Sopenharmony_ci DT9812_GAIN_1 = 4, 8662306a36Sopenharmony_ci DT9812_GAIN_2 = 8, 8762306a36Sopenharmony_ci DT9812_GAIN_4 = 16, 8862306a36Sopenharmony_ci DT9812_GAIN_8 = 32, 8962306a36Sopenharmony_ci DT9812_GAIN_16 = 64, 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cienum { 9362306a36Sopenharmony_ci DT9812_LEAST_USB_FIRMWARE_CMD_CODE = 0, 9462306a36Sopenharmony_ci /* Write Flash memory */ 9562306a36Sopenharmony_ci DT9812_W_FLASH_DATA = 0, 9662306a36Sopenharmony_ci /* Read Flash memory misc config info */ 9762306a36Sopenharmony_ci DT9812_R_FLASH_DATA = 1, 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* 10062306a36Sopenharmony_ci * Register read/write commands for processor 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* Read a single byte of USB memory */ 10462306a36Sopenharmony_ci DT9812_R_SINGLE_BYTE_REG = 2, 10562306a36Sopenharmony_ci /* Write a single byte of USB memory */ 10662306a36Sopenharmony_ci DT9812_W_SINGLE_BYTE_REG = 3, 10762306a36Sopenharmony_ci /* Multiple Reads of USB memory */ 10862306a36Sopenharmony_ci DT9812_R_MULTI_BYTE_REG = 4, 10962306a36Sopenharmony_ci /* Multiple Writes of USB memory */ 11062306a36Sopenharmony_ci DT9812_W_MULTI_BYTE_REG = 5, 11162306a36Sopenharmony_ci /* Read, (AND) with mask, OR value, then write (single) */ 11262306a36Sopenharmony_ci DT9812_RMW_SINGLE_BYTE_REG = 6, 11362306a36Sopenharmony_ci /* Read, (AND) with mask, OR value, then write (multiple) */ 11462306a36Sopenharmony_ci DT9812_RMW_MULTI_BYTE_REG = 7, 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* 11762306a36Sopenharmony_ci * Register read/write commands for SMBus 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* Read a single byte of SMBus */ 12162306a36Sopenharmony_ci DT9812_R_SINGLE_BYTE_SMBUS = 8, 12262306a36Sopenharmony_ci /* Write a single byte of SMBus */ 12362306a36Sopenharmony_ci DT9812_W_SINGLE_BYTE_SMBUS = 9, 12462306a36Sopenharmony_ci /* Multiple Reads of SMBus */ 12562306a36Sopenharmony_ci DT9812_R_MULTI_BYTE_SMBUS = 10, 12662306a36Sopenharmony_ci /* Multiple Writes of SMBus */ 12762306a36Sopenharmony_ci DT9812_W_MULTI_BYTE_SMBUS = 11, 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* 13062306a36Sopenharmony_ci * Register read/write commands for a device 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* Read a single byte of a device */ 13462306a36Sopenharmony_ci DT9812_R_SINGLE_BYTE_DEV = 12, 13562306a36Sopenharmony_ci /* Write a single byte of a device */ 13662306a36Sopenharmony_ci DT9812_W_SINGLE_BYTE_DEV = 13, 13762306a36Sopenharmony_ci /* Multiple Reads of a device */ 13862306a36Sopenharmony_ci DT9812_R_MULTI_BYTE_DEV = 14, 13962306a36Sopenharmony_ci /* Multiple Writes of a device */ 14062306a36Sopenharmony_ci DT9812_W_MULTI_BYTE_DEV = 15, 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* Not sure if we'll need this */ 14362306a36Sopenharmony_ci DT9812_W_DAC_THRESHOLD = 16, 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* Set interrupt on change mask */ 14662306a36Sopenharmony_ci DT9812_W_INT_ON_CHANGE_MASK = 17, 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* Write (or Clear) the CGL for the ADC */ 14962306a36Sopenharmony_ci DT9812_W_CGL = 18, 15062306a36Sopenharmony_ci /* Multiple Reads of USB memory */ 15162306a36Sopenharmony_ci DT9812_R_MULTI_BYTE_USBMEM = 19, 15262306a36Sopenharmony_ci /* Multiple Writes to USB memory */ 15362306a36Sopenharmony_ci DT9812_W_MULTI_BYTE_USBMEM = 20, 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* Issue a start command to a given subsystem */ 15662306a36Sopenharmony_ci DT9812_START_SUBSYSTEM = 21, 15762306a36Sopenharmony_ci /* Issue a stop command to a given subsystem */ 15862306a36Sopenharmony_ci DT9812_STOP_SUBSYSTEM = 22, 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* calibrate the board using CAL_POT_CMD */ 16162306a36Sopenharmony_ci DT9812_CALIBRATE_POT = 23, 16262306a36Sopenharmony_ci /* set the DAC FIFO size */ 16362306a36Sopenharmony_ci DT9812_W_DAC_FIFO_SIZE = 24, 16462306a36Sopenharmony_ci /* Write or Clear the CGL for the DAC */ 16562306a36Sopenharmony_ci DT9812_W_CGL_DAC = 25, 16662306a36Sopenharmony_ci /* Read a single value from a subsystem */ 16762306a36Sopenharmony_ci DT9812_R_SINGLE_VALUE_CMD = 26, 16862306a36Sopenharmony_ci /* Write a single value to a subsystem */ 16962306a36Sopenharmony_ci DT9812_W_SINGLE_VALUE_CMD = 27, 17062306a36Sopenharmony_ci /* Valid DT9812_USB_FIRMWARE_CMD_CODE's will be less than this number */ 17162306a36Sopenharmony_ci DT9812_MAX_USB_FIRMWARE_CMD_CODE, 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistruct dt9812_flash_data { 17562306a36Sopenharmony_ci __le16 numbytes; 17662306a36Sopenharmony_ci __le16 address; 17762306a36Sopenharmony_ci}; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci#define DT9812_MAX_NUM_MULTI_BYTE_RDS \ 18062306a36Sopenharmony_ci ((DT9812_MAX_WRITE_CMD_PIPE_SIZE - 4 - 1) / sizeof(u8)) 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistruct dt9812_read_multi { 18362306a36Sopenharmony_ci u8 count; 18462306a36Sopenharmony_ci u8 address[DT9812_MAX_NUM_MULTI_BYTE_RDS]; 18562306a36Sopenharmony_ci}; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistruct dt9812_write_byte { 18862306a36Sopenharmony_ci u8 address; 18962306a36Sopenharmony_ci u8 value; 19062306a36Sopenharmony_ci}; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci#define DT9812_MAX_NUM_MULTI_BYTE_WRTS \ 19362306a36Sopenharmony_ci ((DT9812_MAX_WRITE_CMD_PIPE_SIZE - 4 - 1) / \ 19462306a36Sopenharmony_ci sizeof(struct dt9812_write_byte)) 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistruct dt9812_write_multi { 19762306a36Sopenharmony_ci u8 count; 19862306a36Sopenharmony_ci struct dt9812_write_byte write[DT9812_MAX_NUM_MULTI_BYTE_WRTS]; 19962306a36Sopenharmony_ci}; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistruct dt9812_rmw_byte { 20262306a36Sopenharmony_ci u8 address; 20362306a36Sopenharmony_ci u8 and_mask; 20462306a36Sopenharmony_ci u8 or_value; 20562306a36Sopenharmony_ci}; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci#define DT9812_MAX_NUM_MULTI_BYTE_RMWS \ 20862306a36Sopenharmony_ci ((DT9812_MAX_WRITE_CMD_PIPE_SIZE - 4 - 1) / \ 20962306a36Sopenharmony_ci sizeof(struct dt9812_rmw_byte)) 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistruct dt9812_rmw_multi { 21262306a36Sopenharmony_ci u8 count; 21362306a36Sopenharmony_ci struct dt9812_rmw_byte rmw[DT9812_MAX_NUM_MULTI_BYTE_RMWS]; 21462306a36Sopenharmony_ci}; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistruct dt9812_usb_cmd { 21762306a36Sopenharmony_ci __le32 cmd; 21862306a36Sopenharmony_ci union { 21962306a36Sopenharmony_ci struct dt9812_flash_data flash_data_info; 22062306a36Sopenharmony_ci struct dt9812_read_multi read_multi_info; 22162306a36Sopenharmony_ci struct dt9812_write_multi write_multi_info; 22262306a36Sopenharmony_ci struct dt9812_rmw_multi rmw_multi_info; 22362306a36Sopenharmony_ci } u; 22462306a36Sopenharmony_ci}; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistruct dt9812_private { 22762306a36Sopenharmony_ci struct mutex mut; 22862306a36Sopenharmony_ci struct { 22962306a36Sopenharmony_ci __u8 addr; 23062306a36Sopenharmony_ci size_t size; 23162306a36Sopenharmony_ci } cmd_wr, cmd_rd; 23262306a36Sopenharmony_ci u16 device; 23362306a36Sopenharmony_ci}; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic int dt9812_read_info(struct comedi_device *dev, 23662306a36Sopenharmony_ci int offset, void *buf, size_t buf_size) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct usb_device *usb = comedi_to_usb_dev(dev); 23962306a36Sopenharmony_ci struct dt9812_private *devpriv = dev->private; 24062306a36Sopenharmony_ci struct dt9812_usb_cmd *cmd; 24162306a36Sopenharmony_ci size_t tbuf_size; 24262306a36Sopenharmony_ci int count, ret; 24362306a36Sopenharmony_ci void *tbuf; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci tbuf_size = max(sizeof(*cmd), buf_size); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci tbuf = kzalloc(tbuf_size, GFP_KERNEL); 24862306a36Sopenharmony_ci if (!tbuf) 24962306a36Sopenharmony_ci return -ENOMEM; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci cmd = tbuf; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci cmd->cmd = cpu_to_le32(DT9812_R_FLASH_DATA); 25462306a36Sopenharmony_ci cmd->u.flash_data_info.address = 25562306a36Sopenharmony_ci cpu_to_le16(DT9812_DIAGS_BOARD_INFO_ADDR + offset); 25662306a36Sopenharmony_ci cmd->u.flash_data_info.numbytes = cpu_to_le16(buf_size); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* DT9812 only responds to 32 byte writes!! */ 25962306a36Sopenharmony_ci ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr), 26062306a36Sopenharmony_ci cmd, sizeof(*cmd), &count, DT9812_USB_TIMEOUT); 26162306a36Sopenharmony_ci if (ret) 26262306a36Sopenharmony_ci goto out; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, devpriv->cmd_rd.addr), 26562306a36Sopenharmony_ci tbuf, buf_size, &count, DT9812_USB_TIMEOUT); 26662306a36Sopenharmony_ci if (!ret) { 26762306a36Sopenharmony_ci if (count == buf_size) 26862306a36Sopenharmony_ci memcpy(buf, tbuf, buf_size); 26962306a36Sopenharmony_ci else 27062306a36Sopenharmony_ci ret = -EREMOTEIO; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ciout: 27362306a36Sopenharmony_ci kfree(tbuf); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci return ret; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic int dt9812_read_multiple_registers(struct comedi_device *dev, 27962306a36Sopenharmony_ci int reg_count, u8 *address, 28062306a36Sopenharmony_ci u8 *value) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct usb_device *usb = comedi_to_usb_dev(dev); 28362306a36Sopenharmony_ci struct dt9812_private *devpriv = dev->private; 28462306a36Sopenharmony_ci struct dt9812_usb_cmd *cmd; 28562306a36Sopenharmony_ci int i, count, ret; 28662306a36Sopenharmony_ci size_t buf_size; 28762306a36Sopenharmony_ci void *buf; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci buf_size = max_t(size_t, sizeof(*cmd), reg_count); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci buf = kzalloc(buf_size, GFP_KERNEL); 29262306a36Sopenharmony_ci if (!buf) 29362306a36Sopenharmony_ci return -ENOMEM; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci cmd = buf; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci cmd->cmd = cpu_to_le32(DT9812_R_MULTI_BYTE_REG); 29862306a36Sopenharmony_ci cmd->u.read_multi_info.count = reg_count; 29962306a36Sopenharmony_ci for (i = 0; i < reg_count; i++) 30062306a36Sopenharmony_ci cmd->u.read_multi_info.address[i] = address[i]; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* DT9812 only responds to 32 byte writes!! */ 30362306a36Sopenharmony_ci ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr), 30462306a36Sopenharmony_ci cmd, sizeof(*cmd), &count, DT9812_USB_TIMEOUT); 30562306a36Sopenharmony_ci if (ret) 30662306a36Sopenharmony_ci goto out; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, devpriv->cmd_rd.addr), 30962306a36Sopenharmony_ci buf, reg_count, &count, DT9812_USB_TIMEOUT); 31062306a36Sopenharmony_ci if (!ret) { 31162306a36Sopenharmony_ci if (count == reg_count) 31262306a36Sopenharmony_ci memcpy(value, buf, reg_count); 31362306a36Sopenharmony_ci else 31462306a36Sopenharmony_ci ret = -EREMOTEIO; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ciout: 31762306a36Sopenharmony_ci kfree(buf); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci return ret; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic int dt9812_write_multiple_registers(struct comedi_device *dev, 32362306a36Sopenharmony_ci int reg_count, u8 *address, 32462306a36Sopenharmony_ci u8 *value) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct usb_device *usb = comedi_to_usb_dev(dev); 32762306a36Sopenharmony_ci struct dt9812_private *devpriv = dev->private; 32862306a36Sopenharmony_ci struct dt9812_usb_cmd *cmd; 32962306a36Sopenharmony_ci int i, count; 33062306a36Sopenharmony_ci int ret; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); 33362306a36Sopenharmony_ci if (!cmd) 33462306a36Sopenharmony_ci return -ENOMEM; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci cmd->cmd = cpu_to_le32(DT9812_W_MULTI_BYTE_REG); 33762306a36Sopenharmony_ci cmd->u.read_multi_info.count = reg_count; 33862306a36Sopenharmony_ci for (i = 0; i < reg_count; i++) { 33962306a36Sopenharmony_ci cmd->u.write_multi_info.write[i].address = address[i]; 34062306a36Sopenharmony_ci cmd->u.write_multi_info.write[i].value = value[i]; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* DT9812 only responds to 32 byte writes!! */ 34462306a36Sopenharmony_ci ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr), 34562306a36Sopenharmony_ci cmd, sizeof(*cmd), &count, DT9812_USB_TIMEOUT); 34662306a36Sopenharmony_ci kfree(cmd); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci return ret; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic int dt9812_rmw_multiple_registers(struct comedi_device *dev, 35262306a36Sopenharmony_ci int reg_count, 35362306a36Sopenharmony_ci struct dt9812_rmw_byte *rmw) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci struct usb_device *usb = comedi_to_usb_dev(dev); 35662306a36Sopenharmony_ci struct dt9812_private *devpriv = dev->private; 35762306a36Sopenharmony_ci struct dt9812_usb_cmd *cmd; 35862306a36Sopenharmony_ci int i, count; 35962306a36Sopenharmony_ci int ret; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); 36262306a36Sopenharmony_ci if (!cmd) 36362306a36Sopenharmony_ci return -ENOMEM; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci cmd->cmd = cpu_to_le32(DT9812_RMW_MULTI_BYTE_REG); 36662306a36Sopenharmony_ci cmd->u.rmw_multi_info.count = reg_count; 36762306a36Sopenharmony_ci for (i = 0; i < reg_count; i++) 36862306a36Sopenharmony_ci cmd->u.rmw_multi_info.rmw[i] = rmw[i]; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* DT9812 only responds to 32 byte writes!! */ 37162306a36Sopenharmony_ci ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr), 37262306a36Sopenharmony_ci cmd, sizeof(*cmd), &count, DT9812_USB_TIMEOUT); 37362306a36Sopenharmony_ci kfree(cmd); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci return ret; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic int dt9812_digital_in(struct comedi_device *dev, u8 *bits) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci struct dt9812_private *devpriv = dev->private; 38162306a36Sopenharmony_ci u8 reg[2] = { F020_SFR_P3, F020_SFR_P1 }; 38262306a36Sopenharmony_ci u8 value[2]; 38362306a36Sopenharmony_ci int ret; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci mutex_lock(&devpriv->mut); 38662306a36Sopenharmony_ci ret = dt9812_read_multiple_registers(dev, 2, reg, value); 38762306a36Sopenharmony_ci if (ret == 0) { 38862306a36Sopenharmony_ci /* 38962306a36Sopenharmony_ci * bits 0-6 in F020_SFR_P3 are bits 0-6 in the digital 39062306a36Sopenharmony_ci * input port bit 3 in F020_SFR_P1 is bit 7 in the 39162306a36Sopenharmony_ci * digital input port 39262306a36Sopenharmony_ci */ 39362306a36Sopenharmony_ci *bits = (value[0] & 0x7f) | ((value[1] & 0x08) << 4); 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci mutex_unlock(&devpriv->mut); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci return ret; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic int dt9812_digital_out(struct comedi_device *dev, u8 bits) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci struct dt9812_private *devpriv = dev->private; 40362306a36Sopenharmony_ci u8 reg[1] = { F020_SFR_P2 }; 40462306a36Sopenharmony_ci u8 value[1] = { bits }; 40562306a36Sopenharmony_ci int ret; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci mutex_lock(&devpriv->mut); 40862306a36Sopenharmony_ci ret = dt9812_write_multiple_registers(dev, 1, reg, value); 40962306a36Sopenharmony_ci mutex_unlock(&devpriv->mut); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci return ret; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic void dt9812_configure_mux(struct comedi_device *dev, 41562306a36Sopenharmony_ci struct dt9812_rmw_byte *rmw, int channel) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct dt9812_private *devpriv = dev->private; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (devpriv->device == DT9812_DEVID_DT9812_10) { 42062306a36Sopenharmony_ci /* In the DT9812/10V MUX is selected by P1.5-7 */ 42162306a36Sopenharmony_ci rmw->address = F020_SFR_P1; 42262306a36Sopenharmony_ci rmw->and_mask = 0xe0; 42362306a36Sopenharmony_ci rmw->or_value = channel << 5; 42462306a36Sopenharmony_ci } else { 42562306a36Sopenharmony_ci /* In the DT9812/2.5V, internal mux is selected by bits 0:2 */ 42662306a36Sopenharmony_ci rmw->address = F020_SFR_AMX0SL; 42762306a36Sopenharmony_ci rmw->and_mask = 0xff; 42862306a36Sopenharmony_ci rmw->or_value = channel & 0x07; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic void dt9812_configure_gain(struct comedi_device *dev, 43362306a36Sopenharmony_ci struct dt9812_rmw_byte *rmw, 43462306a36Sopenharmony_ci enum dt9812_gain gain) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct dt9812_private *devpriv = dev->private; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* In the DT9812/10V, there is an external gain of 0.5 */ 43962306a36Sopenharmony_ci if (devpriv->device == DT9812_DEVID_DT9812_10) 44062306a36Sopenharmony_ci gain <<= 1; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci rmw->address = F020_SFR_ADC0CF; 44362306a36Sopenharmony_ci rmw->and_mask = F020_MASK_ADC0CF_AMP0GN2 | 44462306a36Sopenharmony_ci F020_MASK_ADC0CF_AMP0GN1 | 44562306a36Sopenharmony_ci F020_MASK_ADC0CF_AMP0GN0; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci switch (gain) { 44862306a36Sopenharmony_ci /* 44962306a36Sopenharmony_ci * 000 -> Gain = 1 45062306a36Sopenharmony_ci * 001 -> Gain = 2 45162306a36Sopenharmony_ci * 010 -> Gain = 4 45262306a36Sopenharmony_ci * 011 -> Gain = 8 45362306a36Sopenharmony_ci * 10x -> Gain = 16 45462306a36Sopenharmony_ci * 11x -> Gain = 0.5 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_ci case DT9812_GAIN_0PT5: 45762306a36Sopenharmony_ci rmw->or_value = F020_MASK_ADC0CF_AMP0GN2 | 45862306a36Sopenharmony_ci F020_MASK_ADC0CF_AMP0GN1; 45962306a36Sopenharmony_ci break; 46062306a36Sopenharmony_ci default: 46162306a36Sopenharmony_ci /* this should never happen, just use a gain of 1 */ 46262306a36Sopenharmony_ci case DT9812_GAIN_1: 46362306a36Sopenharmony_ci rmw->or_value = 0x00; 46462306a36Sopenharmony_ci break; 46562306a36Sopenharmony_ci case DT9812_GAIN_2: 46662306a36Sopenharmony_ci rmw->or_value = F020_MASK_ADC0CF_AMP0GN0; 46762306a36Sopenharmony_ci break; 46862306a36Sopenharmony_ci case DT9812_GAIN_4: 46962306a36Sopenharmony_ci rmw->or_value = F020_MASK_ADC0CF_AMP0GN1; 47062306a36Sopenharmony_ci break; 47162306a36Sopenharmony_ci case DT9812_GAIN_8: 47262306a36Sopenharmony_ci rmw->or_value = F020_MASK_ADC0CF_AMP0GN1 | 47362306a36Sopenharmony_ci F020_MASK_ADC0CF_AMP0GN0; 47462306a36Sopenharmony_ci break; 47562306a36Sopenharmony_ci case DT9812_GAIN_16: 47662306a36Sopenharmony_ci rmw->or_value = F020_MASK_ADC0CF_AMP0GN2; 47762306a36Sopenharmony_ci break; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic int dt9812_analog_in(struct comedi_device *dev, 48262306a36Sopenharmony_ci int channel, u16 *value, enum dt9812_gain gain) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci struct dt9812_private *devpriv = dev->private; 48562306a36Sopenharmony_ci struct dt9812_rmw_byte rmw[3]; 48662306a36Sopenharmony_ci u8 reg[3] = { 48762306a36Sopenharmony_ci F020_SFR_ADC0CN, 48862306a36Sopenharmony_ci F020_SFR_ADC0H, 48962306a36Sopenharmony_ci F020_SFR_ADC0L 49062306a36Sopenharmony_ci }; 49162306a36Sopenharmony_ci u8 val[3]; 49262306a36Sopenharmony_ci int ret; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci mutex_lock(&devpriv->mut); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* 1 select the gain */ 49762306a36Sopenharmony_ci dt9812_configure_gain(dev, &rmw[0], gain); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci /* 2 set the MUX to select the channel */ 50062306a36Sopenharmony_ci dt9812_configure_mux(dev, &rmw[1], channel); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci /* 3 start conversion */ 50362306a36Sopenharmony_ci rmw[2].address = F020_SFR_ADC0CN; 50462306a36Sopenharmony_ci rmw[2].and_mask = 0xff; 50562306a36Sopenharmony_ci rmw[2].or_value = F020_MASK_ADC0CN_AD0EN | F020_MASK_ADC0CN_AD0BUSY; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci ret = dt9812_rmw_multiple_registers(dev, 3, rmw); 50862306a36Sopenharmony_ci if (ret) 50962306a36Sopenharmony_ci goto exit; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci /* read the status and ADC */ 51262306a36Sopenharmony_ci ret = dt9812_read_multiple_registers(dev, 3, reg, val); 51362306a36Sopenharmony_ci if (ret) 51462306a36Sopenharmony_ci goto exit; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* 51762306a36Sopenharmony_ci * An ADC conversion takes 16 SAR clocks cycles, i.e. about 9us. 51862306a36Sopenharmony_ci * Therefore, between the instant that AD0BUSY was set via 51962306a36Sopenharmony_ci * dt9812_rmw_multiple_registers and the read of AD0BUSY via 52062306a36Sopenharmony_ci * dt9812_read_multiple_registers, the conversion should be complete 52162306a36Sopenharmony_ci * since these two operations require two USB transactions each taking 52262306a36Sopenharmony_ci * at least a millisecond to complete. However, lets make sure that 52362306a36Sopenharmony_ci * conversion is finished. 52462306a36Sopenharmony_ci */ 52562306a36Sopenharmony_ci if ((val[0] & (F020_MASK_ADC0CN_AD0INT | F020_MASK_ADC0CN_AD0BUSY)) == 52662306a36Sopenharmony_ci F020_MASK_ADC0CN_AD0INT) { 52762306a36Sopenharmony_ci switch (devpriv->device) { 52862306a36Sopenharmony_ci case DT9812_DEVID_DT9812_10: 52962306a36Sopenharmony_ci /* 53062306a36Sopenharmony_ci * For DT9812-10V the personality module set the 53162306a36Sopenharmony_ci * encoding to 2's complement. Hence, convert it before 53262306a36Sopenharmony_ci * returning it 53362306a36Sopenharmony_ci */ 53462306a36Sopenharmony_ci *value = ((val[1] << 8) | val[2]) + 0x800; 53562306a36Sopenharmony_ci break; 53662306a36Sopenharmony_ci case DT9812_DEVID_DT9812_2PT5: 53762306a36Sopenharmony_ci *value = (val[1] << 8) | val[2]; 53862306a36Sopenharmony_ci break; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ciexit: 54362306a36Sopenharmony_ci mutex_unlock(&devpriv->mut); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci return ret; 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic int dt9812_analog_out(struct comedi_device *dev, int channel, u16 value) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci struct dt9812_private *devpriv = dev->private; 55162306a36Sopenharmony_ci struct dt9812_rmw_byte rmw[3]; 55262306a36Sopenharmony_ci int ret; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci mutex_lock(&devpriv->mut); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci switch (channel) { 55762306a36Sopenharmony_ci case 0: 55862306a36Sopenharmony_ci /* 1. Set DAC mode */ 55962306a36Sopenharmony_ci rmw[0].address = F020_SFR_DAC0CN; 56062306a36Sopenharmony_ci rmw[0].and_mask = 0xff; 56162306a36Sopenharmony_ci rmw[0].or_value = F020_MASK_DACXCN_DACXEN; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* 2. load lsb of DAC value first */ 56462306a36Sopenharmony_ci rmw[1].address = F020_SFR_DAC0L; 56562306a36Sopenharmony_ci rmw[1].and_mask = 0xff; 56662306a36Sopenharmony_ci rmw[1].or_value = value & 0xff; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci /* 3. load msb of DAC value next to latch the 12-bit value */ 56962306a36Sopenharmony_ci rmw[2].address = F020_SFR_DAC0H; 57062306a36Sopenharmony_ci rmw[2].and_mask = 0xff; 57162306a36Sopenharmony_ci rmw[2].or_value = (value >> 8) & 0xf; 57262306a36Sopenharmony_ci break; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci case 1: 57562306a36Sopenharmony_ci /* 1. Set DAC mode */ 57662306a36Sopenharmony_ci rmw[0].address = F020_SFR_DAC1CN; 57762306a36Sopenharmony_ci rmw[0].and_mask = 0xff; 57862306a36Sopenharmony_ci rmw[0].or_value = F020_MASK_DACXCN_DACXEN; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* 2. load lsb of DAC value first */ 58162306a36Sopenharmony_ci rmw[1].address = F020_SFR_DAC1L; 58262306a36Sopenharmony_ci rmw[1].and_mask = 0xff; 58362306a36Sopenharmony_ci rmw[1].or_value = value & 0xff; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* 3. load msb of DAC value next to latch the 12-bit value */ 58662306a36Sopenharmony_ci rmw[2].address = F020_SFR_DAC1H; 58762306a36Sopenharmony_ci rmw[2].and_mask = 0xff; 58862306a36Sopenharmony_ci rmw[2].or_value = (value >> 8) & 0xf; 58962306a36Sopenharmony_ci break; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci ret = dt9812_rmw_multiple_registers(dev, 3, rmw); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci mutex_unlock(&devpriv->mut); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci return ret; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic int dt9812_di_insn_bits(struct comedi_device *dev, 59962306a36Sopenharmony_ci struct comedi_subdevice *s, 60062306a36Sopenharmony_ci struct comedi_insn *insn, 60162306a36Sopenharmony_ci unsigned int *data) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci u8 bits = 0; 60462306a36Sopenharmony_ci int ret; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci ret = dt9812_digital_in(dev, &bits); 60762306a36Sopenharmony_ci if (ret) 60862306a36Sopenharmony_ci return ret; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci data[1] = bits; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci return insn->n; 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic int dt9812_do_insn_bits(struct comedi_device *dev, 61662306a36Sopenharmony_ci struct comedi_subdevice *s, 61762306a36Sopenharmony_ci struct comedi_insn *insn, 61862306a36Sopenharmony_ci unsigned int *data) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci if (comedi_dio_update_state(s, data)) 62162306a36Sopenharmony_ci dt9812_digital_out(dev, s->state); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci data[1] = s->state; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci return insn->n; 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic int dt9812_ai_insn_read(struct comedi_device *dev, 62962306a36Sopenharmony_ci struct comedi_subdevice *s, 63062306a36Sopenharmony_ci struct comedi_insn *insn, 63162306a36Sopenharmony_ci unsigned int *data) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci unsigned int chan = CR_CHAN(insn->chanspec); 63462306a36Sopenharmony_ci u16 val = 0; 63562306a36Sopenharmony_ci int ret; 63662306a36Sopenharmony_ci int i; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci for (i = 0; i < insn->n; i++) { 63962306a36Sopenharmony_ci ret = dt9812_analog_in(dev, chan, &val, DT9812_GAIN_1); 64062306a36Sopenharmony_ci if (ret) 64162306a36Sopenharmony_ci return ret; 64262306a36Sopenharmony_ci data[i] = val; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci return insn->n; 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistatic int dt9812_ao_insn_read(struct comedi_device *dev, 64962306a36Sopenharmony_ci struct comedi_subdevice *s, 65062306a36Sopenharmony_ci struct comedi_insn *insn, 65162306a36Sopenharmony_ci unsigned int *data) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci struct dt9812_private *devpriv = dev->private; 65462306a36Sopenharmony_ci int ret; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci mutex_lock(&devpriv->mut); 65762306a36Sopenharmony_ci ret = comedi_readback_insn_read(dev, s, insn, data); 65862306a36Sopenharmony_ci mutex_unlock(&devpriv->mut); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci return ret; 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_cistatic int dt9812_ao_insn_write(struct comedi_device *dev, 66462306a36Sopenharmony_ci struct comedi_subdevice *s, 66562306a36Sopenharmony_ci struct comedi_insn *insn, 66662306a36Sopenharmony_ci unsigned int *data) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci unsigned int chan = CR_CHAN(insn->chanspec); 66962306a36Sopenharmony_ci int i; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci for (i = 0; i < insn->n; i++) { 67262306a36Sopenharmony_ci unsigned int val = data[i]; 67362306a36Sopenharmony_ci int ret; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci ret = dt9812_analog_out(dev, chan, val); 67662306a36Sopenharmony_ci if (ret) 67762306a36Sopenharmony_ci return ret; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci s->readback[chan] = val; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci return insn->n; 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_cistatic int dt9812_find_endpoints(struct comedi_device *dev) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci struct usb_interface *intf = comedi_to_usb_interface(dev); 68862306a36Sopenharmony_ci struct usb_host_interface *host = intf->cur_altsetting; 68962306a36Sopenharmony_ci struct dt9812_private *devpriv = dev->private; 69062306a36Sopenharmony_ci struct usb_endpoint_descriptor *ep; 69162306a36Sopenharmony_ci int i; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (host->desc.bNumEndpoints != 5) { 69462306a36Sopenharmony_ci dev_err(dev->class_dev, "Wrong number of endpoints\n"); 69562306a36Sopenharmony_ci return -ENODEV; 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci for (i = 0; i < host->desc.bNumEndpoints; ++i) { 69962306a36Sopenharmony_ci int dir = -1; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci ep = &host->endpoint[i].desc; 70262306a36Sopenharmony_ci switch (i) { 70362306a36Sopenharmony_ci case 0: 70462306a36Sopenharmony_ci /* unused message pipe */ 70562306a36Sopenharmony_ci dir = USB_DIR_IN; 70662306a36Sopenharmony_ci break; 70762306a36Sopenharmony_ci case 1: 70862306a36Sopenharmony_ci dir = USB_DIR_OUT; 70962306a36Sopenharmony_ci devpriv->cmd_wr.addr = ep->bEndpointAddress; 71062306a36Sopenharmony_ci devpriv->cmd_wr.size = usb_endpoint_maxp(ep); 71162306a36Sopenharmony_ci break; 71262306a36Sopenharmony_ci case 2: 71362306a36Sopenharmony_ci dir = USB_DIR_IN; 71462306a36Sopenharmony_ci devpriv->cmd_rd.addr = ep->bEndpointAddress; 71562306a36Sopenharmony_ci devpriv->cmd_rd.size = usb_endpoint_maxp(ep); 71662306a36Sopenharmony_ci break; 71762306a36Sopenharmony_ci case 3: 71862306a36Sopenharmony_ci /* unused write stream */ 71962306a36Sopenharmony_ci dir = USB_DIR_OUT; 72062306a36Sopenharmony_ci break; 72162306a36Sopenharmony_ci case 4: 72262306a36Sopenharmony_ci /* unused read stream */ 72362306a36Sopenharmony_ci dir = USB_DIR_IN; 72462306a36Sopenharmony_ci break; 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci if ((ep->bEndpointAddress & USB_DIR_IN) != dir) { 72762306a36Sopenharmony_ci dev_err(dev->class_dev, 72862306a36Sopenharmony_ci "Endpoint has wrong direction\n"); 72962306a36Sopenharmony_ci return -ENODEV; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci return 0; 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_cistatic int dt9812_reset_device(struct comedi_device *dev) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci struct usb_device *usb = comedi_to_usb_dev(dev); 73862306a36Sopenharmony_ci struct dt9812_private *devpriv = dev->private; 73962306a36Sopenharmony_ci u32 serial; 74062306a36Sopenharmony_ci u16 vendor; 74162306a36Sopenharmony_ci u16 product; 74262306a36Sopenharmony_ci u8 tmp8; 74362306a36Sopenharmony_ci __le16 tmp16; 74462306a36Sopenharmony_ci __le32 tmp32; 74562306a36Sopenharmony_ci int ret; 74662306a36Sopenharmony_ci int i; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci ret = dt9812_read_info(dev, 0, &tmp8, sizeof(tmp8)); 74962306a36Sopenharmony_ci if (ret) { 75062306a36Sopenharmony_ci /* 75162306a36Sopenharmony_ci * Seems like a configuration reset is necessary if driver is 75262306a36Sopenharmony_ci * reloaded while device is attached 75362306a36Sopenharmony_ci */ 75462306a36Sopenharmony_ci usb_reset_configuration(usb); 75562306a36Sopenharmony_ci for (i = 0; i < 10; i++) { 75662306a36Sopenharmony_ci ret = dt9812_read_info(dev, 1, &tmp8, sizeof(tmp8)); 75762306a36Sopenharmony_ci if (ret == 0) 75862306a36Sopenharmony_ci break; 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci if (ret) { 76162306a36Sopenharmony_ci dev_err(dev->class_dev, 76262306a36Sopenharmony_ci "unable to reset configuration\n"); 76362306a36Sopenharmony_ci return ret; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci ret = dt9812_read_info(dev, 1, &tmp16, sizeof(tmp16)); 76862306a36Sopenharmony_ci if (ret) { 76962306a36Sopenharmony_ci dev_err(dev->class_dev, "failed to read vendor id\n"); 77062306a36Sopenharmony_ci return ret; 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci vendor = le16_to_cpu(tmp16); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci ret = dt9812_read_info(dev, 3, &tmp16, sizeof(tmp16)); 77562306a36Sopenharmony_ci if (ret) { 77662306a36Sopenharmony_ci dev_err(dev->class_dev, "failed to read product id\n"); 77762306a36Sopenharmony_ci return ret; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci product = le16_to_cpu(tmp16); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci ret = dt9812_read_info(dev, 5, &tmp16, sizeof(tmp16)); 78262306a36Sopenharmony_ci if (ret) { 78362306a36Sopenharmony_ci dev_err(dev->class_dev, "failed to read device id\n"); 78462306a36Sopenharmony_ci return ret; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci devpriv->device = le16_to_cpu(tmp16); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci ret = dt9812_read_info(dev, 7, &tmp32, sizeof(tmp32)); 78962306a36Sopenharmony_ci if (ret) { 79062306a36Sopenharmony_ci dev_err(dev->class_dev, "failed to read serial number\n"); 79162306a36Sopenharmony_ci return ret; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci serial = le32_to_cpu(tmp32); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci /* let the user know what node this device is now attached to */ 79662306a36Sopenharmony_ci dev_info(dev->class_dev, "USB DT9812 (%4.4x.%4.4x.%4.4x) #0x%8.8x\n", 79762306a36Sopenharmony_ci vendor, product, devpriv->device, serial); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci if (devpriv->device != DT9812_DEVID_DT9812_10 && 80062306a36Sopenharmony_ci devpriv->device != DT9812_DEVID_DT9812_2PT5) { 80162306a36Sopenharmony_ci dev_err(dev->class_dev, "Unsupported device!\n"); 80262306a36Sopenharmony_ci return -EINVAL; 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci return 0; 80662306a36Sopenharmony_ci} 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_cistatic int dt9812_auto_attach(struct comedi_device *dev, 80962306a36Sopenharmony_ci unsigned long context) 81062306a36Sopenharmony_ci{ 81162306a36Sopenharmony_ci struct usb_interface *intf = comedi_to_usb_interface(dev); 81262306a36Sopenharmony_ci struct dt9812_private *devpriv; 81362306a36Sopenharmony_ci struct comedi_subdevice *s; 81462306a36Sopenharmony_ci bool is_unipolar; 81562306a36Sopenharmony_ci int ret; 81662306a36Sopenharmony_ci int i; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 81962306a36Sopenharmony_ci if (!devpriv) 82062306a36Sopenharmony_ci return -ENOMEM; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci mutex_init(&devpriv->mut); 82362306a36Sopenharmony_ci usb_set_intfdata(intf, devpriv); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci ret = dt9812_find_endpoints(dev); 82662306a36Sopenharmony_ci if (ret) 82762306a36Sopenharmony_ci return ret; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci ret = dt9812_reset_device(dev); 83062306a36Sopenharmony_ci if (ret) 83162306a36Sopenharmony_ci return ret; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci is_unipolar = (devpriv->device == DT9812_DEVID_DT9812_2PT5); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci ret = comedi_alloc_subdevices(dev, 4); 83662306a36Sopenharmony_ci if (ret) 83762306a36Sopenharmony_ci return ret; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci /* Digital Input subdevice */ 84062306a36Sopenharmony_ci s = &dev->subdevices[0]; 84162306a36Sopenharmony_ci s->type = COMEDI_SUBD_DI; 84262306a36Sopenharmony_ci s->subdev_flags = SDF_READABLE; 84362306a36Sopenharmony_ci s->n_chan = 8; 84462306a36Sopenharmony_ci s->maxdata = 1; 84562306a36Sopenharmony_ci s->range_table = &range_digital; 84662306a36Sopenharmony_ci s->insn_bits = dt9812_di_insn_bits; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci /* Digital Output subdevice */ 84962306a36Sopenharmony_ci s = &dev->subdevices[1]; 85062306a36Sopenharmony_ci s->type = COMEDI_SUBD_DO; 85162306a36Sopenharmony_ci s->subdev_flags = SDF_WRITABLE; 85262306a36Sopenharmony_ci s->n_chan = 8; 85362306a36Sopenharmony_ci s->maxdata = 1; 85462306a36Sopenharmony_ci s->range_table = &range_digital; 85562306a36Sopenharmony_ci s->insn_bits = dt9812_do_insn_bits; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci /* Analog Input subdevice */ 85862306a36Sopenharmony_ci s = &dev->subdevices[2]; 85962306a36Sopenharmony_ci s->type = COMEDI_SUBD_AI; 86062306a36Sopenharmony_ci s->subdev_flags = SDF_READABLE | SDF_GROUND; 86162306a36Sopenharmony_ci s->n_chan = 8; 86262306a36Sopenharmony_ci s->maxdata = 0x0fff; 86362306a36Sopenharmony_ci s->range_table = is_unipolar ? &range_unipolar2_5 : &range_bipolar10; 86462306a36Sopenharmony_ci s->insn_read = dt9812_ai_insn_read; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci /* Analog Output subdevice */ 86762306a36Sopenharmony_ci s = &dev->subdevices[3]; 86862306a36Sopenharmony_ci s->type = COMEDI_SUBD_AO; 86962306a36Sopenharmony_ci s->subdev_flags = SDF_WRITABLE; 87062306a36Sopenharmony_ci s->n_chan = 2; 87162306a36Sopenharmony_ci s->maxdata = 0x0fff; 87262306a36Sopenharmony_ci s->range_table = is_unipolar ? &range_unipolar2_5 : &range_bipolar10; 87362306a36Sopenharmony_ci s->insn_write = dt9812_ao_insn_write; 87462306a36Sopenharmony_ci s->insn_read = dt9812_ao_insn_read; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci ret = comedi_alloc_subdev_readback(s); 87762306a36Sopenharmony_ci if (ret) 87862306a36Sopenharmony_ci return ret; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci for (i = 0; i < s->n_chan; i++) 88162306a36Sopenharmony_ci s->readback[i] = is_unipolar ? 0x0000 : 0x0800; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci return 0; 88462306a36Sopenharmony_ci} 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_cistatic void dt9812_detach(struct comedi_device *dev) 88762306a36Sopenharmony_ci{ 88862306a36Sopenharmony_ci struct usb_interface *intf = comedi_to_usb_interface(dev); 88962306a36Sopenharmony_ci struct dt9812_private *devpriv = dev->private; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci if (!devpriv) 89262306a36Sopenharmony_ci return; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci mutex_destroy(&devpriv->mut); 89562306a36Sopenharmony_ci usb_set_intfdata(intf, NULL); 89662306a36Sopenharmony_ci} 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_cistatic struct comedi_driver dt9812_driver = { 89962306a36Sopenharmony_ci .driver_name = "dt9812", 90062306a36Sopenharmony_ci .module = THIS_MODULE, 90162306a36Sopenharmony_ci .auto_attach = dt9812_auto_attach, 90262306a36Sopenharmony_ci .detach = dt9812_detach, 90362306a36Sopenharmony_ci}; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_cistatic int dt9812_usb_probe(struct usb_interface *intf, 90662306a36Sopenharmony_ci const struct usb_device_id *id) 90762306a36Sopenharmony_ci{ 90862306a36Sopenharmony_ci return comedi_usb_auto_config(intf, &dt9812_driver, id->driver_info); 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cistatic const struct usb_device_id dt9812_usb_table[] = { 91262306a36Sopenharmony_ci { USB_DEVICE(0x0867, 0x9812) }, 91362306a36Sopenharmony_ci { } 91462306a36Sopenharmony_ci}; 91562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, dt9812_usb_table); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_cistatic struct usb_driver dt9812_usb_driver = { 91862306a36Sopenharmony_ci .name = "dt9812", 91962306a36Sopenharmony_ci .id_table = dt9812_usb_table, 92062306a36Sopenharmony_ci .probe = dt9812_usb_probe, 92162306a36Sopenharmony_ci .disconnect = comedi_usb_auto_unconfig, 92262306a36Sopenharmony_ci}; 92362306a36Sopenharmony_cimodule_comedi_usb_driver(dt9812_driver, dt9812_usb_driver); 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ciMODULE_AUTHOR("Anders Blomdell <anders.blomdell@control.lth.se>"); 92662306a36Sopenharmony_ciMODULE_DESCRIPTION("Comedi DT9812 driver"); 92762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 928