Commit d033ed9e authored by Linus Torvalds's avatar Linus Torvalds

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
parents 6361c845 bd5717a4
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>;
...
};
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>;
};
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>;
};
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. ...@@ -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> ...@@ -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) ...@@ -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
/*
* 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 ...@@ -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 @@ ...@@ -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 @@ ...@@ -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) ...@@ -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;
......
/* /*
* 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 @@ ...@@ -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 = { ...@@ -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) ...@@ -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) ...@@ -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),
}, },
}; };
......
/*
* 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");
/*
* 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 @@ ...@@ -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); ...@@ -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) ...@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment