staging: comedi: rtd520 complete the refactor to remove all forward declarations
Complete the refactor of the rtd520 driver to remove all the forward declarations. Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com> Cc: Ian Abbott <abbotti@mev.co.uk> Cc: Mori Hess <fmhess@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Родитель
262ea0d476
Коммит
c98d90fd1f
|
@ -702,444 +702,47 @@ struct rtdPrivate {
|
|||
#define RtdDma1Status(dev) \
|
||||
readb(devpriv->lcfg+LCFG_DMACSR1)
|
||||
|
||||
static int rtd_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn, unsigned int *data);
|
||||
static int rtd_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn, unsigned int *data);
|
||||
static int rtd_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn, unsigned int *data);
|
||||
static int rtd_dio_insn_bits(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn, unsigned int *data);
|
||||
static int rtd_dio_insn_config(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn, unsigned int *data);
|
||||
static int rtd_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
|
||||
struct comedi_cmd *cmd);
|
||||
static int rtd_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
|
||||
static int rtd_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
|
||||
/*
|
||||
* static int rtd_ai_poll(struct comedi_device *dev,
|
||||
* struct comedi_subdevice *s);
|
||||
*/
|
||||
static int rtd_ns_to_timer(unsigned int *ns, int roundMode);
|
||||
static irqreturn_t rtd_interrupt(int irq, void *d);
|
||||
static int rtd520_probe_fifo_depth(struct comedi_device *dev);
|
||||
Given a desired period and the clock period (both in ns),
|
||||
return the proper counter value (divider-1).
|
||||
Sets the original period to be the true value.
|
||||
Note: you have to check if the value is larger than the counter range!
|
||||
*/
|
||||
static int rtd_ns_to_timer_base(unsigned int *nanosec, /* desired period (in ns) */
|
||||
int round_mode, int base)
|
||||
{ /* clock period (in ns) */
|
||||
int divider;
|
||||
|
||||
/*
|
||||
* Attach is called by the Comedi core to configure the driver
|
||||
* for a particular board. If you specified a board_name array
|
||||
* in the driver structure, dev->board_ptr contains that
|
||||
* address.
|
||||
*/
|
||||
static int rtd_attach(struct comedi_device *dev, struct comedi_devconfig *it)
|
||||
{ /* board name and options flags */
|
||||
struct comedi_subdevice *s;
|
||||
struct pci_dev *pcidev;
|
||||
int ret;
|
||||
resource_size_t physLas0; /* configuration */
|
||||
resource_size_t physLas1; /* data area */
|
||||
resource_size_t physLcfg; /* PLX9080 */
|
||||
#ifdef USE_DMA
|
||||
int index;
|
||||
#endif
|
||||
|
||||
printk(KERN_INFO "comedi%d: rtd520 attaching.\n", dev->minor);
|
||||
|
||||
#if defined(CONFIG_COMEDI_DEBUG) && defined(USE_DMA)
|
||||
/* You can set this a load time: modprobe comedi comedi_debug=1 */
|
||||
if (0 == comedi_debug) /* force DMA debug printks */
|
||||
comedi_debug = 1;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Allocate the private structure area. alloc_private() is a
|
||||
* convenient macro defined in comedidev.h.
|
||||
*/
|
||||
if (alloc_private(dev, sizeof(struct rtdPrivate)) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Probe the device to determine what device in the series it is.
|
||||
*/
|
||||
for (pcidev = pci_get_device(PCI_VENDOR_ID_RTD, PCI_ANY_ID, NULL);
|
||||
pcidev != NULL;
|
||||
pcidev = pci_get_device(PCI_VENDOR_ID_RTD, PCI_ANY_ID, pcidev)) {
|
||||
int i;
|
||||
|
||||
if (it->options[0] || it->options[1]) {
|
||||
if (pcidev->bus->number != it->options[0]
|
||||
|| PCI_SLOT(pcidev->devfn) != it->options[1]) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(rtd520Boards); ++i) {
|
||||
if (pcidev->device == rtd520Boards[i].device_id) {
|
||||
dev->board_ptr = &rtd520Boards[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dev->board_ptr)
|
||||
break; /* found one */
|
||||
switch (round_mode) {
|
||||
case TRIG_ROUND_NEAREST:
|
||||
default:
|
||||
divider = (*nanosec + base / 2) / base;
|
||||
break;
|
||||
case TRIG_ROUND_DOWN:
|
||||
divider = (*nanosec) / base;
|
||||
break;
|
||||
case TRIG_ROUND_UP:
|
||||
divider = (*nanosec + base - 1) / base;
|
||||
break;
|
||||
}
|
||||
if (!pcidev) {
|
||||
if (it->options[0] && it->options[1]) {
|
||||
printk(KERN_INFO "No RTD card at bus=%d slot=%d.\n",
|
||||
it->options[0], it->options[1]);
|
||||
} else {
|
||||
printk(KERN_INFO "No RTD card found.\n");
|
||||
}
|
||||
return -EIO;
|
||||
}
|
||||
devpriv->pci_dev = pcidev;
|
||||
dev->board_name = thisboard->name;
|
||||
if (divider < 2)
|
||||
divider = 2; /* min is divide by 2 */
|
||||
|
||||
ret = comedi_pci_enable(pcidev, DRV_NAME);
|
||||
if (ret < 0) {
|
||||
printk(KERN_INFO "Failed to enable PCI device and request regions.\n");
|
||||
return ret;
|
||||
}
|
||||
devpriv->got_regions = 1;
|
||||
/* Note: we don't check for max, because different timers
|
||||
have different ranges */
|
||||
|
||||
/*
|
||||
* Initialize base addresses
|
||||
*/
|
||||
/* Get the physical address from PCI config */
|
||||
physLas0 = pci_resource_start(devpriv->pci_dev, LAS0_PCIINDEX);
|
||||
physLas1 = pci_resource_start(devpriv->pci_dev, LAS1_PCIINDEX);
|
||||
physLcfg = pci_resource_start(devpriv->pci_dev, LCFG_PCIINDEX);
|
||||
/* Now have the kernel map this into memory */
|
||||
/* ASSUME page aligned */
|
||||
devpriv->las0 = ioremap_nocache(physLas0, LAS0_PCISIZE);
|
||||
devpriv->las1 = ioremap_nocache(physLas1, LAS1_PCISIZE);
|
||||
devpriv->lcfg = ioremap_nocache(physLcfg, LCFG_PCISIZE);
|
||||
|
||||
if (!devpriv->las0 || !devpriv->las1 || !devpriv->lcfg)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
DPRINTK("%s: LAS0=%llx, LAS1=%llx, CFG=%llx.\n", dev->board_name,
|
||||
(unsigned long long)physLas0, (unsigned long long)physLas1,
|
||||
(unsigned long long)physLcfg);
|
||||
{ /* The RTD driver does this */
|
||||
unsigned char pci_latency;
|
||||
u16 revision;
|
||||
/*uint32_t epld_version; */
|
||||
|
||||
pci_read_config_word(devpriv->pci_dev, PCI_REVISION_ID,
|
||||
&revision);
|
||||
DPRINTK("%s: PCI revision %d.\n", dev->board_name, revision);
|
||||
|
||||
pci_read_config_byte(devpriv->pci_dev,
|
||||
PCI_LATENCY_TIMER, &pci_latency);
|
||||
if (pci_latency < 32) {
|
||||
printk(KERN_INFO "%s: PCI latency changed from %d to %d\n",
|
||||
dev->board_name, pci_latency, 32);
|
||||
pci_write_config_byte(devpriv->pci_dev,
|
||||
PCI_LATENCY_TIMER, 32);
|
||||
} else {
|
||||
DPRINTK("rtd520: PCI latency = %d\n", pci_latency);
|
||||
}
|
||||
|
||||
/*
|
||||
* Undocumented EPLD version (doesn't match RTD driver results)
|
||||
*/
|
||||
/*DPRINTK ("rtd520: Reading epld from %p\n",
|
||||
devpriv->las0+0);
|
||||
epld_version = readl (devpriv->las0+0);
|
||||
if ((epld_version & 0xF0) >> 4 == 0x0F) {
|
||||
DPRINTK("rtd520: pre-v8 EPLD. (%x)\n", epld_version);
|
||||
} else {
|
||||
DPRINTK("rtd520: EPLD version %x.\n", epld_version >> 4);
|
||||
} */
|
||||
}
|
||||
|
||||
/* Show board configuration */
|
||||
printk(KERN_INFO "%s:", dev->board_name);
|
||||
|
||||
/*
|
||||
* Allocate the subdevice structures. alloc_subdevice() is a
|
||||
* convenient macro defined in comedidev.h.
|
||||
*/
|
||||
if (alloc_subdevices(dev, 4) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
s = dev->subdevices + 0;
|
||||
dev->read_subdev = s;
|
||||
/* analog input subdevice */
|
||||
s->type = COMEDI_SUBD_AI;
|
||||
s->subdev_flags =
|
||||
SDF_READABLE | SDF_GROUND | SDF_COMMON | SDF_DIFF | SDF_CMD_READ;
|
||||
s->n_chan = thisboard->aiChans;
|
||||
s->maxdata = (1 << thisboard->aiBits) - 1;
|
||||
if (thisboard->aiMaxGain <= 32)
|
||||
s->range_table = &rtd_ai_7520_range;
|
||||
else
|
||||
s->range_table = &rtd_ai_4520_range;
|
||||
|
||||
s->len_chanlist = RTD_MAX_CHANLIST; /* devpriv->fifoLen */
|
||||
s->insn_read = rtd_ai_rinsn;
|
||||
s->do_cmd = rtd_ai_cmd;
|
||||
s->do_cmdtest = rtd_ai_cmdtest;
|
||||
s->cancel = rtd_ai_cancel;
|
||||
/* s->poll = rtd_ai_poll; *//* not ready yet */
|
||||
|
||||
s = dev->subdevices + 1;
|
||||
/* analog output subdevice */
|
||||
s->type = COMEDI_SUBD_AO;
|
||||
s->subdev_flags = SDF_WRITABLE;
|
||||
s->n_chan = 2;
|
||||
s->maxdata = (1 << thisboard->aiBits) - 1;
|
||||
s->range_table = &rtd_ao_range;
|
||||
s->insn_write = rtd_ao_winsn;
|
||||
s->insn_read = rtd_ao_rinsn;
|
||||
|
||||
s = dev->subdevices + 2;
|
||||
/* digital i/o subdevice */
|
||||
s->type = COMEDI_SUBD_DIO;
|
||||
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
|
||||
/* we only support port 0 right now. Ignoring port 1 and user IO */
|
||||
s->n_chan = 8;
|
||||
s->maxdata = 1;
|
||||
s->range_table = &range_digital;
|
||||
s->insn_bits = rtd_dio_insn_bits;
|
||||
s->insn_config = rtd_dio_insn_config;
|
||||
|
||||
/* timer/counter subdevices (not currently supported) */
|
||||
s = dev->subdevices + 3;
|
||||
s->type = COMEDI_SUBD_COUNTER;
|
||||
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
|
||||
s->n_chan = 3;
|
||||
s->maxdata = 0xffff;
|
||||
|
||||
/* initialize board, per RTD spec */
|
||||
/* also, initialize shadow registers */
|
||||
RtdResetBoard(dev);
|
||||
udelay(100); /* needed? */
|
||||
RtdPlxInterruptWrite(dev, 0);
|
||||
RtdInterruptMask(dev, 0); /* and sets shadow */
|
||||
RtdInterruptClearMask(dev, ~0); /* and sets shadow */
|
||||
RtdInterruptClear(dev); /* clears bits set by mask */
|
||||
RtdInterruptOverrunClear(dev);
|
||||
RtdClearCGT(dev);
|
||||
RtdAdcClearFifo(dev);
|
||||
RtdDacClearFifo(dev, 0);
|
||||
RtdDacClearFifo(dev, 1);
|
||||
/* clear digital IO fifo */
|
||||
RtdDioStatusWrite(dev, 0); /* safe state, set shadow */
|
||||
RtdUtcCtrlPut(dev, 0, 0x30); /* safe state, set shadow */
|
||||
RtdUtcCtrlPut(dev, 1, 0x30); /* safe state, set shadow */
|
||||
RtdUtcCtrlPut(dev, 2, 0x30); /* safe state, set shadow */
|
||||
RtdUtcCtrlPut(dev, 3, 0); /* safe state, set shadow */
|
||||
/* TODO: set user out source ??? */
|
||||
|
||||
/* check if our interrupt is available and get it */
|
||||
ret = request_irq(devpriv->pci_dev->irq, rtd_interrupt,
|
||||
IRQF_SHARED, DRV_NAME, dev);
|
||||
|
||||
if (ret < 0) {
|
||||
printk("Could not get interrupt! (%u)\n",
|
||||
devpriv->pci_dev->irq);
|
||||
return ret;
|
||||
}
|
||||
dev->irq = devpriv->pci_dev->irq;
|
||||
printk(KERN_INFO "( irq=%u )", dev->irq);
|
||||
|
||||
ret = rtd520_probe_fifo_depth(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
devpriv->fifoLen = ret;
|
||||
printk("( fifoLen=%d )", devpriv->fifoLen);
|
||||
|
||||
#ifdef USE_DMA
|
||||
if (dev->irq > 0) {
|
||||
printk("( DMA buff=%d )\n", DMA_CHAIN_COUNT);
|
||||
/*
|
||||
* The PLX9080 has 2 DMA controllers, but there could be
|
||||
* 4 sources: ADC, digital, DAC1, and DAC2. Since only the
|
||||
* ADC supports cmd mode right now, this isn't an issue (yet)
|
||||
*/
|
||||
devpriv->dma0Offset = 0;
|
||||
|
||||
for (index = 0; index < DMA_CHAIN_COUNT; index++) {
|
||||
devpriv->dma0Buff[index] =
|
||||
pci_alloc_consistent(devpriv->pci_dev,
|
||||
sizeof(u16) *
|
||||
devpriv->fifoLen / 2,
|
||||
&devpriv->
|
||||
dma0BuffPhysAddr[index]);
|
||||
if (devpriv->dma0Buff[index] == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto rtd_attach_die_error;
|
||||
}
|
||||
/*DPRINTK ("buff[%d] @ %p virtual, %x PCI\n",
|
||||
index,
|
||||
devpriv->dma0Buff[index],
|
||||
devpriv->dma0BuffPhysAddr[index]); */
|
||||
}
|
||||
|
||||
/*
|
||||
* setup DMA descriptor ring (use cpu_to_le32 for byte
|
||||
* ordering?)
|
||||
*/
|
||||
devpriv->dma0Chain =
|
||||
pci_alloc_consistent(devpriv->pci_dev,
|
||||
sizeof(struct plx_dma_desc) *
|
||||
DMA_CHAIN_COUNT,
|
||||
&devpriv->dma0ChainPhysAddr);
|
||||
for (index = 0; index < DMA_CHAIN_COUNT; index++) {
|
||||
devpriv->dma0Chain[index].pci_start_addr =
|
||||
devpriv->dma0BuffPhysAddr[index];
|
||||
devpriv->dma0Chain[index].local_start_addr =
|
||||
DMALADDR_ADC;
|
||||
devpriv->dma0Chain[index].transfer_size =
|
||||
sizeof(u16) * devpriv->fifoLen / 2;
|
||||
devpriv->dma0Chain[index].next =
|
||||
(devpriv->dma0ChainPhysAddr + ((index +
|
||||
1) %
|
||||
(DMA_CHAIN_COUNT))
|
||||
* sizeof(devpriv->dma0Chain[0]))
|
||||
| DMA_TRANSFER_BITS;
|
||||
/*DPRINTK ("ring[%d] @%lx PCI: %x, local: %x, N: 0x%x, next: %x\n",
|
||||
index,
|
||||
((long)devpriv->dma0ChainPhysAddr
|
||||
+ (index * sizeof(devpriv->dma0Chain[0]))),
|
||||
devpriv->dma0Chain[index].pci_start_addr,
|
||||
devpriv->dma0Chain[index].local_start_addr,
|
||||
devpriv->dma0Chain[index].transfer_size,
|
||||
devpriv->dma0Chain[index].next); */
|
||||
}
|
||||
|
||||
if (devpriv->dma0Chain == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto rtd_attach_die_error;
|
||||
}
|
||||
|
||||
RtdDma0Mode(dev, DMA_MODE_BITS);
|
||||
/* set DMA trigger source */
|
||||
RtdDma0Source(dev, DMAS_ADFIFO_HALF_FULL);
|
||||
} else {
|
||||
printk(KERN_INFO "( no IRQ->no DMA )");
|
||||
}
|
||||
#endif /* USE_DMA */
|
||||
|
||||
if (dev->irq) { /* enable plx9080 interrupts */
|
||||
RtdPlxInterruptWrite(dev, ICS_PIE | ICS_PLIE);
|
||||
}
|
||||
|
||||
printk("\ncomedi%d: rtd520 driver attached.\n", dev->minor);
|
||||
|
||||
return 1;
|
||||
|
||||
#if 0
|
||||
/* hit an error, clean up memory and return ret */
|
||||
/* rtd_attach_die_error: */
|
||||
#ifdef USE_DMA
|
||||
for (index = 0; index < DMA_CHAIN_COUNT; index++) {
|
||||
if (NULL != devpriv->dma0Buff[index]) { /* free buffer memory */
|
||||
pci_free_consistent(devpriv->pci_dev,
|
||||
sizeof(u16) * devpriv->fifoLen / 2,
|
||||
devpriv->dma0Buff[index],
|
||||
devpriv->dma0BuffPhysAddr[index]);
|
||||
devpriv->dma0Buff[index] = NULL;
|
||||
}
|
||||
}
|
||||
if (NULL != devpriv->dma0Chain) {
|
||||
pci_free_consistent(devpriv->pci_dev,
|
||||
sizeof(struct plx_dma_desc)
|
||||
* DMA_CHAIN_COUNT,
|
||||
devpriv->dma0Chain,
|
||||
devpriv->dma0ChainPhysAddr);
|
||||
devpriv->dma0Chain = NULL;
|
||||
}
|
||||
#endif /* USE_DMA */
|
||||
/* subdevices and priv are freed by the core */
|
||||
if (dev->irq) {
|
||||
/* disable interrupt controller */
|
||||
RtdPlxInterruptWrite(dev, RtdPlxInterruptRead(dev)
|
||||
& ~(ICS_PLIE | ICS_DMA0_E | ICS_DMA1_E));
|
||||
free_irq(dev->irq, dev);
|
||||
}
|
||||
|
||||
/* release all regions that were allocated */
|
||||
if (devpriv->las0)
|
||||
iounmap(devpriv->las0);
|
||||
|
||||
if (devpriv->las1)
|
||||
iounmap(devpriv->las1);
|
||||
|
||||
if (devpriv->lcfg)
|
||||
iounmap(devpriv->lcfg);
|
||||
|
||||
if (devpriv->pci_dev)
|
||||
pci_dev_put(devpriv->pci_dev);
|
||||
|
||||
return ret;
|
||||
#endif
|
||||
*nanosec = base * divider;
|
||||
return divider - 1; /* countdown is divisor+1 */
|
||||
}
|
||||
|
||||
static void rtd_detach(struct comedi_device *dev)
|
||||
/*
|
||||
Given a desired period (in ns),
|
||||
return the proper counter value (divider-1) for the internal clock.
|
||||
Sets the original period to be the true value.
|
||||
*/
|
||||
static int rtd_ns_to_timer(unsigned int *ns, int round_mode)
|
||||
{
|
||||
#ifdef USE_DMA
|
||||
int index;
|
||||
#endif
|
||||
|
||||
if (devpriv) {
|
||||
/* Shut down any board ops by resetting it */
|
||||
#ifdef USE_DMA
|
||||
if (devpriv->lcfg) {
|
||||
RtdDma0Control(dev, 0); /* disable DMA */
|
||||
RtdDma1Control(dev, 0); /* disable DMA */
|
||||
RtdPlxInterruptWrite(dev, ICS_PIE | ICS_PLIE);
|
||||
}
|
||||
#endif /* USE_DMA */
|
||||
if (devpriv->las0) {
|
||||
RtdResetBoard(dev);
|
||||
RtdInterruptMask(dev, 0);
|
||||
RtdInterruptClearMask(dev, ~0);
|
||||
RtdInterruptClear(dev); /* clears bits set by mask */
|
||||
}
|
||||
#ifdef USE_DMA
|
||||
/* release DMA */
|
||||
for (index = 0; index < DMA_CHAIN_COUNT; index++) {
|
||||
if (NULL != devpriv->dma0Buff[index]) {
|
||||
pci_free_consistent(devpriv->pci_dev,
|
||||
sizeof(u16) *
|
||||
devpriv->fifoLen / 2,
|
||||
devpriv->dma0Buff[index],
|
||||
devpriv->
|
||||
dma0BuffPhysAddr[index]);
|
||||
devpriv->dma0Buff[index] = NULL;
|
||||
}
|
||||
}
|
||||
if (NULL != devpriv->dma0Chain) {
|
||||
pci_free_consistent(devpriv->pci_dev,
|
||||
sizeof(struct plx_dma_desc) *
|
||||
DMA_CHAIN_COUNT, devpriv->dma0Chain,
|
||||
devpriv->dma0ChainPhysAddr);
|
||||
devpriv->dma0Chain = NULL;
|
||||
}
|
||||
#endif /* USE_DMA */
|
||||
if (dev->irq) {
|
||||
RtdPlxInterruptWrite(dev, RtdPlxInterruptRead(dev)
|
||||
& ~(ICS_PLIE | ICS_DMA0_E |
|
||||
ICS_DMA1_E));
|
||||
free_irq(dev->irq, dev);
|
||||
}
|
||||
if (devpriv->las0)
|
||||
iounmap(devpriv->las0);
|
||||
if (devpriv->las1)
|
||||
iounmap(devpriv->las1);
|
||||
if (devpriv->lcfg)
|
||||
iounmap(devpriv->lcfg);
|
||||
if (devpriv->pci_dev) {
|
||||
if (devpriv->got_regions)
|
||||
comedi_pci_disable(devpriv->pci_dev);
|
||||
pci_dev_put(devpriv->pci_dev);
|
||||
}
|
||||
}
|
||||
return rtd_ns_to_timer_base(ns, round_mode, RTD_CLOCK_BASE);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2117,49 +1720,6 @@ static int rtd_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Given a desired period and the clock period (both in ns),
|
||||
return the proper counter value (divider-1).
|
||||
Sets the original period to be the true value.
|
||||
Note: you have to check if the value is larger than the counter range!
|
||||
*/
|
||||
static int rtd_ns_to_timer_base(unsigned int *nanosec, /* desired period (in ns) */
|
||||
int round_mode, int base)
|
||||
{ /* clock period (in ns) */
|
||||
int divider;
|
||||
|
||||
switch (round_mode) {
|
||||
case TRIG_ROUND_NEAREST:
|
||||
default:
|
||||
divider = (*nanosec + base / 2) / base;
|
||||
break;
|
||||
case TRIG_ROUND_DOWN:
|
||||
divider = (*nanosec) / base;
|
||||
break;
|
||||
case TRIG_ROUND_UP:
|
||||
divider = (*nanosec + base - 1) / base;
|
||||
break;
|
||||
}
|
||||
if (divider < 2)
|
||||
divider = 2; /* min is divide by 2 */
|
||||
|
||||
/* Note: we don't check for max, because different timers
|
||||
have different ranges */
|
||||
|
||||
*nanosec = base * divider;
|
||||
return divider - 1; /* countdown is divisor+1 */
|
||||
}
|
||||
|
||||
/*
|
||||
Given a desired period (in ns),
|
||||
return the proper counter value (divider-1) for the internal clock.
|
||||
Sets the original period to be the true value.
|
||||
*/
|
||||
static int rtd_ns_to_timer(unsigned int *ns, int round_mode)
|
||||
{
|
||||
return rtd_ns_to_timer_base(ns, round_mode, RTD_CLOCK_BASE);
|
||||
}
|
||||
|
||||
/*
|
||||
Output one (or more) analog values to a single port as fast as possible.
|
||||
*/
|
||||
|
@ -2314,6 +1874,416 @@ static int rtd_dio_insn_config(struct comedi_device *dev,
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int rtd_attach(struct comedi_device *dev, struct comedi_devconfig *it)
|
||||
{ /* board name and options flags */
|
||||
struct comedi_subdevice *s;
|
||||
struct pci_dev *pcidev;
|
||||
int ret;
|
||||
resource_size_t physLas0; /* configuration */
|
||||
resource_size_t physLas1; /* data area */
|
||||
resource_size_t physLcfg; /* PLX9080 */
|
||||
#ifdef USE_DMA
|
||||
int index;
|
||||
#endif
|
||||
|
||||
printk(KERN_INFO "comedi%d: rtd520 attaching.\n", dev->minor);
|
||||
|
||||
#if defined(CONFIG_COMEDI_DEBUG) && defined(USE_DMA)
|
||||
/* You can set this a load time: modprobe comedi comedi_debug=1 */
|
||||
if (0 == comedi_debug) /* force DMA debug printks */
|
||||
comedi_debug = 1;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Allocate the private structure area. alloc_private() is a
|
||||
* convenient macro defined in comedidev.h.
|
||||
*/
|
||||
if (alloc_private(dev, sizeof(struct rtdPrivate)) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Probe the device to determine what device in the series it is.
|
||||
*/
|
||||
for (pcidev = pci_get_device(PCI_VENDOR_ID_RTD, PCI_ANY_ID, NULL);
|
||||
pcidev != NULL;
|
||||
pcidev = pci_get_device(PCI_VENDOR_ID_RTD, PCI_ANY_ID, pcidev)) {
|
||||
int i;
|
||||
|
||||
if (it->options[0] || it->options[1]) {
|
||||
if (pcidev->bus->number != it->options[0]
|
||||
|| PCI_SLOT(pcidev->devfn) != it->options[1]) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(rtd520Boards); ++i) {
|
||||
if (pcidev->device == rtd520Boards[i].device_id) {
|
||||
dev->board_ptr = &rtd520Boards[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dev->board_ptr)
|
||||
break; /* found one */
|
||||
}
|
||||
if (!pcidev) {
|
||||
if (it->options[0] && it->options[1]) {
|
||||
printk(KERN_INFO "No RTD card at bus=%d slot=%d.\n",
|
||||
it->options[0], it->options[1]);
|
||||
} else {
|
||||
printk(KERN_INFO "No RTD card found.\n");
|
||||
}
|
||||
return -EIO;
|
||||
}
|
||||
devpriv->pci_dev = pcidev;
|
||||
dev->board_name = thisboard->name;
|
||||
|
||||
ret = comedi_pci_enable(pcidev, DRV_NAME);
|
||||
if (ret < 0) {
|
||||
printk(KERN_INFO "Failed to enable PCI device and request regions.\n");
|
||||
return ret;
|
||||
}
|
||||
devpriv->got_regions = 1;
|
||||
|
||||
/*
|
||||
* Initialize base addresses
|
||||
*/
|
||||
/* Get the physical address from PCI config */
|
||||
physLas0 = pci_resource_start(devpriv->pci_dev, LAS0_PCIINDEX);
|
||||
physLas1 = pci_resource_start(devpriv->pci_dev, LAS1_PCIINDEX);
|
||||
physLcfg = pci_resource_start(devpriv->pci_dev, LCFG_PCIINDEX);
|
||||
/* Now have the kernel map this into memory */
|
||||
/* ASSUME page aligned */
|
||||
devpriv->las0 = ioremap_nocache(physLas0, LAS0_PCISIZE);
|
||||
devpriv->las1 = ioremap_nocache(physLas1, LAS1_PCISIZE);
|
||||
devpriv->lcfg = ioremap_nocache(physLcfg, LCFG_PCISIZE);
|
||||
|
||||
if (!devpriv->las0 || !devpriv->las1 || !devpriv->lcfg)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
DPRINTK("%s: LAS0=%llx, LAS1=%llx, CFG=%llx.\n", dev->board_name,
|
||||
(unsigned long long)physLas0, (unsigned long long)physLas1,
|
||||
(unsigned long long)physLcfg);
|
||||
{ /* The RTD driver does this */
|
||||
unsigned char pci_latency;
|
||||
u16 revision;
|
||||
/*uint32_t epld_version; */
|
||||
|
||||
pci_read_config_word(devpriv->pci_dev, PCI_REVISION_ID,
|
||||
&revision);
|
||||
DPRINTK("%s: PCI revision %d.\n", dev->board_name, revision);
|
||||
|
||||
pci_read_config_byte(devpriv->pci_dev,
|
||||
PCI_LATENCY_TIMER, &pci_latency);
|
||||
if (pci_latency < 32) {
|
||||
printk(KERN_INFO "%s: PCI latency changed from %d to %d\n",
|
||||
dev->board_name, pci_latency, 32);
|
||||
pci_write_config_byte(devpriv->pci_dev,
|
||||
PCI_LATENCY_TIMER, 32);
|
||||
} else {
|
||||
DPRINTK("rtd520: PCI latency = %d\n", pci_latency);
|
||||
}
|
||||
|
||||
/*
|
||||
* Undocumented EPLD version (doesn't match RTD driver results)
|
||||
*/
|
||||
/*DPRINTK ("rtd520: Reading epld from %p\n",
|
||||
devpriv->las0+0);
|
||||
epld_version = readl (devpriv->las0+0);
|
||||
if ((epld_version & 0xF0) >> 4 == 0x0F) {
|
||||
DPRINTK("rtd520: pre-v8 EPLD. (%x)\n", epld_version);
|
||||
} else {
|
||||
DPRINTK("rtd520: EPLD version %x.\n", epld_version >> 4);
|
||||
} */
|
||||
}
|
||||
|
||||
/* Show board configuration */
|
||||
printk(KERN_INFO "%s:", dev->board_name);
|
||||
|
||||
/*
|
||||
* Allocate the subdevice structures. alloc_subdevice() is a
|
||||
* convenient macro defined in comedidev.h.
|
||||
*/
|
||||
if (alloc_subdevices(dev, 4) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
s = dev->subdevices + 0;
|
||||
dev->read_subdev = s;
|
||||
/* analog input subdevice */
|
||||
s->type = COMEDI_SUBD_AI;
|
||||
s->subdev_flags =
|
||||
SDF_READABLE | SDF_GROUND | SDF_COMMON | SDF_DIFF | SDF_CMD_READ;
|
||||
s->n_chan = thisboard->aiChans;
|
||||
s->maxdata = (1 << thisboard->aiBits) - 1;
|
||||
if (thisboard->aiMaxGain <= 32)
|
||||
s->range_table = &rtd_ai_7520_range;
|
||||
else
|
||||
s->range_table = &rtd_ai_4520_range;
|
||||
|
||||
s->len_chanlist = RTD_MAX_CHANLIST; /* devpriv->fifoLen */
|
||||
s->insn_read = rtd_ai_rinsn;
|
||||
s->do_cmd = rtd_ai_cmd;
|
||||
s->do_cmdtest = rtd_ai_cmdtest;
|
||||
s->cancel = rtd_ai_cancel;
|
||||
/* s->poll = rtd_ai_poll; *//* not ready yet */
|
||||
|
||||
s = dev->subdevices + 1;
|
||||
/* analog output subdevice */
|
||||
s->type = COMEDI_SUBD_AO;
|
||||
s->subdev_flags = SDF_WRITABLE;
|
||||
s->n_chan = 2;
|
||||
s->maxdata = (1 << thisboard->aiBits) - 1;
|
||||
s->range_table = &rtd_ao_range;
|
||||
s->insn_write = rtd_ao_winsn;
|
||||
s->insn_read = rtd_ao_rinsn;
|
||||
|
||||
s = dev->subdevices + 2;
|
||||
/* digital i/o subdevice */
|
||||
s->type = COMEDI_SUBD_DIO;
|
||||
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
|
||||
/* we only support port 0 right now. Ignoring port 1 and user IO */
|
||||
s->n_chan = 8;
|
||||
s->maxdata = 1;
|
||||
s->range_table = &range_digital;
|
||||
s->insn_bits = rtd_dio_insn_bits;
|
||||
s->insn_config = rtd_dio_insn_config;
|
||||
|
||||
/* timer/counter subdevices (not currently supported) */
|
||||
s = dev->subdevices + 3;
|
||||
s->type = COMEDI_SUBD_COUNTER;
|
||||
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
|
||||
s->n_chan = 3;
|
||||
s->maxdata = 0xffff;
|
||||
|
||||
/* initialize board, per RTD spec */
|
||||
/* also, initialize shadow registers */
|
||||
RtdResetBoard(dev);
|
||||
udelay(100); /* needed? */
|
||||
RtdPlxInterruptWrite(dev, 0);
|
||||
RtdInterruptMask(dev, 0); /* and sets shadow */
|
||||
RtdInterruptClearMask(dev, ~0); /* and sets shadow */
|
||||
RtdInterruptClear(dev); /* clears bits set by mask */
|
||||
RtdInterruptOverrunClear(dev);
|
||||
RtdClearCGT(dev);
|
||||
RtdAdcClearFifo(dev);
|
||||
RtdDacClearFifo(dev, 0);
|
||||
RtdDacClearFifo(dev, 1);
|
||||
/* clear digital IO fifo */
|
||||
RtdDioStatusWrite(dev, 0); /* safe state, set shadow */
|
||||
RtdUtcCtrlPut(dev, 0, 0x30); /* safe state, set shadow */
|
||||
RtdUtcCtrlPut(dev, 1, 0x30); /* safe state, set shadow */
|
||||
RtdUtcCtrlPut(dev, 2, 0x30); /* safe state, set shadow */
|
||||
RtdUtcCtrlPut(dev, 3, 0); /* safe state, set shadow */
|
||||
/* TODO: set user out source ??? */
|
||||
|
||||
/* check if our interrupt is available and get it */
|
||||
ret = request_irq(devpriv->pci_dev->irq, rtd_interrupt,
|
||||
IRQF_SHARED, DRV_NAME, dev);
|
||||
|
||||
if (ret < 0) {
|
||||
printk("Could not get interrupt! (%u)\n",
|
||||
devpriv->pci_dev->irq);
|
||||
return ret;
|
||||
}
|
||||
dev->irq = devpriv->pci_dev->irq;
|
||||
printk(KERN_INFO "( irq=%u )", dev->irq);
|
||||
|
||||
ret = rtd520_probe_fifo_depth(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
devpriv->fifoLen = ret;
|
||||
printk("( fifoLen=%d )", devpriv->fifoLen);
|
||||
|
||||
#ifdef USE_DMA
|
||||
if (dev->irq > 0) {
|
||||
printk("( DMA buff=%d )\n", DMA_CHAIN_COUNT);
|
||||
/*
|
||||
* The PLX9080 has 2 DMA controllers, but there could be
|
||||
* 4 sources: ADC, digital, DAC1, and DAC2. Since only the
|
||||
* ADC supports cmd mode right now, this isn't an issue (yet)
|
||||
*/
|
||||
devpriv->dma0Offset = 0;
|
||||
|
||||
for (index = 0; index < DMA_CHAIN_COUNT; index++) {
|
||||
devpriv->dma0Buff[index] =
|
||||
pci_alloc_consistent(devpriv->pci_dev,
|
||||
sizeof(u16) *
|
||||
devpriv->fifoLen / 2,
|
||||
&devpriv->
|
||||
dma0BuffPhysAddr[index]);
|
||||
if (devpriv->dma0Buff[index] == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto rtd_attach_die_error;
|
||||
}
|
||||
/*DPRINTK ("buff[%d] @ %p virtual, %x PCI\n",
|
||||
index,
|
||||
devpriv->dma0Buff[index],
|
||||
devpriv->dma0BuffPhysAddr[index]); */
|
||||
}
|
||||
|
||||
/*
|
||||
* setup DMA descriptor ring (use cpu_to_le32 for byte
|
||||
* ordering?)
|
||||
*/
|
||||
devpriv->dma0Chain =
|
||||
pci_alloc_consistent(devpriv->pci_dev,
|
||||
sizeof(struct plx_dma_desc) *
|
||||
DMA_CHAIN_COUNT,
|
||||
&devpriv->dma0ChainPhysAddr);
|
||||
for (index = 0; index < DMA_CHAIN_COUNT; index++) {
|
||||
devpriv->dma0Chain[index].pci_start_addr =
|
||||
devpriv->dma0BuffPhysAddr[index];
|
||||
devpriv->dma0Chain[index].local_start_addr =
|
||||
DMALADDR_ADC;
|
||||
devpriv->dma0Chain[index].transfer_size =
|
||||
sizeof(u16) * devpriv->fifoLen / 2;
|
||||
devpriv->dma0Chain[index].next =
|
||||
(devpriv->dma0ChainPhysAddr + ((index +
|
||||
1) %
|
||||
(DMA_CHAIN_COUNT))
|
||||
* sizeof(devpriv->dma0Chain[0]))
|
||||
| DMA_TRANSFER_BITS;
|
||||
/*DPRINTK ("ring[%d] @%lx PCI: %x, local: %x, N: 0x%x, next: %x\n",
|
||||
index,
|
||||
((long)devpriv->dma0ChainPhysAddr
|
||||
+ (index * sizeof(devpriv->dma0Chain[0]))),
|
||||
devpriv->dma0Chain[index].pci_start_addr,
|
||||
devpriv->dma0Chain[index].local_start_addr,
|
||||
devpriv->dma0Chain[index].transfer_size,
|
||||
devpriv->dma0Chain[index].next); */
|
||||
}
|
||||
|
||||
if (devpriv->dma0Chain == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto rtd_attach_die_error;
|
||||
}
|
||||
|
||||
RtdDma0Mode(dev, DMA_MODE_BITS);
|
||||
/* set DMA trigger source */
|
||||
RtdDma0Source(dev, DMAS_ADFIFO_HALF_FULL);
|
||||
} else {
|
||||
printk(KERN_INFO "( no IRQ->no DMA )");
|
||||
}
|
||||
#endif /* USE_DMA */
|
||||
|
||||
if (dev->irq) { /* enable plx9080 interrupts */
|
||||
RtdPlxInterruptWrite(dev, ICS_PIE | ICS_PLIE);
|
||||
}
|
||||
|
||||
printk("\ncomedi%d: rtd520 driver attached.\n", dev->minor);
|
||||
|
||||
return 1;
|
||||
|
||||
#if 0
|
||||
/* hit an error, clean up memory and return ret */
|
||||
/* rtd_attach_die_error: */
|
||||
#ifdef USE_DMA
|
||||
for (index = 0; index < DMA_CHAIN_COUNT; index++) {
|
||||
if (NULL != devpriv->dma0Buff[index]) { /* free buffer memory */
|
||||
pci_free_consistent(devpriv->pci_dev,
|
||||
sizeof(u16) * devpriv->fifoLen / 2,
|
||||
devpriv->dma0Buff[index],
|
||||
devpriv->dma0BuffPhysAddr[index]);
|
||||
devpriv->dma0Buff[index] = NULL;
|
||||
}
|
||||
}
|
||||
if (NULL != devpriv->dma0Chain) {
|
||||
pci_free_consistent(devpriv->pci_dev,
|
||||
sizeof(struct plx_dma_desc)
|
||||
* DMA_CHAIN_COUNT,
|
||||
devpriv->dma0Chain,
|
||||
devpriv->dma0ChainPhysAddr);
|
||||
devpriv->dma0Chain = NULL;
|
||||
}
|
||||
#endif /* USE_DMA */
|
||||
/* subdevices and priv are freed by the core */
|
||||
if (dev->irq) {
|
||||
/* disable interrupt controller */
|
||||
RtdPlxInterruptWrite(dev, RtdPlxInterruptRead(dev)
|
||||
& ~(ICS_PLIE | ICS_DMA0_E | ICS_DMA1_E));
|
||||
free_irq(dev->irq, dev);
|
||||
}
|
||||
|
||||
/* release all regions that were allocated */
|
||||
if (devpriv->las0)
|
||||
iounmap(devpriv->las0);
|
||||
|
||||
if (devpriv->las1)
|
||||
iounmap(devpriv->las1);
|
||||
|
||||
if (devpriv->lcfg)
|
||||
iounmap(devpriv->lcfg);
|
||||
|
||||
if (devpriv->pci_dev)
|
||||
pci_dev_put(devpriv->pci_dev);
|
||||
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void rtd_detach(struct comedi_device *dev)
|
||||
{
|
||||
#ifdef USE_DMA
|
||||
int index;
|
||||
#endif
|
||||
|
||||
if (devpriv) {
|
||||
/* Shut down any board ops by resetting it */
|
||||
#ifdef USE_DMA
|
||||
if (devpriv->lcfg) {
|
||||
RtdDma0Control(dev, 0); /* disable DMA */
|
||||
RtdDma1Control(dev, 0); /* disable DMA */
|
||||
RtdPlxInterruptWrite(dev, ICS_PIE | ICS_PLIE);
|
||||
}
|
||||
#endif /* USE_DMA */
|
||||
if (devpriv->las0) {
|
||||
RtdResetBoard(dev);
|
||||
RtdInterruptMask(dev, 0);
|
||||
RtdInterruptClearMask(dev, ~0);
|
||||
RtdInterruptClear(dev); /* clears bits set by mask */
|
||||
}
|
||||
#ifdef USE_DMA
|
||||
/* release DMA */
|
||||
for (index = 0; index < DMA_CHAIN_COUNT; index++) {
|
||||
if (NULL != devpriv->dma0Buff[index]) {
|
||||
pci_free_consistent(devpriv->pci_dev,
|
||||
sizeof(u16) *
|
||||
devpriv->fifoLen / 2,
|
||||
devpriv->dma0Buff[index],
|
||||
devpriv->
|
||||
dma0BuffPhysAddr[index]);
|
||||
devpriv->dma0Buff[index] = NULL;
|
||||
}
|
||||
}
|
||||
if (NULL != devpriv->dma0Chain) {
|
||||
pci_free_consistent(devpriv->pci_dev,
|
||||
sizeof(struct plx_dma_desc) *
|
||||
DMA_CHAIN_COUNT, devpriv->dma0Chain,
|
||||
devpriv->dma0ChainPhysAddr);
|
||||
devpriv->dma0Chain = NULL;
|
||||
}
|
||||
#endif /* USE_DMA */
|
||||
if (dev->irq) {
|
||||
RtdPlxInterruptWrite(dev, RtdPlxInterruptRead(dev)
|
||||
& ~(ICS_PLIE | ICS_DMA0_E |
|
||||
ICS_DMA1_E));
|
||||
free_irq(dev->irq, dev);
|
||||
}
|
||||
if (devpriv->las0)
|
||||
iounmap(devpriv->las0);
|
||||
if (devpriv->las1)
|
||||
iounmap(devpriv->las1);
|
||||
if (devpriv->lcfg)
|
||||
iounmap(devpriv->lcfg);
|
||||
if (devpriv->pci_dev) {
|
||||
if (devpriv->got_regions)
|
||||
comedi_pci_disable(devpriv->pci_dev);
|
||||
pci_dev_put(devpriv->pci_dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct comedi_driver rtd520_driver = {
|
||||
.driver_name = "rtd520",
|
||||
.module = THIS_MODULE,
|
||||
|
|
Загрузка…
Ссылка в новой задаче