Andrew Morton wrote:

> 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.
> 
> What a huge driver.

It is not over yet. We have plans to add more stuff later.
 
> >
> > ...
> > +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.

Agree. Will clean it for next release. 
 
> > +   default "n"
> > +   ---help---
> > +     Include support for IDT Tsi721 PCI Express Serial RapidIO
> controller.
> >
> > ...
> >
> > +   /* 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!?!?

Should be "udelay(1)" - 10 usec is too much here.
 
> EFAULT seems an inappropriate errno.

Switching to EIO.
 
> > +   if (ch_stat & TSI721_DMAC_STS_ABORT) {
> > +           /* If DMA operation aborted due to error,
> > +            * reinitialize DMA channel
> > +            */
.....

> > +           iowrite32(0, priv->regs +
> > +                           TSI721_DMAC_DWRCNT(TSI721_DMACH_MAINT));
> > +           udelay(1);
> > +           if (!do_wr)
> > +                   *data = 0xffffffff;
> > +           err = -EFAULT;

Here EIO as well because ABORT error reports more events than just bad
address.

> > ...
> >
> > +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.

Possibly my oversight. I will check if/where flush is needed.
 
> > +   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?

Ugly for sure. I am trying to protect message FIFO and not to lock it
during 
execution of rio_inb_pwrite_handler() because it may take relatively
long time.
Inbound port-write controller does not have any HW queue to store PW
messages
and my goal is to get them from device ASAP and to place into the FIFO.
I will redo this function to make locking to look better.     
 
> >
> > ...
> >
> > +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?

Yes. It works on both. Tested on x86 and powerpc.
 
> > +           *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))) {
> > ...
> >
> > +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?

Old habit. Used to have compiler warnings in some situations.
Not applicable to this case though. Will clean-up.

> > +}
> > +
> > +/**
> > + * 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?

Yes we did. Will fix it here and in one more place.

> 
> > +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?

To reduce a driver footprint yes, but it will not help if
system implements regular MSI only. This may be better handled by using
device specific config option depending on CONFIG_PCI_MSI. This way I
will be able
to remove MSI-X code while keeping MSI function. Marking as TODO.

If code size is not a concern, pci_enable_msix() has dependency on
CONFIG_PCI_MSI
and will fail if MSI support is not enabled.

> 
> > +   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().

It will be nice match to kzalloc().
 
> > +   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.

This memset() may be safely removed. I will review all similar cases.
 
> >
> > ...
> >
> > +struct tsi721_dma_desc {
> > +   __le32 type_id;
> > +

...
> > +
> > +   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)

No excuse to ignore it. Will update.
 
> >
> > ...
> >
> 
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/linuxppc-dev

Reply via email to