~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to drivers/staging/comedi/drivers/8255.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
    comedi/drivers/8255.c
 
3
    Driver for 8255
 
4
 
 
5
    COMEDI - Linux Control and Measurement Device Interface
 
6
    Copyright (C) 1998 David A. Schleef <ds@schleef.org>
 
7
 
 
8
    This program is free software; you can redistribute it and/or modify
 
9
    it under the terms of the GNU General Public License as published by
 
10
    the Free Software Foundation; either version 2 of the License, or
 
11
    (at your option) any later version.
 
12
 
 
13
    This program is distributed in the hope that it will be useful,
 
14
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
    GNU General Public License for more details.
 
17
 
 
18
    You should have received a copy of the GNU General Public License
 
19
    along with this program; if not, write to the Free Software
 
20
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
21
 
 
22
*/
 
23
/*
 
24
Driver: 8255
 
25
Description: generic 8255 support
 
26
Devices: [standard] 8255 (8255)
 
27
Author: ds
 
28
Status: works
 
29
Updated: Fri,  7 Jun 2002 12:56:45 -0700
 
30
 
 
31
The classic in digital I/O.  The 8255 appears in Comedi as a single
 
32
digital I/O subdevice with 24 channels.  The channel 0 corresponds
 
33
to the 8255's port A, bit 0; channel 23 corresponds to port C, bit
 
34
7.  Direction configuration is done in blocks, with channels 0-7,
 
35
8-15, 16-19, and 20-23 making up the 4 blocks.  The only 8255 mode
 
36
supported is mode 0.
 
37
 
 
38
You should enable compilation this driver if you plan to use a board
 
39
that has an 8255 chip.  For multifunction boards, the main driver will
 
40
configure the 8255 subdevice automatically.
 
41
 
 
42
This driver also works independently with ISA and PCI cards that
 
43
directly map the 8255 registers to I/O ports, including cards with
 
44
multiple 8255 chips.  To configure the driver for such a card, the
 
45
option list should be a list of the I/O port bases for each of the
 
46
8255 chips.  For example,
 
47
 
 
48
  comedi_config /dev/comedi0 8255 0x200,0x204,0x208,0x20c
 
49
 
 
50
Note that most PCI 8255 boards do NOT work with this driver, and
 
51
need a separate driver as a wrapper.  For those that do work, the
 
52
I/O port base address can be found in the output of 'lspci -v'.
 
53
 
 
54
*/
 
55
 
 
56
/*
 
57
   This file contains an exported subdevice for driving an 8255.
 
58
 
 
59
   To use this subdevice as part of another driver, you need to
 
60
   set up the subdevice in the attach function of the driver by
 
61
   calling:
 
62
 
 
63
     subdev_8255_init(device, subdevice, callback_function, arg)
 
64
 
 
65
   device and subdevice are pointers to the device and subdevice
 
66
   structures.  callback_function will be called to provide the
 
67
   low-level input/output to the device, i.e., actual register
 
68
   access.  callback_function will be called with the value of arg
 
69
   as the last parameter.  If the 8255 device is mapped as 4
 
70
   consecutive I/O ports, you can use NULL for callback_function
 
71
   and the I/O port base for arg, and an internal function will
 
72
   handle the register access.
 
73
 
 
74
   In addition, if the main driver handles interrupts, you can
 
75
   enable commands on the subdevice by calling subdev_8255_init_irq()
 
76
   instead.  Then, when you get an interrupt that is likely to be
 
77
   from the 8255, you should call subdev_8255_interrupt(), which
 
78
   will copy the latched value to a Comedi buffer.
 
79
 */
 
80
 
 
81
#include "../comedidev.h"
 
82
 
 
83
#include <linux/ioport.h>
 
84
#include <linux/slab.h>
 
85
#include "8255.h"
 
86
 
 
87
#define _8255_SIZE 4
 
88
 
 
89
#define _8255_DATA 0
 
90
#define _8255_CR 3
 
91
 
 
92
#define CR_C_LO_IO      0x01
 
93
#define CR_B_IO         0x02
 
94
#define CR_B_MODE       0x04
 
95
#define CR_C_HI_IO      0x08
 
96
#define CR_A_IO         0x10
 
97
#define CR_A_MODE(a)    ((a)<<5)
 
98
#define CR_CW           0x80
 
99
 
 
100
struct subdev_8255_struct {
 
101
        unsigned long cb_arg;
 
102
        int (*cb_func) (int, int, int, unsigned long);
 
103
        int have_irq;
 
104
};
 
105
 
 
106
#define CALLBACK_ARG    (((struct subdev_8255_struct *)s->private)->cb_arg)
 
107
#define CALLBACK_FUNC   (((struct subdev_8255_struct *)s->private)->cb_func)
 
