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>"); 189