Merge commit 'v3.7-rc1' into stable/for-linus-3.7
[firefly-linux-kernel-4.4.55.git] / drivers / staging / comedi / drivers / adl_pci8164.c
1 /*
2     comedi/drivers/adl_pci8164.c
3
4     Hardware comedi driver fot PCI-8164 Adlink card
5     Copyright (C) 2004 Michel Lachine <mike@mikelachaine.ca>
6
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16
17     You should have received a copy of the GNU General Public License
18     along with this program; if not, write to the Free Software
19     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21 */
22 /*
23 Driver: adl_pci8164
24 Description: Driver for the Adlink PCI-8164 4 Axes Motion Control board
25 Devices: [ADLink] PCI-8164 (adl_pci8164)
26 Author: Michel Lachaine <mike@mikelachaine.ca>
27 Status: experimental
28 Updated: Mon, 14 Apr 2008 15:10:32 +0100
29
30 Configuration Options: not applicable, uses PCI auto config
31 */
32
33 #include "../comedidev.h"
34 #include <linux/kernel.h>
35 #include <linux/delay.h>
36 #include "comedi_fc.h"
37 #include "8253.h"
38
39 #define PCI8164_AXIS_X  0x00
40 #define PCI8164_AXIS_Y  0x08
41 #define PCI8164_AXIS_Z  0x10
42 #define PCI8164_AXIS_U  0x18
43
44 #define PCI8164_MSTS    0x00
45 #define PCI8164_SSTS    0x02
46 #define PCI8164_BUF0    0x04
47 #define PCI8164_BUF1    0x06
48
49 #define PCI8164_CMD     0x00
50 #define PCI8164_OTP     0x02
51
52 #define PCI_DEVICE_ID_PCI8164 0x8164
53
54 /*
55  all the read commands are the same except for the addition a constant
56  * const to the data for inw()
57  */
58 static void adl_pci8164_insn_read(struct comedi_device *dev,
59                                   struct comedi_subdevice *s,
60                                   struct comedi_insn *insn,
61                                   unsigned int *data,
62                                   char *action, unsigned short offset)
63 {
64         int axis, axis_reg;
65         char *axisname;
66
67         axis = CR_CHAN(insn->chanspec);
68
69         switch (axis) {
70         case 0:
71                 axis_reg = PCI8164_AXIS_X;
72                 axisname = "X";
73                 break;
74         case 1:
75                 axis_reg = PCI8164_AXIS_Y;
76                 axisname = "Y";
77                 break;
78         case 2:
79                 axis_reg = PCI8164_AXIS_Z;
80                 axisname = "Z";
81                 break;
82         case 3:
83                 axis_reg = PCI8164_AXIS_U;
84                 axisname = "U";
85                 break;
86         default:
87                 axis_reg = PCI8164_AXIS_X;
88                 axisname = "X";
89         }
90
91         data[0] = inw(dev->iobase + axis_reg + offset);
92         printk(KERN_DEBUG "comedi: pci8164 %s read -> "
93                                                   "%04X:%04X on axis %s\n",
94                                 action, data[0], data[1], axisname);
95 }
96
97 static int adl_pci8164_insn_read_msts(struct comedi_device *dev,
98                                       struct comedi_subdevice *s,
99                                       struct comedi_insn *insn,
100                                       unsigned int *data)
101 {
102         adl_pci8164_insn_read(dev, s, insn, data, "MSTS", PCI8164_MSTS);
103         return 2;
104 }
105
106 static int adl_pci8164_insn_read_ssts(struct comedi_device *dev,
107                                       struct comedi_subdevice *s,
108                                       struct comedi_insn *insn,
109                                       unsigned int *data)
110 {
111         adl_pci8164_insn_read(dev, s, insn, data, "SSTS", PCI8164_SSTS);
112         return 2;
113 }
114
115 static int adl_pci8164_insn_read_buf0(struct comedi_device *dev,
116                                       struct comedi_subdevice *s,
117                                       struct comedi_insn *insn,
118                                       unsigned int *data)
119 {
120         adl_pci8164_insn_read(dev, s, insn, data, "BUF0", PCI8164_BUF0);
121         return 2;
122 }
123
124 static int adl_pci8164_insn_read_buf1(struct comedi_device *dev,
125                                       struct comedi_subdevice *s,
126                                       struct comedi_insn *insn,
127                                       unsigned int *data)
128 {
129         adl_pci8164_insn_read(dev, s, insn, data, "BUF1", PCI8164_BUF1);
130         return 2;
131 }
132
133 /*
134  all the write commands are the same except for the addition a constant
135  * const to the data for outw()
136  */
137 static void adl_pci8164_insn_out(struct comedi_device *dev,
138                                  struct comedi_subdevice *s,
139                                  struct comedi_insn *insn,
140                                  unsigned int *data,
141                                  char *action, unsigned short offset)
142 {
143         unsigned int axis, axis_reg;
144
145         char *axisname;
146
147         axis = CR_CHAN(insn->chanspec);
148
149         switch (axis) {
150         case 0:
151                 axis_reg = PCI8164_AXIS_X;
152                 axisname = "X";
153                 break;
154         case 1:
155                 axis_reg = PCI8164_AXIS_Y;
156                 axisname = "Y";
157                 break;
158         case 2:
159                 axis_reg = PCI8164_AXIS_Z;
160                 axisname = "Z";
161                 break;
162         case 3:
163                 axis_reg = PCI8164_AXIS_U;
164                 axisname = "U";
165                 break;
166         default:
167                 axis_reg = PCI8164_AXIS_X;
168                 axisname = "X";
169         }
170
171         outw(data[0], dev->iobase + axis_reg + offset);
172
173         printk(KERN_DEBUG "comedi: pci8164 %s write -> "
174                                                 "%04X:%04X on axis %s\n",
175                                 action, data[0], data[1], axisname);
176
177 }
178
179 static int adl_pci8164_insn_write_cmd(struct comedi_device *dev,
180                                       struct comedi_subdevice *s,
181                                       struct comedi_insn *insn,
182                                       unsigned int *data)
183 {
184         adl_pci8164_insn_out(dev, s, insn, data, "CMD", PCI8164_CMD);
185         return 2;
186 }
187
188 static int adl_pci8164_insn_write_otp(struct comedi_device *dev,
189                                       struct comedi_subdevice *s,
190                                       struct comedi_insn *insn,
191                                       unsigned int *data)
192 {
193         adl_pci8164_insn_out(dev, s, insn, data, "OTP", PCI8164_OTP);
194         return 2;
195 }
196
197 static int adl_pci8164_insn_write_buf0(struct comedi_device *dev,
198                                        struct comedi_subdevice *s,
199                                        struct comedi_insn *insn,
200                                        unsigned int *data)
201 {
202         adl_pci8164_insn_out(dev, s, insn, data, "BUF0", PCI8164_BUF0);
203         return 2;
204 }
205
206 static int adl_pci8164_insn_write_buf1(struct comedi_device *dev,
207                                        struct comedi_subdevice *s,
208                                        struct comedi_insn *insn,
209                                        unsigned int *data)
210 {
211         adl_pci8164_insn_out(dev, s, insn, data, "BUF1", PCI8164_BUF1);
212         return 2;
213 }
214
215 static int adl_pci8164_attach_pci(struct comedi_device *dev,
216                                   struct pci_dev *pcidev)
217 {
218         struct comedi_subdevice *s;
219         int ret;
220
221         comedi_set_hw_dev(dev, &pcidev->dev);
222
223         dev->board_name = dev->driver->driver_name;
224
225         ret = comedi_pci_enable(pcidev, dev->board_name);
226         if (ret)
227                 return ret;
228         dev->iobase = pci_resource_start(pcidev, 2);
229
230         ret = comedi_alloc_subdevices(dev, 4);
231         if (ret)
232                 return ret;
233
234         s = &dev->subdevices[0];
235         s->type = COMEDI_SUBD_PROC;
236         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
237         s->n_chan = 4;
238         s->maxdata = 0xffff;
239         s->len_chanlist = 4;
240         /* s->range_table = &range_axis; */
241         s->insn_read = adl_pci8164_insn_read_msts;
242         s->insn_write = adl_pci8164_insn_write_cmd;
243
244         s = &dev->subdevices[1];
245         s->type = COMEDI_SUBD_PROC;
246         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
247         s->n_chan = 4;
248         s->maxdata = 0xffff;
249         s->len_chanlist = 4;
250         /* s->range_table = &range_axis; */
251         s->insn_read = adl_pci8164_insn_read_ssts;
252         s->insn_write = adl_pci8164_insn_write_otp;
253
254         s = &dev->subdevices[2];
255         s->type = COMEDI_SUBD_PROC;
256         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
257         s->n_chan = 4;
258         s->maxdata = 0xffff;
259         s->len_chanlist = 4;
260         /* s->range_table = &range_axis; */
261         s->insn_read = adl_pci8164_insn_read_buf0;
262         s->insn_write = adl_pci8164_insn_write_buf0;
263
264         s = &dev->subdevices[3];
265         s->type = COMEDI_SUBD_PROC;
266         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
267         s->n_chan = 4;
268         s->maxdata = 0xffff;
269         s->len_chanlist = 4;
270         /* s->range_table = &range_axis; */
271         s->insn_read = adl_pci8164_insn_read_buf1;
272         s->insn_write = adl_pci8164_insn_write_buf1;
273
274         dev_info(dev->class_dev, "%s attached\n", dev->board_name);
275
276         return 0;
277 }
278
279 static void adl_pci8164_detach(struct comedi_device *dev)
280 {
281         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
282
283         if (pcidev) {
284                 if (dev->iobase)
285                         comedi_pci_disable(pcidev);
286         }
287 }
288
289 static struct comedi_driver adl_pci8164_driver = {
290         .driver_name    = "adl_pci8164",
291         .module         = THIS_MODULE,
292         .attach_pci     = adl_pci8164_attach_pci,
293         .detach         = adl_pci8164_detach,
294 };
295
296 static int __devinit adl_pci8164_pci_probe(struct pci_dev *dev,
297                                            const struct pci_device_id *ent)
298 {
299         return comedi_pci_auto_config(dev, &adl_pci8164_driver);
300 }
301
302 static void __devexit adl_pci8164_pci_remove(struct pci_dev *dev)
303 {
304         comedi_pci_auto_unconfig(dev);
305 }
306
307 static DEFINE_PCI_DEVICE_TABLE(adl_pci8164_pci_table) = {
308         { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI8164) },
309         {0}
310 };
311 MODULE_DEVICE_TABLE(pci, adl_pci8164_pci_table);
312
313 static struct pci_driver adl_pci8164_pci_driver = {
314         .name           = "adl_pci8164",
315         .id_table       = adl_pci8164_pci_table,
316         .probe          = adl_pci8164_pci_probe,
317         .remove         = __devexit_p(adl_pci8164_pci_remove),
318 };
319 module_comedi_pci_driver(adl_pci8164_driver, adl_pci8164_pci_driver);
320
321 MODULE_AUTHOR("Comedi http://www.comedi.org");
322 MODULE_DESCRIPTION("Comedi low-level driver");
323 MODULE_LICENSE("GPL");