108
#define subdevpriv      ((struct subdev_8255_struct *)s->private)
 
109
 
 
110
static int dev_8255_attach(struct comedi_device *dev,
 
111
                           struct comedi_devconfig *it);
 
112
static int dev_8255_detach(struct comedi_device *dev);
 
113
static struct comedi_driver driver_8255 = {
 
114
        .driver_name = "8255",
 
115
        .module = THIS_MODULE,
 
116
        .attach = dev_8255_attach,
 
117
        .detach = dev_8255_detach,
 
118
};
 
119
 
 
120
static int __init driver_8255_init_module(void)
 
121
{
 
122
        return comedi_driver_register(&driver_8255);
 
123
}
 
124
 
 
125
static void __exit driver_8255_cleanup_module(void)
 
126
{
 
127
        comedi_driver_unregister(&driver_8255);
 
128
}
 
129
 
 
130
module_init(driver_8255_init_module);
 
131
module_exit(driver_8255_cleanup_module);
 
132
 
 
133
static void do_config(struct comedi_device *dev, struct comedi_subdevice *s);
 
134
 
 
135
void subdev_8255_interrupt(struct comedi_device *dev,
 
136
                           struct comedi_subdevice *s)
 
137
{
 
138
        short d;
 
139
 
 
140
        d = CALLBACK_FUNC(0, _8255_DATA, 0, CALLBACK_ARG);
 
141
        d |= (CALLBACK_FUNC(0, _8255_DATA + 1, 0, CALLBACK_ARG) << 8);
 
142
 
 
143
        comedi_buf_put(s->async, d);
 
144
        s->async->events |= COMEDI_CB_EOS;
 
145
 
 
146
        comedi_event(dev, s);
 
147
}
 
148
EXPORT_SYMBOL(subdev_8255_interrupt);
 
149
 
 
150
static int subdev_8255_cb(int dir, int port, int data, unsigned long arg)
 
151
{
 
152
        unsigned long iobase = arg;
 
153
 
 
154
        if (dir) {
 
155
                outb(data, iobase + port);
 
156
                return 0;
 
157
        } else {
 
158
                return inb(iobase + port);
 
159
        }
 
160
}
 
161
 
 
162
static int subdev_8255_insn(struct comedi_device *dev,
 
163
                            struct comedi_subdevice *s,
 
164
                            struct comedi_insn *insn, unsigned int *data)
 
165
{
 
166
        if (data[0]) {
 
167
                s->state &= ~data[0];
 
168
                s->state |= (data[0] & data[1]);
 
169
 
 
170
                if (data[0] & 0xff)
 
171
                        CALLBACK_FUNC(1, _8255_DATA, s->state & 0xff,
 
172
                                      CALLBACK_ARG);
 
173
                if (data[0] & 0xff00)
 
174
                        CALLBACK_FUNC(1, _8255_DATA + 1, (s->state >> 8) & 0xff,
 
175
                                      CALLBACK_ARG);
 
176
                if (data[0] & 0xff0000)
 
177
                        CALLBACK_FUNC(1, _8255_DATA + 2,
 
178
                                      (s->state >> 16) & 0xff, CALLBACK_ARG);
 
179
        }
 
180
 
 
181
        data[1] = CALLBACK_FUNC(0, _8255_DATA, 0, CALLBACK_ARG);
 
182
        data[1] |= (CALLBACK_FUNC(0, _8255_DATA + 1, 0, CALLBACK_ARG) << 8);
 
183
        data[1] |= (CALLBACK_FUNC(0, _8255_DATA + 2, 0, CALLBACK_ARG) << 16);
 
184
 
 
185
        return 2;
 
186
}
 
187
 
 
188
static int subdev_8255_insn_config(struct comedi_device *dev,
 
189
                                   struct comedi_subdevice *s,
 
190
                                   struct comedi_insn *insn, unsigned int *data)
 
191
{
 
192
        unsigned int mask;
 
193
        unsigned int bits;
 
194
 
 
195
        mask = 1 << CR_CHAN(insn->chanspec);
 
196
        if (mask & 0x0000ff)
 
197
                bits = 0x0000ff;
 
198
        else if (mask & 0x00ff00)
 
199
                bits = 0x00ff00;
 
200
        else if (mask & 0x0f0000)
 
201
                bits = 0x0f0000;
 
202
        else
 
203
                bits = 0xf00000;
 
204
 
 
205
        switch (data[0]) {
 
206
        case INSN_CONFIG_DIO_INPUT:
 
207
                s->io_bits &= ~bits;
 
208
                break;
 
209
        case INSN_CONFIG_DIO_OUTPUT:
 
210
                s->io_bits |= bits;
 
211
                break;
 
212
        case INSN_CONFIG_DIO_QUERY:
 
213
                data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT;
 
214
                return insn->n;
 
215
                break;
 
216
        default:
 
217
                return -EINVAL;
 
218
        }
 
219
 
 
220
        do_config(dev, s);
 
221
 
 
222
        return 1;
 
223
}
 
