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

hwspinlock: sprd: Add hardware spinlock driver

The Spreadtrum hardware spinlock device can provide hardware assistance
for synchronization between the multiple subsystems.

Signed-off-by: Baolin Wang <baolin.wang@spreadtrum.com>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>

authored by

Baolin Wang and committed by
Bjorn Andersson
d8c8bbbb 35fc8a07

+193
+9
drivers/hwspinlock/Kconfig
··· 39 39 It's safe to say n here if you're not interested in SIRF hardware 40 40 spinlock or just want a bare minimum kernel. 41 41 42 + config HWSPINLOCK_SPRD 43 + tristate "SPRD Hardware Spinlock device" 44 + depends on ARCH_SPRD 45 + depends on HWSPINLOCK 46 + help 47 + Say y here to support the SPRD Hardware Spinlock device. 48 + 49 + If unsure, say N. 50 + 42 51 config HSEM_U8500 43 52 tristate "STE Hardware Semaphore functionality" 44 53 depends on HWSPINLOCK
+1
drivers/hwspinlock/Makefile
··· 6 6 obj-$(CONFIG_HWSPINLOCK_OMAP) += omap_hwspinlock.o 7 7 obj-$(CONFIG_HWSPINLOCK_QCOM) += qcom_hwspinlock.o 8 8 obj-$(CONFIG_HWSPINLOCK_SIRF) += sirf_hwspinlock.o 9 + obj-$(CONFIG_HWSPINLOCK_SPRD) += sprd_hwspinlock.o 9 10 obj-$(CONFIG_HSEM_U8500) += u8500_hsem.o
+183
drivers/hwspinlock/sprd_hwspinlock.c
··· 1 + /* 2 + * Spreadtrum hardware spinlock driver 3 + * Copyright (C) 2017 Spreadtrum - http://www.spreadtrum.com 4 + * 5 + * This program is free software; you can redistribute it and/or 6 + * modify it under the terms of the GNU General Public License 7 + * version 2 as published by the Free Software Foundation. 8 + * 9 + * This program is distributed in the hope that it will be useful, but 10 + * WITHOUT ANY WARRANTY; without even the implied warranty of 11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 + * General Public License for more details. 13 + */ 14 + 15 + #include <linux/bitops.h> 16 + #include <linux/clk.h> 17 + #include <linux/delay.h> 18 + #include <linux/device.h> 19 + #include <linux/hwspinlock.h> 20 + #include <linux/io.h> 21 + #include <linux/kernel.h> 22 + #include <linux/module.h> 23 + #include <linux/of.h> 24 + #include <linux/of_device.h> 25 + #include <linux/platform_device.h> 26 + #include <linux/pm_runtime.h> 27 + #include <linux/slab.h> 28 + 29 + #include "hwspinlock_internal.h" 30 + 31 + /* hwspinlock registers definition */ 32 + #define HWSPINLOCK_RECCTRL 0x4 33 + #define HWSPINLOCK_MASTERID(_X_) (0x80 + 0x4 * (_X_)) 34 + #define HWSPINLOCK_TOKEN(_X_) (0x800 + 0x4 * (_X_)) 35 + 36 + /* unlocked value */ 37 + #define HWSPINLOCK_NOTTAKEN 0x55aa10c5 38 + /* bits definition of RECCTRL reg */ 39 + #define HWSPINLOCK_USER_BITS 0x1 40 + 41 + /* hwspinlock number */ 42 + #define SPRD_HWLOCKS_NUM 32 43 + 44 + struct sprd_hwspinlock_dev { 45 + void __iomem *base; 46 + struct clk *clk; 47 + struct hwspinlock_device bank; 48 + }; 49 + 50 + /* try to lock the hardware spinlock */ 51 + static int sprd_hwspinlock_trylock(struct hwspinlock *lock) 52 + { 53 + struct sprd_hwspinlock_dev *sprd_hwlock = 54 + dev_get_drvdata(lock->bank->dev); 55 + void __iomem *addr = lock->priv; 56 + int user_id, lock_id; 57 + 58 + if (!readl(addr)) 59 + return 1; 60 + 61 + lock_id = hwlock_to_id(lock); 62 + /* get the hardware spinlock master/user id */ 63 + user_id = readl(sprd_hwlock->base + HWSPINLOCK_MASTERID(lock_id)); 64 + dev_warn(sprd_hwlock->bank.dev, 65 + "hwspinlock [%d] lock failed and master/user id = %d!\n", 66 + lock_id, user_id); 67 + return 0; 68 + } 69 + 70 + /* unlock the hardware spinlock */ 71 + static void sprd_hwspinlock_unlock(struct hwspinlock *lock) 72 + { 73 + void __iomem *lock_addr = lock->priv; 74 + 75 + writel(HWSPINLOCK_NOTTAKEN, lock_addr); 76 + } 77 + 78 + /* The specs recommended below number as the retry delay time */ 79 + static void sprd_hwspinlock_relax(struct hwspinlock *lock) 80 + { 81 + ndelay(10); 82 + } 83 + 84 + static const struct hwspinlock_ops sprd_hwspinlock_ops = { 85 + .trylock = sprd_hwspinlock_trylock, 86 + .unlock = sprd_hwspinlock_unlock, 87 + .relax = sprd_hwspinlock_relax, 88 + }; 89 + 90 + static int sprd_hwspinlock_probe(struct platform_device *pdev) 91 + { 92 + struct sprd_hwspinlock_dev *sprd_hwlock; 93 + struct hwspinlock *lock; 94 + struct resource *res; 95 + int i, ret; 96 + 97 + if (!pdev->dev.of_node) 98 + return -ENODEV; 99 + 100 + sprd_hwlock = devm_kzalloc(&pdev->dev, 101 + sizeof(struct sprd_hwspinlock_dev) + 102 + SPRD_HWLOCKS_NUM * sizeof(*lock), 103 + GFP_KERNEL); 104 + if (!sprd_hwlock) 105 + return -ENOMEM; 106 + 107 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 108 + sprd_hwlock->base = devm_ioremap_resource(&pdev->dev, res); 109 + if (IS_ERR(sprd_hwlock->base)) 110 + return PTR_ERR(sprd_hwlock->base); 111 + 112 + sprd_hwlock->clk = devm_clk_get(&pdev->dev, "enable"); 113 + if (IS_ERR(sprd_hwlock->clk)) { 114 + dev_err(&pdev->dev, "get hwspinlock clock failed!\n"); 115 + return PTR_ERR(sprd_hwlock->clk); 116 + } 117 + 118 + clk_prepare_enable(sprd_hwlock->clk); 119 + 120 + /* set the hwspinlock to record user id to identify subsystems */ 121 + writel(HWSPINLOCK_USER_BITS, sprd_hwlock->base + HWSPINLOCK_RECCTRL); 122 + 123 + for (i = 0; i < SPRD_HWLOCKS_NUM; i++) { 124 + lock = &sprd_hwlock->bank.lock[i]; 125 + lock->priv = sprd_hwlock->base + HWSPINLOCK_TOKEN(i); 126 + } 127 + 128 + platform_set_drvdata(pdev, sprd_hwlock); 129 + pm_runtime_enable(&pdev->dev); 130 + 131 + ret = hwspin_lock_register(&sprd_hwlock->bank, &pdev->dev, 132 + &sprd_hwspinlock_ops, 0, SPRD_HWLOCKS_NUM); 133 + if (ret) { 134 + pm_runtime_disable(&pdev->dev); 135 + clk_disable_unprepare(sprd_hwlock->clk); 136 + return ret; 137 + } 138 + 139 + return 0; 140 + } 141 + 142 + static int sprd_hwspinlock_remove(struct platform_device *pdev) 143 + { 144 + struct sprd_hwspinlock_dev *sprd_hwlock = platform_get_drvdata(pdev); 145 + 146 + hwspin_lock_unregister(&sprd_hwlock->bank); 147 + pm_runtime_disable(&pdev->dev); 148 + clk_disable_unprepare(sprd_hwlock->clk); 149 + return 0; 150 + } 151 + 152 + static const struct of_device_id sprd_hwspinlock_of_match[] = { 153 + { .compatible = "sprd,hwspinlock-r3p0", }, 154 + { /* sentinel */ } 155 + }; 156 + MODULE_DEVICE_TABLE(of, sprd_hwspinlock_of_match); 157 + 158 + static struct platform_driver sprd_hwspinlock_driver = { 159 + .probe = sprd_hwspinlock_probe, 160 + .remove = sprd_hwspinlock_remove, 161 + .driver = { 162 + .name = "sprd_hwspinlock", 163 + .of_match_table = of_match_ptr(sprd_hwspinlock_of_match), 164 + }, 165 + }; 166 + 167 + static int __init sprd_hwspinlock_init(void) 168 + { 169 + return platform_driver_register(&sprd_hwspinlock_driver); 170 + } 171 + postcore_initcall(sprd_hwspinlock_init); 172 + 173 + static void __exit sprd_hwspinlock_exit(void) 174 + { 175 + platform_driver_unregister(&sprd_hwspinlock_driver); 176 + } 177 + module_exit(sprd_hwspinlock_exit); 178 + 179 + MODULE_LICENSE("GPL v2"); 180 + MODULE_DESCRIPTION("Hardware spinlock driver for Spreadtrum"); 181 + MODULE_AUTHOR("Baolin Wang <baolin.wang@spreadtrum.com>"); 182 + MODULE_AUTHOR("Lanqing Liu <lanqing.liu@spreadtrum.com>"); 183 + MODULE_AUTHOR("Long Cheng <aiden.cheng@spreadtrum.com>");