LCOV - code coverage report
Current view: top level - drivers/uio - uio_pdrv_genirq.c (source / functions) Hit Total Coverage
Test: Real Lines: 1 92 1.1 %
Date: 2020-10-17 15:46:16 Functions: 0 9 0.0 %
Legend: Neither, QEMU, Real, Both Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0-only
       2                 :            : /*
       3                 :            :  * drivers/uio/uio_pdrv_genirq.c
       4                 :            :  *
       5                 :            :  * Userspace I/O platform driver with generic IRQ handling code.
       6                 :            :  *
       7                 :            :  * Copyright (C) 2008 Magnus Damm
       8                 :            :  *
       9                 :            :  * Based on uio_pdrv.c by Uwe Kleine-Koenig,
      10                 :            :  * Copyright (C) 2008 by Digi International Inc.
      11                 :            :  * All rights reserved.
      12                 :            :  */
      13                 :            : 
      14                 :            : #include <linux/platform_device.h>
      15                 :            : #include <linux/uio_driver.h>
      16                 :            : #include <linux/spinlock.h>
      17                 :            : #include <linux/bitops.h>
      18                 :            : #include <linux/module.h>
      19                 :            : #include <linux/interrupt.h>
      20                 :            : #include <linux/stringify.h>
      21                 :            : #include <linux/pm_runtime.h>
      22                 :            : #include <linux/slab.h>
      23                 :            : 
      24                 :            : #include <linux/of.h>
      25                 :            : #include <linux/of_platform.h>
      26                 :            : #include <linux/of_address.h>
      27                 :            : 
      28                 :            : #define DRIVER_NAME "uio_pdrv_genirq"
      29                 :            : 
      30                 :            : struct uio_pdrv_genirq_platdata {
      31                 :            :         struct uio_info *uioinfo;
      32                 :            :         spinlock_t lock;
      33                 :            :         unsigned long flags;
      34                 :            :         struct platform_device *pdev;
      35                 :            : };
      36                 :            : 
      37                 :            : /* Bits in uio_pdrv_genirq_platdata.flags */
      38                 :            : enum {
      39                 :            :         UIO_IRQ_DISABLED = 0,
      40                 :            : };
      41                 :            : 
      42                 :          0 : static int uio_pdrv_genirq_open(struct uio_info *info, struct inode *inode)
      43                 :            : {
      44                 :          0 :         struct uio_pdrv_genirq_platdata *priv = info->priv;
      45                 :            : 
      46                 :            :         /* Wait until the Runtime PM code has woken up the device */
      47                 :          0 :         pm_runtime_get_sync(&priv->pdev->dev);
      48                 :          0 :         return 0;
      49                 :            : }
      50                 :            : 
      51                 :          0 : static int uio_pdrv_genirq_release(struct uio_info *info, struct inode *inode)
      52                 :            : {
      53                 :          0 :         struct uio_pdrv_genirq_platdata *priv = info->priv;
      54                 :            : 
      55                 :            :         /* Tell the Runtime PM code that the device has become idle */
      56                 :          0 :         pm_runtime_put_sync(&priv->pdev->dev);
      57                 :          0 :         return 0;
      58                 :            : }
      59                 :            : 
      60                 :          0 : static irqreturn_t uio_pdrv_genirq_handler(int irq, struct uio_info *dev_info)
      61                 :            : {
      62                 :          0 :         struct uio_pdrv_genirq_platdata *priv = dev_info->priv;
      63                 :            : 
      64                 :            :         /* Just disable the interrupt in the interrupt controller, and
      65                 :            :          * remember the state so we can allow user space to enable it later.
      66                 :            :          */
      67                 :            : 
      68                 :            :         spin_lock(&priv->lock);
      69                 :          0 :         if (!__test_and_set_bit(UIO_IRQ_DISABLED, &priv->flags))
      70                 :          0 :                 disable_irq_nosync(irq);
      71                 :            :         spin_unlock(&priv->lock);
      72                 :            : 
      73                 :          0 :         return IRQ_HANDLED;
      74                 :            : }
      75                 :            : 
      76                 :          0 : static int uio_pdrv_genirq_irqcontrol(struct uio_info *dev_info, s32 irq_on)
      77                 :            : {
      78                 :          0 :         struct uio_pdrv_genirq_platdata *priv = dev_info->priv;
      79                 :            :         unsigned long flags;
      80                 :            : 
      81                 :            :         /* Allow user space to enable and disable the interrupt
      82                 :            :          * in the interrupt controller, but keep track of the
      83                 :            :          * state to prevent per-irq depth damage.
      84                 :            :          *
      85                 :            :          * Serialize this operation to support multiple tasks and concurrency
      86                 :            :          * with irq handler on SMP systems.
      87                 :            :          */
      88                 :            : 
      89                 :          0 :         spin_lock_irqsave(&priv->lock, flags);
      90                 :          0 :         if (irq_on) {
      91                 :          0 :                 if (__test_and_clear_bit(UIO_IRQ_DISABLED, &priv->flags))
      92                 :          0 :                         enable_irq(dev_info->irq);
      93                 :            :         } else {
      94                 :          0 :                 if (!__test_and_set_bit(UIO_IRQ_DISABLED, &priv->flags))
      95                 :          0 :                         disable_irq_nosync(dev_info->irq);
      96                 :            :         }
      97                 :            :         spin_unlock_irqrestore(&priv->lock, flags);
      98                 :            : 
      99                 :          0 :         return 0;
     100                 :            : }
     101                 :            : 
     102                 :          0 : static int uio_pdrv_genirq_probe(struct platform_device *pdev)
     103                 :            : {
     104                 :            :         struct uio_info *uioinfo = dev_get_platdata(&pdev->dev);
     105                 :          0 :         struct device_node *node = pdev->dev.of_node;
     106                 :            :         struct uio_pdrv_genirq_platdata *priv;
     107                 :            :         struct uio_mem *uiomem;
     108                 :            :         int ret = -EINVAL;
     109                 :            :         int i;
     110                 :            : 
     111                 :          0 :         if (node) {
     112                 :            :                 const char *name;
     113                 :            : 
     114                 :            :                 /* alloc uioinfo for one device */
     115                 :          0 :                 uioinfo = devm_kzalloc(&pdev->dev, sizeof(*uioinfo),
     116                 :            :                                        GFP_KERNEL);
     117                 :          0 :                 if (!uioinfo) {
     118                 :          0 :                         dev_err(&pdev->dev, "unable to kmalloc\n");
     119                 :          0 :                         return -ENOMEM;
     120                 :            :                 }
     121                 :            : 
     122                 :          0 :                 if (!of_property_read_string(node, "linux,uio-name", &name))
     123                 :          0 :                         uioinfo->name = devm_kstrdup(&pdev->dev, name, GFP_KERNEL);
     124                 :            :                 else
     125                 :          0 :                         uioinfo->name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
     126                 :            :                                                        "%pOFn", node);
     127                 :            : 
     128                 :          0 :                 uioinfo->version = "devicetree";
     129                 :            :                 /* Multiple IRQs are not supported */
     130                 :            :         }
     131                 :            : 
     132                 :          0 :         if (!uioinfo || !uioinfo->name || !uioinfo->version) {
     133                 :          0 :                 dev_err(&pdev->dev, "missing platform_data\n");
     134                 :          0 :                 return ret;
     135                 :            :         }
     136                 :            : 
     137                 :          0 :         if (uioinfo->handler || uioinfo->irqcontrol ||
     138                 :          0 :             uioinfo->irq_flags & IRQF_SHARED) {
     139                 :          0 :                 dev_err(&pdev->dev, "interrupt configuration error\n");
     140                 :          0 :                 return ret;
     141                 :            :         }
     142                 :            : 
     143                 :          0 :         priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
     144                 :          0 :         if (!priv) {
     145                 :          0 :                 dev_err(&pdev->dev, "unable to kmalloc\n");
     146                 :          0 :                 return -ENOMEM;
     147                 :            :         }
     148                 :            : 
     149                 :          0 :         priv->uioinfo = uioinfo;
     150                 :          0 :         spin_lock_init(&priv->lock);
     151                 :          0 :         priv->flags = 0; /* interrupt is enabled to begin with */
     152                 :          0 :         priv->pdev = pdev;
     153                 :            : 
     154                 :          0 :         if (!uioinfo->irq) {
     155                 :          0 :                 ret = platform_get_irq_optional(pdev, 0);
     156                 :          0 :                 uioinfo->irq = ret;
     157                 :          0 :                 if (ret == -ENXIO)
     158                 :          0 :                         uioinfo->irq = UIO_IRQ_NONE;
     159                 :          0 :                 else if (ret < 0) {
     160                 :          0 :                         dev_err(&pdev->dev, "failed to get IRQ\n");
     161                 :          0 :                         return ret;
     162                 :            :                 }
     163                 :            :         }
     164                 :            : 
     165                 :          0 :         uiomem = &uioinfo->mem[0];
     166                 :            : 
     167                 :          0 :         for (i = 0; i < pdev->num_resources; ++i) {
     168                 :          0 :                 struct resource *r = &pdev->resource[i];
     169                 :            : 
     170                 :          0 :                 if (r->flags != IORESOURCE_MEM)
     171                 :          0 :                         continue;
     172                 :            : 
     173                 :          0 :                 if (uiomem >= &uioinfo->mem[MAX_UIO_MAPS]) {
     174                 :          0 :                         dev_warn(&pdev->dev, "device has more than "
     175                 :            :                                         __stringify(MAX_UIO_MAPS)
     176                 :            :                                         " I/O memory resources.\n");
     177                 :          0 :                         break;
     178                 :            :                 }
     179                 :            : 
     180                 :          0 :                 uiomem->memtype = UIO_MEM_PHYS;
     181                 :          0 :                 uiomem->addr = r->start;
     182                 :          0 :                 uiomem->size = resource_size(r);
     183                 :          0 :                 uiomem->name = r->name;
     184                 :          0 :                 ++uiomem;
     185                 :            :         }
     186                 :            : 
     187                 :          0 :         while (uiomem < &uioinfo->mem[MAX_UIO_MAPS]) {
     188                 :          0 :                 uiomem->size = 0;
     189                 :          0 :                 ++uiomem;
     190                 :            :         }
     191                 :            : 
     192                 :            :         /* This driver requires no hardware specific kernel code to handle
     193                 :            :          * interrupts. Instead, the interrupt handler simply disables the
     194                 :            :          * interrupt in the interrupt controller. User space is responsible
     195                 :            :          * for performing hardware specific acknowledge and re-enabling of
     196                 :            :          * the interrupt in the interrupt controller.
     197                 :            :          *
     198                 :            :          * Interrupt sharing is not supported.
     199                 :            :          */
     200                 :            : 
     201                 :          0 :         uioinfo->handler = uio_pdrv_genirq_handler;
     202                 :          0 :         uioinfo->irqcontrol = uio_pdrv_genirq_irqcontrol;
     203                 :          0 :         uioinfo->open = uio_pdrv_genirq_open;
     204                 :          0 :         uioinfo->release = uio_pdrv_genirq_release;
     205                 :          0 :         uioinfo->priv = priv;
     206                 :            : 
     207                 :            :         /* Enable Runtime PM for this device:
     208                 :            :          * The device starts in suspended state to allow the hardware to be
     209                 :            :          * turned off by default. The Runtime PM bus code should power on the
     210                 :            :          * hardware and enable clocks at open().
     211                 :            :          */
     212                 :          0 :         pm_runtime_enable(&pdev->dev);
     213                 :            : 
     214                 :          0 :         ret = uio_register_device(&pdev->dev, priv->uioinfo);
     215                 :          0 :         if (ret) {
     216                 :          0 :                 dev_err(&pdev->dev, "unable to register uio device\n");
     217                 :            :                 pm_runtime_disable(&pdev->dev);
     218                 :          0 :                 return ret;
     219                 :            :         }
     220                 :            : 
     221                 :            :         platform_set_drvdata(pdev, priv);
     222                 :          0 :         return 0;
     223                 :            : }
     224                 :            : 
     225                 :          0 : static int uio_pdrv_genirq_remove(struct platform_device *pdev)
     226                 :            : {
     227                 :            :         struct uio_pdrv_genirq_platdata *priv = platform_get_drvdata(pdev);
     228                 :            : 
     229                 :          0 :         uio_unregister_device(priv->uioinfo);
     230                 :          0 :         pm_runtime_disable(&pdev->dev);
     231                 :            : 
     232                 :          0 :         priv->uioinfo->handler = NULL;
     233                 :          0 :         priv->uioinfo->irqcontrol = NULL;
     234                 :            : 
     235                 :          0 :         return 0;
     236                 :            : }
     237                 :            : 
     238                 :          0 : static int uio_pdrv_genirq_runtime_nop(struct device *dev)
     239                 :            : {
     240                 :            :         /* Runtime PM callback shared between ->runtime_suspend()
     241                 :            :          * and ->runtime_resume(). Simply returns success.
     242                 :            :          *
     243                 :            :          * In this driver pm_runtime_get_sync() and pm_runtime_put_sync()
     244                 :            :          * are used at open() and release() time. This allows the
     245                 :            :          * Runtime PM code to turn off power to the device while the
     246                 :            :          * device is unused, ie before open() and after release().
     247                 :            :          *
     248                 :            :          * This Runtime PM callback does not need to save or restore
     249                 :            :          * any registers since user space is responsbile for hardware
     250                 :            :          * register reinitialization after open().
     251                 :            :          */
     252                 :          0 :         return 0;
     253                 :            : }
     254                 :            : 
     255                 :            : static const struct dev_pm_ops uio_pdrv_genirq_dev_pm_ops = {
     256                 :            :         .runtime_suspend = uio_pdrv_genirq_runtime_nop,
     257                 :            :         .runtime_resume = uio_pdrv_genirq_runtime_nop,
     258                 :            : };
     259                 :            : 
     260                 :            : #ifdef CONFIG_OF
     261                 :            : static struct of_device_id uio_of_genirq_match[] = {
     262                 :            :         { /* This is filled with module_parm */ },
     263                 :            :         { /* Sentinel */ },
     264                 :            : };
     265                 :            : MODULE_DEVICE_TABLE(of, uio_of_genirq_match);
     266                 :            : module_param_string(of_id, uio_of_genirq_match[0].compatible, 128, 0);
     267                 :            : MODULE_PARM_DESC(of_id, "Openfirmware id of the device to be handled by uio");
     268                 :            : #endif
     269                 :            : 
     270                 :            : static struct platform_driver uio_pdrv_genirq = {
     271                 :            :         .probe = uio_pdrv_genirq_probe,
     272                 :            :         .remove = uio_pdrv_genirq_remove,
     273                 :            :         .driver = {
     274                 :            :                 .name = DRIVER_NAME,
     275                 :            :                 .pm = &uio_pdrv_genirq_dev_pm_ops,
     276                 :            :                 .of_match_table = of_match_ptr(uio_of_genirq_match),
     277                 :            :         },
     278                 :            : };
     279                 :            : 
     280                 :          3 : module_platform_driver(uio_pdrv_genirq);
     281                 :            : 
     282                 :            : MODULE_AUTHOR("Magnus Damm");
     283                 :            : MODULE_DESCRIPTION("Userspace I/O platform driver with generic IRQ handling");
     284                 :            : MODULE_LICENSE("GPL v2");
     285                 :            : MODULE_ALIAS("platform:" DRIVER_NAME);
    

Generated by: LCOV version 1.14