From: David Kershner <david.kersh...@unisys.com>

s-Par Firmware sends a CREATE_DEVICE information for each device that
gets created. Contained in the CREATE_DEVICE is the GSI interrupt vector
we should request an interrupt on. Save off that information and when
the client driver asks to register for interrupt, register the gsi
number and then request the irq based off that information.

When an interrupt happens, update the flag to inform IO Service Partition
and call the channel_interrupt function. Update the flag in the rearm
function to accept another interrupt.

Signed-off-by: David Kershner <david.kersh...@unisys.com>
Signed-off-by: Tim Sell <timothy.s...@unisys.com>
Signed-off-by: Benjamin Romer <benjamin.ro...@unisys.com>
---
 drivers/staging/unisys/include/visorbus.h       |   5 +
 drivers/staging/unisys/visorbus/visorbus_main.c | 118 ++++++++++++++++++++++--
 drivers/staging/unisys/visorbus/visorchannel.c  |   1 +
 drivers/staging/unisys/visorbus/visorchipset.c  |  17 +++-
 drivers/staging/unisys/visorhba/visorhba_main.c |   4 +
 drivers/staging/unisys/visornic/visornic_main.c |   5 +-
 6 files changed, 137 insertions(+), 13 deletions(-)

diff --git a/drivers/staging/unisys/include/visorbus.h 
b/drivers/staging/unisys/include/visorbus.h
index 5f44289..e2251f7 100644
--- a/drivers/staging/unisys/include/visorbus.h
+++ b/drivers/staging/unisys/include/visorbus.h
@@ -159,6 +159,9 @@ struct visor_device {
        u32 switch_no;
        u32 internal_port_no;
        uuid_le partition_uuid;
+       int irq;
+       int wait_ms;
+       int recv_queue;         /* specifies which queue to receive msgs on */
 };
 
 #define to_visor_device(x) container_of(x, struct visor_device, device)
@@ -176,6 +179,8 @@ int visorbus_clear_channel(struct visor_device *dev,
                           unsigned long offset, u8 ch, unsigned long nbytes);
 int visorbus_registerdevnode(struct visor_device *dev,
                             const char *name, int major, int minor);
+int visorbus_register_for_channel_interrupts(struct visor_device *dev,
+                                            u32 queue);
 void visorbus_enable_channel_interrupts(struct visor_device *dev);
 void visorbus_disable_channel_interrupts(struct visor_device *dev);
 void visorbus_rearm_channel_interrupts(struct visor_device *dev);
diff --git a/drivers/staging/unisys/visorbus/visorbus_main.c 
b/drivers/staging/unisys/visorbus/visorbus_main.c
index afbb9bc..a45c6a2 100644
--- a/drivers/staging/unisys/visorbus/visorbus_main.c
+++ b/drivers/staging/unisys/visorbus/visorbus_main.c
@@ -14,6 +14,8 @@
  * details.
  */
 
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
 #include <linux/uuid.h>
 
 #include "visorbus.h"
@@ -715,13 +717,6 @@ unregister_driver_attributes(struct visor_driver *drv)
        driver_remove_file(&drv->driver, &drv->version_attr);
 }
 
