diff options
| -rw-r--r-- | Documentation/devicetree/bindings/hwlock/hwlock.txt | 59 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/hwlock/omap-hwspinlock.txt | 26 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/hwlock/qcom-hwspinlock.txt | 39 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/hwlock/sirf,hwspinlock.txt | 28 | ||||
| -rw-r--r-- | Documentation/hwspinlock.txt | 10 | ||||
| -rw-r--r-- | MAINTAINERS | 1 | ||||
| -rw-r--r-- | arch/arm/mach-omap2/Makefile | 3 | ||||
| -rw-r--r-- | arch/arm/mach-omap2/hwspinlock.c | 60 | ||||
| -rw-r--r-- | drivers/hwspinlock/Kconfig | 24 | ||||
| -rw-r--r-- | drivers/hwspinlock/Makefile | 2 | ||||
| -rw-r--r-- | drivers/hwspinlock/hwspinlock_core.c | 79 | ||||
| -rw-r--r-- | drivers/hwspinlock/omap_hwspinlock.c | 18 | ||||
| -rw-r--r-- | drivers/hwspinlock/qcom_hwspinlock.c | 181 | ||||
| -rw-r--r-- | drivers/hwspinlock/sirf_hwspinlock.c | 136 | ||||
| -rw-r--r-- | include/linux/hwspinlock.h | 7 | 
15 files changed, 605 insertions, 68 deletions
| diff --git a/Documentation/devicetree/bindings/hwlock/hwlock.txt b/Documentation/devicetree/bindings/hwlock/hwlock.txt new file mode 100644 index 000000000000..085d1f5c916a --- /dev/null +++ b/Documentation/devicetree/bindings/hwlock/hwlock.txt @@ -0,0 +1,59 @@ +Generic hwlock bindings +======================= + +Generic bindings that are common to all the hwlock platform specific driver +implementations. + +Please also look through the individual platform specific hwlock binding +documentations for identifying any additional properties specific to that +platform. + +hwlock providers: +================= + +Required properties: +- #hwlock-cells:        Specifies the number of cells needed to represent a +                        specific lock. + +hwlock users: +============= + +Consumers that require specific hwlock(s) should specify them using the +property "hwlocks", and an optional "hwlock-names" property. + +Required properties: +- hwlocks:              List of phandle to a hwlock provider node and an +                        associated hwlock args specifier as indicated by +                        #hwlock-cells. The list can have just a single hwlock +                        or multiple hwlocks, with each hwlock represented by +                        a phandle and a corresponding args specifier. + +Optional properties: +- hwlock-names:         List of hwlock name strings defined in the same order +                        as the hwlocks, with one name per hwlock. Consumers can +                        use the hwlock-names to match and get a specific hwlock. + + +1. Example of a node using a single specific hwlock: + +The following example has a node requesting a hwlock in the bank defined by +the node hwlock1. hwlock1 is a hwlock provider with an argument specifier +of length 1. + +	node { +		... +		hwlocks = <&hwlock1 2>; +		... +	}; + +2. Example of a node using multiple specific hwlocks: + +The following example has a node requesting two hwlocks, a hwlock within +the hwlock device node 'hwlock1' with #hwlock-cells value of 1, and another +hwlock within the hwlock device node 'hwlock2' with #hwlock-cells value of 2. + +	node { +		... +		hwlocks = <&hwlock1 2>, <&hwlock2 0 3>; +		... +	}; diff --git a/Documentation/devicetree/bindings/hwlock/omap-hwspinlock.txt b/Documentation/devicetree/bindings/hwlock/omap-hwspinlock.txt new file mode 100644 index 000000000000..2c9804f4f4ac --- /dev/null +++ b/Documentation/devicetree/bindings/hwlock/omap-hwspinlock.txt @@ -0,0 +1,26 @@ +OMAP4+ HwSpinlock Driver +======================== + +Required properties: +- compatible:		Should be "ti,omap4-hwspinlock" for +			    OMAP44xx, OMAP54xx, AM33xx, AM43xx, DRA7xx SoCs +- reg:			Contains the hwspinlock module register address space +			(base address and length) +- ti,hwmods:		Name of the hwmod associated with the hwspinlock device +- #hwlock-cells:	Should be 1. The OMAP hwspinlock users will use a +			0-indexed relative hwlock number as the argument +			specifier value for requesting a specific hwspinlock +			within a hwspinlock bank. + +Please look at the generic hwlock binding for usage information for consumers, +"Documentation/devicetree/bindings/hwlock/hwlock.txt" + +Example: + +/* OMAP4 */ +hwspinlock: spinlock@4a0f6000 { +	compatible = "ti,omap4-hwspinlock"; +	reg = <0x4a0f6000 0x1000>; +	ti,hwmods = "spinlock"; +	#hwlock-cells = <1>; +}; diff --git a/Documentation/devicetree/bindings/hwlock/qcom-hwspinlock.txt b/Documentation/devicetree/bindings/hwlock/qcom-hwspinlock.txt new file mode 100644 index 000000000000..4563f524556b --- /dev/null +++ b/Documentation/devicetree/bindings/hwlock/qcom-hwspinlock.txt @@ -0,0 +1,39 @@ +Qualcomm Hardware Mutex Block: + +The hardware block provides mutexes utilized between different processors on +the SoC as part of the communication protocol used by these processors. + +- compatible: +	Usage: required +	Value type: <string> +	Definition: must be one of: +		    "qcom,sfpb-mutex", +		    "qcom,tcsr-mutex" + +- syscon: +	Usage: required +	Value type: <prop-encoded-array> +	Definition: one cell containing: +		    syscon phandle +		    offset of the hwmutex block within the syscon +		    stride of the hwmutex registers + +- #hwlock-cells: +	Usage: required +	Value type: <u32> +	Definition: must be 1, the specified cell represent the lock id +		    (hwlock standard property, see hwlock.txt) + +Example: + +	tcsr_mutex_block: syscon@fd484000 { +		compatible = "syscon"; +		reg = <0xfd484000 0x2000>; +	}; + +	hwlock@fd484000 { +		compatible = "qcom,tcsr-mutex"; +		syscon = <&tcsr_mutex_block 0 0x80>; + +		#hwlock-cells = <1>; +	}; diff --git a/Documentation/devicetree/bindings/hwlock/sirf,hwspinlock.txt b/Documentation/devicetree/bindings/hwlock/sirf,hwspinlock.txt new file mode 100644 index 000000000000..9bb1240a68e0 --- /dev/null +++ b/Documentation/devicetree/bindings/hwlock/sirf,hwspinlock.txt @@ -0,0 +1,28 @@ +SIRF Hardware spinlock device Binding +----------------------------------------------- + +Required properties : +- compatible : shall contain only one of the following: +	"sirf,hwspinlock" + +- reg : the register address of hwspinlock + +- #hwlock-cells : hwlock users only use the hwlock id to represent a specific +	hwlock, so the number of cells should be <1> here. + +Please look at the generic hwlock binding for usage information for consumers, +"Documentation/devicetree/bindings/hwlock/hwlock.txt" + +Example of hwlock provider: +	hwlock { +		compatible = "sirf,hwspinlock"; +		reg = <0x13240000 0x00010000>; +		#hwlock-cells = <1>; +	}; + +Example of hwlock users: +	node { +		... +		hwlocks = <&hwlock 2>; +		... +	}; diff --git a/Documentation/hwspinlock.txt b/Documentation/hwspinlock.txt index 62f7d4ea6e26..61c1ee98e59f 100644 --- a/Documentation/hwspinlock.txt +++ b/Documentation/hwspinlock.txt @@ -48,6 +48,16 @@ independent, drivers.       ids for predefined purposes.       Should be called from a process context (might sleep). +  int of_hwspin_lock_get_id(struct device_node *np, int index); +   - retrieve the global lock id for an OF phandle-based specific lock. +     This function provides a means for DT users of a hwspinlock module +     to get the global lock id of a specific hwspinlock, so that it can +     be requested using the normal hwspin_lock_request_specific() API. +     The function returns a lock id number on success, -EPROBE_DEFER if +     the hwspinlock device is not yet registered with the core, or other +     error values. +     Should be called from a process context (might sleep). +    int hwspin_lock_free(struct hwspinlock *hwlock);     - free a previously-assigned hwspinlock; returns 0 on success, or an       appropriate error code on failure (e.g. -EINVAL if the hwspinlock diff --git a/MAINTAINERS b/MAINTAINERS index 86ea2084bc58..e23ff1cbd12b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7364,7 +7364,6 @@ M:	Ohad Ben-Cohen <ohad@wizery.com>  L:	linux-omap@vger.kernel.org  S:	Maintained  F:	drivers/hwspinlock/omap_hwspinlock.c -F:	arch/arm/mach-omap2/hwspinlock.c  OMAP MMC SUPPORT  M:	Jarkko Lavinen <jarkko.lavinen@nokia.com> diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index f1a68c63dc99..903c85be2897 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -274,8 +274,5 @@ obj-y					+= $(nand-m) $(nand-y)  smsc911x-$(CONFIG_SMSC911X)		:= gpmc-smsc911x.o  obj-y					+= $(smsc911x-m) $(smsc911x-y) -ifneq ($(CONFIG_HWSPINLOCK_OMAP),) -obj-y					+= hwspinlock.o -endif  obj-y					+= common-board-devices.o twl-common.o dss-common.o diff --git a/arch/arm/mach-omap2/hwspinlock.c b/arch/arm/mach-omap2/hwspinlock.c deleted file mode 100644 index ef175acaeaa2..000000000000 --- a/arch/arm/mach-omap2/hwspinlock.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * OMAP hardware spinlock device initialization - * - * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com - * - * Contact: Simon Que <sque@ti.com> - *          Hari Kanigeri <h-kanigeri2@ti.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * General Public License for more details. - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/err.h> -#include <linux/hwspinlock.h> - -#include "soc.h" -#include "omap_hwmod.h" -#include "omap_device.h" - -static struct hwspinlock_pdata omap_hwspinlock_pdata __initdata = { -	.base_id = 0, -}; - -static int __init hwspinlocks_init(void) -{ -	int retval = 0; -	struct omap_hwmod *oh; -	struct platform_device *pdev; -	const char *oh_name = "spinlock"; -	const char *dev_name = "omap_hwspinlock"; - -	/* -	 * Hwmod lookup will fail in case our platform doesn't support the -	 * hardware spinlock module, so it is safe to run this initcall -	 * on all omaps -	 */ -	oh = omap_hwmod_lookup(oh_name); -	if (oh == NULL) -		return -EINVAL; - -	pdev = omap_device_build(dev_name, 0, oh, &omap_hwspinlock_pdata, -				sizeof(struct hwspinlock_pdata)); -	if (IS_ERR(pdev)) { -		pr_err("Can't build omap_device for %s:%s\n", dev_name, -								oh_name); -		retval = PTR_ERR(pdev); -	} - -	return retval; -} -/* early board code might need to reserve specific hwspinlock instances */ -omap_postcore_initcall(hwspinlocks_init); diff --git a/drivers/hwspinlock/Kconfig b/drivers/hwspinlock/Kconfig index 3612cb5b30b2..73a401662853 100644 --- a/drivers/hwspinlock/Kconfig +++ b/drivers/hwspinlock/Kconfig @@ -18,6 +18,30 @@ config HWSPINLOCK_OMAP  	  If unsure, say N. +config HWSPINLOCK_QCOM +	tristate "Qualcomm Hardware Spinlock device" +	depends on ARCH_QCOM +	select HWSPINLOCK +	select MFD_SYSCON +	help +	  Say y here to support the Qualcomm Hardware Mutex functionality, which +	  provides a synchronisation mechanism for the various processors on +	  the SoC. + +	  If unsure, say N. + +config HWSPINLOCK_SIRF +	tristate "SIRF Hardware Spinlock device" +	depends on ARCH_SIRF +	select HWSPINLOCK +	help +	  Say y here to support the SIRF Hardware Spinlock device, which +	  provides a synchronisation mechanism for the various processors +	  on the SoC. + +	  It's safe to say n here if you're not interested in SIRF hardware +	  spinlock or just want a bare minimum kernel. +  config HSEM_U8500  	tristate "STE Hardware Semaphore functionality"  	depends on ARCH_U8500 diff --git a/drivers/hwspinlock/Makefile b/drivers/hwspinlock/Makefile index 93eb64b66486..6b59cb5a4f3a 100644 --- a/drivers/hwspinlock/Makefile +++ b/drivers/hwspinlock/Makefile @@ -4,4 +4,6 @@  obj-$(CONFIG_HWSPINLOCK)		+= hwspinlock_core.o  obj-$(CONFIG_HWSPINLOCK_OMAP)		+= omap_hwspinlock.o +obj-$(CONFIG_HWSPINLOCK_QCOM)		+= qcom_hwspinlock.o +obj-$(CONFIG_HWSPINLOCK_SIRF)		+= sirf_hwspinlock.o  obj-$(CONFIG_HSEM_U8500)		+= u8500_hsem.o diff --git a/drivers/hwspinlock/hwspinlock_core.c b/drivers/hwspinlock/hwspinlock_core.c index 461a0d739d75..52f708bcf77f 100644 --- a/drivers/hwspinlock/hwspinlock_core.c +++ b/drivers/hwspinlock/hwspinlock_core.c @@ -27,6 +27,7 @@  #include <linux/hwspinlock.h>  #include <linux/pm_runtime.h>  #include <linux/mutex.h> +#include <linux/of.h>  #include "hwspinlock_internal.h" @@ -257,6 +258,84 @@ void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags)  }  EXPORT_SYMBOL_GPL(__hwspin_unlock); +/** + * of_hwspin_lock_simple_xlate - translate hwlock_spec to return a lock id + * @bank: the hwspinlock device bank + * @hwlock_spec: hwlock specifier as found in the device tree + * + * This is a simple translation function, suitable for hwspinlock platform + * drivers that only has a lock specifier length of 1. + * + * Returns a relative index of the lock within a specified bank on success, + * or -EINVAL on invalid specifier cell count. + */ +static inline int +of_hwspin_lock_simple_xlate(const struct of_phandle_args *hwlock_spec) +{ +	if (WARN_ON(hwlock_spec->args_count != 1)) +		return -EINVAL; + +	return hwlock_spec->args[0]; +} + +/** + * of_hwspin_lock_get_id() - get lock id for an OF phandle-based specific lock + * @np: device node from which to request the specific hwlock + * @index: index of the hwlock in the list of values + * + * This function provides a means for DT users of the hwspinlock module to + * get the global lock id of a specific hwspinlock using the phandle of the + * hwspinlock device, so that it can be requested using the normal + * hwspin_lock_request_specific() API. + * + * Returns the global lock id number on success, -EPROBE_DEFER if the hwspinlock + * device is not yet registered, -EINVAL on invalid args specifier value or an + * appropriate error as returned from the OF parsing of the DT client node. + */ +int of_hwspin_lock_get_id(struct device_node *np, int index) +{ +	struct of_phandle_args args; +	struct hwspinlock *hwlock; +	struct radix_tree_iter iter; +	void **slot; +	int id; +	int ret; + +	ret = of_parse_phandle_with_args(np, "hwlocks", "#hwlock-cells", index, +					 &args); +	if (ret) +		return ret; + +	/* Find the hwspinlock device: we need its base_id */ +	ret = -EPROBE_DEFER; +	rcu_read_lock(); +	radix_tree_for_each_slot(slot, &hwspinlock_tree, &iter, 0) { +		hwlock = radix_tree_deref_slot(slot); +		if (unlikely(!hwlock)) +			continue; + +		if (hwlock->bank->dev->of_node == args.np) { +			ret = 0; +			break; +		} +	} +	rcu_read_unlock(); +	if (ret < 0) +		goto out; + +	id = of_hwspin_lock_simple_xlate(&args); +	if (id < 0 || id >= hwlock->bank->num_locks) { +		ret = -EINVAL; +		goto out; +	} +	id += hwlock->bank->base_id; + +out: +	of_node_put(args.np); +	return ret ? ret : id; +} +EXPORT_SYMBOL_GPL(of_hwspin_lock_get_id); +  static int hwspin_lock_register_single(struct hwspinlock *hwlock, int id)  {  	struct hwspinlock *tmp; diff --git a/drivers/hwspinlock/omap_hwspinlock.c b/drivers/hwspinlock/omap_hwspinlock.c index 47a275c6ece1..ad2f8cac8487 100644 --- a/drivers/hwspinlock/omap_hwspinlock.c +++ b/drivers/hwspinlock/omap_hwspinlock.c @@ -1,7 +1,7 @@  /*   * OMAP hardware spinlock driver   * - * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2010-2015 Texas Instruments Incorporated - http://www.ti.com   *   * Contact: Simon Que <sque@ti.com>   *          Hari Kanigeri <h-kanigeri2@ti.com> @@ -27,6 +27,7 @@  #include <linux/slab.h>  #include <linux/spinlock.h>  #include <linux/hwspinlock.h> +#include <linux/of.h>  #include <linux/platform_device.h>  #include "hwspinlock_internal.h" @@ -80,14 +81,16 @@ static const struct hwspinlock_ops omap_hwspinlock_ops = {  static int omap_hwspinlock_probe(struct platform_device *pdev)  { -	struct hwspinlock_pdata *pdata = pdev->dev.platform_data; +	struct device_node *node = pdev->dev.of_node;  	struct hwspinlock_device *bank;  	struct hwspinlock *hwlock;  	struct resource *res;  	void __iomem *io_base;  	int num_locks, i, ret; +	/* Only a single hwspinlock block device is supported */ +	int base_id = 0; -	if (!pdata) +	if (!node)  		return -ENODEV;  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -141,7 +144,7 @@ static int omap_hwspinlock_probe(struct platform_device *pdev)  		hwlock->priv = io_base + LOCK_BASE_OFFSET + sizeof(u32) * i;  	ret = hwspin_lock_register(bank, &pdev->dev, &omap_hwspinlock_ops, -						pdata->base_id, num_locks); +						base_id, num_locks);  	if (ret)  		goto reg_fail; @@ -174,11 +177,18 @@ static int omap_hwspinlock_remove(struct platform_device *pdev)  	return 0;  } +static const struct of_device_id omap_hwspinlock_of_match[] = { +	{ .compatible = "ti,omap4-hwspinlock", }, +	{ /* end */ }, +}; +MODULE_DEVICE_TABLE(of, omap_hwspinlock_of_match); +  static struct platform_driver omap_hwspinlock_driver = {  	.probe		= omap_hwspinlock_probe,  	.remove		= omap_hwspinlock_remove,  	.driver		= {  		.name	= "omap_hwspinlock", +		.of_match_table = of_match_ptr(omap_hwspinlock_of_match),  	},  }; diff --git a/drivers/hwspinlock/qcom_hwspinlock.c b/drivers/hwspinlock/qcom_hwspinlock.c new file mode 100644 index 000000000000..c752447fbac7 --- /dev/null +++ b/drivers/hwspinlock/qcom_hwspinlock.c @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2015, Sony Mobile Communications AB + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + */ + +#include <linux/hwspinlock.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> + +#include "hwspinlock_internal.h" + +#define QCOM_MUTEX_APPS_PROC_ID	1 +#define QCOM_MUTEX_NUM_LOCKS	32 + +static int qcom_hwspinlock_trylock(struct hwspinlock *lock) +{ +	struct regmap_field *field = lock->priv; +	u32 lock_owner; +	int ret; + +	ret = regmap_field_write(field, QCOM_MUTEX_APPS_PROC_ID); +	if (ret) +		return ret; + +	ret = regmap_field_read(field, &lock_owner); +	if (ret) +		return ret; + +	return lock_owner == QCOM_MUTEX_APPS_PROC_ID; +} + +static void qcom_hwspinlock_unlock(struct hwspinlock *lock) +{ +	struct regmap_field *field = lock->priv; +	u32 lock_owner; +	int ret; + +	ret = regmap_field_read(field, &lock_owner); +	if (ret) { +		pr_err("%s: unable to query spinlock owner\n", __func__); +		return; +	} + +	if (lock_owner != QCOM_MUTEX_APPS_PROC_ID) { +		pr_err("%s: spinlock not owned by us (actual owner is %d)\n", +				__func__, lock_owner); +	} + +	ret = regmap_field_write(field, 0); +	if (ret) +		pr_err("%s: failed to unlock spinlock\n", __func__); +} + +static const struct hwspinlock_ops qcom_hwspinlock_ops = { +	.trylock	= qcom_hwspinlock_trylock, +	.unlock		= qcom_hwspinlock_unlock, +}; + +static const struct of_device_id qcom_hwspinlock_of_match[] = { +	{ .compatible = "qcom,sfpb-mutex" }, +	{ .compatible = "qcom,tcsr-mutex" }, +	{ } +}; +MODULE_DEVICE_TABLE(of, qcom_hwspinlock_of_match); + +static int qcom_hwspinlock_probe(struct platform_device *pdev) +{ +	struct hwspinlock_device *bank; +	struct device_node *syscon; +	struct reg_field field; +	struct regmap *regmap; +	size_t array_size; +	u32 stride; +	u32 base; +	int ret; +	int i; + +	syscon = of_parse_phandle(pdev->dev.of_node, "syscon", 0); +	if (!syscon) { +		dev_err(&pdev->dev, "no syscon property\n"); +		return -ENODEV; +	} + +	regmap = syscon_node_to_regmap(syscon); +	if (IS_ERR(regmap)) +		return PTR_ERR(regmap); + +	ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, &base); +	if (ret < 0) { +		dev_err(&pdev->dev, "no offset in syscon\n"); +		return -EINVAL; +	} + +	ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 2, &stride); +	if (ret < 0) { +		dev_err(&pdev->dev, "no stride syscon\n"); +		return -EINVAL; +	} + +	array_size = QCOM_MUTEX_NUM_LOCKS * sizeof(struct hwspinlock); +	bank = devm_kzalloc(&pdev->dev, sizeof(*bank) + array_size, GFP_KERNEL); +	if (!bank) +		return -ENOMEM; + +	platform_set_drvdata(pdev, bank); + +	for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) { +		field.reg = base + i * stride; +		field.lsb = 0; +		field.msb = 31; + +		bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev, +							     regmap, field); +	} + +	pm_runtime_enable(&pdev->dev); + +	ret = hwspin_lock_register(bank, &pdev->dev, &qcom_hwspinlock_ops, +				   0, QCOM_MUTEX_NUM_LOCKS); +	if (ret) +		pm_runtime_disable(&pdev->dev); + +	return ret; +} + +static int qcom_hwspinlock_remove(struct platform_device *pdev) +{ +	struct hwspinlock_device *bank = platform_get_drvdata(pdev); +	int ret; + +	ret = hwspin_lock_unregister(bank); +	if (ret) { +		dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret); +		return ret; +	} + +	pm_runtime_disable(&pdev->dev); + +	return 0; +} + +static struct platform_driver qcom_hwspinlock_driver = { +	.probe		= qcom_hwspinlock_probe, +	.remove		= qcom_hwspinlock_remove, +	.driver		= { +		.name	= "qcom_hwspinlock", +		.of_match_table = qcom_hwspinlock_of_match, +	}, +}; + +static int __init qcom_hwspinlock_init(void) +{ +	return platform_driver_register(&qcom_hwspinlock_driver); +} +/* board init code might need to reserve hwspinlocks for predefined purposes */ +postcore_initcall(qcom_hwspinlock_init); + +static void __exit qcom_hwspinlock_exit(void) +{ +	platform_driver_unregister(&qcom_hwspinlock_driver); +} +module_exit(qcom_hwspinlock_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Hardware spinlock driver for Qualcomm SoCs"); diff --git a/drivers/hwspinlock/sirf_hwspinlock.c b/drivers/hwspinlock/sirf_hwspinlock.c new file mode 100644 index 000000000000..16018544d431 --- /dev/null +++ b/drivers/hwspinlock/sirf_hwspinlock.c @@ -0,0 +1,136 @@ +/* + * SIRF hardware spinlock driver + * + * Copyright (c) 2015 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/hwspinlock.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#include "hwspinlock_internal.h" + +struct sirf_hwspinlock { +	void __iomem *io_base; +	struct hwspinlock_device bank; +}; + +/* Number of Hardware Spinlocks*/ +#define	HW_SPINLOCK_NUMBER	30 + +/* Hardware spinlock register offsets */ +#define HW_SPINLOCK_BASE	0x404 +#define HW_SPINLOCK_OFFSET(x)	(HW_SPINLOCK_BASE + 0x4 * (x)) + +static int sirf_hwspinlock_trylock(struct hwspinlock *lock) +{ +	void __iomem *lock_addr = lock->priv; + +	/* attempt to acquire the lock by reading value == 1 from it */ +	return !!readl(lock_addr); +} + +static void sirf_hwspinlock_unlock(struct hwspinlock *lock) +{ +	void __iomem *lock_addr = lock->priv; + +	/* release the lock by writing 0 to it */ +	writel(0, lock_addr); +} + +static const struct hwspinlock_ops sirf_hwspinlock_ops = { +	.trylock = sirf_hwspinlock_trylock, +	.unlock = sirf_hwspinlock_unlock, +}; + +static int sirf_hwspinlock_probe(struct platform_device *pdev) +{ +	struct sirf_hwspinlock *hwspin; +	struct hwspinlock *hwlock; +	int idx, ret; + +	if (!pdev->dev.of_node) +		return -ENODEV; + +	hwspin = devm_kzalloc(&pdev->dev, sizeof(*hwspin) + +			sizeof(*hwlock) * HW_SPINLOCK_NUMBER, GFP_KERNEL); +	if (!hwspin) +		return -ENOMEM; + +	/* retrieve io base */ +	hwspin->io_base = of_iomap(pdev->dev.of_node, 0); +	if (!hwspin->io_base) +		return -ENOMEM; + +	for (idx = 0; idx < HW_SPINLOCK_NUMBER; idx++) { +		hwlock = &hwspin->bank.lock[idx]; +		hwlock->priv = hwspin->io_base + HW_SPINLOCK_OFFSET(idx); +	} + +	platform_set_drvdata(pdev, hwspin); + +	pm_runtime_enable(&pdev->dev); + +	ret = hwspin_lock_register(&hwspin->bank, &pdev->dev, +				   &sirf_hwspinlock_ops, 0, +				   HW_SPINLOCK_NUMBER); +	if (ret) +		goto reg_failed; + +	return 0; + +reg_failed: +	pm_runtime_disable(&pdev->dev); +	iounmap(hwspin->io_base); + +	return ret; +} + +static int sirf_hwspinlock_remove(struct platform_device *pdev) +{ +	struct sirf_hwspinlock *hwspin = platform_get_drvdata(pdev); +	int ret; + +	ret = hwspin_lock_unregister(&hwspin->bank); +	if (ret) { +		dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret); +		return ret; +	} + +	pm_runtime_disable(&pdev->dev); + +	iounmap(hwspin->io_base); + +	return 0; +} + +static const struct of_device_id sirf_hwpinlock_ids[] = { +	{ .compatible = "sirf,hwspinlock", }, +	{}, +}; +MODULE_DEVICE_TABLE(of, sirf_hwpinlock_ids); + +static struct platform_driver sirf_hwspinlock_driver = { +	.probe = sirf_hwspinlock_probe, +	.remove = sirf_hwspinlock_remove, +	.driver = { +		.name = "atlas7_hwspinlock", +		.of_match_table = of_match_ptr(sirf_hwpinlock_ids), +	}, +}; + +module_platform_driver(sirf_hwspinlock_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SIRF Hardware spinlock driver"); +MODULE_AUTHOR("Wei Chen <wei.chen@csr.com>"); diff --git a/include/linux/hwspinlock.h b/include/linux/hwspinlock.h index 3343298e40e8..859d673d98c8 100644 --- a/include/linux/hwspinlock.h +++ b/include/linux/hwspinlock.h @@ -26,6 +26,7 @@  #define HWLOCK_IRQ	0x02	/* Disable interrupts, don't save state */  struct device; +struct device_node;  struct hwspinlock;  struct hwspinlock_device;  struct hwspinlock_ops; @@ -66,6 +67,7 @@ int hwspin_lock_unregister(struct hwspinlock_device *bank);  struct hwspinlock *hwspin_lock_request(void);  struct hwspinlock *hwspin_lock_request_specific(unsigned int id);  int hwspin_lock_free(struct hwspinlock *hwlock); +int of_hwspin_lock_get_id(struct device_node *np, int index);  int hwspin_lock_get_id(struct hwspinlock *hwlock);  int __hwspin_lock_timeout(struct hwspinlock *, unsigned int, int,  							unsigned long *); @@ -120,6 +122,11 @@ void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags)  {  } +static inline int of_hwspin_lock_get_id(struct device_node *np, int index) +{ +	return 0; +} +  static inline int hwspin_lock_get_id(struct hwspinlock *hwlock)  {  	return 0; | 
