Pull hwspinlock updates from Ohad Ben-Cohen: - hwspinlock core DT support from Suman Anna - OMAP hwspinlock DT support from Suman Anna - QCOM hwspinlock DT support from Bjorn Andersson - a new CSR atlas7 hwspinlock driver from Wei Chen - CSR atlas7 hwspinlock DT binding document from Wei Chen - a tiny QCOM hwspinlock driver fix from Bjorn Andersson * tag 'hwspinlock-4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad/hwspinlock: hwspinlock: qcom: Correct msb in regmap_field DT: hwspinlock: add the CSR atlas7 hwspinlock bindings document hwspinlock: add a CSR atlas7 driver hwspinlock: qcom: Add support for Qualcomm HW Mutex block DT: hwspinlock: Add binding documentation for Qualcomm hwmutex hwspinlock/omap: add support for dt nodes Documentation: dt: add the omap hwspinlock bindings document hwspinlock/core: add device tree support Documentation: dt: add common bindings for hwspinlock
Unified · Split
| @@ -0,0 +1,59 @@ | ||
| 1 | +Generic hwlock bindings | |
| 2 | +======================= | |
| 3 | + | |
| 4 | +Generic bindings that are common to all the hwlock platform specific driver | |
| 5 | +implementations. | |
| 6 | + | |
| 7 | +Please also look through the individual platform specific hwlock binding | |
| 8 | +documentations for identifying any additional properties specific to that | |
| 9 | +platform. | |
| 10 | + | |
| 11 | +hwlock providers: | |
| 12 | +================= | |
| 13 | + | |
| 14 | +Required properties: | |
| 15 | +- #hwlock-cells: Specifies the number of cells needed to represent a | |
| 16 | + specific lock. | |
| 17 | + | |
| 18 | +hwlock users: | |
| 19 | +============= | |
| 20 | + | |
| 21 | +Consumers that require specific hwlock(s) should specify them using the | |
| 22 | +property "hwlocks", and an optional "hwlock-names" property. | |
| 23 | + | |
| 24 | +Required properties: | |
| 25 | +- hwlocks: List of phandle to a hwlock provider node and an | |
| 26 | + associated hwlock args specifier as indicated by | |
| 27 | + #hwlock-cells. The list can have just a single hwlock | |
| 28 | + or multiple hwlocks, with each hwlock represented by | |
| 29 | + a phandle and a corresponding args specifier. | |
| 30 | + | |
| 31 | +Optional properties: | |
| 32 | +- hwlock-names: List of hwlock name strings defined in the same order | |
| 33 | + as the hwlocks, with one name per hwlock. Consumers can | |
| 34 | + use the hwlock-names to match and get a specific hwlock. | |
| 35 | + | |
| 36 | + | |
| 37 | +1. Example of a node using a single specific hwlock: | |
| 38 | + | |
| 39 | +The following example has a node requesting a hwlock in the bank defined by | |
| 40 | +the node hwlock1. hwlock1 is a hwlock provider with an argument specifier | |
| 41 | +of length 1. | |
| 42 | + | |
| 43 | + node { | |
| 44 | + ... | |
| 45 | + hwlocks = <&hwlock1 2>; | |
| 46 | + ... | |
| 47 | + }; | |
| 48 | + | |
| 49 | +2. Example of a node using multiple specific hwlocks: | |
| 50 | + | |
| 51 | +The following example has a node requesting two hwlocks, a hwlock within | |
| 52 | +the hwlock device node 'hwlock1' with #hwlock-cells value of 1, and another | |
| 53 | +hwlock within the hwlock device node 'hwlock2' with #hwlock-cells value of 2. | |
| 54 | + | |
| 55 | + node { | |
| 56 | + ... | |
| 57 | + hwlocks = <&hwlock1 2>, <&hwlock2 0 3>; | |
| 58 | + ... | |
| 59 | + }; | |
| @@ -0,0 +1,26 @@ | ||
| 1 | +OMAP4+ HwSpinlock Driver | |
| 2 | +======================== | |
| 3 | + | |
| 4 | +Required properties: | |
| 5 | +- compatible: Should be "ti,omap4-hwspinlock" for | |
| 6 | + OMAP44xx, OMAP54xx, AM33xx, AM43xx, DRA7xx SoCs | |
| 7 | +- reg: Contains the hwspinlock module register address space | |
| 8 | + (base address and length) | |
| 9 | +- ti,hwmods: Name of the hwmod associated with the hwspinlock device | |
| 10 | +- #hwlock-cells: Should be 1. The OMAP hwspinlock users will use a | |
| 11 | + 0-indexed relative hwlock number as the argument | |
| 12 | + specifier value for requesting a specific hwspinlock | |
| 13 | + within a hwspinlock bank. | |
| 14 | + | |
| 15 | +Please look at the generic hwlock binding for usage information for consumers, | |
| 16 | +"Documentation/devicetree/bindings/hwlock/hwlock.txt" | |
| 17 | + | |
| 18 | +Example: | |
| 19 | + | |
| 20 | +/* OMAP4 */ | |
| 21 | +hwspinlock: spinlock@4a0f6000 { | |
| 22 | + compatible = "ti,omap4-hwspinlock"; | |
| 23 | + reg = <0x4a0f6000 0x1000>; | |
| 24 | + ti,hwmods = "spinlock"; | |
| 25 | + #hwlock-cells = <1>; | |
| 26 | +}; | |
| @@ -0,0 +1,39 @@ | ||
| 1 | +Qualcomm Hardware Mutex Block: | |
| 2 | + | |
| 3 | +The hardware block provides mutexes utilized between different processors on | |
| 4 | +the SoC as part of the communication protocol used by these processors. | |
| 5 | + | |
| 6 | +- compatible: | |
| 7 | + Usage: required | |
| 8 | + Value type: <string> | |
| 9 | + Definition: must be one of: | |
| 10 | + "qcom,sfpb-mutex", | |
| 11 | + "qcom,tcsr-mutex" | |
| 12 | + | |
| 13 | +- syscon: | |
| 14 | + Usage: required | |
| 15 | + Value type: <prop-encoded-array> | |
| 16 | + Definition: one cell containing: | |
| 17 | + syscon phandle | |
| 18 | + offset of the hwmutex block within the syscon | |
| 19 | + stride of the hwmutex registers | |
| 20 | + | |
| 21 | +- #hwlock-cells: | |
| 22 | + Usage: required | |
| 23 | + Value type: <u32> | |
| 24 | + Definition: must be 1, the specified cell represent the lock id | |
| 25 | + (hwlock standard property, see hwlock.txt) | |
| 26 | + | |
| 27 | +Example: | |
| 28 | + | |
| 29 | + tcsr_mutex_block: syscon@fd484000 { | |
| 30 | + compatible = "syscon"; | |
| 31 | + reg = <0xfd484000 0x2000>; | |
| 32 | + }; | |
| 33 | + | |
| 34 | + hwlock@fd484000 { | |
| 35 | + compatible = "qcom,tcsr-mutex"; | |
| 36 | + syscon = <&tcsr_mutex_block 0 0x80>; | |
| 37 | + | |
| 38 | + #hwlock-cells = <1>; | |
| 39 | + }; | |
| @@ -0,0 +1,28 @@ | ||
| 1 | +SIRF Hardware spinlock device Binding | |
| 2 | +----------------------------------------------- | |
| 3 | + | |
| 4 | +Required properties : | |
| 5 | +- compatible : shall contain only one of the following: | |
| 6 | + "sirf,hwspinlock" | |
| 7 | + | |
| 8 | +- reg : the register address of hwspinlock | |
| 9 | + | |
| 10 | +- #hwlock-cells : hwlock users only use the hwlock id to represent a specific | |
| 11 | + hwlock, so the number of cells should be <1> here. | |
| 12 | + | |
| 13 | +Please look at the generic hwlock binding for usage information for consumers, | |
| 14 | +"Documentation/devicetree/bindings/hwlock/hwlock.txt" | |
| 15 | + | |
| 16 | +Example of hwlock provider: | |
| 17 | + hwlock { | |
| 18 | + compatible = "sirf,hwspinlock"; | |
| 19 | + reg = <0x13240000 0x00010000>; | |
| 20 | + #hwlock-cells = <1>; | |
| 21 | + }; | |
| 22 | + | |
| 23 | +Example of hwlock users: | |
| 24 | + node { | |
| 25 | + ... | |
| 26 | + hwlocks = <&hwlock 2>; | |
| 27 | + ... | |
| 28 | + }; | |
| @@ -48,6 +48,16 @@ independent, drivers. · 47 unmodified lines | ||
| 48 | 48 | ids for predefined purposes. |
| 49 | 49 | Should be called from a process context (might sleep). |
| 50 | 50 | |
| 51 | + int of_hwspin_lock_get_id(struct device_node *np, int index); | |
| 52 | + - retrieve the global lock id for an OF phandle-based specific lock. | |
| 53 | + This function provides a means for DT users of a hwspinlock module | |
| 54 | + to get the global lock id of a specific hwspinlock, so that it can | |
| 55 | + be requested using the normal hwspin_lock_request_specific() API. | |
| 56 | + The function returns a lock id number on success, -EPROBE_DEFER if | |
| 57 | + the hwspinlock device is not yet registered with the core, or other | |
| 58 | + error values. | |
| 59 | + Should be called from a process context (might sleep). | |
| 60 | + | |
| 51 | 61 | int hwspin_lock_free(struct hwspinlock *hwlock); |
| 52 | 62 | - free a previously-assigned hwspinlock; returns 0 on success, or an |
| 53 | 63 | appropriate error code on failure (e.g. -EINVAL if the hwspinlock |
| @@ -7364,7 +7364,6 @@ M: Ohad Ben-Cohen <ohad@wizery.com> · 7363 unmodified lines | ||
| 7364 | 7364 | L: linux-omap@vger.kernel.org |
| 7365 | 7365 | S: Maintained |
| 7366 | 7366 | F: drivers/hwspinlock/omap_hwspinlock.c |
| 7367 | −F: arch/arm/mach-omap2/hwspinlock.c | |
| 7368 | 7367 | |
| 7369 | 7368 | OMAP MMC SUPPORT |
| 7370 | 7369 | M: Jarkko Lavinen <jarkko.lavinen@nokia.com> |
| @@ -274,8 +274,5 @@ obj-y += $(nand-m) $(nand-y) · 273 unmodified lines | ||
| 274 | 274 | |
| 275 | 275 | smsc911x-$(CONFIG_SMSC911X) := gpmc-smsc911x.o |
| 276 | 276 | obj-y += $(smsc911x-m) $(smsc911x-y) |
| 277 | −ifneq ($(CONFIG_HWSPINLOCK_OMAP),) | |
| 278 | −obj-y += hwspinlock.o | |
| 279 | −endif | |
| 280 | 277 | |
| 281 | 278 | obj-y += common-board-devices.o twl-common.o dss-common.o |
| @@ -1,60 +0,0 @@ | ||
| 1 | −/* | |
| 2 | − * OMAP hardware spinlock device initialization | |
| 3 | − * | |
| 4 | − * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com | |
| 5 | − * | |
| 6 | − * Contact: Simon Que <sque@ti.com> | |
| 7 | − * Hari Kanigeri <h-kanigeri2@ti.com> | |
| 8 | − * | |
| 9 | − * This program is free software; you can redistribute it and/or | |
| 10 | − * modify it under the terms of the GNU General Public License | |
| 11 | − * version 2 as published by the Free Software Foundation. | |
| 12 | − * | |
| 13 | − * This program is distributed in the hope that it will be useful, but | |
| 14 | − * WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 15 | − * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| 16 | − * General Public License for more details. | |
| 17 | − */ | |
| 18 | − | |
| 19 | −#include <linux/kernel.h> | |
| 20 | −#include <linux/init.h> | |
| 21 | −#include <linux/err.h> | |
| 22 | −#include <linux/hwspinlock.h> | |
| 23 | − | |
| 24 | −#include "soc.h" | |
| 25 | −#include "omap_hwmod.h" | |
| 26 | −#include "omap_device.h" | |
| 27 | − | |
| 28 | −static struct hwspinlock_pdata omap_hwspinlock_pdata __initdata = { | |
| 29 | − .base_id = 0, | |
| 30 | −}; | |
| 31 | − | |
| 32 | −static int __init hwspinlocks_init(void) | |
| 33 | −{ | |
| 34 | − int retval = 0; | |
| 35 | − struct omap_hwmod *oh; | |
| 36 | − struct platform_device *pdev; | |
| 37 | − const char *oh_name = "spinlock"; | |
| 38 | − const char *dev_name = "omap_hwspinlock"; | |
| 39 | − | |
| 40 | − /* | |
| 41 | − * Hwmod lookup will fail in case our platform doesn't support the | |
| 42 | − * hardware spinlock module, so it is safe to run this initcall | |
| 43 | − * on all omaps | |
| 44 | − */ | |
| 45 | − oh = omap_hwmod_lookup(oh_name); | |
| 46 | − if (oh == NULL) | |
| 47 | − return -EINVAL; | |
| 48 | − | |
| 49 | − pdev = omap_device_build(dev_name, 0, oh, &omap_hwspinlock_pdata, | |
| 50 | − sizeof(struct hwspinlock_pdata)); | |
| 51 | − if (IS_ERR(pdev)) { | |
| 52 | − pr_err("Can't build omap_device for %s:%s\n", dev_name, | |
| 53 | − oh_name); | |
| 54 | − retval = PTR_ERR(pdev); | |
| 55 | − } | |
| 56 | − | |
| 57 | − return retval; | |
| 58 | −} | |
| 59 | −/* early board code might need to reserve specific hwspinlock instances */ | |
| 60 | −omap_postcore_initcall(hwspinlocks_init); | |
| @@ -18,6 +18,30 @@ config HWSPINLOCK_OMAP · 17 unmodified lines | ||
| 18 | 18 | |
| 19 | 19 | If unsure, say N. |
| 20 | 20 | |
| 21 | +config HWSPINLOCK_QCOM | |
| 22 | + tristate "Qualcomm Hardware Spinlock device" | |
| 23 | + depends on ARCH_QCOM | |
| 24 | + select HWSPINLOCK | |
| 25 | + select MFD_SYSCON | |
| 26 | + help | |
| 27 | + Say y here to support the Qualcomm Hardware Mutex functionality, which | |
| 28 | + provides a synchronisation mechanism for the various processors on | |
| 29 | + the SoC. | |
| 30 | + | |
| 31 | + If unsure, say N. | |
| 32 | + | |
| 33 | +config HWSPINLOCK_SIRF | |
| 34 | + tristate "SIRF Hardware Spinlock device" | |
| 35 | + depends on ARCH_SIRF | |
| 36 | + select HWSPINLOCK | |
| 37 | + help | |
| 38 | + Say y here to support the SIRF Hardware Spinlock device, which | |
| 39 | + provides a synchronisation mechanism for the various processors | |
| 40 | + on the SoC. | |
| 41 | + | |
| 42 | + It's safe to say n here if you're not interested in SIRF hardware | |
| 43 | + spinlock or just want a bare minimum kernel. | |
| 44 | + | |
| 21 | 45 | config HSEM_U8500 |
| 22 | 46 | tristate "STE Hardware Semaphore functionality" |
| 23 | 47 | depends on ARCH_U8500 |
| @@ -4,4 +4,6 @@ · 3 unmodified lines | ||
| 4 | 4 | |
| 5 | 5 | obj-$(CONFIG_HWSPINLOCK) += hwspinlock_core.o |
| 6 | 6 | obj-$(CONFIG_HWSPINLOCK_OMAP) += omap_hwspinlock.o |
| 7 | +obj-$(CONFIG_HWSPINLOCK_QCOM) += qcom_hwspinlock.o | |
| 8 | +obj-$(CONFIG_HWSPINLOCK_SIRF) += sirf_hwspinlock.o | |
| 7 | 9 | obj-$(CONFIG_HSEM_U8500) += u8500_hsem.o |
| @@ -27,6 +27,7 @@ · 26 unmodified lines | ||
| 27 | 27 | #include <linux/hwspinlock.h> |
| 28 | 28 | #include <linux/pm_runtime.h> |
| 29 | 29 | #include <linux/mutex.h> |
| 30 | +#include <linux/of.h> | |
| 30 | 31 | |
| 31 | 32 | #include "hwspinlock_internal.h" |
| 32 | 33 | |
| @@ -257,6 +258,84 @@ void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags) · 224 unmodified lines | ||
| 257 | 258 | } |
| 258 | 259 | EXPORT_SYMBOL_GPL(__hwspin_unlock); |
| 259 | 260 | |
| 261 | +/** | |
| 262 | + * of_hwspin_lock_simple_xlate - translate hwlock_spec to return a lock id | |
| 263 | + * @bank: the hwspinlock device bank | |
| 264 | + * @hwlock_spec: hwlock specifier as found in the device tree | |
| 265 | + * | |
| 266 | + * This is a simple translation function, suitable for hwspinlock platform | |
| 267 | + * drivers that only has a lock specifier length of 1. | |
| 268 | + * | |
| 269 | + * Returns a relative index of the lock within a specified bank on success, | |
| 270 | + * or -EINVAL on invalid specifier cell count. | |
| 271 | + */ | |
| 272 | +static inline int | |
| 273 | +of_hwspin_lock_simple_xlate(const struct of_phandle_args *hwlock_spec) | |
| 274 | +{ | |
| 275 | + if (WARN_ON(hwlock_spec->args_count != 1)) | |
| 276 | + return -EINVAL; | |
| 277 | + | |
| 278 | + return hwlock_spec->args[0]; | |
| 279 | +} | |
| 280 | + | |
| 281 | +/** | |
| 282 | + * of_hwspin_lock_get_id() - get lock id for an OF phandle-based specific lock | |
| 283 | + * @np: device node from which to request the specific hwlock | |
| 284 | + * @index: index of the hwlock in the list of values | |
| 285 | + * | |
| 286 | + * This function provides a means for DT users of the hwspinlock module to | |
| 287 | + * get the global lock id of a specific hwspinlock using the phandle of the | |
| 288 | + * hwspinlock device, so that it can be requested using the normal | |
| 289 | + * hwspin_lock_request_specific() API. | |
| 290 | + * | |
| 291 | + * Returns the global lock id number on success, -EPROBE_DEFER if the hwspinlock | |
| 292 | + * device is not yet registered, -EINVAL on invalid args specifier value or an | |
| 293 | + * appropriate error as returned from the OF parsing of the DT client node. | |
| 294 | + */ | |
| 295 | +int of_hwspin_lock_get_id(struct device_node *np, int index) | |
| 296 | +{ | |
| 297 | + struct of_phandle_args args; | |
| 298 | + struct hwspinlock *hwlock; | |
| 299 | + struct radix_tree_iter iter; | |
| 300 | + void **slot; | |
| 301 | + int id; | |
| 302 | + int ret; | |
| 303 | + | |
| 304 | + ret = of_parse_phandle_with_args(np, "hwlocks", "#hwlock-cells", index, | |
| 305 | + &args); | |
| 306 | + if (ret) | |
| 307 | + return ret; | |
| 308 | + | |
| 309 | + /* Find the hwspinlock device: we need its base_id */ | |
| 310 | + ret = -EPROBE_DEFER; | |
| 311 | + rcu_read_lock(); | |
| 312 | + radix_tree_for_each_slot(slot, &hwspinlock_tree, &iter, 0) { | |
| 313 | + hwlock = radix_tree_deref_slot(slot); | |
| 314 | + if (unlikely(!hwlock)) | |
| 315 | + continue; | |
| 316 | + | |
| 317 | + if (hwlock->bank->dev->of_node == args.np) { | |
| 318 | + ret = 0; | |
| 319 | + break; | |
| 320 | + } | |
| 321 | + } | |
| 322 | + rcu_read_unlock(); | |
| 323 | + if (ret < 0) | |
| 324 | + goto out; | |
| 325 | + | |
| 326 | + id = of_hwspin_lock_simple_xlate(&args); | |
| 327 | + if (id < 0 || id >= hwlock->bank->num_locks) { | |
| 328 | + ret = -EINVAL; | |
| 329 | + goto out; | |
| 330 | + } | |
| 331 | + id += hwlock->bank->base_id; | |
| 332 | + | |
| 333 | +out: | |
| 334 | + of_node_put(args.np); | |
| 335 | + return ret ? ret : id; | |
| 336 | +} | |
| 337 | +EXPORT_SYMBOL_GPL(of_hwspin_lock_get_id); | |
| 338 | + | |
| 260 | 339 | static int hwspin_lock_register_single(struct hwspinlock *hwlock, int id) |
| 261 | 340 | { |
| 262 | 341 | struct hwspinlock *tmp; |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | /* |
| 2 | 2 | * OMAP hardware spinlock driver |
| 3 | 3 | * |
| 4 | − * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com | |
| 4 | + * Copyright (C) 2010-2015 Texas Instruments Incorporated - http://www.ti.com | |
| 5 | 5 | * |
| 6 | 6 | * Contact: Simon Que <sque@ti.com> |
| 7 | 7 | * Hari Kanigeri <h-kanigeri2@ti.com> |
| @@ -27,6 +27,7 @@ · 19 unmodified lines | ||
| 27 | 27 | #include <linux/slab.h> |
| 28 | 28 | #include <linux/spinlock.h> |
| 29 | 29 | #include <linux/hwspinlock.h> |
| 30 | +#include <linux/of.h> | |
| 30 | 31 | #include <linux/platform_device.h> |
| 31 | 32 | |
| 32 | 33 | #include "hwspinlock_internal.h" |
| @@ -80,14 +81,16 @@ static const struct hwspinlock_ops omap_hwspinlock_ops = { · 47 unmodified lines | ||
| 80 | 81 | |
| 81 | 82 | static int omap_hwspinlock_probe(struct platform_device *pdev) |
| 82 | 83 | { |
| 83 | − struct hwspinlock_pdata *pdata = pdev->dev.platform_data; | |
| 84 | + struct device_node *node = pdev->dev.of_node; | |
| 84 | 85 | struct hwspinlock_device *bank; |
| 85 | 86 | struct hwspinlock *hwlock; |
| 86 | 87 | struct resource *res; |
| 87 | 88 | void __iomem *io_base; |
| 88 | 89 | int num_locks, i, ret; |
| 90 | + /* Only a single hwspinlock block device is supported */ | |
| 91 | + int base_id = 0; | |
| 89 | 92 | |
| 90 | − if (!pdata) | |
| 93 | + if (!node) | |
| 91 | 94 | return -ENODEV; |
| 92 | 95 | |
| 93 | 96 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| @@ -141,7 +144,7 @@ static int omap_hwspinlock_probe(struct platform_device *pdev) · 47 unmodified lines | ||
| 141 | 144 | hwlock->priv = io_base + LOCK_BASE_OFFSET + sizeof(u32) * i; |
| 142 | 145 | |
| 143 | 146 | ret = hwspin_lock_register(bank, &pdev->dev, &omap_hwspinlock_ops, |
| 144 | − pdata->base_id, num_locks); | |
| 147 | + base_id, num_locks); | |
| 145 | 148 | if (ret) |
| 146 | 149 | goto reg_fail; |
| 147 | 150 | |
| @@ -174,11 +177,18 @@ static int omap_hwspinlock_remove(struct platform_device *pdev) · 26 unmodified lines | ||
| 174 | 177 | return 0; |
| 175 | 178 | } |
| 176 | 179 | |
| 180 | +static const struct of_device_id omap_hwspinlock_of_match[] = { | |
| 181 | + { .compatible = "ti,omap4-hwspinlock", }, | |
| 182 | + { /* end */ }, | |
| 183 | +}; | |
| 184 | +MODULE_DEVICE_TABLE(of, omap_hwspinlock_of_match); | |
| 185 | + | |
| 177 | 186 | static struct platform_driver omap_hwspinlock_driver = { |
| 178 | 187 | .probe = omap_hwspinlock_probe, |
| 179 | 188 | .remove = omap_hwspinlock_remove, |
| 180 | 189 | .driver = { |
| 181 | 190 | .name = "omap_hwspinlock", |
| 191 | + .of_match_table = of_match_ptr(omap_hwspinlock_of_match), | |
| 182 | 192 | }, |
| 183 | 193 | }; |
| 184 | 194 | |
| @@ -0,0 +1,181 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2013, The Linux Foundation. All rights reserved. | |
| 3 | + * Copyright (c) 2015, Sony Mobile Communications AB | |
| 4 | + * | |
| 5 | + * This software is licensed under the terms of the GNU General Public | |
| 6 | + * License version 2, as published by the Free Software Foundation, and | |
| 7 | + * may be copied, distributed, and modified under those terms. | |
| 8 | + * | |
| 9 | + * This program is distributed in the hope that it will be useful, | |
| 10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 12 | + * GNU General Public License for more details. | |
| 13 | + */ | |
| 14 | + | |
| 15 | +#include <linux/hwspinlock.h> | |
| 16 | +#include <linux/io.h> | |
| 17 | +#include <linux/kernel.h> | |
| 18 | +#include <linux/mfd/syscon.h> | |
| 19 | +#include <linux/module.h> | |
| 20 | +#include <linux/of.h> | |
| 21 | +#include <linux/of_device.h> | |
| 22 | +#include <linux/platform_device.h> | |
| 23 | +#include <linux/pm_runtime.h> | |
| 24 | +#include <linux/regmap.h> | |
| 25 | + | |
| 26 | +#include "hwspinlock_internal.h" | |
| 27 | + | |
| 28 | +#define QCOM_MUTEX_APPS_PROC_ID 1 | |
| 29 | +#define QCOM_MUTEX_NUM_LOCKS 32 | |
| 30 | + | |
| 31 | +static int qcom_hwspinlock_trylock(struct hwspinlock *lock) | |
| 32 | +{ | |
| 33 | + struct regmap_field *field = lock->priv; | |
| 34 | + u32 lock_owner; | |
| 35 | + int ret; | |
| 36 | + | |
| 37 | + ret = regmap_field_write(field, QCOM_MUTEX_APPS_PROC_ID); | |
| 38 | + if (ret) | |
| 39 | + return ret; | |
| 40 | + | |
| 41 | + ret = regmap_field_read(field, &lock_owner); | |
| 42 | + if (ret) | |
| 43 | + return ret; | |
| 44 | + | |
| 45 | + return lock_owner == QCOM_MUTEX_APPS_PROC_ID; | |
| 46 | +} | |
| 47 | + | |
| 48 | +static void qcom_hwspinlock_unlock(struct hwspinlock *lock) | |
| 49 | +{ | |
| 50 | + struct regmap_field *field = lock->priv; | |
| 51 | + u32 lock_owner; | |
| 52 | + int ret; | |
| 53 | + | |
| 54 | + ret = regmap_field_read(field, &lock_owner); | |
| 55 | + if (ret) { | |
| 56 | + pr_err("%s: unable to query spinlock owner\n", __func__); | |
| 57 | + return; | |
| 58 | + } | |
| 59 | + | |
| 60 | + if (lock_owner != QCOM_MUTEX_APPS_PROC_ID) { | |
| 61 | + pr_err("%s: spinlock not owned by us (actual owner is %d)\n", | |
| 62 | + __func__, lock_owner); | |
| 63 | + } | |
| 64 | + | |
| 65 | + ret = regmap_field_write(field, 0); | |
| 66 | + if (ret) | |
| 67 | + pr_err("%s: failed to unlock spinlock\n", __func__); | |
| 68 | +} | |
| 69 | + | |
| 70 | +static const struct hwspinlock_ops qcom_hwspinlock_ops = { | |
| 71 | + .trylock = qcom_hwspinlock_trylock, | |
| 72 | + .unlock = qcom_hwspinlock_unlock, | |
| 73 | +}; | |
| 74 | + | |
| 75 | +static const struct of_device_id qcom_hwspinlock_of_match[] = { | |
| 76 | + { .compatible = "qcom,sfpb-mutex" }, | |
| 77 | + { .compatible = "qcom,tcsr-mutex" }, | |
| 78 | + { } | |
| 79 | +}; | |
| 80 | +MODULE_DEVICE_TABLE(of, qcom_hwspinlock_of_match); | |
| 81 | + | |
| 82 | +static int qcom_hwspinlock_probe(struct platform_device *pdev) | |
| 83 | +{ | |
| 84 | + struct hwspinlock_device *bank; | |
| 85 | + struct device_node *syscon; | |
| 86 | + struct reg_field field; | |
| 87 | + struct regmap *regmap; | |
| 88 | + size_t array_size; | |
| 89 | + u32 stride; | |
| 90 | + u32 base; | |
| 91 | + int ret; | |
| 92 | + int i; | |
| 93 | + | |
| 94 | + syscon = of_parse_phandle(pdev->dev.of_node, "syscon", 0); | |
| 95 | + if (!syscon) { | |
| 96 | + dev_err(&pdev->dev, "no syscon property\n"); | |
| 97 | + return -ENODEV; | |
| 98 | + } | |
| 99 | + | |
| 100 | + regmap = syscon_node_to_regmap(syscon); | |
| 101 | + if (IS_ERR(regmap)) | |
| 102 | + return PTR_ERR(regmap); | |
| 103 | + | |
| 104 | + ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, &base); | |
| 105 | + if (ret < 0) { | |
| 106 | + dev_err(&pdev->dev, "no offset in syscon\n"); | |
| 107 | + return -EINVAL; | |
| 108 | + } | |
| 109 | + | |
| 110 | + ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 2, &stride); | |
| 111 | + if (ret < 0) { | |
| 112 | + dev_err(&pdev->dev, "no stride syscon\n"); | |
| 113 | + return -EINVAL; | |
| 114 | + } | |
| 115 | + | |
| 116 | + array_size = QCOM_MUTEX_NUM_LOCKS * sizeof(struct hwspinlock); | |
| 117 | + bank = devm_kzalloc(&pdev->dev, sizeof(*bank) + array_size, GFP_KERNEL); | |
| 118 | + if (!bank) | |
| 119 | + return -ENOMEM; | |
| 120 | + | |
| 121 | + platform_set_drvdata(pdev, bank); | |
| 122 | + | |
| 123 | + for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) { | |
| 124 | + field.reg = base + i * stride; | |
| 125 | + field.lsb = 0; | |
| 126 | + field.msb = 31; | |
| 127 | + | |
| 128 | + bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev, | |
| 129 | + regmap, field); | |
| 130 | + } | |
| 131 | + | |
| 132 | + pm_runtime_enable(&pdev->dev); | |
| 133 | + | |
| 134 | + ret = hwspin_lock_register(bank, &pdev->dev, &qcom_hwspinlock_ops, | |
| 135 | + 0, QCOM_MUTEX_NUM_LOCKS); | |
| 136 | + if (ret) | |
| 137 | + pm_runtime_disable(&pdev->dev); | |
| 138 | + | |
| 139 | + return ret; | |
| 140 | +} | |
| 141 | + | |
| 142 | +static int qcom_hwspinlock_remove(struct platform_device *pdev) | |
| 143 | +{ | |
| 144 | + struct hwspinlock_device *bank = platform_get_drvdata(pdev); | |
| 145 | + int ret; | |
| 146 | + | |
| 147 | + ret = hwspin_lock_unregister(bank); | |
| 148 | + if (ret) { | |
| 149 | + dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret); | |
| 150 | + return ret; | |
| 151 | + } | |
| 152 | + | |
| 153 | + pm_runtime_disable(&pdev->dev); | |
| 154 | + | |
| 155 | + return 0; | |
| 156 | +} | |
| 157 | + | |
| 158 | +static struct platform_driver qcom_hwspinlock_driver = { | |
| 159 | + .probe = qcom_hwspinlock_probe, | |
| 160 | + .remove = qcom_hwspinlock_remove, | |
| 161 | + .driver = { | |
| 162 | + .name = "qcom_hwspinlock", | |
| 163 | + .of_match_table = qcom_hwspinlock_of_match, | |
| 164 | + }, | |
| 165 | +}; | |
| 166 | + | |
| 167 | +static int __init qcom_hwspinlock_init(void) | |
| 168 | +{ | |
| 169 | + return platform_driver_register(&qcom_hwspinlock_driver); | |
| 170 | +} | |
| 171 | +/* board init code might need to reserve hwspinlocks for predefined purposes */ | |
| 172 | +postcore_initcall(qcom_hwspinlock_init); | |
| 173 | + | |
| 174 | +static void __exit qcom_hwspinlock_exit(void) | |
| 175 | +{ | |
| 176 | + platform_driver_unregister(&qcom_hwspinlock_driver); | |
| 177 | +} | |
| 178 | +module_exit(qcom_hwspinlock_exit); | |
| 179 | + | |
| 180 | +MODULE_LICENSE("GPL v2"); | |
| 181 | +MODULE_DESCRIPTION("Hardware spinlock driver for Qualcomm SoCs"); | |
| @@ -0,0 +1,136 @@ | ||
| 1 | +/* | |
| 2 | + * SIRF hardware spinlock driver | |
| 3 | + * | |
| 4 | + * Copyright (c) 2015 Cambridge Silicon Radio Limited, a CSR plc group company. | |
| 5 | + * | |
| 6 | + * Licensed under GPLv2. | |
| 7 | + */ | |
| 8 | + | |
| 9 | +#include <linux/kernel.h> | |
| 10 | +#include <linux/module.h> | |
| 11 | +#include <linux/device.h> | |
| 12 | +#include <linux/io.h> | |
| 13 | +#include <linux/pm_runtime.h> | |
| 14 | +#include <linux/slab.h> | |
| 15 | +#include <linux/spinlock.h> | |
| 16 | +#include <linux/hwspinlock.h> | |
| 17 | +#include <linux/platform_device.h> | |
| 18 | +#include <linux/of.h> | |
| 19 | +#include <linux/of_address.h> | |
| 20 | + | |
| 21 | +#include "hwspinlock_internal.h" | |
| 22 | + | |
| 23 | +struct sirf_hwspinlock { | |
| 24 | + void __iomem *io_base; | |
| 25 | + struct hwspinlock_device bank; | |
| 26 | +}; | |
| 27 | + | |
| 28 | +/* Number of Hardware Spinlocks*/ | |
| 29 | +#define HW_SPINLOCK_NUMBER 30 | |
| 30 | + | |
| 31 | +/* Hardware spinlock register offsets */ | |
| 32 | +#define HW_SPINLOCK_BASE 0x404 | |
| 33 | +#define HW_SPINLOCK_OFFSET(x) (HW_SPINLOCK_BASE + 0x4 * (x)) | |
| 34 | + | |
| 35 | +static int sirf_hwspinlock_trylock(struct hwspinlock *lock) | |
| 36 | +{ | |
| 37 | + void __iomem *lock_addr = lock->priv; | |
| 38 | + | |
| 39 | + /* attempt to acquire the lock by reading value == 1 from it */ | |
| 40 | + return !!readl(lock_addr); | |
| 41 | +} | |
| 42 | + | |
| 43 | +static void sirf_hwspinlock_unlock(struct hwspinlock *lock) | |
| 44 | +{ | |
| 45 | + void __iomem *lock_addr = lock->priv; | |
| 46 | + | |
| 47 | + /* release the lock by writing 0 to it */ | |
| 48 | + writel(0, lock_addr); | |
| 49 | +} | |
| 50 | + | |
| 51 | +static const struct hwspinlock_ops sirf_hwspinlock_ops = { | |
| 52 | + .trylock = sirf_hwspinlock_trylock, | |
| 53 | + .unlock = sirf_hwspinlock_unlock, | |
| 54 | +}; | |
| 55 | + | |
| 56 | +static int sirf_hwspinlock_probe(struct platform_device *pdev) | |
| 57 | +{ | |
| 58 | + struct sirf_hwspinlock *hwspin; | |
| 59 | + struct hwspinlock *hwlock; | |
| 60 | + int idx, ret; | |
| 61 | + | |
| 62 | + if (!pdev->dev.of_node) | |
| 63 | + return -ENODEV; | |
| 64 | + | |
| 65 | + hwspin = devm_kzalloc(&pdev->dev, sizeof(*hwspin) + | |
| 66 | + sizeof(*hwlock) * HW_SPINLOCK_NUMBER, GFP_KERNEL); | |
| 67 | + if (!hwspin) | |
| 68 | + return -ENOMEM; | |
| 69 | + | |
| 70 | + /* retrieve io base */ | |
| 71 | + hwspin->io_base = of_iomap(pdev->dev.of_node, 0); | |
| 72 | + if (!hwspin->io_base) | |
| 73 | + return -ENOMEM; | |
| 74 | + | |
| 75 | + for (idx = 0; idx < HW_SPINLOCK_NUMBER; idx++) { | |
| 76 | + hwlock = &hwspin->bank.lock[idx]; | |
| 77 | + hwlock->priv = hwspin->io_base + HW_SPINLOCK_OFFSET(idx); | |
| 78 | + } | |
| 79 | + | |
| 80 | + platform_set_drvdata(pdev, hwspin); | |
| 81 | + | |
| 82 | + pm_runtime_enable(&pdev->dev); | |
| 83 | + | |
| 84 | + ret = hwspin_lock_register(&hwspin->bank, &pdev->dev, | |
| 85 | + &sirf_hwspinlock_ops, 0, | |
| 86 | + HW_SPINLOCK_NUMBER); | |
| 87 | + if (ret) | |
| 88 | + goto reg_failed; | |
| 89 | + | |
| 90 | + return 0; | |
| 91 | + | |
| 92 | +reg_failed: | |
| 93 | + pm_runtime_disable(&pdev->dev); | |
| 94 | + iounmap(hwspin->io_base); | |
| 95 | + | |
| 96 | + return ret; | |
| 97 | +} | |
| 98 | + | |
| 99 | +static int sirf_hwspinlock_remove(struct platform_device *pdev) | |
| 100 | +{ | |
| 101 | + struct sirf_hwspinlock *hwspin = platform_get_drvdata(pdev); | |
| 102 | + int ret; | |
| 103 | + | |
| 104 | + ret = hwspin_lock_unregister(&hwspin->bank); | |
| 105 | + if (ret) { | |
| 106 | + dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret); | |
| 107 | + return ret; | |
| 108 | + } | |
| 109 | + | |
| 110 | + pm_runtime_disable(&pdev->dev); | |
| 111 | + | |
| 112 | + iounmap(hwspin->io_base); | |
| 113 | + | |
| 114 | + return 0; | |
| 115 | +} | |
| 116 | + | |
| 117 | +static const struct of_device_id sirf_hwpinlock_ids[] = { | |
| 118 | + { .compatible = "sirf,hwspinlock", }, | |
| 119 | + {}, | |
| 120 | +}; | |
| 121 | +MODULE_DEVICE_TABLE(of, sirf_hwpinlock_ids); | |
| 122 | + | |
| 123 | +static struct platform_driver sirf_hwspinlock_driver = { | |
| 124 | + .probe = sirf_hwspinlock_probe, | |
| 125 | + .remove = sirf_hwspinlock_remove, | |
| 126 | + .driver = { | |
| 127 | + .name = "atlas7_hwspinlock", | |
| 128 | + .of_match_table = of_match_ptr(sirf_hwpinlock_ids), | |
| 129 | + }, | |
| 130 | +}; | |
| 131 | + | |
| 132 | +module_platform_driver(sirf_hwspinlock_driver); | |
| 133 | + | |
| 134 | +MODULE_LICENSE("GPL v2"); | |
| 135 | +MODULE_DESCRIPTION("SIRF Hardware spinlock driver"); | |
| 136 | +MODULE_AUTHOR("Wei Chen <wei.chen@csr.com>"); | |
| @@ -26,6 +26,7 @@ · 25 unmodified lines | ||
| 26 | 26 | #define HWLOCK_IRQ 0x02 /* Disable interrupts, don't save state */ |
| 27 | 27 | |
| 28 | 28 | struct device; |
| 29 | +struct device_node; | |
| 29 | 30 | struct hwspinlock; |
| 30 | 31 | struct hwspinlock_device; |
| 31 | 32 | struct hwspinlock_ops; |
| @@ -66,6 +67,7 @@ int hwspin_lock_unregister(struct hwspinlock_device *bank); · 34 unmodified lines | ||
| 66 | 67 | struct hwspinlock *hwspin_lock_request(void); |
| 67 | 68 | struct hwspinlock *hwspin_lock_request_specific(unsigned int id); |
| 68 | 69 | int hwspin_lock_free(struct hwspinlock *hwlock); |
| 70 | +int of_hwspin_lock_get_id(struct device_node *np, int index); | |
| 69 | 71 | int hwspin_lock_get_id(struct hwspinlock *hwlock); |
| 70 | 72 | int __hwspin_lock_timeout(struct hwspinlock *, unsigned int, int, |
| 71 | 73 | unsigned long *); |
| @@ -120,6 +122,11 @@ void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags) · 48 unmodified lines | ||
| 120 | 122 | { |
| 121 | 123 | } |
| 122 | 124 | |
| 125 | +static inline int of_hwspin_lock_get_id(struct device_node *np, int index) | |
| 126 | +{ | |
| 127 | + return 0; | |
| 128 | +} | |
| 129 | + | |
| 123 | 130 | static inline int hwspin_lock_get_id(struct hwspinlock *hwlock) |
| 124 | 131 | { |
| 125 | 132 | return 0; |