1#include <linux/module.h> 2#include <linux/pci.h> 3 4#include "../comedidev.h" 5#include "comedi_fc.h" 6#include "amcc_s5933.h" 7 8#include "addi-data/addi_common.h" 9 10#include "addi-data/hwdrv_apci3120.c" 11 12enum apci3120_boardid { 13 BOARD_APCI3120, 14 BOARD_APCI3001, 15}; 16 17static const struct addi_board apci3120_boardtypes[] = { 18 [BOARD_APCI3120] = { 19 .pc_DriverName = "apci3120", 20 .i_NbrAiChannel = 16, 21 .i_NbrAiChannelDiff = 8, 22 .i_AiChannelList = 16, 23 .i_NbrAoChannel = 8, 24 .i_AiMaxdata = 0xffff, 25 .i_AoMaxdata = 0x3fff, 26 .i_NbrDiChannel = 4, 27 .i_NbrDoChannel = 4, 28 .i_DoMaxdata = 0x0f, 29 .interrupt = apci3120_interrupt, 30 }, 31 [BOARD_APCI3001] = { 32 .pc_DriverName = "apci3001", 33 .i_NbrAiChannel = 16, 34 .i_NbrAiChannelDiff = 8, 35 .i_AiChannelList = 16, 36 .i_AiMaxdata = 0xfff, 37 .i_NbrDiChannel = 4, 38 .i_NbrDoChannel = 4, 39 .i_DoMaxdata = 0x0f, 40 .interrupt = apci3120_interrupt, 41 }, 42}; 43 44static irqreturn_t v_ADDI_Interrupt(int irq, void *d) 45{ 46 struct comedi_device *dev = d; 47 const struct addi_board *this_board = dev->board_ptr; 48 49 this_board->interrupt(irq, d); 50 return IRQ_RETVAL(1); 51} 52 53static int apci3120_auto_attach(struct comedi_device *dev, 54 unsigned long context) 55{ 56 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 57 const struct addi_board *this_board = NULL; 58 struct addi_private *devpriv; 59 struct comedi_subdevice *s; 60 int ret, order, i; 61 62 if (context < ARRAY_SIZE(apci3120_boardtypes)) 63 this_board = &apci3120_boardtypes[context]; 64 if (!this_board) 65 return -ENODEV; 66 dev->board_ptr = this_board; 67 dev->board_name = this_board->pc_DriverName; 68 69 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 70 if (!devpriv) 71 return -ENOMEM; 72 73 ret = comedi_pci_enable(dev); 74 if (ret) 75 return ret; 76 pci_set_master(pcidev); 77 78 dev->iobase = pci_resource_start(pcidev, 1); 79 devpriv->iobase = dev->iobase; 80 devpriv->i_IobaseAmcc = pci_resource_start(pcidev, 0); 81 devpriv->i_IobaseAddon = pci_resource_start(pcidev, 2); 82 devpriv->i_IobaseReserved = pci_resource_start(pcidev, 3); 83 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 91 /* Allocate DMA buffers */ 92 for (i = 0; i < 2; i++) { 93 for (order = 2; order >= 0; order--) { 94 devpriv->ul_DmaBufferVirtual[i] = 95 dma_alloc_coherent(dev->hw_dev, PAGE_SIZE << order, 96 &devpriv->ul_DmaBufferHw[i], 97 GFP_KERNEL); 98 99 if (devpriv->ul_DmaBufferVirtual[i]) 100 break; 101 } 102 if (!devpriv->ul_DmaBufferVirtual[i]) 103 break; 104 devpriv->ui_DmaBufferSize[i] = PAGE_SIZE << order; 105 } 106 if (devpriv->ul_DmaBufferVirtual[0]) 107 devpriv->us_UseDma = 1; 108 109 if (devpriv->ul_DmaBufferVirtual[1]) 110 devpriv->b_DmaDoubleBuffer = 1; 111 112 ret = comedi_alloc_subdevices(dev, 5); 113 if (ret) 114 return ret; 115 116 /* Allocate and Initialise AI Subdevice Structures */ 117 s = &dev->subdevices[0]; 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; 123 if (this_board->i_NbrAiChannel) 124 s->n_chan = this_board->i_NbrAiChannel; 125 else 126 s->n_chan = this_board->i_NbrAiChannelDiff; 127 s->maxdata = this_board->i_AiMaxdata; 128 s->len_chanlist = this_board->i_AiChannelList; 129 s->range_table = &range_apci3120_ai; 130 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; 136 137 /* Allocate and Initialise AO Subdevice Structures */ 138 s = &dev->subdevices[1]; 139 if (this_board->i_NbrAoChannel) { 140 s->type = COMEDI_SUBD_AO; 141 s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON; 142 s->n_chan = this_board->i_NbrAoChannel; 143 s->maxdata = this_board->i_AoMaxdata; 144 s->len_chanlist = this_board->i_NbrAoChannel; 145 s->range_table = &range_apci3120_ao; 146 s->insn_write = apci3120_ao_insn_write; 147 } else { 148 s->type = COMEDI_SUBD_UNUSED; 149 } 150 151 /* Allocate and Initialise DI Subdevice Structures */ 152 s = &dev->subdevices[2]; 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; 159 s->insn_bits = apci3120_di_insn_bits; 160 161 /* Allocate and Initialise DO Subdevice Structures */ 162 s = &dev->subdevices[3]; 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; 170 s->insn_bits = apci3120_do_insn_bits; 171 172 /* Allocate and Initialise Timer Subdevice Structures */ 173 s = &dev->subdevices[4]; 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 181 s->insn_write = apci3120_write_insn_timer; 182 s->insn_read = apci3120_read_insn_timer; 183 s->insn_config = apci3120_config_insn_timer; 184 185 apci3120_reset(dev); 186 return 0; 187} 188 189static void apci3120_detach(struct comedi_device *dev) 190{ 191 struct addi_private *devpriv = dev->private; 192 193 if (dev->iobase) 194 apci3120_reset(dev); 195 comedi_pci_detach(dev); 196 if (devpriv) { 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 } 207 } 208 } 209} 210 211static struct comedi_driver apci3120_driver = { 212 .driver_name = "addi_apci_3120", 213 .module = THIS_MODULE, 214 .auto_attach = apci3120_auto_attach, 215 .detach = apci3120_detach, 216}; 217 218static int apci3120_pci_probe(struct pci_dev *dev, 219 const struct pci_device_id *id) 220{ 221 return comedi_pci_auto_config(dev, &apci3120_driver, id->driver_data); 222} 223 224static const struct pci_device_id apci3120_pci_table[] = { 225 { PCI_VDEVICE(AMCC, 0x818d), BOARD_APCI3120 }, 226 { PCI_VDEVICE(AMCC, 0x828d), BOARD_APCI3001 }, 227 { 0 } 228}; 229MODULE_DEVICE_TABLE(pci, apci3120_pci_table); 230 231static struct pci_driver apci3120_pci_driver = { 232 .name = "addi_apci_3120", 233 .id_table = apci3120_pci_table, 234 .probe = apci3120_pci_probe, 235 .remove = comedi_pci_auto_unconfig, 236}; 237module_comedi_pci_driver(apci3120_driver, apci3120_pci_driver); 238 239MODULE_AUTHOR("Comedi http://www.comedi.org"); 240MODULE_DESCRIPTION("ADDI-DATA APCI-3120, Analog input board"); 241MODULE_LICENSE("GPL"); 242