summaryrefslogtreecommitdiff
path: root/drivers/uio/uio_pdrv_genirq.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/uio/uio_pdrv_genirq.c')
-rw-r--r--drivers/uio/uio_pdrv_genirq.c140
1 files changed, 60 insertions, 80 deletions
diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c
index 4eb8eaf71be8..0a1885d1b2e3 100644
--- a/drivers/uio/uio_pdrv_genirq.c
+++ b/drivers/uio/uio_pdrv_genirq.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* drivers/uio/uio_pdrv_genirq.c
*
@@ -8,10 +9,6 @@
* Based on uio_pdrv.c by Uwe Kleine-Koenig,
* Copyright (C) 2008 by Digi International Inc.
* All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
*/
#include <linux/platform_device.h>
@@ -23,10 +20,11 @@
#include <linux/stringify.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
+#include <linux/irq.h>
#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/of_address.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
#define DRIVER_NAME "uio_pdrv_genirq"
@@ -102,43 +100,58 @@ static int uio_pdrv_genirq_irqcontrol(struct uio_info *dev_info, s32 irq_on)
return 0;
}
+static void uio_pdrv_genirq_cleanup(void *data)
+{
+ struct device *dev = data;
+
+ pm_runtime_disable(dev);
+}
+
static int uio_pdrv_genirq_probe(struct platform_device *pdev)
{
- struct uio_info *uioinfo = pdev->dev.platform_data;
+ struct uio_info *uioinfo = dev_get_platdata(&pdev->dev);
+ struct fwnode_handle *node = dev_fwnode(&pdev->dev);
struct uio_pdrv_genirq_platdata *priv;
struct uio_mem *uiomem;
int ret = -EINVAL;
int i;
- if (pdev->dev.of_node) {
+ if (node) {
+ const char *name;
+
/* alloc uioinfo for one device */
- uioinfo = kzalloc(sizeof(*uioinfo), GFP_KERNEL);
+ uioinfo = devm_kzalloc(&pdev->dev, sizeof(*uioinfo),
+ GFP_KERNEL);
if (!uioinfo) {
- ret = -ENOMEM;
dev_err(&pdev->dev, "unable to kmalloc\n");
- return ret;
+ return -ENOMEM;
}
- uioinfo->name = pdev->dev.of_node->name;
+
+ if (!device_property_read_string(&pdev->dev, "linux,uio-name", &name))
+ uioinfo->name = devm_kstrdup(&pdev->dev, name, GFP_KERNEL);
+ else
+ uioinfo->name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+ "%pfwP", node);
+
uioinfo->version = "devicetree";
/* Multiple IRQs are not supported */
}
if (!uioinfo || !uioinfo->name || !uioinfo->version) {
dev_err(&pdev->dev, "missing platform_data\n");
- goto bad0;
+ return ret;
}
if (uioinfo->handler || uioinfo->irqcontrol ||
uioinfo->irq_flags & IRQF_SHARED) {
dev_err(&pdev->dev, "interrupt configuration error\n");
- goto bad0;
+ return ret;
}
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
- ret = -ENOMEM;
dev_err(&pdev->dev, "unable to kmalloc\n");
- goto bad0;
+ return -ENOMEM;
}
priv->uioinfo = uioinfo;
@@ -147,13 +160,29 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
priv->pdev = pdev;
if (!uioinfo->irq) {
- ret = platform_get_irq(pdev, 0);
+ ret = platform_get_irq_optional(pdev, 0);
uioinfo->irq = ret;
- if (ret == -ENXIO && pdev->dev.of_node)
+ if (ret == -ENXIO)
uioinfo->irq = UIO_IRQ_NONE;
+ else if (ret == -EPROBE_DEFER)
+ return ret;
else if (ret < 0) {
dev_err(&pdev->dev, "failed to get IRQ\n");
- goto bad1;
+ return ret;
+ }
+ }
+
+ if (uioinfo->irq) {
+ /*
+ * If a level interrupt, dont do lazy disable. Otherwise the
+ * irq will fire again since clearing of the actual cause, on
+ * device level, is done in userspace
+ * irqd_is_level_type() isn't used since isn't valid until
+ * irq is configured.
+ */
+ if (irq_get_trigger_type(uioinfo->irq) & IRQ_TYPE_LEVEL_MASK) {
+ dev_dbg(&pdev->dev, "disable lazy unmask\n");
+ irq_set_status_flags(uioinfo->irq, IRQ_DISABLE_UNLAZY);
}
}
@@ -173,8 +202,10 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
}
uiomem->memtype = UIO_MEM_PHYS;
- uiomem->addr = r->start;
- uiomem->size = resource_size(r);
+ uiomem->addr = r->start & PAGE_MASK;
+ uiomem->offs = r->start & ~PAGE_MASK;
+ uiomem->size = (uiomem->offs + resource_size(r)
+ + PAGE_SIZE - 1) & PAGE_MASK;
uiomem->name = r->name;
++uiomem;
}
@@ -206,82 +237,31 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
*/
pm_runtime_enable(&pdev->dev);
- ret = uio_register_device(&pdev->dev, priv->uioinfo);
- if (ret) {
+ ret = devm_add_action_or_reset(&pdev->dev, uio_pdrv_genirq_cleanup,
+ &pdev->dev);
+ if (ret)
+ return ret;
+
+ ret = devm_uio_register_device(&pdev->dev, priv->uioinfo);
+ if (ret)
dev_err(&pdev->dev, "unable to register uio device\n");
- goto bad2;
- }
- platform_set_drvdata(pdev, priv);
- return 0;
- bad2:
- pm_runtime_disable(&pdev->dev);
- bad1:
- kfree(priv);
- bad0:
- /* kfree uioinfo for OF */
- if (pdev->dev.of_node)
- kfree(uioinfo);
return ret;
}
-static int uio_pdrv_genirq_remove(struct platform_device *pdev)
-{
- struct uio_pdrv_genirq_platdata *priv = platform_get_drvdata(pdev);
-
- uio_unregister_device(priv->uioinfo);
- pm_runtime_disable(&pdev->dev);
-
- priv->uioinfo->handler = NULL;
- priv->uioinfo->irqcontrol = NULL;
-
- /* kfree uioinfo for OF */
- if (pdev->dev.of_node)
- kfree(priv->uioinfo);
-
- kfree(priv);
- return 0;
-}
-
-static int uio_pdrv_genirq_runtime_nop(struct device *dev)
-{
- /* Runtime PM callback shared between ->runtime_suspend()
- * and ->runtime_resume(). Simply returns success.
- *
- * In this driver pm_runtime_get_sync() and pm_runtime_put_sync()
- * are used at open() and release() time. This allows the
- * Runtime PM code to turn off power to the device while the
- * device is unused, ie before open() and after release().
- *
- * This Runtime PM callback does not need to save or restore
- * any registers since user space is responsbile for hardware
- * register reinitialization after open().
- */
- return 0;
-}
-
-static const struct dev_pm_ops uio_pdrv_genirq_dev_pm_ops = {
- .runtime_suspend = uio_pdrv_genirq_runtime_nop,
- .runtime_resume = uio_pdrv_genirq_runtime_nop,
-};
-
#ifdef CONFIG_OF
static struct of_device_id uio_of_genirq_match[] = {
{ /* This is filled with module_parm */ },
{ /* Sentinel */ },
};
-MODULE_DEVICE_TABLE(of, uio_of_genirq_match);
module_param_string(of_id, uio_of_genirq_match[0].compatible, 128, 0);
MODULE_PARM_DESC(of_id, "Openfirmware id of the device to be handled by uio");
#endif
static struct platform_driver uio_pdrv_genirq = {
.probe = uio_pdrv_genirq_probe,
- .remove = uio_pdrv_genirq_remove,
.driver = {
.name = DRIVER_NAME,
- .owner = THIS_MODULE,
- .pm = &uio_pdrv_genirq_dev_pm_ops,
.of_match_table = of_match_ptr(uio_of_genirq_match),
},
};