- 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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJVk+rDAAoJELLolMlTRIoMlI4QAJoRNBFlH777Xg4ubztrbAmn jcm2Fb3xj1y64WzRZ0CxfD61GtlwSvux/cZNuFW5cjDRx5RnErF8or8s8Rab7jIy 9xFqlaJS0F/HVc5rT78Mv5aER4/FGU8kZfX9us2zcg4Pq17YWdaQvFkWhWarTK3s aNcEKHz7RRNnkACzm5sqbJMCGSf7OSMoExDRfp90n9s09hZC6ups17LSusTYdmxx c4dhlrAPRUs6qSAgJ2/0oIuCZEQd+LjFK28nm2lJEi48rQFq/pSd3cBTdilEW/1R iZSrmBMpb2y5WhBZmmbv1EZMhh477iaOAs3L7fGf6SI167ASoYjJtVPXUhkCd+ju iMYwxcZiLj+iH6/Q9wWzuf2HvARd941b+IBhmDTrWGXyuQvX6ZbJGwJNh723yxAI Sr3ORZg0cCMgrihcKxiDjpmLtgpJk3uXxxgKJkCvgRmB0AsvYidEG3qW8K9GX3fA 7O9lxt6jf+02g5iG5TLlaNgNvcIczQK3ng5qh33K5sAq3ItS/skzLwRIUW9wVf6O GpAnirqpTKLIsRnURymc4hODfX2tYrPQNeXWV1Pj2LpskrMQAqXvxas5+sUJ77BC 5c1yvZriMD3+stlgSJBdbwO7oPmJGDYtqEiuw5yAxKkPEd99M35EGLrMQgW3HYBm uIzdFIjv8BVNBgJ8WzOf =2HLz -----END PGP SIGNATURE----- Merge tag 'hwspinlock-4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad/hwspinlock 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
This commit is contained in:
commit
d033ed9eea
59
Documentation/devicetree/bindings/hwlock/hwlock.txt
Normal file
59
Documentation/devicetree/bindings/hwlock/hwlock.txt
Normal file
|
@ -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>;
|
||||||
|
...
|
||||||
|
};
|
26
Documentation/devicetree/bindings/hwlock/omap-hwspinlock.txt
Normal file
26
Documentation/devicetree/bindings/hwlock/omap-hwspinlock.txt
Normal file
|
@ -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>;
|
||||||
|
};
|
39
Documentation/devicetree/bindings/hwlock/qcom-hwspinlock.txt
Normal file
39
Documentation/devicetree/bindings/hwlock/qcom-hwspinlock.txt
Normal file
|
@ -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>;
|
||||||
|
};
|
28
Documentation/devicetree/bindings/hwlock/sirf,hwspinlock.txt
Normal file
28
Documentation/devicetree/bindings/hwlock/sirf,hwspinlock.txt
Normal file
|
@ -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>;
|
||||||
|
...
|
||||||
|
};
|
|
@ -48,6 +48,16 @@ independent, drivers.
|
||||||
ids for predefined purposes.
|
ids for predefined purposes.
|
||||||
Should be called from a process context (might sleep).
|
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);
|
int hwspin_lock_free(struct hwspinlock *hwlock);
|
||||||
- free a previously-assigned hwspinlock; returns 0 on success, or an
|
- free a previously-assigned hwspinlock; returns 0 on success, or an
|
||||||
appropriate error code on failure (e.g. -EINVAL if the hwspinlock
|
appropriate error code on failure (e.g. -EINVAL if the hwspinlock
|
||||||
|
|
|
@ -7364,7 +7364,6 @@ M: Ohad Ben-Cohen <ohad@wizery.com>
|
||||||
L: linux-omap@vger.kernel.org
|
L: linux-omap@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/hwspinlock/omap_hwspinlock.c
|
F: drivers/hwspinlock/omap_hwspinlock.c
|
||||||
F: arch/arm/mach-omap2/hwspinlock.c
|
|
||||||
|
|
||||||
OMAP MMC SUPPORT
|
OMAP MMC SUPPORT
|
||||||
M: Jarkko Lavinen <jarkko.lavinen@nokia.com>
|
M: Jarkko Lavinen <jarkko.lavinen@nokia.com>
|
||||||
|
|
|
@ -274,8 +274,5 @@ obj-y += $(nand-m) $(nand-y)
|
||||||
|
|
||||||
smsc911x-$(CONFIG_SMSC911X) := gpmc-smsc911x.o
|
smsc911x-$(CONFIG_SMSC911X) := gpmc-smsc911x.o
|
||||||
obj-y += $(smsc911x-m) $(smsc911x-y)
|
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
|
obj-y += common-board-devices.o twl-common.o dss-common.o
|
||||||
|
|
|
@ -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);
|
|
|
@ -18,6 +18,30 @@ config HWSPINLOCK_OMAP
|
||||||
|
|
||||||
If unsure, say N.
|
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
|
config HSEM_U8500
|
||||||
tristate "STE Hardware Semaphore functionality"
|
tristate "STE Hardware Semaphore functionality"
|
||||||
depends on ARCH_U8500
|
depends on ARCH_U8500
|
||||||
|
|
|
@ -4,4 +4,6 @@
|
||||||
|
|
||||||
obj-$(CONFIG_HWSPINLOCK) += hwspinlock_core.o
|
obj-$(CONFIG_HWSPINLOCK) += hwspinlock_core.o
|
||||||
obj-$(CONFIG_HWSPINLOCK_OMAP) += omap_hwspinlock.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
|
obj-$(CONFIG_HSEM_U8500) += u8500_hsem.o
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <linux/hwspinlock.h>
|
#include <linux/hwspinlock.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
|
||||||
#include "hwspinlock_internal.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);
|
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)
|
static int hwspin_lock_register_single(struct hwspinlock *hwlock, int id)
|
||||||
{
|
{
|
||||||
struct hwspinlock *tmp;
|
struct hwspinlock *tmp;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* OMAP hardware spinlock driver
|
* 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>
|
* Contact: Simon Que <sque@ti.com>
|
||||||
* Hari Kanigeri <h-kanigeri2@ti.com>
|
* Hari Kanigeri <h-kanigeri2@ti.com>
|
||||||
|
@ -27,6 +27,7 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/hwspinlock.h>
|
#include <linux/hwspinlock.h>
|
||||||
|
#include <linux/of.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
#include "hwspinlock_internal.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)
|
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_device *bank;
|
||||||
struct hwspinlock *hwlock;
|
struct hwspinlock *hwlock;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
void __iomem *io_base;
|
void __iomem *io_base;
|
||||||
int num_locks, i, ret;
|
int num_locks, i, ret;
|
||||||
|
/* Only a single hwspinlock block device is supported */
|
||||||
|
int base_id = 0;
|
||||||
|
|
||||||
if (!pdata)
|
if (!node)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
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;
|
hwlock->priv = io_base + LOCK_BASE_OFFSET + sizeof(u32) * i;
|
||||||
|
|
||||||
ret = hwspin_lock_register(bank, &pdev->dev, &omap_hwspinlock_ops,
|
ret = hwspin_lock_register(bank, &pdev->dev, &omap_hwspinlock_ops,
|
||||||
pdata->base_id, num_locks);
|
base_id, num_locks);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto reg_fail;
|
goto reg_fail;
|
||||||
|
|
||||||
|
@ -174,11 +177,18 @@ static int omap_hwspinlock_remove(struct platform_device *pdev)
|
||||||
return 0;
|
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 = {
|
static struct platform_driver omap_hwspinlock_driver = {
|
||||||
.probe = omap_hwspinlock_probe,
|
.probe = omap_hwspinlock_probe,
|
||||||
.remove = omap_hwspinlock_remove,
|
.remove = omap_hwspinlock_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "omap_hwspinlock",
|
.name = "omap_hwspinlock",
|
||||||
|
.of_match_table = of_match_ptr(omap_hwspinlock_of_match),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
181
drivers/hwspinlock/qcom_hwspinlock.c
Normal file
181
drivers/hwspinlock/qcom_hwspinlock.c
Normal file
|
@ -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");
|
136
drivers/hwspinlock/sirf_hwspinlock.c
Normal file
136
drivers/hwspinlock/sirf_hwspinlock.c
Normal file
|
@ -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>");
|
|
@ -26,6 +26,7 @@
|
||||||
#define HWLOCK_IRQ 0x02 /* Disable interrupts, don't save state */
|
#define HWLOCK_IRQ 0x02 /* Disable interrupts, don't save state */
|
||||||
|
|
||||||
struct device;
|
struct device;
|
||||||
|
struct device_node;
|
||||||
struct hwspinlock;
|
struct hwspinlock;
|
||||||
struct hwspinlock_device;
|
struct hwspinlock_device;
|
||||||
struct hwspinlock_ops;
|
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(void);
|
||||||
struct hwspinlock *hwspin_lock_request_specific(unsigned int id);
|
struct hwspinlock *hwspin_lock_request_specific(unsigned int id);
|
||||||
int hwspin_lock_free(struct hwspinlock *hwlock);
|
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_get_id(struct hwspinlock *hwlock);
|
||||||
int __hwspin_lock_timeout(struct hwspinlock *, unsigned int, int,
|
int __hwspin_lock_timeout(struct hwspinlock *, unsigned int, int,
|
||||||
unsigned long *);
|
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)
|
static inline int hwspin_lock_get_id(struct hwspinlock *hwlock)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in a new issue