224
 
 
225
static void do_config(struct comedi_device *dev, struct comedi_subdevice *s)
 
226
{
 
227
        int config;
 
228
 
 
229
        config = CR_CW;
 
230
        /* 1 in io_bits indicates output, 1 in config indicates input */
 
231
        if (!(s->io_bits & 0x0000ff))
 
232
                config |= CR_A_IO;
 
233
        if (!(s->io_bits & 0x00ff00))
 
234
                config |= CR_B_IO;
 
235
        if (!(s->io_bits & 0x0f0000))
 
236
                config |= CR_C_LO_IO;
 
237
        if (!(s->io_bits & 0xf00000))
 
238
                config |= CR_C_HI_IO;
 
239
        CALLBACK_FUNC(1, _8255_CR, config, CALLBACK_ARG);
 
240
}
 
241
 
 
242
static int subdev_8255_cmdtest(struct comedi_device *dev,
 
243
                               struct comedi_subdevice *s,
 
244
                               struct comedi_cmd *cmd)
 
245
{
 
246
        int err = 0;
 
247
        unsigned int tmp;
 
248
 
 
249
        /* step 1 */
 
250
 
 
251
        tmp = cmd->start_src;
 
252
        cmd->start_src &= TRIG_NOW;
 
253
        if (!cmd->start_src || tmp != cmd->start_src)
 
254
                err++;
 
255
 
 
256
        tmp = cmd->scan_begin_src;
 
257
        cmd->scan_begin_src &= TRIG_EXT;
 
258
        if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
 
259
                err++;
 
260
 
 
261
        tmp = cmd->convert_src;
 
262
        cmd->convert_src &= TRIG_FOLLOW;
 
263
        if (!cmd->convert_src || tmp != cmd->convert_src)
 
264
                err++;
 
265
 
 
266
        tmp = cmd->scan_end_src;
 
267
        cmd->scan_end_src &= TRIG_COUNT;
 
268
        if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
 
269
                err++;
 
270
 
 
271
        tmp = cmd->stop_src;
 
272
        cmd->stop_src &= TRIG_NONE;
 
273
        if (!cmd->stop_src || tmp != cmd->stop_src)
 
274
                err++;
 
275
 
 
276
        if (err)
 
277
                return 1;
 
278
 
 
279
        /* step 2 */
 
280
 
 
281
        if (err)
 
282
                return 2;
 
283
 
 
284
        /* step 3 */
 
285
 
 
286
        if (cmd->start_arg != 0) {
 
287
                cmd->start_arg = 0;
 
288
                err++;
 
289
        }
 
290
        if (cmd->scan_begin_arg != 0) {
 
291
                cmd->scan_begin_arg = 0;
 
292
                err++;
 
293
        }
 
294
        if (cmd->convert_arg != 0) {
 
295
                cmd->convert_arg = 0;
 
296
                err++;
 
297
        }
 
298
        if (cmd->scan_end_arg != 1) {
 
299
                cmd->scan_end_arg = 1;
 
300
                err++;
 
301
        }
 
302
        if (cmd->stop_arg != 0) {
 
303
                cmd->stop_arg = 0;
 
304
                err++;
 
305
        }
 
306
 
 
307
        if (err)
 
308
                return 3;
 
309
 
 
310
        /* step 4 */
 
311
 
 
312
        if (err)
 
313
                return 4;
 
314
 
 
315
        return 0;
 
316
}
 
317
 
 
318
static int subdev_8255_cmd(struct comedi_device *dev,
 
319
                           struct comedi_subdevice *s)
 
320
{
 
321
        /* FIXME */
 
322
 
 
323
        return 0;
 
324
}
 
325
 
 
326
static int subdev_8255_cancel(struct comedi_device *dev,
 
327
                              struct comedi_subdevice *s)
 
328
{
 
329
        /* FIXME */
 
330
 
 
331
        return 0;
 
332
}
 
333
 
 
334
int subdev_8255_init(struct comedi_device *dev, struct comedi_subdevice *s,
 
335
                     int (*cb) (int, int, int, unsigned long),
 
336
                     unsigned long arg)
 
