linux/drivers/hwspinlock/u8500_hsem.c
<<
>>
Prefs
   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/slab.h>
  20#include <linux/spinlock.h>
  21#include <linux/hwspinlock.h>
  22#include <linux/platform_device.h>
  23
  24#include "hwspinlock_internal.h"
  25
  26/*
  27 * Implementation of STE's HSem protocol 1 without interrutps.
  28 * The only masterID we allow is '0x01' to force people to use
  29 * HSems for synchronisation between processors rather than processes
  30 * on the ARM core.
  31 */
  32
  33#define U8500_MAX_SEMAPHORE             32      /* a total of 32 semaphore */
  34#define RESET_SEMAPHORE                 (0)     /* free */
  35
  36/*
  37 * CPU ID for master running u8500 kernel.
  38 * Hswpinlocks should only be used to synchonise operations
  39 * between the Cortex A9 core and the other CPUs.  Hence
  40 * forcing the masterID to a preset value.
  41 */
  42#define HSEM_MASTER_ID                  0x01
  43
  44#define HSEM_REGISTER_OFFSET            0x08
  45
  46#define HSEM_CTRL_REG                   0x00
  47#define HSEM_ICRALL                     0x90
  48#define HSEM_PROTOCOL_1                 0x01
  49
  50static int u8500_hsem_trylock(struct hwspinlock *lock)
  51{
  52        void __iomem *lock_addr = lock->priv;
  53
  54        writel(HSEM_MASTER_ID, lock_addr);
  55
  56        /* get only first 4 bit and compare to masterID.
  57         * if equal, we have the semaphore, otherwise
  58         * someone else has it.
  59         */
  60        return (HSEM_MASTER_ID == (0x0F & readl(lock_addr)));
  61}
  62
  63static void u8500_hsem_unlock(struct hwspinlock *lock)
  64{
  65        void __iomem *lock_addr = lock->priv;
  66
  67        /* release the lock by writing 0 to it */
  68        writel(RESET_SEMAPHORE, lock_addr);
  69}
  70
  71/*
  72 * u8500: what value is recommended here ?
  73 */
  74static void u8500_hsem_relax(struct hwspinlock *lock)
  75{
  76        ndelay(50);
  77}
  78
  79static const struct hwspinlock_ops u8500_hwspinlock_ops = {
  80        .trylock        = u8500_hsem_trylock,
  81        .unlock         = u8500_hsem_unlock,
  82        .relax          = u8500_hsem_relax,
  83};
  84
  85static int u8500_hsem_probe(struct platform_device *pdev)
  86{
  87        struct hwspinlock_pdata *pdata = pdev->dev.platform_data;
  88        struct hwspinlock_device *bank;
  89        struct hwspinlock *hwlock;
  90        void __iomem *io_base;
  91        int i, num_locks = U8500_MAX_SEMAPHORE;
  92        ulong val;
  93
  94        if (!pdata)
  95                return -ENODEV;
  96
  97        io_base = devm_platform_ioremap_resource(pdev, 0);
  98        if (IS_ERR(io_base))
  99                return PTR_ERR(io_base);
 100
 101        /* make sure protocol 1 is selected */
 102        val = readl(io_base + HSEM_CTRL_REG);
 103        writel((val & ~HSEM_PROTOCOL_1), io_base + HSEM_CTRL_REG);
 104
 105        /* clear all interrupts */
 106        writel(0xFFFF, io_base + HSEM_ICRALL);
 107
 108        bank = devm_kzalloc(&pdev->dev, struct_size(bank, lock, num_locks),
 109                            GFP_KERNEL);
 110        if (!bank)
 111                return -ENOMEM;
 112
 113        platform_set_drvdata(pdev, bank);
 114
 115        for (i = 0, hwlock = &bank->lock[0]; i < num_locks; i++, hwlock++)
 116                hwlock->priv = io_base + HSEM_REGISTER_OFFSET + sizeof(u32) * i;
 117
 118        return devm_hwspin_lock_register(&pdev->dev, bank,
 119                                         &u8500_hwspinlock_ops,
 120                                         pdata->base_id, num_locks);
 121}
 122
 123static int u8500_hsem_remove(struct platform_device *pdev)
 124{
 125        struct hwspinlock_device *bank = platform_get_drvdata(pdev);
 126        void __iomem *io_base = bank->lock[0].priv - HSEM_REGISTER_OFFSET;
 127
 128        /* clear all interrupts */
 129        writel(0xFFFF, io_base + HSEM_ICRALL);
 130
 131        return 0;
 132}
 133
 134static struct platform_driver u8500_hsem_driver = {
 135        .probe          = u8500_hsem_probe,
 136        .remove         = u8500_hsem_remove,
 137        .driver         = {
 138                .name   = "u8500_hsem",
 139        },
 140};
 141
 142static int __init u8500_hsem_init(void)
 143{
 144        return platform_driver_register(&u8500_hsem_driver);
 145}
 146/* board init code might need to reserve hwspinlocks for predefined purposes */
 147postcore_initcall(u8500_hsem_init);
 148
 149static void __exit u8500_hsem_exit(void)
 150{
 151        platform_driver_unregister(&u8500_hsem_driver);
 152}
 153module_exit(u8500_hsem_exit);
 154
 155MODULE_LICENSE("GPL v2");
 156MODULE_DESCRIPTION("Hardware Spinlock driver for u8500");
 157MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");
 158