Commit | Line | Data |
---|---|---|
e30ec011 | 1 | /* |
629af240 HS |
2 | * pcm3724.c |
3 | * Comedi driver for Advantech PCM-3724 Digital I/O board | |
4 | * | |
5 | * Drew Csillag <drew_csillag@yahoo.com> | |
6 | */ | |
e30ec011 | 7 | |
e30ec011 | 8 | /* |
629af240 HS |
9 | * Driver: pcm3724 |
10 | * Description: Advantech PCM-3724 | |
11 | * Devices: [Advantech] PCM-3724 (pcm3724) | |
12 | * Author: Drew Csillag <drew_csillag@yahoo.com> | |
13 | * Status: tested | |
14 | * | |
15 | * This is driver for digital I/O boards PCM-3724 with 48 DIO. | |
16 | * It needs 8255.o for operations and only immediate mode is supported. | |
17 | * See the source for configuration details. | |
18 | * | |
19 | * Copy/pasted/hacked from pcm724.c | |
20 | * | |
21 | * Configuration Options: | |
22 | * [0] - I/O port base address | |
e30ec011 DC |
23 | */ |
24 | ||
ce157f80 | 25 | #include <linux/module.h> |
e30ec011 DC |
26 | #include "../comedidev.h" |
27 | ||
e30ec011 DC |
28 | #include "8255.h" |
29 | ||
5891b5b2 HS |
30 | /* |
31 | * Register I/O Map | |
32 | * | |
33 | * This board has two standard 8255 devices that provide six 8-bit DIO ports | |
34 | * (48 channels total). Six 74HCT245 chips (one for each port) buffer the | |
35 | * I/O lines to increase driving capability. Because the 74HCT245 is a | |
36 | * bidirectional, tri-state line buffer, two additional I/O ports are used | |
37 | * to control the direction of data and the enable of each port. | |
38 | */ | |
39 | #define PCM3724_8255_0_BASE 0x00 | |
40 | #define PCM3724_8255_1_BASE 0x04 | |
41 | #define PCM3724_DIO_DIR_REG 0x08 | |
42 | #define PCM3724_DIO_DIR_C0_OUT BIT(0) | |
43 | #define PCM3724_DIO_DIR_B0_OUT BIT(1) | |
44 | #define PCM3724_DIO_DIR_A0_OUT BIT(2) | |
45 | #define PCM3724_DIO_DIR_C1_OUT BIT(3) | |
46 | #define PCM3724_DIO_DIR_B1_OUT BIT(4) | |
47 | #define PCM3724_DIO_DIR_A1_OUT BIT(5) | |
48 | #define PCM3724_GATE_CTRL_REG 0x09 | |
49 | #define PCM3724_GATE_CTRL_C0_ENA BIT(0) | |
50 | #define PCM3724_GATE_CTRL_B0_ENA BIT(1) | |
51 | #define PCM3724_GATE_CTRL_A0_ENA BIT(2) | |
52 | #define PCM3724_GATE_CTRL_C1_ENA BIT(3) | |
53 | #define PCM3724_GATE_CTRL_B1_ENA BIT(4) | |
54 | #define PCM3724_GATE_CTRL_A1_ENA BIT(5) | |
e30ec011 | 55 | |
2696fb57 | 56 | /* used to track configured dios */ |
8e7b864b | 57 | struct priv_pcm3724 { |
e30ec011 DC |
58 | int dio_1; |
59 | int dio_2; | |
8e7b864b BP |
60 | }; |
61 | ||
da91b269 | 62 | static int compute_buffer(int config, int devno, struct comedi_subdevice *s) |
e30ec011 DC |
63 | { |
64 | /* 1 in io_bits indicates output */ | |
65 | if (s->io_bits & 0x0000ff) { | |
50ae2a52 | 66 | if (devno == 0) |
5891b5b2 | 67 | config |= PCM3724_DIO_DIR_A0_OUT; |
50ae2a52 | 68 | else |
5891b5b2 | 69 | config |= PCM3724_DIO_DIR_A1_OUT; |
e30ec011 DC |
70 | } |
71 | if (s->io_bits & 0x00ff00) { | |
50ae2a52 | 72 | if (devno == 0) |
5891b5b2 | 73 | config |= PCM3724_DIO_DIR_B0_OUT; |
50ae2a52 | 74 | else |
5891b5b2 | 75 | config |= PCM3724_DIO_DIR_B1_OUT; |
e30ec011 DC |
76 | } |
77 | if (s->io_bits & 0xff0000) { | |
50ae2a52 | 78 | if (devno == 0) |
5891b5b2 | 79 | config |= PCM3724_DIO_DIR_C0_OUT; |
50ae2a52 | 80 | else |
5891b5b2 | 81 | config |= PCM3724_DIO_DIR_C1_OUT; |
e30ec011 DC |
82 | } |
83 | return config; | |
84 | } | |
85 | ||
0a85b6f0 MT |
86 | static void do_3724_config(struct comedi_device *dev, |
87 | struct comedi_subdevice *s, int chanspec) | |
e30ec011 | 88 | { |
0a96639f HS |
89 | struct comedi_subdevice *s_dio1 = &dev->subdevices[0]; |
90 | struct comedi_subdevice *s_dio2 = &dev->subdevices[1]; | |
e30ec011 DC |
91 | int config; |
92 | int buffer_config; | |
93 | unsigned long port_8255_cfg; | |
94 | ||
f0162091 | 95 | config = I8255_CTRL_CW; |
e30ec011 DC |
96 | buffer_config = 0; |
97 | ||
98 | /* 1 in io_bits indicates output, 1 in config indicates input */ | |
50ae2a52 | 99 | if (!(s->io_bits & 0x0000ff)) |
f0162091 | 100 | config |= I8255_CTRL_A_IO; |
50ae2a52 BA |
101 | |
102 | if (!(s->io_bits & 0x00ff00)) | |
f0162091 | 103 | config |= I8255_CTRL_B_IO; |
50ae2a52 BA |
104 | |
105 | if (!(s->io_bits & 0xff0000)) | |
f0162091 | 106 | config |= I8255_CTRL_C_HI_IO | I8255_CTRL_C_LO_IO; |
e30ec011 | 107 | |
0a96639f HS |
108 | buffer_config = compute_buffer(0, 0, s_dio1); |
109 | buffer_config = compute_buffer(buffer_config, 1, s_dio2); | |
e30ec011 | 110 | |
0a96639f | 111 | if (s == s_dio1) |
f0162091 | 112 | port_8255_cfg = dev->iobase + I8255_CTRL_REG; |
50ae2a52 | 113 | else |
f0162091 | 114 | port_8255_cfg = dev->iobase + I8255_SIZE + I8255_CTRL_REG; |
50ae2a52 | 115 | |
5891b5b2 | 116 | outb(buffer_config, dev->iobase + PCM3724_DIO_DIR_REG); |
50ae2a52 | 117 | |
e30ec011 DC |
118 | outb(config, port_8255_cfg); |
119 | } | |
120 | ||
0a85b6f0 MT |
121 | static void enable_chan(struct comedi_device *dev, struct comedi_subdevice *s, |
122 | int chanspec) | |
e30ec011 | 123 | { |
9a1a6cf8 | 124 | struct priv_pcm3724 *priv = dev->private; |
0a96639f | 125 | struct comedi_subdevice *s_dio1 = &dev->subdevices[0]; |
e30ec011 DC |
126 | unsigned int mask; |
127 | int gatecfg; | |
e30ec011 DC |
128 | |
129 | gatecfg = 0; | |
e30ec011 DC |
130 | |
131 | mask = 1 << CR_CHAN(chanspec); | |
0a96639f | 132 | if (s == s_dio1) |
e30ec011 | 133 | priv->dio_1 |= mask; |
0a96639f | 134 | else |
e30ec011 | 135 | priv->dio_2 |= mask; |
50ae2a52 BA |
136 | |
137 | if (priv->dio_1 & 0xff0000) | |
5891b5b2 | 138 | gatecfg |= PCM3724_GATE_CTRL_C0_ENA; |
50ae2a52 BA |
139 | |
140 | if (priv->dio_1 & 0xff00) | |
5891b5b2 | 141 | gatecfg |= PCM3724_GATE_CTRL_B0_ENA; |
50ae2a52 BA |
142 | |
143 | if (priv->dio_1 & 0xff) | |
5891b5b2 | 144 | gatecfg |= PCM3724_GATE_CTRL_A0_ENA; |
50ae2a52 BA |
145 | |
146 | if (priv->dio_2 & 0xff0000) | |
5891b5b2 | 147 | gatecfg |= PCM3724_GATE_CTRL_C1_ENA; |
50ae2a52 BA |
148 | |
149 | if (priv->dio_2 & 0xff00) | |
5891b5b2 | 150 | gatecfg |= PCM3724_GATE_CTRL_B1_ENA; |
50ae2a52 BA |
151 | |
152 | if (priv->dio_2 & 0xff) | |
5891b5b2 | 153 | gatecfg |= PCM3724_GATE_CTRL_A1_ENA; |
50ae2a52 | 154 | |
5891b5b2 | 155 | outb(gatecfg, dev->iobase + PCM3724_GATE_CTRL_REG); |
e30ec011 DC |
156 | } |
157 | ||
158 | /* overriding the 8255 insn config */ | |
0a85b6f0 MT |
159 | static int subdev_3724_insn_config(struct comedi_device *dev, |
160 | struct comedi_subdevice *s, | |
5dacadcc HS |
161 | struct comedi_insn *insn, |
162 | unsigned int *data) | |
e30ec011 | 163 | { |
5dacadcc | 164 | unsigned int chan = CR_CHAN(insn->chanspec); |
e30ec011 | 165 | unsigned int mask; |
5dacadcc HS |
166 | int ret; |
167 | ||
168 | if (chan < 8) | |
169 | mask = 0x0000ff; | |
170 | else if (chan < 16) | |
171 | mask = 0x00ff00; | |
172 | else if (chan < 20) | |
173 | mask = 0x0f0000; | |
50ae2a52 | 174 | else |
5dacadcc HS |
175 | mask = 0xf00000; |
176 | ||
177 | ret = comedi_dio_insn_config(dev, s, insn, data, mask); | |
178 | if (ret) | |
179 | return ret; | |
e30ec011 DC |
180 | |
181 | do_3724_config(dev, s, insn->chanspec); | |
182 | enable_chan(dev, s, insn->chanspec); | |
5dacadcc HS |
183 | |
184 | return insn->n; | |
e30ec011 DC |
185 | } |
186 | ||
0a85b6f0 MT |
187 | static int pcm3724_attach(struct comedi_device *dev, |
188 | struct comedi_devconfig *it) | |
e30ec011 | 189 | { |
9a1a6cf8 | 190 | struct priv_pcm3724 *priv; |
0a96639f | 191 | struct comedi_subdevice *s; |
5f1514bf HS |
192 | int ret, i; |
193 | ||
0bdab509 | 194 | priv = comedi_alloc_devpriv(dev, sizeof(*priv)); |
c34fa261 HS |
195 | if (!priv) |
196 | return -ENOMEM; | |
e30ec011 | 197 | |
862755ec | 198 | ret = comedi_request_region(dev, it->options[0], 0x10); |
011c131f HS |
199 | if (ret) |
200 | return ret; | |
e30ec011 | 201 | |
5f1514bf | 202 | ret = comedi_alloc_subdevices(dev, 2); |
8b6c5694 | 203 | if (ret) |
e30ec011 DC |
204 | return ret; |
205 | ||
206 | for (i = 0; i < dev->n_subdevices; i++) { | |
0a96639f | 207 | s = &dev->subdevices[i]; |
f0162091 | 208 | ret = subdev_8255_init(dev, s, NULL, i * I8255_SIZE); |
e6439a45 HS |
209 | if (ret) |
210 | return ret; | |
0a96639f | 211 | s->insn_config = subdev_3724_insn_config; |
273f4bef | 212 | } |
e30ec011 DC |
213 | return 0; |
214 | } | |
215 | ||
294f930d | 216 | static struct comedi_driver pcm3724_driver = { |
5f01bd51 HS |
217 | .driver_name = "pcm3724", |
218 | .module = THIS_MODULE, | |
219 | .attach = pcm3724_attach, | |
588ba6dc | 220 | .detach = comedi_legacy_detach, |
5f01bd51 | 221 | }; |
294f930d | 222 | module_comedi_driver(pcm3724_driver); |
5f01bd51 | 223 | |
90f703d3 | 224 | MODULE_AUTHOR("Comedi http://www.comedi.org"); |
647d8aec | 225 | MODULE_DESCRIPTION("Comedi driver for Advantech PCM-3724 Digital I/O board"); |
90f703d3 | 226 | MODULE_LICENSE("GPL"); |