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