um: virtio_uml: Allow probing from devicetree
authorVincent Whitchurch <vincent.whitchurch@axis.com>
Tue, 21 Dec 2021 09:04:46 +0000 (10:04 +0100)
committerRichard Weinberger <richard@nod.at>
Wed, 22 Dec 2021 19:40:05 +0000 (20:40 +0100)
Allow the virtio_uml device to be probed from the devicetree so that
sub-devices can be specified using the standard virtio bindings, for
example:

  virtio@1 {
    compatible = "virtio,uml";
    socket-path = "i2c.sock";
    virtio-device-id = <0x22>;

    i2c-controller {
      compatible = "virtio,device22";
      #address-cells = <0x01>;
      #size-cells = <0x00>;

      light-sensor@01 {
        compatible = "ti,opt3001";
        reg = <0x01>;
      };
    };
  };

Signed-off-by: Vincent Whitchurch <vincent.whitchurch@axis.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
arch/um/drivers/virtio_uml.c

index 7755cb4..ba562d6 100644 (file)
@@ -21,6 +21,7 @@
  * Based on Virtio MMIO driver by Pawel Moll, copyright 2011-2014, ARM Ltd.
  */
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/virtio.h>
@@ -49,6 +50,7 @@ struct virtio_uml_platform_data {
 struct virtio_uml_device {
        struct virtio_device vdev;
        struct platform_device *pdev;
+       struct virtio_uml_platform_data *pdata;
 
        spinlock_t sock_lock;
        int sock, req_fd, irq;
@@ -149,7 +151,7 @@ static int vhost_user_recv(struct virtio_uml_device *vu_dev,
        if (rc == -ECONNRESET && vu_dev->registered) {
                struct virtio_uml_platform_data *pdata;
 
-               pdata = vu_dev->pdev->dev.platform_data;
+               pdata = vu_dev->pdata;
 
                virtio_break_device(&vu_dev->vdev);
                schedule_work(&pdata->conn_broken_wk);
@@ -1115,21 +1117,63 @@ void virtio_uml_set_no_vq_suspend(struct virtio_device *vdev,
                 no_vq_suspend ? "dis" : "en");
 }
 
+static void vu_of_conn_broken(struct work_struct *wk)
+{
+       /*
+        * We can't remove the device from the devicetree so the only thing we
+        * can do is warn.
+        */
+       WARN_ON(1);
+}
+
 /* Platform device */
 
+static struct virtio_uml_platform_data *
+virtio_uml_create_pdata(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct virtio_uml_platform_data *pdata;
+       int ret;
+
+       if (!np)
+               return ERR_PTR(-EINVAL);
+
+       pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return ERR_PTR(-ENOMEM);
+
+       INIT_WORK(&pdata->conn_broken_wk, vu_of_conn_broken);
+       pdata->pdev = pdev;
+
+       ret = of_property_read_string(np, "socket-path", &pdata->socket_path);
+       if (ret)
+               return ERR_PTR(ret);
+
+       ret = of_property_read_u32(np, "virtio-device-id",
+                                  &pdata->virtio_device_id);
+       if (ret)
+               return ERR_PTR(ret);
+
+       return pdata;
+}
+
 static int virtio_uml_probe(struct platform_device *pdev)
 {
        struct virtio_uml_platform_data *pdata = pdev->dev.platform_data;
        struct virtio_uml_device *vu_dev;
        int rc;
 
-       if (!pdata)
-               return -EINVAL;
+       if (!pdata) {
+               pdata = virtio_uml_create_pdata(pdev);
+               if (IS_ERR(pdata))
+                       return PTR_ERR(pdata);
+       }
 
        vu_dev = kzalloc(sizeof(*vu_dev), GFP_KERNEL);
        if (!vu_dev)
                return -ENOMEM;
 
+       vu_dev->pdata = pdata;
        vu_dev->vdev.dev.parent = &pdev->dev;
        vu_dev->vdev.dev.release = virtio_uml_release_dev;
        vu_dev->vdev.config = &virtio_uml_config_ops;