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
s526_gpct_write(struct comedi_device * dev,unsigned int chan,unsigned int val)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
s526_gpct_read(struct comedi_device * dev,unsigned int chan)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
s526_gpct_rinsn(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)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
s526_gpct_insn_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)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
s526_gpct_winsn(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)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
s526_eoc(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned long context)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
s526_ai_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)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
s526_ao_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)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
s526_dio_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)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
s526_dio_insn_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)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
s526_attach(struct comedi_device * dev,struct comedi_devconfig * it)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