337
{
 
338
        s->type = COMEDI_SUBD_DIO;
 
339
        s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 
340
        s->n_chan = 24;
 
341
        s->range_table = &range_digital;
 
342
        s->maxdata = 1;
 
343
 
 
344
        s->private = kmalloc(sizeof(struct subdev_8255_struct), GFP_KERNEL);
 
345
        if (!s->private)
 
346
                return -ENOMEM;
 
347
 
 
348
        CALLBACK_ARG = arg;
 
349
        if (cb == NULL)
 
350
                CALLBACK_FUNC = subdev_8255_cb;
 
351
        else
 
352
                CALLBACK_FUNC = cb;
 
353
        s->insn_bits = subdev_8255_insn;
 
354
        s->insn_config = subdev_8255_insn_config;
 
355
 
 
356
        s->state = 0;
 
357
        s->io_bits = 0;
 
358
        do_config(dev, s);
 
359
 
 
360
        return 0;
 
361
}
 
362
EXPORT_SYMBOL(subdev_8255_init);
 
363
 
 
364
int subdev_8255_init_irq(struct comedi_device *dev, struct comedi_subdevice *s,
 
365
                         int (*cb) (int, int, int, unsigned long),
 
366
                         unsigned long arg)
 
367
{
 
368
        int ret;
 
369
 
 
370
        ret = subdev_8255_init(dev, s, cb, arg);
 
371
        if (ret < 0)
 
372
                return ret;
 
373
 
 
374
        s->do_cmdtest = subdev_8255_cmdtest;
 
375
        s->do_cmd = subdev_8255_cmd;
 
376
        s->cancel = subdev_8255_cancel;
 
377
 
 
378
        subdevpriv->have_irq = 1;
 
379
 
 
380
        return 0;
 
381
}
 
382
EXPORT_SYMBOL(subdev_8255_init_irq);
 
383
 
 
384
void subdev_8255_cleanup(struct comedi_device *dev, struct comedi_subdevice *s)
 
385
{
 
386
        kfree(s->private);
 
387
}
 
388
EXPORT_SYMBOL(subdev_8255_cleanup);
 
389
 
 
390
/*
 
391
 
 
392
   Start of the 8255 standalone device
 
393
 
 
394
 */
 
395
 
 
396
static int dev_8255_attach(struct comedi_device *dev,
 
397
                           struct comedi_devconfig *it)
 
398
{
 
399
        int ret;
 
400
        unsigned long iobase;
 
401
        int i;
 
402
 
 
403
        dev->board_name = "8255";
 
404
 
 
405
        for (i = 0; i < COMEDI_NDEVCONFOPTS; i++) {
 
406
                iobase = it->options[i];
 
407
                if (!iobase)
 
408
                        break;
 
409
        }
 
410
        if (i == 0) {
 
411
                printk(KERN_WARNING
 
412
                       "comedi%d: 8255: no devices specified\n", dev->minor);
 
413
                return -EINVAL;
 
414
        }
 
415
 
 
416
        ret = alloc_subdevices(dev, i);
 
417
        if (ret < 0) {
 
418
                /* FIXME this printk call should give a proper message, the
 
419
                 * below line just maintains previous functionality */
 
420
                printk("comedi%d: 8255:", dev->minor);
 
421
                return ret;
 
422
        }
 
423
 
 
424
        printk(KERN_INFO "comedi%d: 8255:", dev->minor);
 
425
 
 
426
        for (i = 0; i < dev->n_subdevices; i++) {
 
427
                iobase = it->options[i];
 
428
 
 
429
                printk(" 0x%04lx", iobase);
 
430
                if (!request_region(iobase, _8255_SIZE, "8255")) {
 
431
                        printk(" (I/O port conflict)");
 
432
 
 
433
                        dev->subdevices[i].type = COMEDI_SUBD_UNUSED;
 
434
                } else {
 
435
                        subdev_8255_init(dev, dev->subdevices + i, NULL,
 
436
                                         iobase);
 
437
                }
 
438
        }
 
439
 
 
440
        printk("\n");
 
441
 
 
442
        return 0;
 
443
}
 
444
 
 
445
static int dev_8255_detach(struct comedi_device *dev)
 
446
{
 
447
        int i;
 
448
        unsigned long iobase;
 
449
        struct comedi_subdevice *s;
 
450
 
 
451
        printk(KERN_INFO "comedi%d: 8255: remove\n", dev->minor);
 
452
 
 
453
        for (i = 0; i < dev->n_subdevices; i++) {
 
454
                s = dev->subdevices + i;
 
455
                if (s->type != COMEDI_SUBD_UNUSED) {
 
456
                        iobase = CALLBACK_ARG;
 
457
                        release_region(iobase, _8255_SIZE);
 
458
                }
 
459
                subdev_8255_cleanup(dev, s);
 
460
        }
 
461
 
 
462
        return 0;
 
463
}
 
464
 
 
465
MODULE_AUTHOR("Comedi http://www.comedi.org");
 
466
MODULE_DESCRIPTION("Comedi low-level driver");
 
467
MODULE_LICENSE("GPL");