root/drivers/hwspinlock/u8500_hsem.c

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

DEFINITIONS

This source file includes following definitions.
  1. u8500_hsem_trylock
  2. u8500_hsem_unlock
  3. u8500_hsem_relax
  4. u8500_hsem_probe
  5. u8500_hsem_remove
  6. u8500_hsem_init
  7. u8500_hsem_exit

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * u8500 HWSEM driver
   4  *
   5  * Copyright (C) 2010-2011 ST-Ericsson
   6  *
   7  * Implements u8500 semaphore handling for protocol 1, no interrupts.
   8  *
   9  * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
  10  * Heavily borrowed from the work of :
  11  *   Simon Que <sque@ti.com>
  12  *   Hari Kanigeri <h-kanigeri2@ti.com>
  13  *   Ohad Ben-Cohen <ohad@wizery.com>
  14  */
  15 
  16 #include <linux/module.h>
  17 #include <linux/delay.h>
  18 #include <linux/io.h>
  19 #include <linux/pm_runtime.h>
  20 #include <linux/slab.h>
  21 #include <linux/spinlock.h>
  22 #include <linux/hwspinlock.h>
  23 #include <linux/platform_device.h>
  24 
  25 #include "hwspinlock_internal.h"
  26 
  27 /*
  28  * Implementation of STE's HSem protocol 1 without interrutps.
  29  * The only masterID we allow is '0x01' to force people to use
  30  * HSems for synchronisation between processors rather than processes
  31  * on the ARM core.
  32  */
  33 
  34 #define U8500_MAX_SEMAPHORE             32      /* a total of 32 semaphore */
  35 #define RESET_SEMAPHORE                 (0)     /* free */
  36 
  37 /*
  38  * CPU ID for master running u8500 kernel.
  39  * Hswpinlocks should only be used to synchonise operations
  40  * between the Cortex A9 core and the other CPUs.  Hence
  41  * forcing the masterID to a preset value.
  42  */
  43 #define HSEM_MASTER_ID                  0x01
  44 
  45 #define HSEM_REGISTER_OFFSET            0x08
  46 
  47 #define HSEM_CTRL_REG                   0x00
  48 #define HSEM_ICRALL                     0x90
  49 #define HSEM_PROTOCOL_1                 0x01
  50 
  51 static int u8500_hsem_trylock(struct hwspinlock *lock)
  52 {
  53         void __iomem *lock_addr = lock->priv;
  54 
  55         writel(HSEM_MASTER_ID, lock_addr);
  56 
  57         /* get only first 4 bit and compare to masterID.
  58          * if equal, we have the semaphore, otherwise
  59          * someone else has it.
  60          */
  61         return (HSEM_MASTER_ID == (0x0F & readl(lock_addr)));
  62 }
  63 
  64 static void u8500_hsem_unlock(struct hwspinlock *lock)
  65 {
  66         void __iomem *lock_addr = lock->priv;
  67 
  68         /* release the lock by writing 0 to it */
  69         writel(RESET_SEMAPHORE, lock_addr);
  70 }
  71 
  72 /*
  73  * u8500: what value is recommended here ?
  74  */
  75 static void u8500_hsem_relax(struct hwspinlock *lock)
  76 {
  77         ndelay(50);
  78 }
  79 
  80 static const struct hwspinlock_ops u8500_hwspinlock_ops = {
  81         .trylock        = u8500_hsem_trylock,
  82         .unlock         = u8500_hsem_unlock,
  83         .relax          = u8500_hsem_relax,
  84 };
  85 
  86 static int u8500_hsem_probe(struct platform_device *pdev)
  87 {
  88         struct hwspinlock_pdata *pdata = pdev->dev.platform_data;
  89         struct hwspinlock_device *bank;
  90         struct hwspinlock *hwlock;
  91         struct resource *res;
  92         void __iomem *io_base;
  93         int i, ret, num_locks = U8500_MAX_SEMAPHORE;
  94         ulong val;
  95 
  96         if (!pdata)
  97                 return -ENODEV;
  98 
  99         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 100         if (!res)
 101                 return -ENODEV;
 102 
 103         io_base = ioremap(res->start, resource_size(res));
 104         if (!io_base)
 105                 return -ENOMEM;
 106 
 107         /* make sure protocol 1 is selected */
 108         val = readl(io_base + HSEM_CTRL_REG);
 109         writel((val & ~HSEM_PROTOCOL_1), io_base + HSEM_CTRL_REG);
 110 
 111         /* clear all interrupts */
 112         writel(0xFFFF, io_base + HSEM_ICRALL);
 113 
 114         bank = kzalloc(struct_size(bank, lock, num_locks), GFP_KERNEL);
 115         if (!bank) {
 116                 ret = -ENOMEM;
 117                 goto iounmap_base;
 118         }
 119 
 120         platform_set_drvdata(pdev, bank);
 121 
 122         for (i = 0, hwlock = &bank->lock[0]; i < num_locks; i++, hwlock++)
 123                 hwlock->priv = io_base + HSEM_REGISTER_OFFSET + sizeof(u32) * i;
 124 
 125         /* no pm needed for HSem but required to comply with hwspilock core */
 126         pm_runtime_enable(&pdev->dev);
 127 
 128         ret = hwspin_lock_register(bank, &pdev->dev, &u8500_hwspinlock_ops,
 129                                                 pdata->base_id, num_locks);
 130         if (ret)
 131                 goto reg_fail;
 132 
 133         return 0;
 134 
 135 reg_fail:
 136         pm_runtime_disable(&pdev->dev);
 137         kfree(bank);
 138 iounmap_base:
 139         iounmap(io_base);
 140         return ret;
 141 }
 142 
 143 static int u8500_hsem_remove(struct platform_device *pdev)
 144 {
 145         struct hwspinlock_device *bank = platform_get_drvdata(pdev);
 146         void __iomem *io_base = bank->lock[0].priv - HSEM_REGISTER_OFFSET;
 147         int ret;
 148 
 149         /* clear all interrupts */
 150         writel(0xFFFF, io_base + HSEM_ICRALL);
 151 
 152         ret = hwspin_lock_unregister(bank);
 153         if (ret) {
 154                 dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret);
 155                 return ret;
 156         }
 157 
 158         pm_runtime_disable(&pdev->dev);
 159         iounmap(io_base);
 160         kfree(bank);
 161 
 162         return 0;
 163 }
 164 
 165 static struct platform_driver u8500_hsem_driver = {
 166         .probe          = u8500_hsem_probe,
 167         .remove         = u8500_hsem_remove,
 168         .driver         = {
 169                 .name   = "u8500_hsem",
 170         },
 171 };
 172 
 173 static int __init u8500_hsem_init(void)
 174 {
 175         return platform_driver_register(&u8500_hsem_driver);
 176 }
 177 /* board init code might need to reserve hwspinlocks for predefined purposes */
 178 postcore_initcall(u8500_hsem_init);
 179 
 180 static void __exit u8500_hsem_exit(void)
 181 {
 182         platform_driver_unregister(&u8500_hsem_driver);
 183 }
 184 module_exit(u8500_hsem_exit);
 185 
 186 MODULE_LICENSE("GPL v2");
 187 MODULE_DESCRIPTION("Hardware Spinlock driver for u8500");
 188 MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");

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