Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
adl_pci7x3x.c
Go to the documentation of this file.
1 /*
2  * COMEDI driver for the ADLINK PCI-723x/743x series boards.
3  * Copyright (C) 2012 H Hartley Sweeten <[email protected]>
4  *
5  * Based on the adl_pci7230 driver written by:
6  * David Fernandez <[email protected]>
7  * and the adl_pci7432 driver written by:
8  * Michel Lachaine <[email protected]>
9  *
10  * COMEDI - Linux Control and Measurement Device Interface
11  * Copyright (C) 2000 David A. Schleef <[email protected]>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26  */
27 
28 /*
29 Driver: adl_pci7x3x
30 Description: 32/64-Channel Isolated Digital I/O Boards
31 Devices: (ADLink) PCI-7230 [adl_pci7230] - 16 input / 16 output
32  (ADLink) PCI-7233 [adl_pci7233] - 32 input
33  (ADLink) PCI-7234 [adl_pci7234] - 32 output
34  (ADLink) PCI-7432 [adl_pci7432] - 32 input / 32 output
35  (ADLink) PCI-7433 [adl_pci7433] - 64 input
36  (ADLink) PCI-7434 [adl_pci7434] - 64 output
37 Author: H Hartley Sweeten <[email protected]>
38 Updated: Thu, 02 Aug 2012 14:27:46 -0700
39 Status: untested
40 
41 This driver only attaches using the PCI PnP auto config support
42 in the comedi core. The module parameter 'comedi_autoconfig'
43 must be 1 (default) to enable this feature. The COMEDI_DEVCONFIG
44 ioctl, used by the comedi_config utility, is not supported by
45 this driver.
46 
47 The PCI-7230, PCI-7432 and PCI-7433 boards also support external
48 interrupt signals on digital input channels 0 and 1. The PCI-7233
49 has dual-interrupt sources for change-of-state (COS) on any 16
50 digital input channels of LSB and for COS on any 16 digital input
51 lines of MSB. Interrupts are not currently supported by this
52 driver.
53 
54 Configuration Options: not applicable
55 */
56 
57 #include "../comedidev.h"
58 
59 /*
60  * PCI Device ID's supported by this driver
61  */
62 #define PCI_DEVICE_ID_PCI7230 0x7230
63 #define PCI_DEVICE_ID_PCI7233 0x7233
64 #define PCI_DEVICE_ID_PCI7234 0x7234
65 #define PCI_DEVICE_ID_PCI7432 0x7432
66 #define PCI_DEVICE_ID_PCI7433 0x7433
67 #define PCI_DEVICE_ID_PCI7434 0x7434
68 
69 /*
70  * Register I/O map (32-bit access only)
71  */
72 #define PCI7X3X_DIO_REG 0x00
73 #define PCI743X_DIO_REG 0x04
74 
76  const char *name;
77  unsigned short device;
78  int nsubdevs;
79  int di_nchan;
80  int do_nchan;
81 };
82 
83 static const struct adl_pci7x3x_boardinfo adl_pci7x3x_boards[] = {
84  {
85  .name = "adl_pci7230",
86  .device = PCI_DEVICE_ID_PCI7230,
87  .nsubdevs = 2,
88  .di_nchan = 16,
89  .do_nchan = 16,
90  }, {
91  .name = "adl_pci7233",
92  .device = PCI_DEVICE_ID_PCI7233,
93  .nsubdevs = 1,
94  .di_nchan = 32,
95  }, {
96  .name = "adl_pci7234",
97  .device = PCI_DEVICE_ID_PCI7234,
98  .nsubdevs = 1,
99  .do_nchan = 32,
100  }, {
101  .name = "adl_pci7432",
102  .device = PCI_DEVICE_ID_PCI7432,
103  .nsubdevs = 2,
104  .di_nchan = 32,
105  .do_nchan = 32,
106  }, {
107  .name = "adl_pci7433",
108  .device = PCI_DEVICE_ID_PCI7433,
109  .nsubdevs = 2,
110  .di_nchan = 64,
111  }, {
112  .name = "adl_pci7434",
113  .device = PCI_DEVICE_ID_PCI7434,
114  .nsubdevs = 2,
115  .do_nchan = 64,
116  }
117 };
118 
119 static int adl_pci7x3x_do_insn_bits(struct comedi_device *dev,
120  struct comedi_subdevice *s,
121  struct comedi_insn *insn,
122  unsigned int *data)
123 {
124  unsigned long reg = (unsigned long)s->private;
125  unsigned int mask = data[0];
126  unsigned int bits = data[1];
127 
128  if (mask) {
129  s->state &= ~mask;
130  s->state |= (bits & mask);
131 
132  outl(s->state, dev->iobase + reg);
133  }
134 
135  /*
136  * NOTE: The output register is not readable.
137  * This returned state will not be correct until all the
138  * outputs have been updated.
139  */
140  data[1] = s->state;
141 
142  return insn->n;
143 }
144 
145 static int adl_pci7x3x_di_insn_bits(struct comedi_device *dev,
146  struct comedi_subdevice *s,
147  struct comedi_insn *insn,
148  unsigned int *data)
149 {
150  unsigned long reg = (unsigned long)s->private;
151 
152  data[1] = inl(dev->iobase + reg);
153 
154  return insn->n;
155 }
156 
157 static const void *adl_pci7x3x_find_boardinfo(struct comedi_device *dev,
158  struct pci_dev *pcidev)
159 {
160  const struct adl_pci7x3x_boardinfo *board;
161  int i;
162 
163  for (i = 0; i < ARRAY_SIZE(adl_pci7x3x_boards); i++) {
164  board = &adl_pci7x3x_boards[i];
165  if (pcidev->device == board->device)
166  return board;
167  }
168  return NULL;
169 }
170 
171 static int adl_pci7x3x_attach_pci(struct comedi_device *dev,
172  struct pci_dev *pcidev)
173 {
174  const struct adl_pci7x3x_boardinfo *board;
175  struct comedi_subdevice *s;
176  int subdev;
177  int nchan;
178  int ret;
179 
180  comedi_set_hw_dev(dev, &pcidev->dev);
181 
182  board = adl_pci7x3x_find_boardinfo(dev, pcidev);
183  if (!board)
184  return -ENODEV;
185  dev->board_ptr = board;
186  dev->board_name = board->name;
187 
188  ret = comedi_pci_enable(pcidev, dev->board_name);
189  if (ret)
190  return ret;
191  dev->iobase = pci_resource_start(pcidev, 2);
192 
193  /*
194  * One or two subdevices are setup by this driver depending on
195  * the number of digital inputs and/or outputs provided by the
196  * board. Each subdevice has a maximum of 32 channels.
197  *
198  * PCI-7230 - 2 subdevices: 0 - 16 input, 1 - 16 output
199  * PCI-7233 - 1 subdevice: 0 - 32 input
200  * PCI-7234 - 1 subdevice: 0 - 32 output
201  * PCI-7432 - 2 subdevices: 0 - 32 input, 1 - 32 output
202  * PCI-7433 - 2 subdevices: 0 - 32 input, 1 - 32 input
203  * PCI-7434 - 2 subdevices: 0 - 32 output, 1 - 32 output
204  */
205  ret = comedi_alloc_subdevices(dev, board->nsubdevs);
206  if (ret)
207  return ret;
208 
209  subdev = 0;
210 
211  if (board->di_nchan) {
212  nchan = min(board->di_nchan, 32);
213 
214  s = &dev->subdevices[subdev];
215  /* Isolated digital inputs 0 to 15/31 */
216  s->type = COMEDI_SUBD_DI;
218  s->n_chan = nchan;
219  s->maxdata = 1;
220  s->insn_bits = adl_pci7x3x_di_insn_bits;
222 
223  s->private = (void *)PCI7X3X_DIO_REG;
224 
225  subdev++;
226 
227  nchan = board->di_nchan - nchan;
228  if (nchan) {
229  s = &dev->subdevices[subdev];
230  /* Isolated digital inputs 32 to 63 */
231  s->type = COMEDI_SUBD_DI;
233  s->n_chan = nchan;
234  s->maxdata = 1;
235  s->insn_bits = adl_pci7x3x_di_insn_bits;
237 
238  s->private = (void *)PCI743X_DIO_REG;
239 
240  subdev++;
241  }
242  }
243 
244  if (board->do_nchan) {
245  nchan = min(board->do_nchan, 32);
246 
247  s = &dev->subdevices[subdev];
248  /* Isolated digital outputs 0 to 15/31 */
249  s->type = COMEDI_SUBD_DO;
251  s->n_chan = nchan;
252  s->maxdata = 1;
253  s->insn_bits = adl_pci7x3x_do_insn_bits;
255 
256  s->private = (void *)PCI7X3X_DIO_REG;
257 
258  subdev++;
259 
260  nchan = board->do_nchan - nchan;
261  if (nchan) {
262  s = &dev->subdevices[subdev];
263  /* Isolated digital outputs 32 to 63 */
264  s->type = COMEDI_SUBD_DO;
266  s->n_chan = nchan;
267  s->maxdata = 1;
268  s->insn_bits = adl_pci7x3x_do_insn_bits;
270 
271  s->private = (void *)PCI743X_DIO_REG;
272 
273  subdev++;
274  }
275  }
276 
277  dev_info(dev->class_dev, "%s attached (%d inputs/%d outputs)\n",
278  dev->board_name, board->di_nchan, board->do_nchan);
279 
280  return 0;
281 }
282 
283 static void adl_pci7x3x_detach(struct comedi_device *dev)
284 {
285  struct pci_dev *pcidev = comedi_to_pci_dev(dev);
286 
287  if (pcidev) {
288  if (dev->iobase)
289  comedi_pci_disable(pcidev);
290  }
291 }
292 
293 static struct comedi_driver adl_pci7x3x_driver = {
294  .driver_name = "adl_pci7x3x",
295  .module = THIS_MODULE,
296  .attach_pci = adl_pci7x3x_attach_pci,
297  .detach = adl_pci7x3x_detach,
298 };
299 
300 static int __devinit adl_pci7x3x_pci_probe(struct pci_dev *dev,
301  const struct pci_device_id *ent)
302 {
303  return comedi_pci_auto_config(dev, &adl_pci7x3x_driver);
304 }
305 
306 static void __devexit adl_pci7x3x_pci_remove(struct pci_dev *dev)
307 {
309 }
310 
311 static DEFINE_PCI_DEVICE_TABLE(adl_pci7x3x_pci_table) = {
318  { 0 }
319 };
320 MODULE_DEVICE_TABLE(pci, adl_pci7x3x_pci_table);
321 
322 static struct pci_driver adl_pci7x3x_pci_driver = {
323  .name = "adl_pci7x3x",
324  .id_table = adl_pci7x3x_pci_table,
325  .probe = adl_pci7x3x_pci_probe,
326  .remove = __devexit_p(adl_pci7x3x_pci_remove),
327 };
328 module_comedi_pci_driver(adl_pci7x3x_driver, adl_pci7x3x_pci_driver);
329 
330 MODULE_DESCRIPTION("ADLINK PCI-723x/743x Isolated Digital I/O boards");
331 MODULE_AUTHOR("H Hartley Sweeten <[email protected]>");
332 MODULE_LICENSE("GPL");