Commit | Line | Data |
---|---|---|
ce157f80 | 1 | #include <linux/module.h> |
33782dd5 HS |
2 | #include <linux/pci.h> |
3 | ||
3d41c443 HS |
4 | #include "../comedidev.h" |
5 | #include "comedi_fc.h" | |
bf36f012 | 6 | #include "amcc_s5933.h" |
3d41c443 HS |
7 | |
8 | #include "addi-data/addi_common.h" | |
3d41c443 | 9 | |
bf6a1578 | 10 | #include "addi-data/hwdrv_apci3120.c" |
bb6986f0 | 11 | |
1df0e5b0 HS |
12 | enum apci3120_boardid { |
13 | BOARD_APCI3120, | |
14 | BOARD_APCI3001, | |
15 | }; | |
16 | ||
20a22b70 | 17 | static const struct addi_board apci3120_boardtypes[] = { |
1df0e5b0 | 18 | [BOARD_APCI3120] = { |
c0a053b8 | 19 | .pc_DriverName = "apci3120", |
c0a053b8 HS |
20 | .i_NbrAiChannel = 16, |
21 | .i_NbrAiChannelDiff = 8, | |
22 | .i_AiChannelList = 16, | |
23 | .i_NbrAoChannel = 8, | |
24 | .i_AiMaxdata = 0xffff, | |
25 | .i_AoMaxdata = 0x3fff, | |
c0a053b8 HS |
26 | .i_NbrDiChannel = 4, |
27 | .i_NbrDoChannel = 4, | |
28 | .i_DoMaxdata = 0x0f, | |
805077b9 | 29 | .interrupt = apci3120_interrupt, |
1df0e5b0 HS |
30 | }, |
31 | [BOARD_APCI3001] = { | |
973781a8 | 32 | .pc_DriverName = "apci3001", |
973781a8 HS |
33 | .i_NbrAiChannel = 16, |
34 | .i_NbrAiChannelDiff = 8, | |
35 | .i_AiChannelList = 16, | |
36 | .i_AiMaxdata = 0xfff, | |
973781a8 HS |
37 | .i_NbrDiChannel = 4, |
38 | .i_NbrDoChannel = 4, | |
39 | .i_DoMaxdata = 0x0f, | |
805077b9 | 40 | .interrupt = apci3120_interrupt, |
c0a053b8 HS |
41 | }, |
42 | }; | |
43 | ||
bb6986f0 HS |
44 | static irqreturn_t v_ADDI_Interrupt(int irq, void *d) |
45 | { | |
46 | struct comedi_device *dev = d; | |
db2d623b | 47 | const struct addi_board *this_board = dev->board_ptr; |
bb6986f0 HS |
48 | |
49 | this_board->interrupt(irq, d); | |
50 | return IRQ_RETVAL(1); | |
51 | } | |
52 | ||
a690b7e5 | 53 | static int apci3120_auto_attach(struct comedi_device *dev, |
1df0e5b0 | 54 | unsigned long context) |
bb6986f0 | 55 | { |
891e62c3 | 56 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
1df0e5b0 | 57 | const struct addi_board *this_board = NULL; |
bb6986f0 HS |
58 | struct addi_private *devpriv; |
59 | struct comedi_subdevice *s; | |
9c97e588 | 60 | int ret, order, i; |
bb6986f0 | 61 | |
1df0e5b0 HS |
62 | if (context < ARRAY_SIZE(apci3120_boardtypes)) |
63 | this_board = &apci3120_boardtypes[context]; | |
bb6986f0 HS |
64 | if (!this_board) |
65 | return -ENODEV; | |
66 | dev->board_ptr = this_board; | |
67 | dev->board_name = this_board->pc_DriverName; | |
68 | ||
0bdab509 | 69 | devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); |
bb6986f0 HS |
70 | if (!devpriv) |
71 | return -ENOMEM; | |
bb6986f0 | 72 | |
818f569f | 73 | ret = comedi_pci_enable(dev); |
bb6986f0 HS |
74 | if (ret) |
75 | return ret; | |
4fbe36f2 | 76 | pci_set_master(pcidev); |
bb6986f0 | 77 | |
65fe75a6 | 78 | dev->iobase = pci_resource_start(pcidev, 1); |
887f706e HS |
79 | devpriv->iobase = dev->iobase; |
80 | devpriv->i_IobaseAmcc = pci_resource_start(pcidev, 0); | |
81 | devpriv->i_IobaseAddon = pci_resource_start(pcidev, 2); | |
bb6986f0 HS |
82 | devpriv->i_IobaseReserved = pci_resource_start(pcidev, 3); |
83 | ||
bb6986f0 HS |
84 | if (pcidev->irq > 0) { |
85 | ret = request_irq(pcidev->irq, v_ADDI_Interrupt, IRQF_SHARED, | |
86 | dev->board_name, dev); | |
87 | if (ret == 0) | |
88 | dev->irq = pcidev->irq; | |
89 | } | |
90 | ||
4fbe36f2 | 91 | /* Allocate DMA buffers */ |
4fbe36f2 | 92 | for (i = 0; i < 2; i++) { |
9c97e588 | 93 | for (order = 2; order >= 0; order--) { |
4fbe36f2 | 94 | devpriv->ul_DmaBufferVirtual[i] = |
fbfd9c8a IA |
95 | dma_alloc_coherent(dev->hw_dev, PAGE_SIZE << order, |
96 | &devpriv->ul_DmaBufferHw[i], | |
97 | GFP_KERNEL); | |
4fbe36f2 HS |
98 | |
99 | if (devpriv->ul_DmaBufferVirtual[i]) | |
100 | break; | |
101 | } | |
df567feb IA |
102 | if (!devpriv->ul_DmaBufferVirtual[i]) |
103 | break; | |
9c97e588 | 104 | devpriv->ui_DmaBufferSize[i] = PAGE_SIZE << order; |
bb6986f0 | 105 | } |
062fdcad IA |
106 | if (devpriv->ul_DmaBufferVirtual[0]) |
107 | devpriv->us_UseDma = 1; | |
4fbe36f2 HS |
108 | |
109 | if (devpriv->ul_DmaBufferVirtual[1]) | |
110 | devpriv->b_DmaDoubleBuffer = 1; | |
bb6986f0 | 111 | |
aea9c4e2 | 112 | ret = comedi_alloc_subdevices(dev, 5); |
bb6986f0 HS |
113 | if (ret) |
114 | return ret; | |
115 | ||
116 | /* Allocate and Initialise AI Subdevice Structures */ | |
117 | s = &dev->subdevices[0]; | |
50231a91 HS |
118 | dev->read_subdev = s; |
119 | s->type = COMEDI_SUBD_AI; | |
120 | s->subdev_flags = | |
121 | SDF_READABLE | SDF_COMMON | SDF_GROUND | |
122 | | SDF_DIFF; | |
301b1ffc | 123 | if (this_board->i_NbrAiChannel) |
50231a91 | 124 | s->n_chan = this_board->i_NbrAiChannel; |
301b1ffc | 125 | else |
50231a91 | 126 | s->n_chan = this_board->i_NbrAiChannelDiff; |
50231a91 HS |
127 | s->maxdata = this_board->i_AiMaxdata; |
128 | s->len_chanlist = this_board->i_AiChannelList; | |
1ba296b9 | 129 | s->range_table = &range_apci3120_ai; |
50231a91 | 130 | |
805077b9 FA |
131 | s->insn_config = apci3120_ai_insn_config; |
132 | s->insn_read = apci3120_ai_insn_read; | |
133 | s->do_cmdtest = apci3120_ai_cmdtest; | |
134 | s->do_cmd = apci3120_ai_cmd; | |
135 | s->cancel = apci3120_cancel; | |
bb6986f0 HS |
136 | |
137 | /* Allocate and Initialise AO Subdevice Structures */ | |
138 | s = &dev->subdevices[1]; | |
48fdf084 | 139 | if (this_board->i_NbrAoChannel) { |
bb6986f0 HS |
140 | s->type = COMEDI_SUBD_AO; |
141 | s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON; | |
48fdf084 HS |
142 | s->n_chan = this_board->i_NbrAoChannel; |
143 | s->maxdata = this_board->i_AoMaxdata; | |
144 | s->len_chanlist = this_board->i_NbrAoChannel; | |
0bb482e2 | 145 | s->range_table = &range_apci3120_ao; |
805077b9 | 146 | s->insn_write = apci3120_ao_insn_write; |
bb6986f0 HS |
147 | } else { |
148 | s->type = COMEDI_SUBD_UNUSED; | |
149 | } | |
f2c872e1 | 150 | |
bb6986f0 HS |
151 | /* Allocate and Initialise DI Subdevice Structures */ |
152 | s = &dev->subdevices[2]; | |
f2c872e1 HS |
153 | s->type = COMEDI_SUBD_DI; |
154 | s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON; | |
155 | s->n_chan = this_board->i_NbrDiChannel; | |
156 | s->maxdata = 1; | |
157 | s->len_chanlist = this_board->i_NbrDiChannel; | |
158 | s->range_table = &range_digital; | |
a7f4b3ca | 159 | s->insn_bits = apci3120_di_insn_bits; |
f2c872e1 | 160 | |
bb6986f0 HS |
161 | /* Allocate and Initialise DO Subdevice Structures */ |
162 | s = &dev->subdevices[3]; | |
43deb75d HS |
163 | s->type = COMEDI_SUBD_DO; |
164 | s->subdev_flags = | |
165 | SDF_READABLE | SDF_WRITEABLE | SDF_GROUND | SDF_COMMON; | |
166 | s->n_chan = this_board->i_NbrDoChannel; | |
167 | s->maxdata = this_board->i_DoMaxdata; | |
168 | s->len_chanlist = this_board->i_NbrDoChannel; | |
169 | s->range_table = &range_digital; | |
66511843 | 170 | s->insn_bits = apci3120_do_insn_bits; |
bb6986f0 HS |
171 | |
172 | /* Allocate and Initialise Timer Subdevice Structures */ | |
173 | s = &dev->subdevices[4]; | |
53b168b9 HS |
174 | s->type = COMEDI_SUBD_TIMER; |
175 | s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON; | |
176 | s->n_chan = 1; | |
177 | s->maxdata = 0; | |
178 | s->len_chanlist = 1; | |
179 | s->range_table = &range_digital; | |
180 | ||
805077b9 FA |
181 | s->insn_write = apci3120_write_insn_timer; |
182 | s->insn_read = apci3120_read_insn_timer; | |
183 | s->insn_config = apci3120_config_insn_timer; | |
bb6986f0 | 184 | |
805077b9 | 185 | apci3120_reset(dev); |
bb6986f0 HS |
186 | return 0; |
187 | } | |
188 | ||
189 | static void apci3120_detach(struct comedi_device *dev) | |
190 | { | |
bb6986f0 HS |
191 | struct addi_private *devpriv = dev->private; |
192 | ||
aac307f9 HS |
193 | if (dev->iobase) |
194 | apci3120_reset(dev); | |
195 | comedi_pci_detach(dev); | |
bb6986f0 | 196 | if (devpriv) { |
fbfd9c8a IA |
197 | unsigned int i; |
198 | ||
199 | for (i = 0; i < 2; i++) { | |
200 | if (devpriv->ul_DmaBufferVirtual[i]) { | |
201 | dma_free_coherent(dev->hw_dev, | |
202 | devpriv->ui_DmaBufferSize[i], | |
203 | devpriv-> | |
204 | ul_DmaBufferVirtual[i], | |
205 | devpriv->ul_DmaBufferHw[i]); | |
206 | } | |
bb6986f0 HS |
207 | } |
208 | } | |
bb6986f0 HS |
209 | } |
210 | ||
20a22b70 HS |
211 | static struct comedi_driver apci3120_driver = { |
212 | .driver_name = "addi_apci_3120", | |
213 | .module = THIS_MODULE, | |
891e62c3 | 214 | .auto_attach = apci3120_auto_attach, |
bb6986f0 | 215 | .detach = apci3120_detach, |
20a22b70 HS |
216 | }; |
217 | ||
a690b7e5 | 218 | static int apci3120_pci_probe(struct pci_dev *dev, |
b8f4ac23 | 219 | const struct pci_device_id *id) |
20a22b70 | 220 | { |
b8f4ac23 | 221 | return comedi_pci_auto_config(dev, &apci3120_driver, id->driver_data); |
20a22b70 HS |
222 | } |
223 | ||
41e043fc | 224 | static const struct pci_device_id apci3120_pci_table[] = { |
aee351b2 IA |
225 | { PCI_VDEVICE(AMCC, 0x818d), BOARD_APCI3120 }, |
226 | { PCI_VDEVICE(AMCC, 0x828d), BOARD_APCI3001 }, | |
317285d7 HS |
227 | { 0 } |
228 | }; | |
20a22b70 | 229 | MODULE_DEVICE_TABLE(pci, apci3120_pci_table); |
317285d7 | 230 | |
20a22b70 HS |
231 | static struct pci_driver apci3120_pci_driver = { |
232 | .name = "addi_apci_3120", | |
233 | .id_table = apci3120_pci_table, | |
234 | .probe = apci3120_pci_probe, | |
9901a4d7 | 235 | .remove = comedi_pci_auto_unconfig, |
20a22b70 HS |
236 | }; |
237 | module_comedi_pci_driver(apci3120_driver, apci3120_pci_driver); | |
90f703d3 AT |
238 | |
239 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
b5ebcaa8 | 240 | MODULE_DESCRIPTION("ADDI-DATA APCI-3120, Analog input board"); |
90f703d3 | 241 | MODULE_LICENSE("GPL"); |