NTB: switchtec: Add link event notifier callback
authorLogan Gunthorpe <logang@deltatee.com>
Thu, 3 Aug 2017 18:19:43 +0000 (12:19 -0600)
committerJon Mason <jdmason@kudzu.us>
Sun, 19 Nov 2017 01:37:11 +0000 (20:37 -0500)
In order for the Switchtec NTB code to handle link change events we
create a notifier callback in the switchtec code which gets called
whenever an appropriate event interrupt occurs.

In order to preserve userspace's ability to follow these events,
we compare the event count with a stored copy from last time we
checked.

Signed-off-by: Logan Gunthorpe <logang@deltatee.com>
Reviewed-by: Stephen Bates <sbates@raithlin.com>
Reviewed-by: Kurt Schwemmer <kurt.schwemmer@microsemi.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Signed-off-by: Jon Mason <jdmason@kudzu.us>
drivers/pci/switch/switchtec.c
include/linux/switchtec.h

index 39c9218d733fcfaf9fd494462bc6faa2a62a3c41..807569e62beeeb53a2e355645a4ee10af522f5be 100644 (file)
@@ -978,6 +978,49 @@ static const struct file_operations switchtec_fops = {
        .compat_ioctl = switchtec_dev_ioctl,
 };
 
+static void link_event_work(struct work_struct *work)
+{
+       struct switchtec_dev *stdev;
+
+       stdev = container_of(work, struct switchtec_dev, link_event_work);
+
+       if (stdev->link_notifier)
+               stdev->link_notifier(stdev);
+}
+
+static void check_link_state_events(struct switchtec_dev *stdev)
+{
+       int idx;
+       u32 reg;
+       int count;
+       int occurred = 0;
+
+       for (idx = 0; idx < stdev->pff_csr_count; idx++) {
+               reg = ioread32(&stdev->mmio_pff_csr[idx].link_state_hdr);
+               dev_dbg(&stdev->dev, "link_state: %d->%08x\n", idx, reg);
+               count = (reg >> 5) & 0xFF;
+
+               if (count != stdev->link_event_count[idx]) {
+                       occurred = 1;
+                       stdev->link_event_count[idx] = count;
+               }
+       }
+
+       if (occurred)
+               schedule_work(&stdev->link_event_work);
+}
+
+static void enable_link_state_events(struct switchtec_dev *stdev)
+{
+       int idx;
+
+       for (idx = 0; idx < stdev->pff_csr_count; idx++) {
+               iowrite32(SWITCHTEC_EVENT_CLEAR |
+                         SWITCHTEC_EVENT_EN_IRQ,
+                         &stdev->mmio_pff_csr[idx].link_state_hdr);
+       }
+}
+
 static void stdev_release(struct device *dev)
 {
        struct switchtec_dev *stdev = to_stdev(dev);
@@ -1030,6 +1073,7 @@ static struct switchtec_dev *stdev_create(struct pci_dev *pdev)
        stdev->mrpc_busy = 0;
        INIT_WORK(&stdev->mrpc_work, mrpc_event_work);
        INIT_DELAYED_WORK(&stdev->mrpc_timeout, mrpc_timeout_work);
+       INIT_WORK(&stdev->link_event_work, link_event_work);
        init_waitqueue_head(&stdev->event_wq);
        atomic_set(&stdev->event_cnt, 0);
 
@@ -1073,6 +1117,9 @@ static int mask_event(struct switchtec_dev *stdev, int eid, int idx)
        if (!(hdr & SWITCHTEC_EVENT_OCCURRED && hdr & SWITCHTEC_EVENT_EN_IRQ))
                return 0;
 
+       if (eid == SWITCHTEC_IOCTL_EVENT_LINK_STATE)
+               return 0;
+
        dev_dbg(&stdev->dev, "%s: %d %d %x\n", __func__, eid, idx, hdr);
        hdr &= ~(SWITCHTEC_EVENT_EN_IRQ | SWITCHTEC_EVENT_OCCURRED);
        iowrite32(hdr, hdr_reg);
@@ -1092,6 +1139,7 @@ static int mask_all_events(struct switchtec_dev *stdev, int eid)
                for (idx = 0; idx < stdev->pff_csr_count; idx++) {
                        if (!stdev->pff_local[idx])
                                continue;
+
                        count += mask_event(stdev, eid, idx);
                }
        } else {
@@ -1116,6 +1164,8 @@ static irqreturn_t switchtec_event_isr(int irq, void *dev)
                iowrite32(reg, &stdev->mmio_part_cfg->mrpc_comp_hdr);
        }
 
+       check_link_state_events(stdev);
+
        for (eid = 0; eid < SWITCHTEC_IOCTL_MAX_EVENTS; eid++)
                event_count += mask_all_events(stdev, eid);
 
@@ -1242,6 +1292,7 @@ static int switchtec_pci_probe(struct pci_dev *pdev,
        iowrite32(SWITCHTEC_EVENT_CLEAR |
                  SWITCHTEC_EVENT_EN_IRQ,
                  &stdev->mmio_part_cfg->mrpc_comp_hdr);
+       enable_link_state_events(stdev);
 
        rc = cdev_device_add(&stdev->cdev, &stdev->dev);
        if (rc)
index 9c2be76312579ea4f795a5a2e57688223f16078e..d8159944f01387ecea5db3293b33c57e102c84df 100644 (file)
@@ -353,6 +353,10 @@ struct switchtec_dev {
 
        wait_queue_head_t event_wq;
        atomic_t event_cnt;
+
+       struct work_struct link_event_work;
+       void (*link_notifier)(struct switchtec_dev *stdev);
+       u8 link_event_count[SWITCHTEC_MAX_PFF_CSR];
 };
 
 static inline struct switchtec_dev *to_stdev(struct device *dev)