1/*
2 * addi_apci_2200.c
3 * Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code of this module.
4 * Project manager: Eric Stolz
5 *
6 *	ADDI-DATA GmbH
7 *	Dieselstrasse 3
8 *	D-77833 Ottersweier
9 *	Tel: +19(0)7223/9493-0
10 *	Fax: +49(0)7223/9493-92
11 *	http://www.addi-data.com
12 *	info@addi-data.com
13 *
14 * This program is free software; you can redistribute it and/or modify it
15 * under the terms of the GNU General Public License as published by the
16 * Free Software Foundation; either version 2 of the License, or (at your
17 * option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful, but WITHOUT
20 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
22 * more details.
23 */
24
25#include <linux/module.h>
26#include <linux/pci.h>
27
28#include "../comedidev.h"
29#include "addi_watchdog.h"
30
31/*
32 * I/O Register Map
33 */
34#define APCI2200_DI_REG			0x00
35#define APCI2200_DO_REG			0x04
36#define APCI2200_WDOG_REG		0x08
37
38static int apci2200_di_insn_bits(struct comedi_device *dev,
39				 struct comedi_subdevice *s,
40				 struct comedi_insn *insn,
41				 unsigned int *data)
42{
43	data[1] = inw(dev->iobase + APCI2200_DI_REG);
44
45	return insn->n;
46}
47
48static int apci2200_do_insn_bits(struct comedi_device *dev,
49				 struct comedi_subdevice *s,
50				 struct comedi_insn *insn,
51				 unsigned int *data)
52{
53	s->state = inw(dev->iobase + APCI2200_DO_REG);
54
55	if (comedi_dio_update_state(s, data))
56		outw(s->state, dev->iobase + APCI2200_DO_REG);
57
58	data[1] = s->state;
59
60	return insn->n;
61}
62
63static int apci2200_reset(struct comedi_device *dev)
64{
65	outw(0x0, dev->iobase + APCI2200_DO_REG);
66
67	addi_watchdog_reset(dev->iobase + APCI2200_WDOG_REG);
68
69	return 0;
70}
71
72static int apci2200_auto_attach(struct comedi_device *dev,
73				unsigned long context_unused)
74{
75	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
76	struct comedi_subdevice *s;
77	int ret;
78
79	ret = comedi_pci_enable(dev);
80	if (ret)
81		return ret;
82
83	dev->iobase = pci_resource_start(pcidev, 1);
84
85	ret = comedi_alloc_subdevices(dev, 3);
86	if (ret)
87		return ret;
88
89	/* Initialize the digital input subdevice */
90	s = &dev->subdevices[0];
91	s->type		= COMEDI_SUBD_DI;
92	s->subdev_flags	= SDF_READABLE;
93	s->n_chan	= 8;
94	s->maxdata	= 1;
95	s->range_table	= &range_digital;
96	s->insn_bits	= apci2200_di_insn_bits;
97
98	/* Initialize the digital output subdevice */
99	s = &dev->subdevices[1];
100	s->type		= COMEDI_SUBD_DO;
101	s->subdev_flags	= SDF_WRITEABLE;
102	s->n_chan	= 16;
103	s->maxdata	= 1;
104	s->range_table	= &range_digital;
105	s->insn_bits	= apci2200_do_insn_bits;
106
107	/* Initialize the watchdog subdevice */
108	s = &dev->subdevices[2];
109	ret = addi_watchdog_init(s, dev->iobase + APCI2200_WDOG_REG);
110	if (ret)
111		return ret;
112
113	apci2200_reset(dev);
114	return 0;
115}
116
117static void apci2200_detach(struct comedi_device *dev)
118{
119	if (dev->iobase)
120		apci2200_reset(dev);
121	comedi_pci_detach(dev);
122}
123
124static struct comedi_driver apci2200_driver = {
125	.driver_name	= "addi_apci_2200",
126	.module		= THIS_MODULE,
127	.auto_attach	= apci2200_auto_attach,
128	.detach		= apci2200_detach,
129};
130
131static int apci2200_pci_probe(struct pci_dev *dev,
132			      const struct pci_device_id *id)
133{
134	return comedi_pci_auto_config(dev, &apci2200_driver, id->driver_data);
135}
136
137static const struct pci_device_id apci2200_pci_table[] = {
138	{ PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1005) },
139	{ 0 }
140};
141MODULE_DEVICE_TABLE(pci, apci2200_pci_table);
142
143static struct pci_driver apci2200_pci_driver = {
144	.name		= "addi_apci_2200",
145	.id_table	= apci2200_pci_table,
146	.probe		= apci2200_pci_probe,
147	.remove		= comedi_pci_auto_unconfig,
148};
149module_comedi_pci_driver(apci2200_driver, apci2200_pci_driver);
150
151MODULE_DESCRIPTION("ADDI-DATA APCI-2200 Relay board, optically isolated");
152MODULE_AUTHOR("Comedi http://www.comedi.org");
153MODULE_LICENSE("GPL");
154