Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.0-rc1 188 lines 4.6 kB view raw
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 51static 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 64static 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 */ 75static void u8500_hsem_relax(struct hwspinlock *lock) 76{ 77 ndelay(50); 78} 79 80static const struct hwspinlock_ops u8500_hwspinlock_ops = { 81 .trylock = u8500_hsem_trylock, 82 .unlock = u8500_hsem_unlock, 83 .relax = u8500_hsem_relax, 84}; 85 86static 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 135reg_fail: 136 pm_runtime_disable(&pdev->dev); 137 kfree(bank); 138iounmap_base: 139 iounmap(io_base); 140 return ret; 141} 142 143static 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 165static 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 173static 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 */ 178postcore_initcall(u8500_hsem_init); 179 180static void __exit u8500_hsem_exit(void) 181{ 182 platform_driver_unregister(&u8500_hsem_driver); 183} 184module_exit(u8500_hsem_exit); 185 186MODULE_LICENSE("GPL v2"); 187MODULE_DESCRIPTION("Hardware Spinlock driver for u8500"); 188MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");