#define PRESCALED_TIMER_BASE 10000 // 100kHz 'prescaled' clock for slow aquisition, maybe I'll support this someday
#define QUARTER_AI_FIFO_SIZE 2048 // 1/4 analog input fifo size
// size in bytes of transfers used for dma transfers, also size of buffers that make up dma ring
-#define AI_DMA_TRANSFER_SIZE 0x4000
+#define DMA_TRANSFER_SIZE 0x4000
// number of dma transfers we will chain together into a ring (and the number of dma buffers we maintain)
#define DMA_RING_COUNT 10
unsigned long plx9080_iobase;
unsigned long main_iobase;
unsigned long dio_counter_iobase;
+ // local address (used by dma controller)
+ u32 local_main_iobase;
volatile unsigned int ai_count; // number of analog input samples remaining
u16 *ai_buffer[DMA_RING_COUNT]; // dma buffers for analog input
dma_addr_t ai_buffer_phys_addr[DMA_RING_COUNT]; // physical addresses of ai dma buffers
+ struct plx_dma_desc *dma_desc; // array of dma descriptors read by plx9080, allocated to get proper alignment
volatile unsigned int ao_count; // number of analog output samples remaining
unsigned int ao_value[2]; // remember what the analog outputs are set to, to allow readback
unsigned int hw_revision; // stc chip hardware revision number
unsigned long plx9080_iobase;
unsigned long main_iobase;
unsigned long dio_counter_iobase;
+ u32 local_range, local_decode;
+#ifdef PCIDMA
+ unsigned int bits;
+#endif
printk("comedi%d: cb_pcidas64\n",dev->minor);
devpriv->main_iobase = (unsigned long)ioremap(main_iobase, MAIN_IOSIZE);
devpriv->dio_counter_iobase = (unsigned long)ioremap(dio_counter_iobase, DIO_COUNTER_IOSIZE);
+ // figure out what local address is for main_iobase
+ local_range = readl(devpriv->plx9080_iobase + PLX_LAS0RNG_REG) & LRNG_MEM_MASK;
+ local_decode = readl(devpriv->plx9080_iobase + PLX_LAS0MAP_REG) & local_range & LMAP_MEM_MASK ;
+ devpriv->local_main_iobase = (devpriv->main_phys_iobase & ~local_range) | local_decode;
+
devpriv->hw_revision = HW_REVISION(readw(devpriv->main_iobase + HW_STATUS_REG));
// get irq
}
dev->irq = pcidev->irq;
-#ifdef PCIDMA
- // alocate pci dma buffers
- for(index = 0; index < DMA_RING_COUNT; i++)
- {
- devpriv->ai_buffer[index] =
- pci_alloc_consistent(devpriv->hw_dev, DMA_BUFFER_SIZE, &devpriv->ai_buffer_phys_addr[index]);
- }
-#endif
-
#ifdef PCIDAS64_DEBUG
printk(" plx9080 phys io addr 0x%lx\n", devpriv->plx9080_phys_iobase);
printk(" diocounter virt io addr 0x%lx\n", devpriv->dio_counter_iobase);
printk(" irq %i\n", dev->irq);
+printk(" local main io addr 0x%ulx\n", devpriv->local_main_iobase);
+
printk(" stc hardware revision %i\n", devpriv->hw_revision);
// plx9080 dump
#endif
+#ifdef PCIDMA
+ // alocate pci dma buffers
+ for(index = 0; index < DMA_RING_COUNT; index++)
+ {
+ devpriv->ai_buffer[index] =
+ pci_alloc_consistent(devpriv->hw_dev, DMA_TRANSFER_SIZE, &devpriv->ai_buffer_phys_addr[index]);
+ }
+ // allocate dma descriptors
+ devpriv->dma_desc = kmalloc(sizeof(struct plx_dma_desc) * DMA_RING_COUNT, GFP_KERNEL);
+ // initialize dma descriptors
+ for(index = 0; index < DMA_RING_COUNT; index++)
+ {
+ devpriv->dma_desc[index].pci_start_addr = virt_to_bus(devpriv->ai_buffer[index]);
+ devpriv->dma_desc[index].local_start_addr = devpriv->local_main_iobase + ADC_FIFO_REG;
+ devpriv->dma_desc[index].transfer_size = DMA_TRANSFER_SIZE;
+ devpriv->dma_desc[index].next = virt_to_bus(&devpriv->dma_desc[(index + 1) % (DMA_RING_COUNT)]) |
+ PLX_DESC_IN_PCI_BIT | PLX_INTR_TERM_COUNT | PLX_XFER_LOCAL_TO_PCI;
+ }
+ // do some initialization of plx9080 dma registers
+ /* XXX there are some other bits that can be set here that might
+ * improve performance, but I just want to get it working */
+ bits = 0;
+ // localspace0 bus is 16 bits wide
+ bits |= PLX_LOCAL_BUS_16_WIDE_BITS;
+ // enable ready input, not sure if this is necessary
+ bits |= PLX_DMA_EN_READYIN_BIT;
+ // enable dma chaining
+ bits |= PLX_EN_CHAIN_BIT;
+ // enable interrupt on dma done (probably don't need this, since chain never finishes)
+ bits |= PLX_EN_DMA_DONE_INTR_BIT;
+ // don't increment local address during transfers (we are transferring from a fixed fifo register)
+ bits |= PLX_LOCAL_ADDR_CONST_BIT;
+ // route dma interrupt to pci bus
+ bits |= PLX_DMA_INTR_PCI_BIT;
+ writel(bits, devpriv->plx9080_iobase + PLX_DMA0_MODE_REG);
+
+#endif
/*
* Allocate the subdevice structures.
pci_free_consistent(devpriv->hw_dev, DMA_BUFFER_SIZE,
devpriv->ai_buffer[i], devpriv->ai_buffer_phys_addr[i]);
}
+ // free dma descriptors
+ kfree(devpriv->dma_desc);
#endif
}
if(dev->irq)
{
comedi_async *async = s->async;
comedi_cmd *cmd = &async->cmd;
- unsigned int bits;
+ u32 bits;
unsigned int convert_counter_value;
unsigned int scan_counter_value;
unsigned int i;
// clear adc buffer
writew(0, devpriv->main_iobase + ADC_BUFFER_CLEAR_REG);
+#ifdef PCIDMA
+ // give location of first dma descriptor
+ bits = virt_to_bus(devpriv->dma_desc);
+ bits |= PLX_DESC_IN_PCI_BIT | PLX_INTR_TERM_COUNT | PLX_XFER_LOCAL_TO_PCI;
+ writel(bits, devpriv->plx9080_iobase + PLX_DMA0_DESCRIPTOR_REG);
+ // enable dma transfer
+ writeb(PLX_DMA_EN_BIT | PLX_DMA_START_BIT, devpriv->plx9080_iobase + PLX_DMA0_CS_REG);
+ writeb(0, devpriv->plx9080_iobase + PLX_DMA1_CS_REG);
+#else
+ writeb(0, devpriv->plx9080_iobase + PLX_DMA0_CS_REG);
+ writeb(0, devpriv->plx9080_iobase + PLX_DMA1_CS_REG);
+#endif
+
// enable interrupts
devpriv->intr_enable_bits |= EN_ADC_OVERRUN_BIT | EN_ADC_DONE_INTR_BIT;
if(cmd->stop_src == TRIG_EXT)
else
devpriv->intr_enable_bits |= ADC_INTR_QFULL_BITS; // for clairity only, since quarter-full bits are zero
writew(devpriv->intr_enable_bits, devpriv->main_iobase + INTR_ENABLE_REG);
- // enable interrupts on plx 9080 XXX enabling more interrupt sources than are actually used
- bits = ICS_PIE | ICS_PLIE | ICS_PAIE | ICS_PDIE | ICS_LIE | ICS_LDIE | ICS_DMA0_E | ICS_DMA1_E;
+ // enable interrupts on plx 9080
+ // XXX enabling more interrupt sources than are actually used
+ bits = ICS_PIE | ICS_PLIE | ICS_PAIE | ICS_PDIE | ICS_LIE | ICS_LDIE | ICS_DMA0_E | ICS_DMA1_E | ICS_MBIE;
writel(bits, devpriv->plx9080_iobase + PLX_INTRCS_REG);
-#ifdef PCIDMA
-//XXX
-#else
- writeb(0, devpriv->plx9080_iobase + PLX_DMA0_CS_REG);
- writeb(0, devpriv->plx9080_iobase + PLX_DMA1_CS_REG);
-#endif
-
/* set mode, disable software conversion gate */
bits = ADC_CONTROL1_DUMMY_BITS | SW_NOGATE_BIT;
if(cmd->convert_src == TRIG_EXT)
if((status &
(ADC_INTR_PENDING_BIT | ADC_DONE_BIT | ADC_STOP_BIT |
DAC_INTR_PENDING_BIT | DAC_DONE_BIT | EXT_INTR_PENDING_BIT)) == 0 &&
- (plx_status & (ICS_DMA0_A | ICS_DMA1_A | ICS_LDIA | ICS_LIA | ICS_PAIA | ICS_PDIA)) == 0)
+ (plx_status & (ICS_DMA0_A | ICS_DMA1_A | ICS_LDIA | ICS_LIA | ICS_PAIA | ICS_PDIA |
+ ICS_MBIA(0) | ICS_MBIA(1) |ICS_MBIA(2) | ICS_MBIA(3))) == 0)
{
#ifdef PCIDAS64_DEBUG
intr_count++;
**
**********************************************************************/
-#define PLX_LASMAP_REG 0x0000 /* L, Local Addr Space Range Register */
-#define LASMAP_IO 0x00000001 /* Map to: 1=I/O, 0=Mem */
-#define LMAP_ANY32 0x00000000 /* Locate anywhere in 32 bit */
-#define LMAP_LT1MB 0x00000002 /* Locate in 1st meg */
-#define LMAP_ANY64 0x00000004 /* Locate anywhere in 64 bit */
+#define PLX_LAS0RNG_REG 0x0000 /* L, Local Addr Space 0 Range Register */
+#define LRNG_IO 0x00000001 /* Map to: 1=I/O, 0=Mem */
+#define LRNG_ANY32 0x00000000 /* Locate anywhere in 32 bit */
+#define LRNG_LT1MB 0x00000002 /* Locate in 1st meg */
+#define LRNG_ANY64 0x00000004 /* Locate anywhere in 64 bit */
+#define LRNG_MEM_MASK 0xfffffff0 // bits that specify range for memory io
+#define LRNG_IO_MASK 0xfffffffa // bits that specify range for normal io
-#define PLX_LASRNG_REG 0x0004 /* L, Local Addr Space Range Register */
-#define LRNG_EN 0x00000001 /* Enable slave decode */
-#define LRNG_IO 0xFFFFFFFC /* Decode bits if I/O spc */
-#define LRNG_MEM 0xFFFFFFF0 /* Decode bits if mem spc */
+#define PLX_LAS0MAP_REG 0x0004 /* L, Local Addr Space 0 Remap Register */
+#define LMAP_EN 0x00000001 /* Enable slave decode */
+#define LMAP_MEM_MASK 0xfffffff0 // bits that specify decode for memory io
+#define LMAP_IO_MASK 0xfffffffa // bits that specify decode bits for normal io
/* Note: The Local Arbitration Register is only present on the 9060ES part.
#define ICS_AERR 0x00000001 /* Assert LSERR on ABORT */
#define ICS_PERR 0x00000002 /* Assert LSERR on Parity Error */
#define ICS_SERR 0x00000004 /* Generate PCI SERR# */
+#define ICS_MBIE 0x00000008 // mailbox interrupt enable
#define ICS_PIE 0x00000100 /* PCI Interrupt Enable */
#define ICS_PDIE 0x00000200 /* PCI Doorbell Interrupt Enable */
#define ICS_PAIE 0x00000400 /* PCI Abort Interrupt Enable */
#define ICS_TA_DMA0 0x02000000 /* Target Abort - DMA #0 */
#define ICS_TA_DMA1 0x04000000 /* Target Abort - DMA #1 */
#define ICS_TA_RA 0x08000000 /* Target Abort - Retry Timeout */
+#define ICS_MBIA(x) (0x10000000 << ((x) & 0x3)) // mailbox x is active
#define PLX_CONTROL_REG 0x006C /* L, EEPROM Cntl & PCI Cmd Codes */
#define CTL_RDMA 0x0000000F /* DMA Read Command */
#define PLX_REVISION_REG 0x74 // silicon revision
#define PLX_DMA0_MODE_REG 0x80 // dma channel 0 mode register
+#define PLX_LOCAL_BUS_16_WIDE_BITS 0x1
+#define PLX_LOCAL_BUS_WIDTH_MASK 0x3
+#define PLX_DMA_EN_READYIN_BIT 0x40 // enable ready in input
+#define PLX_EN_CHAIN_BIT 0x200 // enables chaining
+#define PLX_EN_DMA_DONE_INTR_BIT 0x400 // enables interrupt on dma done
+#define PLX_LOCAL_ADDR_CONST_BIT 0x800 // hold local address constant (don't increment)
+#define PLX_DMA_INTR_PCI_BIT 0x20000 // routes dma interrupt to pci bus (instead of local bus)
#define PLX_DMA0_PCI_ADDRESS_REG 0x84 // pci address that dma transfers start at
#define PLX_DMA0_DESCRIPTOR_REG 0x90 // descriptor pointer register
#define PLX_DMA1_DESCRIPTOR_REG 0xa4
-#define PLX_LOCAL_BUS_16_WIDE_BITS 0x1
-#define PLX_LOCAL_BUS_WIDTH_MASK 0x3
-#define PLX_EN_CHAIN_BIT 0x200 // enables chaining
-#define PLX_EN_DMA_DONE_INTR_BIT 0x400 // enables interrupt on dma done
-#define PLX_LOCAL_ADDR_CONST_BIT 0x800 // hold local address constant (don't increment)
-#define PLX_DMA_INTR_PCI_BIT 0x20000 // routes dma interrupt to pci bus (instead of local bus)
+#define PLX_DESC_IN_PCI_BIT 0x1 // descriptor is located in pci space (not local space)
+#define PLX_END_OF_CHAIN_BIT 0x2 // end of chain bit
+#define PLX_INTR_TERM_COUNT 0x4 // interrupt when this descriptor's transfer is finished
+#define PLX_XFER_LOCAL_TO_PCI 0x8 // transfer from local to pci bus (not pci to local)
#define PLX_DMA0_CS_REG 0xa8 // command status register
#define PLX_DMA1_CS_REG 0xa9
+#define PLX_DMA_EN_BIT 0x1 // enable dma channel
+#define PLX_DMA_START_BIT 0x2 // start dma transfer
#define PLX_CLEAR_DMA_INTR_BIT 0x8
#define PLX_DMA0_THRESHOLD_REG 0xb0 // command status register