rk3288 lcdc: add overscan for extern screen
[firefly-linux-kernel-4.4.55.git] / drivers / virtio / virtio_pci.c
index 4bcc8b82640be13af193478aaa2c4699bc609d2b..a7ce73029f5989de1cf3c53fc318bcfebfc99c87 100644 (file)
@@ -48,6 +48,7 @@ struct virtio_pci_device
        int msix_enabled;
        int intx_enabled;
        struct msix_entry *msix_entries;
+       cpumask_var_t *msix_affinity_masks;
        /* Name strings for interrupts. This size should be enough,
         * and I'm too lazy to allocate each name separately. */
        char (*msix_names)[256];
@@ -55,6 +56,10 @@ struct virtio_pci_device
        unsigned msix_vectors;
        /* Vectors allocated, excluding per-vq vectors if any */
        unsigned msix_used_vectors;
+
+       /* Status saved during hibernate/restore */
+       u8 saved_status;
+
        /* Whether we have vector per vq */
        bool per_vq_vectors;
 };
@@ -75,9 +80,6 @@ struct virtio_pci_vq_info
        /* the number of entries in the queue */
        int num;
 
-       /* the index of the queue */
-       int queue_index;
-
        /* the virtual address of the ring queue */
        void *queue;
 
@@ -89,9 +91,9 @@ struct virtio_pci_vq_info
 };
 
 /* Qumranet donated their vendor ID for devices 0x1000 thru 0x10FF. */
-static struct pci_device_id virtio_pci_id_table[] = {
-       { 0x1af4, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
-       { 0 },
+static DEFINE_PCI_DEVICE_TABLE(virtio_pci_id_table) = {
+       { PCI_DEVICE(0x1af4, PCI_ANY_ID) },
+       { 0 }
 };
 
 MODULE_DEVICE_TABLE(pci, virtio_pci_id_table);
@@ -169,22 +171,39 @@ static void vp_set_status(struct virtio_device *vdev, u8 status)
        iowrite8(status, vp_dev->ioaddr + VIRTIO_PCI_STATUS);
 }
 
+/* wait for pending irq handlers */
+static void vp_synchronize_vectors(struct virtio_device *vdev)
+{
+       struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+       int i;
+
+       if (vp_dev->intx_enabled)
+               synchronize_irq(vp_dev->pci_dev->irq);
+
+       for (i = 0; i < vp_dev->msix_vectors; ++i)
+               synchronize_irq(vp_dev->msix_entries[i].vector);
+}
+
 static void vp_reset(struct virtio_device *vdev)
 {
        struct virtio_pci_device *vp_dev = to_vp_device(vdev);
        /* 0 status means a reset. */
        iowrite8(0, vp_dev->ioaddr + VIRTIO_PCI_STATUS);
+       /* Flush out the status write, and flush in device writes,
+        * including MSi-X interrupts, if any. */
+       ioread8(vp_dev->ioaddr + VIRTIO_PCI_STATUS);
+       /* Flush pending VQ/configuration callbacks. */
+       vp_synchronize_vectors(vdev);
 }
 
 /* the notify function used when creating a virt queue */
 static void vp_notify(struct virtqueue *vq)
 {
        struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev);
-       struct virtio_pci_vq_info *info = vq->priv;
 
        /* we write the queue's selector into the notification register to
         * signal the other end */
-       iowrite16(info->queue_index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_NOTIFY);
+       iowrite16(vq->index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_NOTIFY);
 }
 
 /* Handle a configuration change: Tell driver if it wants to know. */
@@ -257,6 +276,10 @@ static void vp_free_vectors(struct virtio_device *vdev)
        for (i = 0; i < vp_dev->msix_used_vectors; ++i)
                free_irq(vp_dev->msix_entries[i].vector, vp_dev);
 
+       for (i = 0; i < vp_dev->msix_vectors; i++)
+               if (vp_dev->msix_affinity_masks[i])
+                       free_cpumask_var(vp_dev->msix_affinity_masks[i]);
+
        if (vp_dev->msix_enabled) {
                /* Disable the vector used for configuration */
                iowrite16(VIRTIO_MSI_NO_VECTOR,
@@ -274,6 +297,8 @@ static void vp_free_vectors(struct virtio_device *vdev)
        vp_dev->msix_names = NULL;
        kfree(vp_dev->msix_entries);
        vp_dev->msix_entries = NULL;
+       kfree(vp_dev->msix_affinity_masks);
+       vp_dev->msix_affinity_masks = NULL;
 }
 
 static int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors,
@@ -292,6 +317,15 @@ static int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors,
                                     GFP_KERNEL);
        if (!vp_dev->msix_names)
                goto error;
