18ffdff6aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+ 28ffdff6aSGreg Kroah-Hartman /* 38ffdff6aSGreg Kroah-Hartman * s526.c 48ffdff6aSGreg Kroah-Hartman * Sensoray s526 Comedi driver 58ffdff6aSGreg Kroah-Hartman * 68ffdff6aSGreg Kroah-Hartman * COMEDI - Linux Control and Measurement Device Interface 78ffdff6aSGreg Kroah-Hartman * Copyright (C) 2000 David A. Schleef <ds@schleef.org> 88ffdff6aSGreg Kroah-Hartman */ 98ffdff6aSGreg Kroah-Hartman 108ffdff6aSGreg Kroah-Hartman /* 118ffdff6aSGreg Kroah-Hartman * Driver: s526 128ffdff6aSGreg Kroah-Hartman * Description: Sensoray 526 driver 138ffdff6aSGreg Kroah-Hartman * Devices: [Sensoray] 526 (s526) 148ffdff6aSGreg Kroah-Hartman * Author: Richie 158ffdff6aSGreg Kroah-Hartman * Everett Wang <everett.wang@everteq.com> 168ffdff6aSGreg Kroah-Hartman * Updated: Thu, 14 Sep. 2006 178ffdff6aSGreg Kroah-Hartman * Status: experimental 188ffdff6aSGreg Kroah-Hartman * 198ffdff6aSGreg Kroah-Hartman * Encoder works 208ffdff6aSGreg Kroah-Hartman * Analog input works 218ffdff6aSGreg Kroah-Hartman * Analog output works 228ffdff6aSGreg Kroah-Hartman * PWM output works 238ffdff6aSGreg Kroah-Hartman * Commands are not supported yet. 248ffdff6aSGreg Kroah-Hartman * 258ffdff6aSGreg Kroah-Hartman * Configuration Options: 268ffdff6aSGreg Kroah-Hartman * [0] - I/O port base address 278ffdff6aSGreg Kroah-Hartman */ 288ffdff6aSGreg Kroah-Hartman 298ffdff6aSGreg Kroah-Hartman #include <linux/module.h> 30*df0e68c1SIan Abbott #include <linux/comedi/comedidev.h> 318ffdff6aSGreg Kroah-Hartman 328ffdff6aSGreg Kroah-Hartman /* 338ffdff6aSGreg Kroah-Hartman * Register I/O map 348ffdff6aSGreg Kroah-Hartman */ 358ffdff6aSGreg Kroah-Hartman #define S526_TIMER_REG 0x00 368ffdff6aSGreg Kroah-Hartman #define S526_TIMER_LOAD(x) (((x) & 0xff) << 8) 378ffdff6aSGreg Kroah-Hartman #define S526_TIMER_MODE ((x) << 1) 388ffdff6aSGreg Kroah-Hartman #define S526_TIMER_MANUAL S526_TIMER_MODE(0) 398ffdff6aSGreg Kroah-Hartman #define S526_TIMER_AUTO S526_TIMER_MODE(1) 408ffdff6aSGreg Kroah-Hartman #define S526_TIMER_RESTART BIT(0) 418ffdff6aSGreg Kroah-Hartman #define S526_WDOG_REG 0x02 428ffdff6aSGreg Kroah-Hartman #define S526_WDOG_INVERTED BIT(4) 438ffdff6aSGreg Kroah-Hartman #define S526_WDOG_ENA BIT(3) 448ffdff6aSGreg Kroah-Hartman #define S526_WDOG_INTERVAL(x) (((x) & 0x7) << 0) 458ffdff6aSGreg Kroah-Hartman #define S526_AO_CTRL_REG 0x04 468ffdff6aSGreg Kroah-Hartman #define S526_AO_CTRL_RESET BIT(3) 478ffdff6aSGreg Kroah-Hartman #define S526_AO_CTRL_CHAN(x) (((x) & 0x3) << 1) 488ffdff6aSGreg Kroah-Hartman #define S526_AO_CTRL_START BIT(0) 498ffdff6aSGreg Kroah-Hartman #define S526_AI_CTRL_REG 0x06 508ffdff6aSGreg Kroah-Hartman #define S526_AI_CTRL_DELAY BIT(15) 518ffdff6aSGreg Kroah-Hartman #define S526_AI_CTRL_CONV(x) (1 << (5 + ((x) & 0x9))) 528ffdff6aSGreg Kroah-Hartman #define S526_AI_CTRL_READ(x) (((x) & 0xf) << 1) 538ffdff6aSGreg Kroah-Hartman #define S526_AI_CTRL_START BIT(0) 548ffdff6aSGreg Kroah-Hartman #define S526_AO_REG 0x08 558ffdff6aSGreg Kroah-Hartman #define S526_AI_REG 0x08 568ffdff6aSGreg Kroah-Hartman #define S526_DIO_CTRL_REG 0x0a 578ffdff6aSGreg Kroah-Hartman #define S526_DIO_CTRL_DIO3_NEG BIT(15) /* irq on DIO3 neg/pos edge */ 588ffdff6aSGreg Kroah-Hartman #define S526_DIO_CTRL_DIO2_NEG BIT(14) /* irq on DIO2 neg/pos edge */ 598ffdff6aSGreg Kroah-Hartman #define S526_DIO_CTRL_DIO1_NEG BIT(13) /* irq on DIO1 neg/pos edge */ 608ffdff6aSGreg Kroah-Hartman #define S526_DIO_CTRL_DIO0_NEG BIT(12) /* irq on DIO0 neg/pos edge */ 618ffdff6aSGreg Kroah-Hartman #define S526_DIO_CTRL_GRP2_OUT BIT(11) 628ffdff6aSGreg Kroah-Hartman #define S526_DIO_CTRL_GRP1_OUT BIT(10) 638ffdff6aSGreg Kroah-Hartman #define S526_DIO_CTRL_GRP2_NEG BIT(8) /* irq on DIO[4-7] neg/pos edge */ 648ffdff6aSGreg Kroah-Hartman #define S526_INT_ENA_REG 0x0c 658ffdff6aSGreg Kroah-Hartman #define S526_INT_STATUS_REG 0x0e 668ffdff6aSGreg Kroah-Hartman #define S526_INT_DIO(x) BIT(8 + ((x) & 0x7)) 678ffdff6aSGreg Kroah-Hartman #define S526_INT_EEPROM BIT(7) /* status only */ 688ffdff6aSGreg Kroah-Hartman #define S526_INT_CNTR(x) BIT(3 + (3 - ((x) & 0x3))) 698ffdff6aSGreg Kroah-Hartman #define S526_INT_AI BIT(2) 708ffdff6aSGreg Kroah-Hartman #define S526_INT_AO BIT(1) 718ffdff6aSGreg Kroah-Hartman #define S526_INT_TIMER BIT(0) 728ffdff6aSGreg Kroah-Hartman #define S526_MISC_REG 0x10 738ffdff6aSGreg Kroah-Hartman #define S526_MISC_LED_OFF BIT(0) 748ffdff6aSGreg Kroah-Hartman #define S526_GPCT_LSB_REG(x) (0x12 + ((x) * 8)) 758ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MSB_REG(x) (0x14 + ((x) * 8)) 768ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_REG(x) (0x16 + ((x) * 8)) 778ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_COUT_SRC(x) ((x) << 0) 788ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_COUT_SRC_MASK S526_GPCT_MODE_COUT_SRC(0x1) 798ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_COUT_SRC_RCAP S526_GPCT_MODE_COUT_SRC(0) 808ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_COUT_SRC_RTGL S526_GPCT_MODE_COUT_SRC(1) 818ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_COUT_POL(x) ((x) << 1) 828ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_COUT_POL_MASK S526_GPCT_MODE_COUT_POL(0x1) 838ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_COUT_POL_NORM S526_GPCT_MODE_COUT_POL(0) 848ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_COUT_POL_INV S526_GPCT_MODE_COUT_POL(1) 858ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_AUTOLOAD(x) ((x) << 2) 868ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_AUTOLOAD_MASK S526_GPCT_MODE_AUTOLOAD(0x7) 878ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_AUTOLOAD_NONE S526_GPCT_MODE_AUTOLOAD(0) 888ffdff6aSGreg Kroah-Hartman /* these 3 bits can be OR'ed */ 898ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_AUTOLOAD_RO S526_GPCT_MODE_AUTOLOAD(0x1) 908ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_AUTOLOAD_IXFALL S526_GPCT_MODE_AUTOLOAD(0x2) 918ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_AUTOLOAD_IXRISE S526_GPCT_MODE_AUTOLOAD(0x4) 928ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_HWCTEN_SRC(x) ((x) << 5) 938ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_HWCTEN_SRC_MASK S526_GPCT_MODE_HWCTEN_SRC(0x3) 948ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_HWCTEN_SRC_CEN S526_GPCT_MODE_HWCTEN_SRC(0) 958ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_HWCTEN_SRC_IX S526_GPCT_MODE_HWCTEN_SRC(1) 968ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_HWCTEN_SRC_IXRF S526_GPCT_MODE_HWCTEN_SRC(2) 978ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_HWCTEN_SRC_NRCAP S526_GPCT_MODE_HWCTEN_SRC(3) 988ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_CTEN_CTRL(x) ((x) << 7) 998ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_CTEN_CTRL_MASK S526_GPCT_MODE_CTEN_CTRL(0x3) 1008ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_CTEN_CTRL_DIS S526_GPCT_MODE_CTEN_CTRL(0) 1018ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_CTEN_CTRL_ENA S526_GPCT_MODE_CTEN_CTRL(1) 1028ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_CTEN_CTRL_HW S526_GPCT_MODE_CTEN_CTRL(2) 1038ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_CTEN_CTRL_INVHW S526_GPCT_MODE_CTEN_CTRL(3) 1048ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_CLK_SRC(x) ((x) << 9) 1058ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_CLK_SRC_MASK S526_GPCT_MODE_CLK_SRC(0x3) 1068ffdff6aSGreg Kroah-Hartman /* if count direction control set to quadrature */ 1078ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_CLK_SRC_QUADX1 S526_GPCT_MODE_CLK_SRC(0) 1088ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_CLK_SRC_QUADX2 S526_GPCT_MODE_CLK_SRC(1) 1098ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_CLK_SRC_QUADX4 S526_GPCT_MODE_CLK_SRC(2) 1108ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_CLK_SRC_QUADX4_ S526_GPCT_MODE_CLK_SRC(3) 1118ffdff6aSGreg Kroah-Hartman /* if count direction control set to software control */ 1128ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_CLK_SRC_ARISE S526_GPCT_MODE_CLK_SRC(0) 1138ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_CLK_SRC_AFALL S526_GPCT_MODE_CLK_SRC(1) 1148ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_CLK_SRC_INT S526_GPCT_MODE_CLK_SRC(2) 1158ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_CLK_SRC_INTHALF S526_GPCT_MODE_CLK_SRC(3) 1168ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_CT_DIR(x) ((x) << 11) 1178ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_CT_DIR_MASK S526_GPCT_MODE_CT_DIR(0x1) 1188ffdff6aSGreg Kroah-Hartman /* if count direction control set to software control */ 1198ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_CT_DIR_UP S526_GPCT_MODE_CT_DIR(0) 1208ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_CT_DIR_DOWN S526_GPCT_MODE_CT_DIR(1) 1218ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_CTDIR_CTRL(x) ((x) << 12) 1228ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_CTDIR_CTRL_MASK S526_GPCT_MODE_CTDIR_CTRL(0x1) 1238ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_CTDIR_CTRL_QUAD S526_GPCT_MODE_CTDIR_CTRL(0) 1248ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_CTDIR_CTRL_SOFT S526_GPCT_MODE_CTDIR_CTRL(1) 1258ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_LATCH_CTRL(x) ((x) << 13) 1268ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_LATCH_CTRL_MASK S526_GPCT_MODE_LATCH_CTRL(0x1) 1278ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_LATCH_CTRL_READ S526_GPCT_MODE_LATCH_CTRL(0) 1288ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_LATCH_CTRL_EVENT S526_GPCT_MODE_LATCH_CTRL(1) 1298ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_PR_SELECT(x) ((x) << 14) 1308ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_PR_SELECT_MASK S526_GPCT_MODE_PR_SELECT(0x1) 1318ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_PR_SELECT_PR0 S526_GPCT_MODE_PR_SELECT(0) 1328ffdff6aSGreg Kroah-Hartman #define S526_GPCT_MODE_PR_SELECT_PR1 S526_GPCT_MODE_PR_SELECT(1) 1338ffdff6aSGreg Kroah-Hartman /* Control/Status - R = readable, W = writeable, C = write 1 to clear */ 1348ffdff6aSGreg Kroah-Hartman #define S526_GPCT_CTRL_REG(x) (0x18 + ((x) * 8)) 1358ffdff6aSGreg Kroah-Hartman #define S526_GPCT_CTRL_EV_STATUS(x) ((x) << 0) /* RC */ 1368ffdff6aSGreg Kroah-Hartman #define S526_GPCT_CTRL_EV_STATUS_MASK S526_GPCT_EV_STATUS(0xf) 1378ffdff6aSGreg Kroah-Hartman #define S526_GPCT_CTRL_EV_STATUS_NONE S526_GPCT_EV_STATUS(0) 1388ffdff6aSGreg Kroah-Hartman /* these 4 bits can be OR'ed */ 1398ffdff6aSGreg Kroah-Hartman #define S526_GPCT_CTRL_EV_STATUS_ECAP S526_GPCT_EV_STATUS(0x1) 1408ffdff6aSGreg Kroah-Hartman #define S526_GPCT_CTRL_EV_STATUS_ICAPN S526_GPCT_EV_STATUS(0x2) 1418ffdff6aSGreg Kroah-Hartman #define S526_GPCT_CTRL_EV_STATUS_ICAPP S526_GPCT_EV_STATUS(0x4) 1428ffdff6aSGreg Kroah-Hartman #define S526_GPCT_CTRL_EV_STATUS_RCAP S526_GPCT_EV_STATUS(0x8) 1438ffdff6aSGreg Kroah-Hartman #define S526_GPCT_CTRL_COUT_STATUS BIT(4) /* R */ 1448ffdff6aSGreg Kroah-Hartman #define S526_GPCT_CTRL_INDEX_STATUS BIT(5) /* R */ 1458ffdff6aSGreg Kroah-Hartman #define S525_GPCT_CTRL_INTEN(x) ((x) << 6) /* W */ 1468ffdff6aSGreg Kroah-Hartman #define S525_GPCT_CTRL_INTEN_MASK S526_GPCT_CTRL_INTEN(0xf) 1478ffdff6aSGreg Kroah-Hartman #define S525_GPCT_CTRL_INTEN_NONE S526_GPCT_CTRL_INTEN(0) 1488ffdff6aSGreg Kroah-Hartman /* these 4 bits can be OR'ed */ 1498ffdff6aSGreg Kroah-Hartman #define S525_GPCT_CTRL_INTEN_ERROR S526_GPCT_CTRL_INTEN(0x1) 1508ffdff6aSGreg Kroah-Hartman #define S525_GPCT_CTRL_INTEN_IXFALL S526_GPCT_CTRL_INTEN(0x2) 1518ffdff6aSGreg Kroah-Hartman #define S525_GPCT_CTRL_INTEN_IXRISE S526_GPCT_CTRL_INTEN(0x4) 1528ffdff6aSGreg Kroah-Hartman #define S525_GPCT_CTRL_INTEN_RO S526_GPCT_CTRL_INTEN(0x8) 1538ffdff6aSGreg Kroah-Hartman #define S525_GPCT_CTRL_LATCH_SEL(x) ((x) << 10) /* W */ 1548ffdff6aSGreg Kroah-Hartman #define S525_GPCT_CTRL_LATCH_SEL_MASK S526_GPCT_CTRL_LATCH_SEL(0x7) 1558ffdff6aSGreg Kroah-Hartman #define S525_GPCT_CTRL_LATCH_SEL_NONE S526_GPCT_CTRL_LATCH_SEL(0) 1568ffdff6aSGreg Kroah-Hartman /* these 3 bits can be OR'ed */ 1578ffdff6aSGreg Kroah-Hartman #define S525_GPCT_CTRL_LATCH_SEL_IXFALL S526_GPCT_CTRL_LATCH_SEL(0x1) 1588ffdff6aSGreg Kroah-Hartman #define S525_GPCT_CTRL_LATCH_SEL_IXRISE S526_GPCT_CTRL_LATCH_SEL(0x2) 1598ffdff6aSGreg Kroah-Hartman #define S525_GPCT_CTRL_LATCH_SEL_ITIMER S526_GPCT_CTRL_LATCH_SEL(0x4) 1608ffdff6aSGreg Kroah-Hartman #define S525_GPCT_CTRL_CT_ARM BIT(13) /* W */ 1618ffdff6aSGreg Kroah-Hartman #define S525_GPCT_CTRL_CT_LOAD BIT(14) /* W */ 1628ffdff6aSGreg Kroah-Hartman #define S526_GPCT_CTRL_CT_RESET BIT(15) /* W */ 1638ffdff6aSGreg Kroah-Hartman #define S526_EEPROM_DATA_REG 0x32 1648ffdff6aSGreg Kroah-Hartman #define S526_EEPROM_CTRL_REG 0x34 1658ffdff6aSGreg Kroah-Hartman #define S526_EEPROM_CTRL_ADDR(x) (((x) & 0x3f) << 3) 1668ffdff6aSGreg Kroah-Hartman #define S526_EEPROM_CTRL(x) (((x) & 0x3) << 1) 1678ffdff6aSGreg Kroah-Hartman #define S526_EEPROM_CTRL_READ S526_EEPROM_CTRL(2) 1688ffdff6aSGreg Kroah-Hartman #define S526_EEPROM_CTRL_START BIT(0) 1698ffdff6aSGreg Kroah-Hartman 1708ffdff6aSGreg Kroah-Hartman struct s526_private { 1718ffdff6aSGreg Kroah-Hartman unsigned int gpct_config[4]; 1728ffdff6aSGreg Kroah-Hartman unsigned short ai_ctrl; 1738ffdff6aSGreg Kroah-Hartman }; 1748ffdff6aSGreg Kroah-Hartman 1758ffdff6aSGreg Kroah-Hartman static void s526_gpct_write(struct comedi_device *dev, 1768ffdff6aSGreg Kroah-Hartman unsigned int chan, unsigned int val) 1778ffdff6aSGreg Kroah-Hartman { 1788ffdff6aSGreg Kroah-Hartman /* write high word then low word */ 1798ffdff6aSGreg Kroah-Hartman outw((val >> 16) & 0xffff, dev->iobase + S526_GPCT_MSB_REG(chan)); 1808ffdff6aSGreg Kroah-Hartman outw(val & 0xffff, dev->iobase + S526_GPCT_LSB_REG(chan)); 1818ffdff6aSGreg Kroah-Hartman } 1828ffdff6aSGreg Kroah-Hartman 1838ffdff6aSGreg Kroah-Hartman static unsigned int s526_gpct_read(struct comedi_device *dev, 1848ffdff6aSGreg Kroah-Hartman unsigned int chan) 1858ffdff6aSGreg Kroah-Hartman { 1868ffdff6aSGreg Kroah-Hartman unsigned int val; 1878ffdff6aSGreg Kroah-Hartman 1888ffdff6aSGreg Kroah-Hartman /* read the low word then high word */ 1898ffdff6aSGreg Kroah-Hartman val = inw(dev->iobase + S526_GPCT_LSB_REG(chan)) & 0xffff; 1908ffdff6aSGreg Kroah-Hartman val |= (inw(dev->iobase + S526_GPCT_MSB_REG(chan)) & 0xff) << 16; 1918ffdff6aSGreg Kroah-Hartman 1928ffdff6aSGreg Kroah-Hartman return val; 1938ffdff6aSGreg Kroah-Hartman } 1948ffdff6aSGreg Kroah-Hartman 1958ffdff6aSGreg Kroah-Hartman static int s526_gpct_rinsn(struct comedi_device *dev, 1968ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 1978ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 1988ffdff6aSGreg Kroah-Hartman unsigned int *data) 1998ffdff6aSGreg Kroah-Hartman { 2008ffdff6aSGreg Kroah-Hartman unsigned int chan = CR_CHAN(insn->chanspec); 2018ffdff6aSGreg Kroah-Hartman int i; 2028ffdff6aSGreg Kroah-Hartman 2038ffdff6aSGreg Kroah-Hartman for (i = 0; i < insn->n; i++) 2048ffdff6aSGreg Kroah-Hartman data[i] = s526_gpct_read(dev, chan); 2058ffdff6aSGreg Kroah-Hartman 2068ffdff6aSGreg Kroah-Hartman return insn->n; 2078ffdff6aSGreg Kroah-Hartman } 2088ffdff6aSGreg Kroah-Hartman 2098ffdff6aSGreg Kroah-Hartman static int s526_gpct_insn_config(struct comedi_device *dev, 2108ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 2118ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 2128ffdff6aSGreg Kroah-Hartman unsigned int *data) 2138ffdff6aSGreg Kroah-Hartman { 2148ffdff6aSGreg Kroah-Hartman struct s526_private *devpriv = dev->private; 2158ffdff6aSGreg Kroah-Hartman unsigned int chan = CR_CHAN(insn->chanspec); 2168ffdff6aSGreg Kroah-Hartman unsigned int val; 2178ffdff6aSGreg Kroah-Hartman 2188ffdff6aSGreg Kroah-Hartman /* 2198ffdff6aSGreg Kroah-Hartman * Check what type of Counter the user requested 2208ffdff6aSGreg Kroah-Hartman * data[0] contains the Application type 2218ffdff6aSGreg Kroah-Hartman */ 2228ffdff6aSGreg Kroah-Hartman switch (data[0]) { 2238ffdff6aSGreg Kroah-Hartman case INSN_CONFIG_GPCT_QUADRATURE_ENCODER: 2248ffdff6aSGreg Kroah-Hartman /* 2258ffdff6aSGreg Kroah-Hartman * data[0]: Application Type 2268ffdff6aSGreg Kroah-Hartman * data[1]: Counter Mode Register Value 2278ffdff6aSGreg Kroah-Hartman * data[2]: Pre-load Register Value 2288ffdff6aSGreg Kroah-Hartman * data[3]: Conter Control Register 2298ffdff6aSGreg Kroah-Hartman */ 2308ffdff6aSGreg Kroah-Hartman devpriv->gpct_config[chan] = data[0]; 2318ffdff6aSGreg Kroah-Hartman 2328ffdff6aSGreg Kroah-Hartman #if 1 2338ffdff6aSGreg Kroah-Hartman /* Set Counter Mode Register */ 2348ffdff6aSGreg Kroah-Hartman val = data[1] & 0xffff; 2358ffdff6aSGreg Kroah-Hartman outw(val, dev->iobase + S526_GPCT_MODE_REG(chan)); 2368ffdff6aSGreg Kroah-Hartman 2378ffdff6aSGreg Kroah-Hartman /* Reset the counter if it is software preload */ 2388ffdff6aSGreg Kroah-Hartman if ((val & S526_GPCT_MODE_AUTOLOAD_MASK) == 2398ffdff6aSGreg Kroah-Hartman S526_GPCT_MODE_AUTOLOAD_NONE) { 2408ffdff6aSGreg Kroah-Hartman /* Reset the counter */ 2418ffdff6aSGreg Kroah-Hartman outw(S526_GPCT_CTRL_CT_RESET, 2428ffdff6aSGreg Kroah-Hartman dev->iobase + S526_GPCT_CTRL_REG(chan)); 2438ffdff6aSGreg Kroah-Hartman /* 2448ffdff6aSGreg Kroah-Hartman * Load the counter from PR0 2458ffdff6aSGreg Kroah-Hartman * outw(S526_GPCT_CTRL_CT_LOAD, 2468ffdff6aSGreg Kroah-Hartman * dev->iobase + S526_GPCT_CTRL_REG(chan)); 2478ffdff6aSGreg Kroah-Hartman */ 2488ffdff6aSGreg Kroah-Hartman } 2498ffdff6aSGreg Kroah-Hartman #else 2508ffdff6aSGreg Kroah-Hartman val = S526_GPCT_MODE_CTDIR_CTRL_QUAD; 2518ffdff6aSGreg Kroah-Hartman 2528ffdff6aSGreg Kroah-Hartman /* data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */ 2538ffdff6aSGreg Kroah-Hartman if (data[1] == GPCT_X2) 2548ffdff6aSGreg Kroah-Hartman val |= S526_GPCT_MODE_CLK_SRC_QUADX2; 2558ffdff6aSGreg Kroah-Hartman else if (data[1] == GPCT_X4) 2568ffdff6aSGreg Kroah-Hartman val |= S526_GPCT_MODE_CLK_SRC_QUADX4; 2578ffdff6aSGreg Kroah-Hartman else 2588ffdff6aSGreg Kroah-Hartman val |= S526_GPCT_MODE_CLK_SRC_QUADX1; 2598ffdff6aSGreg Kroah-Hartman 2608ffdff6aSGreg Kroah-Hartman /* When to take into account the indexpulse: */ 2618ffdff6aSGreg Kroah-Hartman /* 2628ffdff6aSGreg Kroah-Hartman * if (data[2] == GPCT_IndexPhaseLowLow) { 2638ffdff6aSGreg Kroah-Hartman * } else if (data[2] == GPCT_IndexPhaseLowHigh) { 2648ffdff6aSGreg Kroah-Hartman * } else if (data[2] == GPCT_IndexPhaseHighLow) { 2658ffdff6aSGreg Kroah-Hartman * } else if (data[2] == GPCT_IndexPhaseHighHigh) { 2668ffdff6aSGreg Kroah-Hartman * } 2678ffdff6aSGreg Kroah-Hartman */ 2688ffdff6aSGreg Kroah-Hartman /* Take into account the index pulse? */ 2698ffdff6aSGreg Kroah-Hartman if (data[3] == GPCT_RESET_COUNTER_ON_INDEX) { 2708ffdff6aSGreg Kroah-Hartman /* Auto load with INDEX^ */ 2718ffdff6aSGreg Kroah-Hartman val |= S526_GPCT_MODE_AUTOLOAD_IXRISE; 2728ffdff6aSGreg Kroah-Hartman } 2738ffdff6aSGreg Kroah-Hartman 2748ffdff6aSGreg Kroah-Hartman /* Set Counter Mode Register */ 2758ffdff6aSGreg Kroah-Hartman val = data[1] & 0xffff; 2768ffdff6aSGreg Kroah-Hartman outw(val, dev->iobase + S526_GPCT_MODE_REG(chan)); 2778ffdff6aSGreg Kroah-Hartman 2788ffdff6aSGreg Kroah-Hartman /* Load the pre-load register */ 2798ffdff6aSGreg Kroah-Hartman s526_gpct_write(dev, chan, data[2]); 2808ffdff6aSGreg Kroah-Hartman 2818ffdff6aSGreg Kroah-Hartman /* Write the Counter Control Register */ 2828ffdff6aSGreg Kroah-Hartman if (data[3]) 2838ffdff6aSGreg Kroah-Hartman outw(data[3] & 0xffff, 2848ffdff6aSGreg Kroah-Hartman dev->iobase + S526_GPCT_CTRL_REG(chan)); 2858ffdff6aSGreg Kroah-Hartman 2868ffdff6aSGreg Kroah-Hartman /* Reset the counter if it is software preload */ 2878ffdff6aSGreg Kroah-Hartman if ((val & S526_GPCT_MODE_AUTOLOAD_MASK) == 2888ffdff6aSGreg Kroah-Hartman S526_GPCT_MODE_AUTOLOAD_NONE) { 2898ffdff6aSGreg Kroah-Hartman /* Reset the counter */ 2908ffdff6aSGreg Kroah-Hartman outw(S526_GPCT_CTRL_CT_RESET, 2918ffdff6aSGreg Kroah-Hartman dev->iobase + S526_GPCT_CTRL_REG(chan)); 2928ffdff6aSGreg Kroah-Hartman /* Load the counter from PR0 */ 2938ffdff6aSGreg Kroah-Hartman outw(S526_GPCT_CTRL_CT_LOAD, 2948ffdff6aSGreg Kroah-Hartman dev->iobase + S526_GPCT_CTRL_REG(chan)); 2958ffdff6aSGreg Kroah-Hartman } 2968ffdff6aSGreg Kroah-Hartman #endif 2978ffdff6aSGreg Kroah-Hartman break; 2988ffdff6aSGreg Kroah-Hartman 2998ffdff6aSGreg Kroah-Hartman case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR: 3008ffdff6aSGreg Kroah-Hartman /* 3018ffdff6aSGreg Kroah-Hartman * data[0]: Application Type 3028ffdff6aSGreg Kroah-Hartman * data[1]: Counter Mode Register Value 3038ffdff6aSGreg Kroah-Hartman * data[2]: Pre-load Register 0 Value 3048ffdff6aSGreg Kroah-Hartman * data[3]: Pre-load Register 1 Value 3058ffdff6aSGreg Kroah-Hartman * data[4]: Conter Control Register 3068ffdff6aSGreg Kroah-Hartman */ 3078ffdff6aSGreg Kroah-Hartman devpriv->gpct_config[chan] = data[0]; 3088ffdff6aSGreg Kroah-Hartman 3098ffdff6aSGreg Kroah-Hartman /* Set Counter Mode Register */ 3108ffdff6aSGreg Kroah-Hartman val = data[1] & 0xffff; 3118ffdff6aSGreg Kroah-Hartman /* Select PR0 */ 3128ffdff6aSGreg Kroah-Hartman val &= ~S526_GPCT_MODE_PR_SELECT_MASK; 3138ffdff6aSGreg Kroah-Hartman val |= S526_GPCT_MODE_PR_SELECT_PR0; 3148ffdff6aSGreg Kroah-Hartman outw(val, dev->iobase + S526_GPCT_MODE_REG(chan)); 3158ffdff6aSGreg Kroah-Hartman 3168ffdff6aSGreg Kroah-Hartman /* Load the pre-load register 0 */ 3178ffdff6aSGreg Kroah-Hartman s526_gpct_write(dev, chan, data[2]); 3188ffdff6aSGreg Kroah-Hartman 3198ffdff6aSGreg Kroah-Hartman /* Set Counter Mode Register */ 3208ffdff6aSGreg Kroah-Hartman val = data[1] & 0xffff; 3218ffdff6aSGreg Kroah-Hartman /* Select PR1 */ 3228ffdff6aSGreg Kroah-Hartman val &= ~S526_GPCT_MODE_PR_SELECT_MASK; 3238ffdff6aSGreg Kroah-Hartman val |= S526_GPCT_MODE_PR_SELECT_PR1; 3248ffdff6aSGreg Kroah-Hartman outw(val, dev->iobase + S526_GPCT_MODE_REG(chan)); 3258ffdff6aSGreg Kroah-Hartman 3268ffdff6aSGreg Kroah-Hartman /* Load the pre-load register 1 */ 3278ffdff6aSGreg Kroah-Hartman s526_gpct_write(dev, chan, data[3]); 3288ffdff6aSGreg Kroah-Hartman 3298ffdff6aSGreg Kroah-Hartman /* Write the Counter Control Register */ 3308ffdff6aSGreg Kroah-Hartman if (data[4]) { 3318ffdff6aSGreg Kroah-Hartman val = data[4] & 0xffff; 3328ffdff6aSGreg Kroah-Hartman outw(val, dev->iobase + S526_GPCT_CTRL_REG(chan)); 3338ffdff6aSGreg Kroah-Hartman } 3348ffdff6aSGreg Kroah-Hartman break; 3358ffdff6aSGreg Kroah-Hartman 3368ffdff6aSGreg Kroah-Hartman case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR: 3378ffdff6aSGreg Kroah-Hartman /* 3388ffdff6aSGreg Kroah-Hartman * data[0]: Application Type 3398ffdff6aSGreg Kroah-Hartman * data[1]: Counter Mode Register Value 3408ffdff6aSGreg Kroah-Hartman * data[2]: Pre-load Register 0 Value 3418ffdff6aSGreg Kroah-Hartman * data[3]: Pre-load Register 1 Value 3428ffdff6aSGreg Kroah-Hartman * data[4]: Conter Control Register 3438ffdff6aSGreg Kroah-Hartman */ 3448ffdff6aSGreg Kroah-Hartman devpriv->gpct_config[chan] = data[0]; 3458ffdff6aSGreg Kroah-Hartman 3468ffdff6aSGreg Kroah-Hartman /* Set Counter Mode Register */ 3478ffdff6aSGreg Kroah-Hartman val = data[1] & 0xffff; 3488ffdff6aSGreg Kroah-Hartman /* Select PR0 */ 3498ffdff6aSGreg Kroah-Hartman val &= ~S526_GPCT_MODE_PR_SELECT_MASK; 3508ffdff6aSGreg Kroah-Hartman val |= S526_GPCT_MODE_PR_SELECT_PR0; 3518ffdff6aSGreg Kroah-Hartman outw(val, dev->iobase + S526_GPCT_MODE_REG(chan)); 3528ffdff6aSGreg Kroah-Hartman 3538ffdff6aSGreg Kroah-Hartman /* Load the pre-load register 0 */ 3548ffdff6aSGreg Kroah-Hartman s526_gpct_write(dev, chan, data[2]); 3558ffdff6aSGreg Kroah-Hartman 3568ffdff6aSGreg Kroah-Hartman /* Set Counter Mode Register */ 3578ffdff6aSGreg Kroah-Hartman val = data[1] & 0xffff; 3588ffdff6aSGreg Kroah-Hartman /* Select PR1 */ 3598ffdff6aSGreg Kroah-Hartman val &= ~S526_GPCT_MODE_PR_SELECT_MASK; 3608ffdff6aSGreg Kroah-Hartman val |= S526_GPCT_MODE_PR_SELECT_PR1; 3618ffdff6aSGreg Kroah-Hartman outw(val, dev->iobase + S526_GPCT_MODE_REG(chan)); 3628ffdff6aSGreg Kroah-Hartman 3638ffdff6aSGreg Kroah-Hartman /* Load the pre-load register 1 */ 3648ffdff6aSGreg Kroah-Hartman s526_gpct_write(dev, chan, data[3]); 3658ffdff6aSGreg Kroah-Hartman 3668ffdff6aSGreg Kroah-Hartman /* Write the Counter Control Register */ 3678ffdff6aSGreg Kroah-Hartman if (data[4]) { 3688ffdff6aSGreg Kroah-Hartman val = data[4] & 0xffff; 3698ffdff6aSGreg Kroah-Hartman outw(val, dev->iobase + S526_GPCT_CTRL_REG(chan)); 3708ffdff6aSGreg Kroah-Hartman } 3718ffdff6aSGreg Kroah-Hartman break; 3728ffdff6aSGreg Kroah-Hartman 3738ffdff6aSGreg Kroah-Hartman default: 3748ffdff6aSGreg Kroah-Hartman return -EINVAL; 3758ffdff6aSGreg Kroah-Hartman } 3768ffdff6aSGreg Kroah-Hartman 3778ffdff6aSGreg Kroah-Hartman return insn->n; 3788ffdff6aSGreg Kroah-Hartman } 3798ffdff6aSGreg Kroah-Hartman 3808ffdff6aSGreg Kroah-Hartman static int s526_gpct_winsn(struct comedi_device *dev, 3818ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 3828ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 3838ffdff6aSGreg Kroah-Hartman unsigned int *data) 3848ffdff6aSGreg Kroah-Hartman { 3858ffdff6aSGreg Kroah-Hartman struct s526_private *devpriv = dev->private; 3868ffdff6aSGreg Kroah-Hartman unsigned int chan = CR_CHAN(insn->chanspec); 3878ffdff6aSGreg Kroah-Hartman 3888ffdff6aSGreg Kroah-Hartman inw(dev->iobase + S526_GPCT_MODE_REG(chan)); /* Is this required? */ 3898ffdff6aSGreg Kroah-Hartman 3908ffdff6aSGreg Kroah-Hartman /* Check what Application of Counter this channel is configured for */ 3918ffdff6aSGreg Kroah-Hartman switch (devpriv->gpct_config[chan]) { 3928ffdff6aSGreg Kroah-Hartman case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR: 3938ffdff6aSGreg Kroah-Hartman /* 3948ffdff6aSGreg Kroah-Hartman * data[0] contains the PULSE_WIDTH 3958ffdff6aSGreg Kroah-Hartman * data[1] contains the PULSE_PERIOD 3968ffdff6aSGreg Kroah-Hartman * @pre PULSE_PERIOD > PULSE_WIDTH > 0 3978ffdff6aSGreg Kroah-Hartman * The above periods must be expressed as a multiple of the 3988ffdff6aSGreg Kroah-Hartman * pulse frequency on the selected source 3998ffdff6aSGreg Kroah-Hartman */ 4008ffdff6aSGreg Kroah-Hartman if ((data[1] <= data[0]) || !data[0]) 4018ffdff6aSGreg Kroah-Hartman return -EINVAL; 4028ffdff6aSGreg Kroah-Hartman /* to write the PULSE_WIDTH */ 4038ffdff6aSGreg Kroah-Hartman fallthrough; 4048ffdff6aSGreg Kroah-Hartman case INSN_CONFIG_GPCT_QUADRATURE_ENCODER: 4058ffdff6aSGreg Kroah-Hartman case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR: 4068ffdff6aSGreg Kroah-Hartman s526_gpct_write(dev, chan, data[0]); 4078ffdff6aSGreg Kroah-Hartman break; 4088ffdff6aSGreg Kroah-Hartman 4098ffdff6aSGreg Kroah-Hartman default: 4108ffdff6aSGreg Kroah-Hartman return -EINVAL; 4118ffdff6aSGreg Kroah-Hartman } 4128ffdff6aSGreg Kroah-Hartman 4138ffdff6aSGreg Kroah-Hartman return insn->n; 4148ffdff6aSGreg Kroah-Hartman } 4158ffdff6aSGreg Kroah-Hartman 4168ffdff6aSGreg Kroah-Hartman static int s526_eoc(struct comedi_device *dev, 4178ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 4188ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 4198ffdff6aSGreg Kroah-Hartman unsigned long context) 4208ffdff6aSGreg Kroah-Hartman { 4218ffdff6aSGreg Kroah-Hartman unsigned int status; 4228ffdff6aSGreg Kroah-Hartman 4238ffdff6aSGreg Kroah-Hartman status = inw(dev->iobase + S526_INT_STATUS_REG); 4248ffdff6aSGreg Kroah-Hartman if (status & context) { 4258ffdff6aSGreg Kroah-Hartman /* we got our eoc event, clear it */ 4268ffdff6aSGreg Kroah-Hartman outw(context, dev->iobase + S526_INT_STATUS_REG); 4278ffdff6aSGreg Kroah-Hartman return 0; 4288ffdff6aSGreg Kroah-Hartman } 4298ffdff6aSGreg Kroah-Hartman return -EBUSY; 4308ffdff6aSGreg Kroah-Hartman } 4318ffdff6aSGreg Kroah-Hartman 4328ffdff6aSGreg Kroah-Hartman static int s526_ai_insn_read(struct comedi_device *dev, 4338ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 4348ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 4358ffdff6aSGreg Kroah-Hartman unsigned int *data) 4368ffdff6aSGreg Kroah-Hartman { 4378ffdff6aSGreg Kroah-Hartman struct s526_private *devpriv = dev->private; 4388ffdff6aSGreg Kroah-Hartman unsigned int chan = CR_CHAN(insn->chanspec); 4398ffdff6aSGreg Kroah-Hartman unsigned int ctrl; 4408ffdff6aSGreg Kroah-Hartman unsigned int val; 4418ffdff6aSGreg Kroah-Hartman int ret; 4428ffdff6aSGreg Kroah-Hartman int i; 4438ffdff6aSGreg Kroah-Hartman 4448ffdff6aSGreg Kroah-Hartman ctrl = S526_AI_CTRL_CONV(chan) | S526_AI_CTRL_READ(chan) | 4458ffdff6aSGreg Kroah-Hartman S526_AI_CTRL_START; 4468ffdff6aSGreg Kroah-Hartman if (ctrl != devpriv->ai_ctrl) { 4478ffdff6aSGreg Kroah-Hartman /* 4488ffdff6aSGreg Kroah-Hartman * The multiplexor needs to change, enable the 15us 4498ffdff6aSGreg Kroah-Hartman * delay for the first sample. 4508ffdff6aSGreg Kroah-Hartman */ 4518ffdff6aSGreg Kroah-Hartman devpriv->ai_ctrl = ctrl; 4528ffdff6aSGreg Kroah-Hartman ctrl |= S526_AI_CTRL_DELAY; 4538ffdff6aSGreg Kroah-Hartman } 4548ffdff6aSGreg Kroah-Hartman 4558ffdff6aSGreg Kroah-Hartman for (i = 0; i < insn->n; i++) { 4568ffdff6aSGreg Kroah-Hartman /* trigger conversion */ 4578ffdff6aSGreg Kroah-Hartman outw(ctrl, dev->iobase + S526_AI_CTRL_REG); 4588ffdff6aSGreg Kroah-Hartman ctrl &= ~S526_AI_CTRL_DELAY; 4598ffdff6aSGreg Kroah-Hartman 4608ffdff6aSGreg Kroah-Hartman /* wait for conversion to end */ 4618ffdff6aSGreg Kroah-Hartman ret = comedi_timeout(dev, s, insn, s526_eoc, S526_INT_AI); 4628ffdff6aSGreg Kroah-Hartman if (ret) 4638ffdff6aSGreg Kroah-Hartman return ret; 4648ffdff6aSGreg Kroah-Hartman 4658ffdff6aSGreg Kroah-Hartman val = inw(dev->iobase + S526_AI_REG); 4668ffdff6aSGreg Kroah-Hartman data[i] = comedi_offset_munge(s, val); 4678ffdff6aSGreg Kroah-Hartman } 4688ffdff6aSGreg Kroah-Hartman 4698ffdff6aSGreg Kroah-Hartman return insn->n; 4708ffdff6aSGreg Kroah-Hartman } 4718ffdff6aSGreg Kroah-Hartman 4728ffdff6aSGreg Kroah-Hartman static int s526_ao_insn_write(struct comedi_device *dev, 4738ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 4748ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 4758ffdff6aSGreg Kroah-Hartman unsigned int *data) 4768ffdff6aSGreg Kroah-Hartman { 4778ffdff6aSGreg Kroah-Hartman unsigned int chan = CR_CHAN(insn->chanspec); 4788ffdff6aSGreg Kroah-Hartman unsigned int ctrl = S526_AO_CTRL_CHAN(chan); 4798ffdff6aSGreg Kroah-Hartman unsigned int val = s->readback[chan]; 4808ffdff6aSGreg Kroah-Hartman int ret; 4818ffdff6aSGreg Kroah-Hartman int i; 4828ffdff6aSGreg Kroah-Hartman 4838ffdff6aSGreg Kroah-Hartman outw(ctrl, dev->iobase + S526_AO_CTRL_REG); 4848ffdff6aSGreg Kroah-Hartman ctrl |= S526_AO_CTRL_START; 4858ffdff6aSGreg Kroah-Hartman 4868ffdff6aSGreg Kroah-Hartman for (i = 0; i < insn->n; i++) { 4878ffdff6aSGreg Kroah-Hartman val = data[i]; 4888ffdff6aSGreg Kroah-Hartman outw(val, dev->iobase + S526_AO_REG); 4898ffdff6aSGreg Kroah-Hartman outw(ctrl, dev->iobase + S526_AO_CTRL_REG); 4908ffdff6aSGreg Kroah-Hartman 4918ffdff6aSGreg Kroah-Hartman /* wait for conversion to end */ 4928ffdff6aSGreg Kroah-Hartman ret = comedi_timeout(dev, s, insn, s526_eoc, S526_INT_AO); 4938ffdff6aSGreg Kroah-Hartman if (ret) 4948ffdff6aSGreg Kroah-Hartman return ret; 4958ffdff6aSGreg Kroah-Hartman } 4968ffdff6aSGreg Kroah-Hartman s->readback[chan] = val; 4978ffdff6aSGreg Kroah-Hartman 4988ffdff6aSGreg Kroah-Hartman return insn->n; 4998ffdff6aSGreg Kroah-Hartman } 5008ffdff6aSGreg Kroah-Hartman 5018ffdff6aSGreg Kroah-Hartman static int s526_dio_insn_bits(struct comedi_device *dev, 5028ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 5038ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 5048ffdff6aSGreg Kroah-Hartman unsigned int *data) 5058ffdff6aSGreg Kroah-Hartman { 5068ffdff6aSGreg Kroah-Hartman if (comedi_dio_update_state(s, data)) 5078ffdff6aSGreg Kroah-Hartman outw(s->state, dev->iobase + S526_DIO_CTRL_REG); 5088ffdff6aSGreg Kroah-Hartman 5098ffdff6aSGreg Kroah-Hartman data[1] = inw(dev->iobase + S526_DIO_CTRL_REG) & 0xff; 5108ffdff6aSGreg Kroah-Hartman 5118ffdff6aSGreg Kroah-Hartman return insn->n; 5128ffdff6aSGreg Kroah-Hartman } 5138ffdff6aSGreg Kroah-Hartman 5148ffdff6aSGreg Kroah-Hartman static int s526_dio_insn_config(struct comedi_device *dev, 5158ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 5168ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 5178ffdff6aSGreg Kroah-Hartman unsigned int *data) 5188ffdff6aSGreg Kroah-Hartman { 5198ffdff6aSGreg Kroah-Hartman unsigned int chan = CR_CHAN(insn->chanspec); 5208ffdff6aSGreg Kroah-Hartman unsigned int mask; 5218ffdff6aSGreg Kroah-Hartman int ret; 5228ffdff6aSGreg Kroah-Hartman 5238ffdff6aSGreg Kroah-Hartman /* 5248ffdff6aSGreg Kroah-Hartman * Digital I/O can be configured as inputs or outputs in 5258ffdff6aSGreg Kroah-Hartman * groups of 4; DIO group 1 (DIO0-3) and DIO group 2 (DIO4-7). 5268ffdff6aSGreg Kroah-Hartman */ 5278ffdff6aSGreg Kroah-Hartman if (chan < 4) 5288ffdff6aSGreg Kroah-Hartman mask = 0x0f; 5298ffdff6aSGreg Kroah-Hartman else 5308ffdff6aSGreg Kroah-Hartman mask = 0xf0; 5318ffdff6aSGreg Kroah-Hartman 5328ffdff6aSGreg Kroah-Hartman ret = comedi_dio_insn_config(dev, s, insn, data, mask); 5338ffdff6aSGreg Kroah-Hartman if (ret) 5348ffdff6aSGreg Kroah-Hartman return ret; 5358ffdff6aSGreg Kroah-Hartman 5368ffdff6aSGreg Kroah-Hartman if (s->io_bits & 0x0f) 5378ffdff6aSGreg Kroah-Hartman s->state |= S526_DIO_CTRL_GRP1_OUT; 5388ffdff6aSGreg Kroah-Hartman else 5398ffdff6aSGreg Kroah-Hartman s->state &= ~S526_DIO_CTRL_GRP1_OUT; 5408ffdff6aSGreg Kroah-Hartman if (s->io_bits & 0xf0) 5418ffdff6aSGreg Kroah-Hartman s->state |= S526_DIO_CTRL_GRP2_OUT; 5428ffdff6aSGreg Kroah-Hartman else 5438ffdff6aSGreg Kroah-Hartman s->state &= ~S526_DIO_CTRL_GRP2_OUT; 5448ffdff6aSGreg Kroah-Hartman 5458ffdff6aSGreg Kroah-Hartman outw(s->state, dev->iobase + S526_DIO_CTRL_REG); 5468ffdff6aSGreg Kroah-Hartman 5478ffdff6aSGreg Kroah-Hartman return insn->n; 5488ffdff6aSGreg Kroah-Hartman } 5498ffdff6aSGreg Kroah-Hartman 5508ffdff6aSGreg Kroah-Hartman static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it) 5518ffdff6aSGreg Kroah-Hartman { 5528ffdff6aSGreg Kroah-Hartman struct s526_private *devpriv; 5538ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s; 5548ffdff6aSGreg Kroah-Hartman int ret; 5558ffdff6aSGreg Kroah-Hartman 5568ffdff6aSGreg Kroah-Hartman ret = comedi_request_region(dev, it->options[0], 0x40); 5578ffdff6aSGreg Kroah-Hartman if (ret) 5588ffdff6aSGreg Kroah-Hartman return ret; 5598ffdff6aSGreg Kroah-Hartman 5608ffdff6aSGreg Kroah-Hartman devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 5618ffdff6aSGreg Kroah-Hartman if (!devpriv) 5628ffdff6aSGreg Kroah-Hartman return -ENOMEM; 5638ffdff6aSGreg Kroah-Hartman 5648ffdff6aSGreg Kroah-Hartman ret = comedi_alloc_subdevices(dev, 4); 5658ffdff6aSGreg Kroah-Hartman if (ret) 5668ffdff6aSGreg Kroah-Hartman return ret; 5678ffdff6aSGreg Kroah-Hartman 5688ffdff6aSGreg Kroah-Hartman /* General-Purpose Counter/Timer (GPCT) */ 5698ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[0]; 5708ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_COUNTER; 5718ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL; 5728ffdff6aSGreg Kroah-Hartman s->n_chan = 4; 5738ffdff6aSGreg Kroah-Hartman s->maxdata = 0x00ffffff; 5748ffdff6aSGreg Kroah-Hartman s->insn_read = s526_gpct_rinsn; 5758ffdff6aSGreg Kroah-Hartman s->insn_config = s526_gpct_insn_config; 5768ffdff6aSGreg Kroah-Hartman s->insn_write = s526_gpct_winsn; 5778ffdff6aSGreg Kroah-Hartman 5788ffdff6aSGreg Kroah-Hartman /* 5798ffdff6aSGreg Kroah-Hartman * Analog Input subdevice 5808ffdff6aSGreg Kroah-Hartman * channels 0 to 7 are the regular differential inputs 5818ffdff6aSGreg Kroah-Hartman * channel 8 is "reference 0" (+10V) 5828ffdff6aSGreg Kroah-Hartman * channel 9 is "reference 1" (0V) 5838ffdff6aSGreg Kroah-Hartman */ 5848ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[1]; 5858ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_AI; 5868ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_READABLE | SDF_DIFF; 5878ffdff6aSGreg Kroah-Hartman s->n_chan = 10; 5888ffdff6aSGreg Kroah-Hartman s->maxdata = 0xffff; 5898ffdff6aSGreg Kroah-Hartman s->range_table = &range_bipolar10; 5908ffdff6aSGreg Kroah-Hartman s->len_chanlist = 16; 5918ffdff6aSGreg Kroah-Hartman s->insn_read = s526_ai_insn_read; 5928ffdff6aSGreg Kroah-Hartman 5938ffdff6aSGreg Kroah-Hartman /* Analog Output subdevice */ 5948ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[2]; 5958ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_AO; 5968ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_WRITABLE; 5978ffdff6aSGreg Kroah-Hartman s->n_chan = 4; 5988ffdff6aSGreg Kroah-Hartman s->maxdata = 0xffff; 5998ffdff6aSGreg Kroah-Hartman s->range_table = &range_bipolar10; 6008ffdff6aSGreg Kroah-Hartman s->insn_write = s526_ao_insn_write; 6018ffdff6aSGreg Kroah-Hartman 6028ffdff6aSGreg Kroah-Hartman ret = comedi_alloc_subdev_readback(s); 6038ffdff6aSGreg Kroah-Hartman if (ret) 6048ffdff6aSGreg Kroah-Hartman return ret; 6058ffdff6aSGreg Kroah-Hartman 6068ffdff6aSGreg Kroah-Hartman /* Digital I/O subdevice */ 6078ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[3]; 6088ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_DIO; 6098ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 6108ffdff6aSGreg Kroah-Hartman s->n_chan = 8; 6118ffdff6aSGreg Kroah-Hartman s->maxdata = 1; 6128ffdff6aSGreg Kroah-Hartman s->range_table = &range_digital; 6138ffdff6aSGreg Kroah-Hartman s->insn_bits = s526_dio_insn_bits; 6148ffdff6aSGreg Kroah-Hartman s->insn_config = s526_dio_insn_config; 6158ffdff6aSGreg Kroah-Hartman 6168ffdff6aSGreg Kroah-Hartman return 0; 6178ffdff6aSGreg Kroah-Hartman } 6188ffdff6aSGreg Kroah-Hartman 6198ffdff6aSGreg Kroah-Hartman static struct comedi_driver s526_driver = { 6208ffdff6aSGreg Kroah-Hartman .driver_name = "s526", 6218ffdff6aSGreg Kroah-Hartman .module = THIS_MODULE, 6228ffdff6aSGreg Kroah-Hartman .attach = s526_attach, 6238ffdff6aSGreg Kroah-Hartman .detach = comedi_legacy_detach, 6248ffdff6aSGreg Kroah-Hartman }; 6258ffdff6aSGreg Kroah-Hartman module_comedi_driver(s526_driver); 6268ffdff6aSGreg Kroah-Hartman 6278ffdff6aSGreg Kroah-Hartman MODULE_AUTHOR("Comedi https://www.comedi.org"); 6288ffdff6aSGreg Kroah-Hartman MODULE_DESCRIPTION("Comedi low-level driver"); 6298ffdff6aSGreg Kroah-Hartman MODULE_LICENSE("GPL"); 630