On Fri, 12 Aug 2011 15:45:34 -0400 Alexandre Bounine <alexandre.boun...@idt.com> wrote:
> Add RapidIO mport driver for IDT TSI721 PCI Express-to-SRIO bridge device. > The driver provides full set of callback functions defined for mport devices > in RapidIO subsystem. It also is compatible with current version of RIONET > driver (Ethernet over RapidIO messaging services). > > This patch is applicable to kernel versions starting from 2.6.39. What a huge driver. > > ... > > --- /dev/null > +++ b/drivers/rapidio/devices/Kconfig > @@ -0,0 +1,10 @@ > +# > +# RapidIO master port configuration > +# > + > +config RAPIDIO_TSI721 > + bool "IDT Tsi721 PCI Express SRIO Controller support" > + depends on RAPIDIO && PCI && PCIEPORTBUS The dependency on PCI is redundant - PCIEPORTBUS already depends on PCI. Doesn't matter much though. > + default "n" > + ---help--- > + Include support for IDT Tsi721 PCI Express Serial RapidIO controller. > > ... > > +static int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size, > + u16 destid, u8 hopcount, u32 offset, int len, > + u32 *data, int do_wr) > +{ > + struct tsi721_dma_desc *bd_ptr; > + u32 rd_count, swr_ptr, ch_stat; > + int i, err = 0; > + u32 op = do_wr ? MAINT_WR : MAINT_RD; > + > + if (offset > (RIO_MAINT_SPACE_SZ - len) || (len != sizeof(u32))) > + return -EINVAL; > + > + bd_ptr = priv->bdma[TSI721_DMACH_MAINT].bd_base; > + > + rd_count = ioread32( > + priv->regs + TSI721_DMAC_DRDCNT(TSI721_DMACH_MAINT)); > + > + /* Initialize DMA descriptor */ > + bd_ptr[0].type_id = cpu_to_le32((DTYPE2 << 29) | (op << 19) | destid); > + bd_ptr[0].bcount = cpu_to_le32((sys_size << 26) | 0x04); > + bd_ptr[0].raddr_lo = cpu_to_le32((hopcount << 24) | offset); > + bd_ptr[0].raddr_hi = 0; > + if (do_wr) > + bd_ptr[0].data[0] = cpu_to_be32p(data); > + else > + bd_ptr[0].data[0] = 0xffffffff; > + > + mb(); > + > + /* Start DMA operation */ > + iowrite32(rd_count + 2, > + priv->regs + TSI721_DMAC_DWRCNT(TSI721_DMACH_MAINT)); > + (void)ioread32(priv->regs + TSI721_DMAC_DWRCNT(TSI721_DMACH_MAINT)); > + i = 0; > + > + /* Wait until DMA transfer is finished */ > + while ((ch_stat = ioread32(priv->regs + > + TSI721_DMAC_STS(TSI721_DMACH_MAINT))) & TSI721_DMAC_STS_RUN) { > + udelay(10); > + i++; > + if (i >= 5000000) { > + dev_dbg(&priv->pdev->dev, > + "%s : DMA[%d] read timeout ch_status=%x\n", > + __func__, TSI721_DMACH_MAINT, ch_stat); > + if (!do_wr) > + *data = 0xffffffff; > + err = -EFAULT; > + goto err_out; > + } > + } Fifty seconds!?!? EFAULT seems an inappropriate errno. > + if (ch_stat & TSI721_DMAC_STS_ABORT) { > + /* If DMA operation aborted due to error, > + * reinitialize DMA channel > + */ > + dev_dbg(&priv->pdev->dev, "%s : DMA ABORT ch_stat=%x\n", > + __func__, ch_stat); > + dev_dbg(&priv->pdev->dev, "OP=%d : destid=%x hc=%x off=%x\n", > + do_wr ? MAINT_WR : MAINT_RD, destid, hopcount, offset); > + iowrite32(TSI721_DMAC_INT_ALL, > + priv->regs + TSI721_DMAC_INT(TSI721_DMACH_MAINT)); > + iowrite32(TSI721_DMAC_CTL_INIT, > + priv->regs + TSI721_DMAC_CTL(TSI721_DMACH_MAINT)); > + udelay(10); > + iowrite32(0, priv->regs + > + TSI721_DMAC_DWRCNT(TSI721_DMACH_MAINT)); > + udelay(1); > + if (!do_wr) > + *data = 0xffffffff; > + err = -EFAULT; > + goto err_out; > + } > + > + if (!do_wr) > + *data = be32_to_cpu(bd_ptr[0].data[0]); > + > + /* > + * Update descriptor status FIFO RD pointer. > + * NOTE: Skipping check and clear FIFO entries because we are waiting > + * for transfer to be completed. > + */ > + swr_ptr = ioread32(priv->regs + TSI721_DMAC_DSWP(TSI721_DMACH_MAINT)); > + iowrite32(swr_ptr, priv->regs + TSI721_DMAC_DSRP(TSI721_DMACH_MAINT)); > +err_out: > + > + return err; > +} > > ... > > +static int > +tsi721_pw_handler(struct rio_mport *mport) > +{ > + struct tsi721_device *priv = mport->priv; > + u32 pw_stat; > + u32 pw_buf[TSI721_RIO_PW_MSG_SIZE/sizeof(u32)]; > + > + > + pw_stat = ioread32(priv->regs + TSI721_RIO_PW_RX_STAT); > + > + if (pw_stat & TSI721_RIO_PW_RX_STAT_PW_VAL) { > + pw_buf[0] = ioread32(priv->regs + TSI721_RIO_PW_RX_CAPT(0)); > + pw_buf[1] = ioread32(priv->regs + TSI721_RIO_PW_RX_CAPT(1)); > + pw_buf[2] = ioread32(priv->regs + TSI721_RIO_PW_RX_CAPT(2)); > + pw_buf[3] = ioread32(priv->regs + TSI721_RIO_PW_RX_CAPT(3)); > + > + /* Queue PW message (if there is room in FIFO), > + * otherwise discard it. > + */ > + spin_lock(&priv->pw_fifo_lock); > + if (kfifo_avail(&priv->pw_fifo) >= TSI721_RIO_PW_MSG_SIZE) > + kfifo_in(&priv->pw_fifo, pw_buf, > + TSI721_RIO_PW_MSG_SIZE); > + else > + priv->pw_discard_count++; > + spin_unlock(&priv->pw_fifo_lock); > + } > + > + /* Clear pending PW interrupts */ > + iowrite32(TSI721_RIO_PW_RX_STAT_PW_DISC | TSI721_RIO_PW_RX_STAT_PW_VAL, > + priv->regs + TSI721_RIO_PW_RX_STAT); > + > + schedule_work(&priv->pw_work); I see scheduled work being done, but no flush_scheduled_work() or similar. This is often a bug, leading to code being executed after device shutdown or even after rmmod. > + return 0; > +} > + > +static void tsi721_pw_dpc(struct work_struct *work) > +{ > + struct tsi721_device *priv = container_of(work, struct tsi721_device, > + pw_work); > + unsigned long flags; > + u32 msg_buffer[RIO_PW_MSG_SIZE/sizeof(u32)]; /* Use full size PW message > + buffer for RIO layer */ > + > + /* > + * Process port-write messages > + */ > + spin_lock_irqsave(&priv->pw_fifo_lock, flags); > + while (kfifo_out(&priv->pw_fifo, (unsigned char *)msg_buffer, > + TSI721_RIO_PW_MSG_SIZE)) { > + /* Process one message */ > + spin_unlock_irqrestore(&priv->pw_fifo_lock, flags); > +#ifdef DEBUG_PW > + { > + u32 i; > + pr_debug("%s : Port-Write Message:", __func__); > + for (i = 0; i < RIO_PW_MSG_SIZE/sizeof(u32); ) { > + pr_debug("0x%02x: %08x %08x %08x %08x", i*4, > + msg_buffer[i], msg_buffer[i + 1], > + msg_buffer[i + 2], msg_buffer[i + 3]); > + i += 4; > + } > + pr_debug("\n"); > + } > +#endif > + /* Pass the port-write message to RIO core for processing */ > + rio_inb_pwrite_handler((union rio_pw_msg *)msg_buffer); > + spin_lock_irqsave(&priv->pw_fifo_lock, flags); > + } > + spin_unlock_irqrestore(&priv->pw_fifo_lock, flags); > +} The lock handling in this function is pretty ugly-looking. Is it correct? > > ... > > +static void tsi721_db_dpc(struct work_struct *work) > +{ > + struct tsi721_device *priv = container_of(work, struct tsi721_device, > + idb_work); > + struct rio_mport *mport; > + struct rio_dbell *dbell; > + int found = 0; > + u32 wr_ptr, rd_ptr; > + u64 *idb_entry; > + u32 regval; > + union { > + u64 msg; > + u8 bytes[8]; > + } idb; > + > + /* > + * Process queued inbound doorbells > + */ > + mport = priv->mport; > + > + wr_ptr = ioread32(priv->regs + TSI721_IDQ_WP(IDB_QUEUE)); > + rd_ptr = ioread32(priv->regs + TSI721_IDQ_RP(IDB_QUEUE)); > + > + while (wr_ptr != rd_ptr) { > + idb_entry = (u64 *)(priv->idb_base + > + (TSI721_IDB_ENTRY_SIZE * rd_ptr)); > + rd_ptr++; > + idb.msg = *idb_entry; Is this code correct on both little-endian and big-endian hardware? > + *idb_entry = 0; > + > + /* Process one doorbell */ > + list_for_each_entry(dbell, &mport->dbells, node) { > + if ((dbell->res->start <= DBELL_INF(idb.bytes)) && > + (dbell->res->end >= DBELL_INF(idb.bytes))) { > + found = 1; > + break; > + } > + } > + > + if (found) { > + dbell->dinb(mport, dbell->dev_id, DBELL_SID(idb.bytes), > + DBELL_TID(idb.bytes), DBELL_INF(idb.bytes)); > + } else { > + dev_dbg(&priv->pdev->dev, > + "spurious inb doorbell, sid %2.2x tid %2.2x" > + " info %4.4x\n", DBELL_SID(idb.bytes), > + DBELL_TID(idb.bytes), DBELL_INF(idb.bytes)); > + } > + } > + > + iowrite32(rd_ptr & (IDB_QSIZE - 1), > + priv->regs + TSI721_IDQ_RP(IDB_QUEUE)); > + > + /* Re-enable IDB interrupts */ > + regval = ioread32(priv->regs + TSI721_SR_CHINTE(IDB_QUEUE)); > + regval |= TSI721_SR_CHINT_IDBQRCV; > + iowrite32(regval, > + priv->regs + TSI721_SR_CHINTE(IDB_QUEUE)); > +} > + > > ... > > +static void tsi721_interrupts_init(struct tsi721_device *priv) > +{ > + u32 intr; > + > + /* Enable IDB interrupts */ > + iowrite32(TSI721_SR_CHINT_ALL, > + priv->regs + TSI721_SR_CHINT(IDB_QUEUE)); > + iowrite32(TSI721_SR_CHINT_IDBQRCV, > + priv->regs + TSI721_SR_CHINTE(IDB_QUEUE)); > + iowrite32(TSI721_INT_SR2PC_CHAN(IDB_QUEUE), > + priv->regs + TSI721_DEV_CHAN_INTE); > + > + /* Enable SRIO MAC interrupts */ > + iowrite32(TSI721_RIO_EM_DEV_INT_EN_INT, > + priv->regs + TSI721_RIO_EM_DEV_INT_EN); > + > + if (priv->flags & TSI721_USING_MSIX) > + intr = TSI721_DEV_INT_SRIO; > + else > + intr = TSI721_DEV_INT_SR2PC_CH | TSI721_DEV_INT_SRIO | > + TSI721_DEV_INT_SMSG_CH; > + > + iowrite32(intr, priv->regs + TSI721_DEV_INTE); > + (void)ioread32(priv->regs + TSI721_DEV_INTE); Why include all of these void casts, btw? > +} > + > +/** > + * tsi721_request_msix - register interrupt service for MSI-X mode. > + * @mport: RapidIO master port structure > + * > + * Registers MSI-X interrupt service routines for interrupts that are active > + * immediately after mport initialization. Messaging interrupt service > routines > + * should be registered during corresponding open requests. > + */ > +static int tsi721_request_msix(struct rio_mport *mport) > +{ > + struct tsi721_device *priv = mport->priv; > + int err = 0; > + > + err = request_irq(priv->msix[TSI721_VECT_IDB].vector, > + tsi721_sr2pc_ch_msix, 0, > + priv->msix[TSI721_VECT_IDB].irq_name, (void *)mport); > + if (err) > + goto out; > + > + err = request_irq(priv->msix[TSI721_VECT_PWRX].vector, > + tsi721_srio_msix, 0, > + priv->msix[TSI721_VECT_PWRX].irq_name, (void *)mport); Did we leak the first IRQ here? > +out: > + return err; > +} > + > > ... > > +static int tsi721_enable_msix(struct tsi721_device *priv) > +{ > + struct msix_entry entries[TSI721_VECT_MAX]; > + int err; > + int i; > + > + entries[TSI721_VECT_IDB].entry = TSI721_MSIX_SR2PC_IDBQ_RCV(IDB_QUEUE); > + entries[TSI721_VECT_PWRX].entry = TSI721_MSIX_SRIO_MAC_INT; > + > + /* > + * Initialize MSI-X entries for Messaging Engine: > + * this driver supports four RIO mailboxes (inbound and outbound) > + * NOTE: Inbound message MBOX 0...4 use IB channels 4...7. Therefore > + * offset +4 is added to IB MBOX number. > + */ > + for (i = 0; i < RIO_MAX_MBOX; i++) { > + entries[TSI721_VECT_IMB0_RCV + i].entry = > + TSI721_MSIX_IMSG_DQ_RCV(i + 4); > + entries[TSI721_VECT_IMB0_INT + i].entry = > + TSI721_MSIX_IMSG_INT(i + 4); > + entries[TSI721_VECT_OMB0_DONE + i].entry = > + TSI721_MSIX_OMSG_DONE(i); > + entries[TSI721_VECT_OMB0_INT + i].entry = > + TSI721_MSIX_OMSG_INT(i); > + } > + > + err = pci_enable_msix(priv->pdev, entries, ARRAY_SIZE(entries)); > + if (err) { > + if (err > 0) > + dev_info(&priv->pdev->dev, > + "Only %d MSI-X vectors available, " > + "not using MSI-X\n", err); > + return err; > + } > + > + /* > + * Copy MSI-X vector information into tsi721 private structure > + */ > + priv->msix[TSI721_VECT_IDB].vector = entries[TSI721_VECT_IDB].vector; > + snprintf(priv->msix[TSI721_VECT_IDB].irq_name, IRQ_DEVICE_NAME_MAX, > + DRV_NAME "-idb@pci:%s", pci_name(priv->pdev)); > + priv->msix[TSI721_VECT_PWRX].vector = entries[TSI721_VECT_PWRX].vector; > + snprintf(priv->msix[TSI721_VECT_PWRX].irq_name, IRQ_DEVICE_NAME_MAX, > + DRV_NAME "-pwrx@pci:%s", pci_name(priv->pdev)); > + > + for (i = 0; i < RIO_MAX_MBOX; i++) { > + priv->msix[TSI721_VECT_IMB0_RCV + i].vector = > + entries[TSI721_VECT_IMB0_RCV + i].vector; > + snprintf(priv->msix[TSI721_VECT_IMB0_RCV + i].irq_name, > + IRQ_DEVICE_NAME_MAX, DRV_NAME "-imbr%d@pci:%s", > + i, pci_name(priv->pdev)); > + > + priv->msix[TSI721_VECT_IMB0_INT + i].vector = > + entries[TSI721_VECT_IMB0_INT + i].vector; > + snprintf(priv->msix[TSI721_VECT_IMB0_INT + i].irq_name, > + IRQ_DEVICE_NAME_MAX, DRV_NAME "-imbi%d@pci:%s", > + i, pci_name(priv->pdev)); > + > + priv->msix[TSI721_VECT_OMB0_DONE + i].vector = > + entries[TSI721_VECT_OMB0_DONE + i].vector; > + snprintf(priv->msix[TSI721_VECT_OMB0_DONE + i].irq_name, > + IRQ_DEVICE_NAME_MAX, DRV_NAME "-ombd%d@pci:%s", > + i, pci_name(priv->pdev)); > + > + priv->msix[TSI721_VECT_OMB0_INT + i].vector = > + entries[TSI721_VECT_OMB0_INT + i].vector; > + snprintf(priv->msix[TSI721_VECT_OMB0_INT + i].irq_name, > + IRQ_DEVICE_NAME_MAX, DRV_NAME "-ombi%d@pci:%s", > + i, pci_name(priv->pdev)); > + } Did we need a dependency on CONFIG_PCI_MSI? > + return 0; > +} > + > > ... > > +static int tsi721_bdma_ch_init(struct tsi721_device *priv, int chnum) > +{ > + struct tsi721_dma_desc *bd_ptr; > + u64 *sts_ptr; > + dma_addr_t bd_phys, sts_phys; > + int sts_size; > + int bd_num = priv->bdma[chnum].bd_num; > + > + dev_dbg(&priv->pdev->dev, "Init Block DMA Engine, CH%d\n", chnum); > + > + /* > + * Initialize DMA channel for maintenance requests > + */ > + > + /* Allocate space for DMA descriptors */ > + bd_ptr = dma_alloc_coherent(&priv->pdev->dev, > + bd_num * sizeof(struct tsi721_dma_desc), > + &bd_phys, GFP_KERNEL); > + if (!bd_ptr) > + return -ENOMEM; > + > + priv->bdma[chnum].bd_phys = bd_phys; > + priv->bdma[chnum].bd_base = bd_ptr; > + > + memset(bd_ptr, 0, bd_num * sizeof(struct tsi721_dma_desc)); There it is again. Perhaps we need a dma_zalloc_coherent(). > + dev_dbg(&priv->pdev->dev, "DMA descriptors @ %p (phys = %llx)\n", > + bd_ptr, (unsigned long long)bd_phys); > + > + /* Allocate space for descriptor status FIFO */ > + sts_size = (bd_num >= TSI721_DMA_MINSTSSZ) ? > + bd_num : TSI721_DMA_MINSTSSZ; > + sts_size = roundup_pow_of_two(sts_size); > + sts_ptr = dma_alloc_coherent(&priv->pdev->dev, > + sts_size * sizeof(struct tsi721_dma_sts), > + &sts_phys, GFP_KERNEL); > + if (!sts_ptr) { > + /* Free space allocated for DMA descriptors */ > + dma_free_coherent(&priv->pdev->dev, > + bd_num * sizeof(struct tsi721_dma_desc), > + bd_ptr, bd_phys); > + priv->bdma[chnum].bd_base = NULL; > + return -ENOMEM; > + } > + > + priv->bdma[chnum].sts_phys = sts_phys; > + priv->bdma[chnum].sts_base = sts_ptr; > + priv->bdma[chnum].sts_size = sts_size; > + > + memset(sts_ptr, 0, sts_size); and again. > + dev_dbg(&priv->pdev->dev, > + "desc status FIFO @ %p (phys = %llx) size=0x%x\n", > + sts_ptr, (unsigned long long)sts_phys, sts_size); > + > + /* Initialize DMA descriptors ring */ > + bd_ptr[bd_num - 1].type_id = cpu_to_le32(DTYPE3 << 29); > + bd_ptr[bd_num - 1].next_lo = cpu_to_le32((u64)bd_phys & > + TSI721_DMAC_DPTRL_MASK); > + bd_ptr[bd_num - 1].next_hi = cpu_to_le32((u64)bd_phys >> 32); > + > + /* Setup DMA descriptor pointers */ > + iowrite32(((u64)bd_phys >> 32), > + priv->regs + TSI721_DMAC_DPTRH(chnum)); > + iowrite32(((u64)bd_phys & TSI721_DMAC_DPTRL_MASK), > + priv->regs + TSI721_DMAC_DPTRL(chnum)); > + > + /* Setup descriptor status FIFO */ > + iowrite32(((u64)sts_phys >> 32), > + priv->regs + TSI721_DMAC_DSBH(chnum)); > + iowrite32(((u64)sts_phys & TSI721_DMAC_DSBL_MASK), > + priv->regs + TSI721_DMAC_DSBL(chnum)); > + iowrite32(TSI721_DMAC_DSSZ_SIZE(sts_size), > + priv->regs + TSI721_DMAC_DSSZ(chnum)); > + > + /* Clear interrupt bits */ > + iowrite32(TSI721_DMAC_INT_ALL, > + priv->regs + TSI721_DMAC_INT(chnum)); > + > + (void)ioread32(priv->regs + TSI721_DMAC_INT(chnum)); > + > + /* Toggle DMA channel initialization */ > + iowrite32(TSI721_DMAC_CTL_INIT, priv->regs + TSI721_DMAC_CTL(chnum)); > + (void)ioread32(priv->regs + TSI721_DMAC_CTL(chnum)); > + udelay(10); > + > + return 0; > +} > + > > ... > > +struct tsi721_dma_desc { > + __le32 type_id; > + > +#define TSI721_DMAD_DEVID 0x0000ffff > +#define TSI721_DMAD_CRF 0x00010000 > +#define TSI721_DMAD_PRIO 0x00060000 > +#define TSI721_DMAD_RTYPE 0x00780000 > +#define TSI721_DMAD_IOF 0x08000000 > +#define TSI721_DMAD_DTYPE 0xe0000000 > + > + __le32 bcount; > + > +#define TSI721_DMAD_BCOUNT1 0x03ffffff /* if DTYPE == 1 */ > +#define TSI721_DMAD_BCOUNT2 0x0000000f /* if DTYPE == 2 */ > +#define TSI721_DMAD_TT 0x0c000000 > +#define TSI721_DMAD_RADDR0 0xc0000000 > + > + union { > + __le32 raddr_lo; /* if DTYPE == (1 || 2) */ > + __le32 next_lo; /* if DTYPE == 3 */ > + }; > + > +#define TSI721_DMAD_CFGOFF 0x00ffffff > +#define TSI721_DMAD_HOPCNT 0xff000000 > + > + union { > + __le32 raddr_hi; /* if DTYPE == (1 || 2) */ > + __le32 next_hi; /* if DTYPE == 3 */ > + }; > + > + union { > + struct { /* if DTYPE == 1 */ > + __le32 bufptr_lo; > + __le32 bufptr_hi; > + __le32 s_dist; > + __le32 s_size; > + } t1; > + __le32 data[4]; /* if DTYPE == 2 */ > + u32 reserved[4]; /* if DTYPE == 3 */ > + }; > +} __attribute__((aligned(32))); We have the __aligned helper for this. (more below) > > ... > _______________________________________________ Linuxppc-dev mailing list Linuxppc-dev@lists.ozlabs.org https://lists.ozlabs.org/listinfo/linuxppc-dev