1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Driver for Amplicon PCI263 relay board. |
4 | * |
5 | * Copyright (C) 2002 MEV Ltd. <https://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 | |
11 | /* |
12 | * Driver: amplc_pci263 |
13 | * Description: Amplicon PCI263 |
14 | * Author: Ian Abbott <abbotti@mev.co.uk> |
15 | * Devices: [Amplicon] PCI263 (amplc_pci263) |
16 | * Updated: Fri, 12 Apr 2013 15:19:36 +0100 |
17 | * Status: works |
18 | * |
19 | * Configuration options: not applicable, uses PCI auto config |
20 | * |
21 | * The board appears as one subdevice, with 16 digital outputs, each |
22 | * connected to a reed-relay. Relay contacts are closed when output is 1. |
23 | * The state of the outputs can be read. |
24 | */ |
25 | |
26 | #include <linux/module.h> |
27 | #include <linux/comedi/comedi_pci.h> |
28 | |
29 | /* PCI263 registers */ |
30 | #define PCI263_DO_0_7_REG 0x00 |
31 | #define PCI263_DO_8_15_REG 0x01 |
32 | |
33 | static int pci263_do_insn_bits(struct comedi_device *dev, |
34 | struct comedi_subdevice *s, |
35 | struct comedi_insn *insn, |
36 | unsigned int *data) |
37 | { |
38 | if (comedi_dio_update_state(s, data)) { |
39 | outb(value: s->state & 0xff, port: dev->iobase + PCI263_DO_0_7_REG); |
40 | outb(value: (s->state >> 8) & 0xff, port: dev->iobase + PCI263_DO_8_15_REG); |
41 | } |
42 | |
43 | data[1] = s->state; |
44 | |
45 | return insn->n; |
46 | } |
47 | |
48 | static int pci263_auto_attach(struct comedi_device *dev, |
49 | unsigned long context_unused) |
50 | { |
51 | struct pci_dev *pci_dev = comedi_to_pci_dev(dev); |
52 | struct comedi_subdevice *s; |
53 | int ret; |
54 | |
55 | ret = comedi_pci_enable(dev); |
56 | if (ret) |
57 | return ret; |
58 | |
59 | dev->iobase = pci_resource_start(pci_dev, 2); |
60 | ret = comedi_alloc_subdevices(dev, num_subdevices: 1); |
61 | if (ret) |
62 | return ret; |
63 | |
64 | /* Digital Output subdevice */ |
65 | s = &dev->subdevices[0]; |
66 | s->type = COMEDI_SUBD_DO; |
67 | s->subdev_flags = SDF_WRITABLE; |
68 | s->n_chan = 16; |
69 | s->maxdata = 1; |
70 | s->range_table = &range_digital; |
71 | s->insn_bits = pci263_do_insn_bits; |
72 | |
73 | /* read initial relay state */ |
74 | s->state = inb(port: dev->iobase + PCI263_DO_0_7_REG) | |
75 | (inb(port: dev->iobase + PCI263_DO_8_15_REG) << 8); |
76 | |
77 | return 0; |
78 | } |
79 | |
80 | static struct comedi_driver amplc_pci263_driver = { |
81 | .driver_name = "amplc_pci263" , |
82 | .module = THIS_MODULE, |
83 | .auto_attach = pci263_auto_attach, |
84 | .detach = comedi_pci_detach, |
85 | }; |
86 | |
87 | static const struct pci_device_id pci263_pci_table[] = { |
88 | { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, 0x000c) }, |
89 | {0} |
90 | }; |
91 | MODULE_DEVICE_TABLE(pci, pci263_pci_table); |
92 | |
93 | static int amplc_pci263_pci_probe(struct pci_dev *dev, |
94 | const struct pci_device_id *id) |
95 | { |
96 | return comedi_pci_auto_config(pcidev: dev, driver: &lc_pci263_driver, |
97 | context: id->driver_data); |
98 | } |
99 | |
100 | static struct pci_driver amplc_pci263_pci_driver = { |
101 | .name = "amplc_pci263" , |
102 | .id_table = pci263_pci_table, |
103 | .probe = &lc_pci263_pci_probe, |
104 | .remove = comedi_pci_auto_unconfig, |
105 | }; |
106 | module_comedi_pci_driver(amplc_pci263_driver, amplc_pci263_pci_driver); |
107 | |
108 | MODULE_AUTHOR("Comedi https://www.comedi.org" ); |
109 | MODULE_DESCRIPTION("Comedi driver for Amplicon PCI263 relay board" ); |
110 | MODULE_LICENSE("GPL" ); |
111 | |