xref: /linux/drivers/comedi/drivers/s526.c (revision df0e68c1e9945e2ee86d266ce45597bbd8299b06)
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