cfb69fa9c89daf76f50a4221472aebbc3c4e7bc8
[deliverable/linux.git] / drivers / staging / comedi / drivers / amplc_pc263.c
1 /*
2 comedi/drivers/amplc_pc263.c
3 Driver for Amplicon PC263 and PCI263 relay boards.
4
5 Copyright (C) 2002 MEV Ltd. <http://www.mev.co.uk/>
6
7 COMEDI - Linux Control and Measurement Device Interface
8 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
9
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23
24 */
25 /*
26 Driver: amplc_pc263
27 Description: Amplicon PC263, PCI263
28 Author: Ian Abbott <abbotti@mev.co.uk>
29 Devices: [Amplicon] PC263 (pc263), PCI263 (pci263 or amplc_pc263)
30 Updated: Wed, 22 Oct 2008 14:10:53 +0100
31 Status: works
32
33 Configuration options - PC263:
34 [0] - I/O port base address
35
36 Configuration options - PCI263:
37 [0] - PCI bus of device (optional)
38 [1] - PCI slot of device (optional)
39 If bus/slot is not specified, the first available PCI device will be
40 used.
41
42 Each board appears as one subdevice, with 16 digital outputs, each
43 connected to a reed-relay. Relay contacts are closed when output is 1.
44 The state of the outputs can be read.
45 */
46
47 #include "../comedidev.h"
48
49 #define PC263_DRIVER_NAME "amplc_pc263"
50
51 /* PCI263 PCI configuration register information */
52 #define PCI_VENDOR_ID_AMPLICON 0x14dc
53 #define PCI_DEVICE_ID_AMPLICON_PCI263 0x000c
54 #define PCI_DEVICE_ID_INVALID 0xffff
55
56 /* PC263 / PCI263 registers */
57 #define PC263_IO_SIZE 2
58
59 /*
60 * Board descriptions for Amplicon PC263 / PCI263.
61 */
62
63 enum pc263_bustype { isa_bustype, pci_bustype };
64 enum pc263_model { pc263_model, pci263_model, anypci_model };
65
66 struct pc263_board {
67 const char *name;
68 unsigned short devid;
69 enum pc263_bustype bustype;
70 enum pc263_model model;
71 };
72 static const struct pc263_board pc263_boards[] = {
73 #if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_ISA)
74 {
75 .name = "pc263",
76 .bustype = isa_bustype,
77 .model = pc263_model,
78 },
79 #endif
80 #if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI)
81 {
82 .name = "pci263",
83 .devid = PCI_DEVICE_ID_AMPLICON_PCI263,
84 .bustype = pci_bustype,
85 .model = pci263_model,
86 },
87 {
88 .name = PC263_DRIVER_NAME,
89 .devid = PCI_DEVICE_ID_INVALID,
90 .bustype = pci_bustype,
91 .model = anypci_model, /* wildcard */
92 },
93 #endif
94 };
95
96 /* this structure is for data unique to this hardware driver. If
97 several hardware drivers keep similar information in this structure,
98 feel free to suggest moving the variable to the struct comedi_device struct.
99 */
100 struct pc263_private {
101 /* PCI device. */
102 struct pci_dev *pci_dev;
103 };
104
105 /*
106 * This function looks for a board matching the supplied PCI device.
107 */
108 static const struct pc263_board *pc263_find_pci_board(struct pci_dev *pci_dev)
109 {
110 unsigned int i;
111
112 for (i = 0; i < ARRAY_SIZE(pc263_boards); i++)
113 if (pc263_boards[i].bustype == pci_bustype &&
114 pci_dev->device == pc263_boards[i].devid)
115 return &pc263_boards[i];
116 return NULL;
117 }
118
119
120 /*
121 * This function looks for a PCI device matching the requested board name,
122 * bus and slot.
123 */
124 static struct pci_dev *
125 pc263_find_pci(struct comedi_device *dev, int bus, int slot)
126 {
127 const struct pc263_board *thisboard = comedi_board(dev);
128 struct pci_dev *pci_dev = NULL;
129
130 /* Look for matching PCI device. */
131 for (pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID, NULL);
132 pci_dev != NULL;
133 pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON,
134 PCI_ANY_ID, pci_dev)) {
135 /* If bus/slot specified, check them. */
136 if (bus || slot) {
137 if (bus != pci_dev->bus->number
138 || slot != PCI_SLOT(pci_dev->devfn))
139 continue;
140 }
141 if (thisboard->model == anypci_model) {
142 /* Wildcard board matches any supported PCI board. */
143 const struct pc263_board *foundboard;
144 foundboard = pc263_find_pci_board(pci_dev);
145 if (foundboard == NULL)
146 continue;
147 /* Replace wildcard board_ptr. */
148 dev->board_ptr = thisboard = foundboard;
149 } else {
150 /* Match specific model name. */
151 if (pci_dev->device != thisboard->devid)
152 continue;
153 }
154
155 /* Found a match. */
156 return pci_dev;
157 }
158 /* No match found. */
159 if (bus || slot) {
160 dev_err(dev->class_dev,
161 "error! no %s found at pci %02x:%02x!\n",
162 thisboard->name, bus, slot);
163 } else {
164 dev_err(dev->class_dev, "error! no %s found!\n",
165 thisboard->name);
166 }
167 return NULL;
168 }
169 /*
170 * This function checks and requests an I/O region, reporting an error
171 * if there is a conflict.
172 */
173 static int pc263_request_region(struct comedi_device *dev, unsigned long from,
174 unsigned long extent)
175 {
176 if (!from || !request_region(from, extent, PC263_DRIVER_NAME)) {
177 dev_err(dev->class_dev, "I/O port conflict (%#lx,%lu)!\n",
178 from, extent);
179 return -EIO;
180 }
181 return 0;
182 }
183
184 static int pc263_do_insn_bits(struct comedi_device *dev,
185 struct comedi_subdevice *s,
186 struct comedi_insn *insn, unsigned int *data)
187 {
188 if (insn->n != 2)
189 return -EINVAL;
190
191 /* The insn data is a mask in data[0] and the new data
192 * in data[1], each channel cooresponding to a bit. */
193 if (data[0]) {
194 s->state &= ~data[0];
195 s->state |= data[0] & data[1];
196 /* Write out the new digital output lines */
197 outb(s->state & 0xFF, dev->iobase);
198 outb(s->state >> 8, dev->iobase + 1);
199 }
200 return 2;
201 }
202
203 static void pc263_report_attach(struct comedi_device *dev)
204 {
205 const struct pc263_board *thisboard = comedi_board(dev);
206 struct pc263_private *devpriv = dev->private;
207 char tmpbuf[40];
208
209 if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_ISA) &&
210 thisboard->bustype == isa_bustype)
211 snprintf(tmpbuf, sizeof(tmpbuf), "(base %#lx) ", dev->iobase);
212 else if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI) &&
213 thisboard->bustype == pci_bustype)
214 snprintf(tmpbuf, sizeof(tmpbuf), "(pci %s) ",
215 pci_name(devpriv->pci_dev));
216 else
217 tmpbuf[0] = '\0';
218 dev_info(dev->class_dev, "%s %sattached\n", dev->board_name, tmpbuf);
219 }
220
221 static int pc263_common_attach(struct comedi_device *dev, unsigned long iobase)
222 {
223 const struct pc263_board *thisboard = comedi_board(dev);
224 struct comedi_subdevice *s;
225 int ret;
226
227 dev->board_name = thisboard->name;
228 dev->iobase = iobase;
229
230 ret = comedi_alloc_subdevices(dev, 1);
231 if (ret < 0) {
232 dev_err(dev->class_dev, "error! out of memory!\n");
233 return ret;
234 }
235
236 s = dev->subdevices + 0;
237 /* digital output subdevice */
238 s->type = COMEDI_SUBD_DO;
239 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
240 s->n_chan = 16;
241 s->maxdata = 1;
242 s->range_table = &range_digital;
243 s->insn_bits = pc263_do_insn_bits;
244 /* read initial relay state */
245 s->state = inb(dev->iobase) | (inb(dev->iobase + 1) << 8);
246
247 pc263_report_attach(dev);
248 return 1;
249 }
250
251 static int pc263_pci_common_attach(struct comedi_device *dev,
252 struct pci_dev *pci_dev)
253 {
254 struct pc263_private *devpriv = dev->private;
255 unsigned long iobase;
256 int ret;
257
258 devpriv->pci_dev = pci_dev;
259 ret = comedi_pci_enable(pci_dev, PC263_DRIVER_NAME);
260 if (ret < 0) {
261 dev_err(dev->class_dev,
262 "error! cannot enable PCI device and request regions!\n");
263 return ret;
264 }
265 iobase = pci_resource_start(pci_dev, 2);
266 return pc263_common_attach(dev, iobase);
267 }
268
269 /*
270 * Attach is called by the Comedi core to configure the driver
271 * for a particular board. If you specified a board_name array
272 * in the driver structure, dev->board_ptr contains that
273 * address.
274 */
275 static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it)
276 {
277 const struct pc263_board *thisboard = comedi_board(dev);
278 int ret;
279
280 dev_info(dev->class_dev, PC263_DRIVER_NAME ": attach\n");
281
282 /* Process options and reserve resources according to bus type. */
283 if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_ISA) &&
284 thisboard->bustype == isa_bustype) {
285 unsigned long iobase = it->options[0];
286 ret = pc263_request_region(dev, iobase, PC263_IO_SIZE);
287 if (ret < 0)
288 return ret;
289 return pc263_common_attach(dev, iobase);
290 } else if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI) &&
291 thisboard->bustype == pci_bustype) {
292 struct pci_dev *pci_dev;
293 int bus, slot;
294
295 ret = alloc_private(dev, sizeof(struct pc263_private));
296 if (ret < 0) {
297 dev_err(dev->class_dev, "error! out of memory!\n");
298 return ret;
299 }
300 bus = it->options[0];
301 slot = it->options[1];
302 pci_dev = pc263_find_pci(dev, bus, slot);
303 if (pci_dev == NULL)
304 return -EIO;
305 return pc263_pci_common_attach(dev, pci_dev);
306 } else {
307 dev_err(dev->class_dev, PC263_DRIVER_NAME
308 ": BUG! cannot determine board type!\n");
309 return -EINVAL;
310 }
311 }
312 /*
313 * The attach_pci hook (if non-NULL) is called at PCI probe time in preference
314 * to the "manual" attach hook. dev->board_ptr is NULL on entry. There should
315 * be a board entry matching the supplied PCI device.
316 */
317 static int __devinit pc263_attach_pci(struct comedi_device *dev,
318 struct pci_dev *pci_dev)
319 {
320 int ret;
321
322 if (!IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI))
323 return -EINVAL;
324
325 dev_info(dev->class_dev, PC263_DRIVER_NAME ": attach pci %s\n",
326 pci_name(pci_dev));
327 ret = alloc_private(dev, sizeof(struct pc263_private));
328 if (ret < 0) {
329 dev_err(dev->class_dev, "error! out of memory!\n");
330 return ret;
331 }
332 dev->board_ptr = pc263_find_pci_board(pci_dev);
333 if (dev->board_ptr == NULL) {
334 dev_err(dev->class_dev, "BUG! cannot determine board type!\n");
335 return -EINVAL;
336 }
337 return pc263_pci_common_attach(dev, pci_dev);
338 }
339
340 static void pc263_detach(struct comedi_device *dev)
341 {
342 struct pc263_private *devpriv = dev->private;
343
344 if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI) && devpriv &&
345 devpriv->pci_dev) {
346 if (dev->iobase)
347 comedi_pci_disable(devpriv->pci_dev);
348 pci_dev_put(devpriv->pci_dev);
349 } else if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_ISA)) {
350 if (dev->iobase)
351 release_region(dev->iobase, PC263_IO_SIZE);
352 }
353 }
354
355 /*
356 * The struct comedi_driver structure tells the Comedi core module
357 * which functions to call to configure/deconfigure (attach/detach)
358 * the board, and also about the kernel module that contains
359 * the device code.
360 */
361 static struct comedi_driver amplc_pc263_driver = {
362 .driver_name = PC263_DRIVER_NAME,
363 .module = THIS_MODULE,
364 .attach = pc263_attach,
365 .attach_pci = pc263_attach_pci,
366 .detach = pc263_detach,
367 .board_name = &pc263_boards[0].name,
368 .offset = sizeof(struct pc263_board),
369 .num_names = ARRAY_SIZE(pc263_boards),
370 };
371
372 #if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI)
373 static DEFINE_PCI_DEVICE_TABLE(pc263_pci_table) = {
374 { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI263) },
375 {0}
376 };
377 MODULE_DEVICE_TABLE(pci, pc263_pci_table);
378
379 static int __devinit amplc_pc263_pci_probe(struct pci_dev *dev,
380 const struct pci_device_id
381 *ent)
382 {
383 return comedi_pci_auto_config(dev, &amplc_pc263_driver);
384 }
385
386 static void __devexit amplc_pc263_pci_remove(struct pci_dev *dev)
387 {
388 comedi_pci_auto_unconfig(dev);
389 }
390
391 static struct pci_driver amplc_pc263_pci_driver = {
392 .name = PC263_DRIVER_NAME,
393 .id_table = pc263_pci_table,
394 .probe = &amplc_pc263_pci_probe,
395 .remove = __devexit_p(&amplc_pc263_pci_remove)
396 };
397 module_comedi_pci_driver(amplc_pc263_driver, amplc_pc263_pci_driver);
398 #else
399 module_comedi_driver(amplc_pc263_driver);
400 #endif
401
402 MODULE_AUTHOR("Comedi http://www.comedi.org");
403 MODULE_DESCRIPTION("Comedi low-level driver");
404 MODULE_LICENSE("GPL");
This page took 0.055778 seconds and 4 git commands to generate.