root/drivers/hwspinlock/omap_hwspinlock.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. omap_hwspinlock_trylock
  2. omap_hwspinlock_unlock
  3. omap_hwspinlock_relax
  4. omap_hwspinlock_probe
  5. omap_hwspinlock_remove
  6. omap_hwspinlock_init
  7. omap_hwspinlock_exit

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * OMAP hardware spinlock driver
   4  *
   5  * Copyright (C) 2010-2015 Texas Instruments Incorporated - http://www.ti.com
   6  *
   7  * Contact: Simon Que <sque@ti.com>
   8  *          Hari Kanigeri <h-kanigeri2@ti.com>
   9  *          Ohad Ben-Cohen <ohad@wizery.com>
  10  */
  11 
  12 #include <linux/kernel.h>
  13 #include <linux/module.h>
  14 #include <linux/device.h>
  15 #include <linux/delay.h>
  16 #include <linux/io.h>
  17 #include <linux/bitops.h>
  18 #include <linux/pm_runtime.h>
  19 #include <linux/slab.h>
  20 #include <linux/spinlock.h>
  21 #include <linux/hwspinlock.h>
  22 #include <linux/of.h>
  23 #include <linux/platform_device.h>
  24 
  25 #include "hwspinlock_internal.h"
  26 
  27 /* Spinlock register offsets */
  28 #define SYSSTATUS_OFFSET                0x0014
  29 #define LOCK_BASE_OFFSET                0x0800
  30 
  31 #define SPINLOCK_NUMLOCKS_BIT_OFFSET    (24)
  32 
  33 /* Possible values of SPINLOCK_LOCK_REG */
  34 #define SPINLOCK_NOTTAKEN               (0)     /* free */
  35 #define SPINLOCK_TAKEN                  (1)     /* locked */
  36 
  37 static int omap_hwspinlock_trylock(struct hwspinlock *lock)
  38 {
  39         void __iomem *lock_addr = lock->priv;
  40 
  41         /* attempt to acquire the lock by reading its value */
  42         return (SPINLOCK_NOTTAKEN == readl(lock_addr));
  43 }
  44 
  45 static void omap_hwspinlock_unlock(struct hwspinlock *lock)
  46 {
  47         void __iomem *lock_addr = lock->priv;
  48 
  49         /* release the lock by writing 0 to it */
  50         writel(SPINLOCK_NOTTAKEN, lock_addr);
  51 }
  52 
  53 /*
  54  * relax the OMAP interconnect while spinning on it.
  55  *
  56  * The specs recommended that the retry delay time will be
  57  * just over half of the time that a requester would be
  58  * expected to hold the lock.
  59  *
  60  * The number below is taken from an hardware specs example,
  61  * obviously it is somewhat arbitrary.
  62  */
  63 static void omap_hwspinlock_relax(struct hwspinlock *lock)
  64 {
  65         ndelay(50);
  66 }
  67 
  68 static const struct hwspinlock_ops omap_hwspinlock_ops = {
  69         .trylock = omap_hwspinlock_trylock,
  70         .unlock = omap_hwspinlock_unlock,
  71         .relax = omap_hwspinlock_relax,
  72 };
  73 
  74 static int omap_hwspinlock_probe(struct platform_device *pdev)
  75 {
  76         struct device_node *node = pdev->dev.of_node;
  77         struct hwspinlock_device *bank;
  78         struct hwspinlock *hwlock;
  79         struct resource *res;
  80         void __iomem *io_base;
  81         int num_locks, i, ret;
  82         /* Only a single hwspinlock block device is supported */
  83         int base_id = 0;
  84 
  85         if (!node)
  86                 return -ENODEV;
  87 
  88         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  89         if (!res)
  90                 return -ENODEV;
  91 
  92         io_base = ioremap(res->start, resource_size(res));
  93         if (!io_base)
  94                 return -ENOMEM;
  95 
  96         /*
  97          * make sure the module is enabled and clocked before reading
  98          * the module SYSSTATUS register
  99          */
 100         pm_runtime_enable(&pdev->dev);
 101         ret = pm_runtime_get_sync(&pdev->dev);
 102         if (ret < 0) {
 103                 pm_runtime_put_noidle(&pdev->dev);
 104                 goto iounmap_base;
 105         }
 106 
 107         /* Determine number of locks */
 108         i = readl(io_base + SYSSTATUS_OFFSET);
 109         i >>= SPINLOCK_NUMLOCKS_BIT_OFFSET;
 110 
 111         /*
 112          * runtime PM will make sure the clock of this module is
 113          * enabled again iff at least one lock is requested
 114          */
 115         ret = pm_runtime_put(&pdev->dev);
 116         if (ret < 0)
 117                 goto iounmap_base;
 118 
 119         /* one of the four lsb's must be set, and nothing else */
 120         if (hweight_long(i & 0xf) != 1 || i > 8) {
 121                 ret = -EINVAL;
 122                 goto iounmap_base;
 123         }
 124 
 125         num_locks = i * 32; /* actual number of locks in this device */
 126 
 127         bank = kzalloc(struct_size(bank, lock, num_locks), GFP_KERNEL);
 128         if (!bank) {
 129                 ret = -ENOMEM;
 130                 goto iounmap_base;
 131         }
 132 
 133         platform_set_drvdata(pdev, bank);
 134 
 135         for (i = 0, hwlock = &bank->lock[0]; i < num_locks; i++, hwlock++)
 136                 hwlock->priv = io_base + LOCK_BASE_OFFSET + sizeof(u32) * i;
 137 
 138         ret = hwspin_lock_register(bank, &pdev->dev, &omap_hwspinlock_ops,
 139                                                 base_id, num_locks);
 140         if (ret)
 141                 goto reg_fail;
 142 
 143         dev_dbg(&pdev->dev, "Registered %d locks with HwSpinlock core\n",
 144                 num_locks);
 145 
 146         return 0;
 147 
 148 reg_fail:
 149         kfree(bank);
 150 iounmap_base:
 151         pm_runtime_disable(&pdev->dev);
 152         iounmap(io_base);
 153         return ret;
 154 }
 155 
 156 static int omap_hwspinlock_remove(struct platform_device *pdev)
 157 {
 158         struct hwspinlock_device *bank = platform_get_drvdata(pdev);
 159         void __iomem *io_base = bank->lock[0].priv - LOCK_BASE_OFFSET;
 160         int ret;
 161 
 162         ret = hwspin_lock_unregister(bank);
 163         if (ret) {
 164                 dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret);
 165                 return ret;
 166         }
 167 
 168         pm_runtime_disable(&pdev->dev);
 169         iounmap(io_base);
 170         kfree(bank);
 171 
 172         return 0;
 173 }
 174 
 175 static const struct of_device_id omap_hwspinlock_of_match[] = {
 176         { .compatible = "ti,omap4-hwspinlock", },
 177         { .compatible = "ti,am654-hwspinlock", },
 178         { /* end */ },
 179 };
 180 MODULE_DEVICE_TABLE(of, omap_hwspinlock_of_match);
 181 
 182 static struct platform_driver omap_hwspinlock_driver = {
 183         .probe          = omap_hwspinlock_probe,
 184         .remove         = omap_hwspinlock_remove,
 185         .driver         = {
 186                 .name   = "omap_hwspinlock",
 187                 .of_match_table = of_match_ptr(omap_hwspinlock_of_match),
 188         },
 189 };
 190 
 191 static int __init omap_hwspinlock_init(void)
 192 {
 193         return platform_driver_register(&omap_hwspinlock_driver);
 194 }
 195 /* board init code might need to reserve hwspinlocks for predefined purposes */
 196 postcore_initcall(omap_hwspinlock_init);
 197 
 198 static void __exit omap_hwspinlock_exit(void)
 199 {
 200         platform_driver_unregister(&omap_hwspinlock_driver);
 201 }
 202 module_exit(omap_hwspinlock_exit);
 203 
 204 MODULE_LICENSE("GPL v2");
 205 MODULE_DESCRIPTION("Hardware spinlock driver for OMAP");
 206 MODULE_AUTHOR("Simon Que <sque@ti.com>");
 207 MODULE_AUTHOR("Hari Kanigeri <h-kanigeri2@ti.com>");
 208 MODULE_AUTHOR("Ohad Ben-Cohen <ohad@wizery.com>");

/* [<][>][^][v][top][bottom][index][help] */