-visorbus_rearm_channel_interrupts(struct visor_device *dev)
-{
-       if (!visor_periodic_work_nextperiod(dev->periodic_work))
-               put_device(&dev->device);
-}
-EXPORT_SYMBOL_GPL(visorbus_rearm_channel_interrupts);
-
 static void
 dev_periodic_work(void *xdev)
 {
@@ -969,19 +964,63 @@ EXPORT_SYMBOL_GPL(visorbus_registerdevnode);
  *  interrupt function periodically...
  */
 void
+visorbus_rearm_channel_interrupts(struct visor_device *dev)
+{
+       if (dev->irq)
+               visorchannel_set_sig_features(dev->visorchannel,
+                                             dev->recv_queue,
+                                             ULTRA_CHANNEL_ENABLE_INTS);
+       else if (!visor_periodic_work_nextperiod(dev->periodic_work))
+               put_device(&dev->device);
+}
+EXPORT_SYMBOL_GPL(visorbus_rearm_channel_interrupts);
+
+void
 visorbus_enable_channel_interrupts(struct visor_device *dev)
 {
-       dev_start_periodic_work(dev);
+       if (dev->irq)
+               visorchannel_set_sig_features(dev->visorchannel,
+                                             dev->recv_queue,
+                                             ULTRA_CHANNEL_ENABLE_INTS);
+       else
+               dev_start_periodic_work(dev);
 }
 EXPORT_SYMBOL_GPL(visorbus_enable_channel_interrupts);
 
 void
 visorbus_disable_channel_interrupts(struct visor_device *dev)
 {
-       dev_stop_periodic_work(dev);
+       if (!dev->irq)
+               visorchannel_clear_sig_features(dev->visorchannel,
+                                               dev->recv_queue,
+                                               ULTRA_CHANNEL_ENABLE_INTS);
+       else
+               dev_stop_periodic_work(dev);
 }
 EXPORT_SYMBOL_GPL(visorbus_disable_channel_interrupts);
 
+static irqreturn_t
+visorbus_isr(int irq, void *dev_id)
+{
+       struct visor_device *dev = (struct visor_device *)dev_id;
+       struct visor_driver *drv = to_visor_driver(dev->device.driver);
+
+       /* Disable the interrupt in hardware for this device.
+        * When the device is done handling the interrupt, it has
+        * the responsibility of re-arming the interrupt so the SP
+        * can send another one.
+        */
+       visorchannel_clear_sig_features(dev->visorchannel,
+                                       dev->recv_queue,
+                                       ULTRA_CHANNEL_ENABLE_INTS);
+       if (drv->channel_interrupt) {
+               drv->channel_interrupt(dev);
+               return IRQ_HANDLED;
+       }
+
+       return IRQ_NONE;
+}
+
 int visorbus_set_channel_features(struct visor_device *dev, u64 feature_bits)
 {
        int channel_offset = 0, err = 0;
@@ -1037,6 +1076,53 @@ int visorbus_clear_channel_features(struct visor_device 
*dev, u64 feature_bits)
        return err;
 }
 
+#define INTERRUPT_VECTOR_MASK 0x3f
+int visorbus_register_for_channel_interrupts(struct visor_device *dev,
+                                            u32 queue)
+{
+       int err = 0;
+
+       if (!dev->irq)
+               goto stay_in_polling;
+
+       err = request_irq(dev->irq, visorbus_isr, IRQF_SHARED,
+                         dev_name(&dev->device), dev);
+       if (err < 0) {
+               dev_err(&dev->device,
+                       "failed to request irq %d continuing to poll. err = %d",
+                       dev->irq, err);
+               goto stay_in_polling;
+       }
+
+       dev_info(&dev->device, "IRQ=%d registered\n", dev->irq);
+
+       err = visorbus_set_channel_features(dev, ULTRA_IO_DRIVER_ENABLES_INTS |
+                                           ULTRA_IO_DRIVER_DISABLES_INTS);
+       if (err) {
+               dev_err(&dev->device,
+                       "%s failed to set ENALBES ints from chan (%d)\n",
+                       __func__, err);
+               goto stay_in_polling;
+       }
+
+       err = visorbus_clear_channel_features(dev, ULTRA_IO_CHANNEL_IS_POLLING);
+       if (err) {
+               dev_err(&dev->device,
+                       "%s failed to clear POOLING flag from chan (%d)\n",
+                       __func__, err);
+               goto stay_in_polling;
+       }
+
+       dev->wait_ms = 2000;
+       dev->recv_queue = queue;
+       return 0;
+
+stay_in_polling:
+       dev->irq = 0;
+       return err;
+}
+EXPORT_SYMBOL_GPL(visorbus_register_for_channel_interrupts);
+
 /** This is how everything starts from the device end.
  *  This function is called when a channel first appears via a ControlVM
  *  message.  In response, this function allocates a visor_device to
@@ -1086,6 +1172,20 @@ create_visor_device(struct visor_device *dev)
        dev_set_name(&dev->device, "vbus%u:dev%u",
                     chipset_bus_no, chipset_dev_no);
 
+       /* Automatically set driver into POLLING mode, if the driver
+        * wants to use interrupts, it's probe can call
+        * visorbus_register_for_interrupts.
+        */
+       rc = visorbus_set_channel_features(dev, ULTRA_IO_CHANNEL_IS_POLLING |
+                                          ULTRA_IO_DRIVER_DISABLES_INTS);
+       if (rc) {
+               dev_err(&dev->device,
+                       "%s failed to set channel features for chan (%d)\n",
+                       __func__, rc);
+               goto away;
+       }
+       dev->wait_ms = 2;
+
        /*  device_add does this:
         *    bus_add_device(dev)
         *    ->device_attach(dev)
diff --git a/drivers/staging/unisys/visorbus/visorchannel.c 
b/drivers/staging/unisys/visorbus/visorchannel.c
index 2ef79fa..618784e 100644
--- a/drivers/staging/unisys/visorbus/visorchannel.c
+++ b/drivers/staging/unisys/visorbus/visorchannel.c
@@ -21,6 +21,7 @@
 
 #include <linux/uuid.h>
 #include <linux/io.h>
+#include <linux/llist.h>
 
 #include "version.h"
 #include "visorbus.h"
diff --git a/drivers/staging/unisys/visorbus/visorchipset.c 
b/drivers/staging/unisys/visorbus/visorchipset.c
index 07594f4..4239606 100644
--- a/drivers/staging/unisys/visorbus/visorchipset.c
+++ b/drivers/staging/unisys/visorbus/visorchipset.c
@@ -1205,6 +1205,7 @@ my_device_create(struct controlvm_message *inmsg)
        struct visor_device *bus_info;
        struct visorchannel *visorchannel;
        int rc = CONTROLVM_RESP_SUCCESS;
+       int gsi_vector;
 
        bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL);
        if (!bus_info) {
@@ -1237,12 +1238,24 @@ my_device_create(struct controlvm_message *inmsg)
                goto cleanup;
        }
 
+       dev_info->device.parent = &bus_info->device;
+
        dev_info->chipset_bus_no = bus_no;
        dev_info->chipset_dev_no = dev_no;
        dev_info->inst = cmd->create_device.dev_inst_uuid;
 
-       /* not sure where the best place to set the 'parent' */
-       dev_info->device.parent = &bus_info->device;
+       gsi_vector = cmd->create_device.intr.recv_irq_handle;
+       if (gsi_vector > 0) {
+               dev_info->irq = acpi_register_gsi(&dev_info->device, gsi_vector,
+                                                 ACPI_LEVEL_SENSITIVE,
+                                                 ACPI_ACTIVE_LOW);
+               if (dev_info->irq < 0) {
+                       dev_err(&dev_info->device,
+                               "Error registering gsi number: %d (%d)",
+                               gsi_vector, dev_info->irq);
+                       dev_info->irq = 0;
+               }
+       }
 
        POSTCODE_LINUX_4(DEVICE_CREATE_ENTRY_PC, dev_no, bus_no,
                         POSTCODE_SEVERITY_INFO);
diff --git a/drivers/staging/unisys/visorhba/visorhba_main.c 
b/drivers/staging/unisys/visorhba/visorhba_main.c
index 0f6bb26..e87bc2b 100644
--- a/drivers/staging/unisys/visorhba/visorhba_main.c
+++ b/drivers/staging/unisys/visorhba/visorhba_main.c
@@ -1143,6 +1143,10 @@ static int visorhba_probe(struct visor_device *dev)
        tasklet_init(&devdata->tasklet, process_incoming_rsps,
                     (unsigned long)devdata);
 
+       /* I want to use real interrupts if available so need to
+        * register
+        */
+       visorbus_register_for_channel_interrupts(dev, IOCHAN_FROM_IOPART);
        visorbus_enable_channel_interrupts(dev);
 
        scsi_scan_host(scsihost);
diff --git a/drivers/staging/unisys/visornic/visornic_main.c 
b/drivers/staging/unisys/visornic/visornic_main.c
index e8ad219..39a97f3 100644
--- a/drivers/staging/unisys/visornic/visornic_main.c
+++ b/drivers/staging/unisys/visornic/visornic_main.c
@@ -1880,10 +1880,11 @@ static int visornic_probe(struct visor_device *dev)
        netif_napi_add(netdev, &devdata->napi, visornic_poll, NAPI_WEIGHT);
 
        /*
-        * Note: Interupts have to be enable before the while
-        * loop below because the napi routine is responsible for
+        * Note: Interupts have to be enable before we register
+        * because the napi routine is responsible for
         * setting enab_dis_acked
         */
+       visorbus_register_for_channel_interrupts(dev, IOCHAN_FROM_IOPART);
        visorbus_enable_channel_interrupts(dev);
 
        err = register_netdev(netdev);
-- 
2.5.0

_______________________________________________
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

Reply via email to