+       vp_dev->msix_affinity_masks
+               = kzalloc(nvectors * sizeof *vp_dev->msix_affinity_masks,
+                         GFP_KERNEL);
+       if (!vp_dev->msix_affinity_masks)
+               goto error;
+       for (i = 0; i < nvectors; ++i)
+               if (!alloc_cpumask_var(&vp_dev->msix_affinity_masks[i],
+                                       GFP_KERNEL))
+                       goto error;
 
        for (i = 0; i < nvectors; ++i)
                vp_dev->msix_entries[i].entry = i;
@@ -380,7 +414,6 @@ static struct virtqueue *setup_vq(struct virtio_device *vdev, unsigned index,
        if (!info)
                return ERR_PTR(-ENOMEM);
 
-       info->queue_index = index;
        info->num = num;
        info->msix_vector = msix_vec;
 
@@ -396,8 +429,8 @@ static struct virtqueue *setup_vq(struct virtio_device *vdev, unsigned index,
                  vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
 
        /* create the vring */
-       vq = vring_new_virtqueue(info->num, VIRTIO_PCI_VRING_ALIGN,
-                                vdev, info->queue, vp_notify, callback, name);
+       vq = vring_new_virtqueue(index, info->num, VIRTIO_PCI_VRING_ALIGN, vdev,
+                                true, info->queue, vp_notify, callback, name);
        if (!vq) {
                err = -ENOMEM;
                goto out_activate_queue;
@@ -415,9 +448,13 @@ static struct virtqueue *setup_vq(struct virtio_device *vdev, unsigned index,
                }
        }
 
-       spin_lock_irqsave(&vp_dev->lock, flags);
-       list_add(&info->node, &vp_dev->virtqueues);
-       spin_unlock_irqrestore(&vp_dev->lock, flags);
+       if (callback) {
+               spin_lock_irqsave(&vp_dev->lock, flags);
+               list_add(&info->node, &vp_dev->virtqueues);
+               spin_unlock_irqrestore(&vp_dev->lock, flags);
+       } else {
+               INIT_LIST_HEAD(&info->node);
+       }
 
        return vq;
 
@@ -441,7 +478,7 @@ static void vp_del_vq(struct virtqueue *vq)
        list_del(&info->node);
        spin_unlock_irqrestore(&vp_dev->lock, flags);
 
-       iowrite16(info->queue_index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
+       iowrite16(vq->index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
 
        if (vp_dev->msix_enabled) {
                iowrite16(VIRTIO_MSI_NO_VECTOR,
@@ -516,7 +553,10 @@ static int vp_try_to_find_vqs(struct virtio_device *vdev, unsigned nvqs,
        vp_dev->per_vq_vectors = per_vq_vectors;
        allocated_vectors = vp_dev->msix_used_vectors;
        for (i = 0; i < nvqs; ++i) {
-               if (!callbacks[i] || !vp_dev->msix_enabled)
+               if (!names[i]) {
+                       vqs[i] = NULL;
+                       continue;
+               } else if (!callbacks[i] || !vp_dev->msix_enabled)
                        msix_vec = VIRTIO_MSI_NO_VECTOR;
                else if (vp_dev->per_vq_vectors)
                        msix_vec = allocated_vectors++;
@@ -576,7 +616,43 @@ static int vp_find_vqs(struct virtio_device *vdev, unsigned nvqs,
                                  false, false);
 }
 
-static struct virtio_config_ops virtio_pci_config_ops = {
+static const char *vp_bus_name(struct virtio_device *vdev)
+{
+       struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+
+       return pci_name(vp_dev->pci_dev);
+}
+
+/* Setup the affinity for a virtqueue:
+ * - force the affinity for per vq vector
+ * - OR over all affinities for shared MSI
+ * - ignore the affinity request if we're using INTX
+ */
+static int vp_set_vq_affinity(struct virtqueue *vq, int cpu)
+{
+       struct virtio_device *vdev = vq->vdev;
+       struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+       struct virtio_pci_vq_info *info = vq->priv;
+       struct cpumask *mask;
+       unsigned int irq;
+
+       if (!vq->callback)
+               return -EINVAL;
+
+       if (vp_dev->msix_enabled) {
+               mask = vp_dev->msix_affinity_masks[info->msix_vector];
+               irq = vp_dev->msix_entries[info->msix_vector].vector;
+               if (cpu == -1)
+                       irq_set_affinity_hint(irq, NULL);
+               else {
+                       cpumask_set_cpu(cpu, mask);
+                       irq_set_affinity_hint(irq, mask);
+               }
+       }
+       return 0;
+}
+
+static const struct virtio_config_ops virtio_pci_config_ops = {
        .get            = vp_get,
        .set            = vp_set,
        .get_status     = vp_get_status,
@@ -586,20 +662,22 @@ static struct virtio_config_ops virtio_pci_config_ops = {
        .del_vqs        = vp_del_vqs,
        .get_features   = vp_get_features,
        .finalize_features = vp_finalize_features,
+       .bus_name       = vp_bus_name,
+       .set_vq_affinity = vp_set_vq_affinity,
 };
 
 static void virtio_pci_release_dev(struct device *_d)
 {
-       struct virtio_device *dev = container_of(_d, struct virtio_device,
-                                                dev);
-       struct virtio_pci_device *vp_dev = to_vp_device(dev);
-
-       kfree(vp_dev);
+       /*
+        * No need for a release method as we allocate/free
+        * all devices together with the pci devices.
+        * Provide an empty one to avoid getting a warning from core.
+        */
 }
 
 /* the PCI probing function */
-static int __devinit virtio_pci_probe(struct pci_dev *pci_dev,
-                                     const struct pci_device_id *id)
+static int virtio_pci_probe(struct pci_dev *pci_dev,
+                           const struct pci_device_id *id)
 {
        struct virtio_pci_device *vp_dev;
        int err;
@@ -639,8 +717,10 @@ static int __devinit virtio_pci_probe(struct pci_dev *pci_dev,
                goto out_enable_device;
 
        vp_dev->ioaddr = pci_iomap(pci_dev, 0, 0);
-       if (vp_dev->ioaddr == NULL)
+       if (vp_dev->ioaddr == NULL) {
+               err = -ENOMEM;
                goto out_req_regions;
+       }
 
        pci_set_drvdata(pci_dev, vp_dev);
        pci_set_master(pci_dev);
@@ -671,7 +751,7 @@ out:
        return err;
 }
 
-static void __devexit virtio_pci_remove(struct pci_dev *pci_dev)
+static void virtio_pci_remove(struct pci_dev *pci_dev)
 {
        struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
 
@@ -682,45 +762,70 @@ static void __devexit virtio_pci_remove(struct pci_dev *pci_dev)
        pci_iounmap(pci_dev, vp_dev->ioaddr);
        pci_release_regions(pci_dev);
        pci_disable_device(pci_dev);
+       kfree(vp_dev);
 }
 
 #ifdef CONFIG_PM
-static int virtio_pci_suspend(struct pci_dev *pci_dev, pm_message_t state)
+static int virtio_pci_freeze(struct device *dev)
 {
-       pci_save_state(pci_dev);
-       pci_set_power_state(pci_dev, PCI_D3hot);
-       return 0;
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+       struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
+       struct virtio_driver *drv;
+       int ret;
+
+       drv = container_of(vp_dev->vdev.dev.driver,
+                          struct virtio_driver, driver);
+
+       ret = 0;
+       vp_dev->saved_status = vp_get_status(&vp_dev->vdev);
+       if (drv && drv->freeze)
+               ret = drv->freeze(&vp_dev->vdev);
+
+       if (!ret)
+               pci_disable_device(pci_dev);
+       return ret;
 }
 
-static int virtio_pci_resume(struct pci_dev *pci_dev)
+static int virtio_pci_restore(struct device *dev)
 {
-       pci_restore_state(pci_dev);
-       pci_set_power_state(pci_dev, PCI_D0);
-       return 0;
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+       struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
+       struct virtio_driver *drv;
+       int ret;
+
+       drv = container_of(vp_dev->vdev.dev.driver,
+                          struct virtio_driver, driver);
+
+       ret = pci_enable_device(pci_dev);
+       if (ret)
+               return ret;
+
+       pci_set_master(pci_dev);
+       vp_finalize_features(&vp_dev->vdev);
+
+       if (drv && drv->restore)
+               ret = drv->restore(&vp_dev->vdev);
+
+       /* Finally, tell the device we're all set */
+       if (!ret)
+               vp_set_status(&vp_dev->vdev, vp_dev->saved_status);
+
+       return ret;
 }
+
+static const struct dev_pm_ops virtio_pci_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(virtio_pci_freeze, virtio_pci_restore)
+};
 #endif
 
 static struct pci_driver virtio_pci_driver = {
        .name           = "virtio-pci",
        .id_table       = virtio_pci_id_table,
        .probe          = virtio_pci_probe,
-       .remove         = __devexit_p(virtio_pci_remove),
+       .remove         = virtio_pci_remove,
 #ifdef CONFIG_PM
-       .suspend        = virtio_pci_suspend,
-       .resume         = virtio_pci_resume,
+       .driver.pm      = &virtio_pci_pm_ops,
 #endif
 };
 
-static int __init virtio_pci_init(void)
-{
-       return pci_register_driver(&virtio_pci_driver);
-}
-
-module_init(virtio_pci_init);
-
-static void __exit virtio_pci_exit(void)
-{
-       pci_unregister_driver(&virtio_pci_driver);
-}
-
-module_exit(virtio_pci_exit);
+module_pci_driver(virtio_pci_driver);