summaryrefslogtreecommitdiff
path: root/drivers/hwspinlock
diff options
context:
space:
mode:
authorAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-09-08 01:01:14 -0300
committerAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-09-08 01:01:14 -0300
commite5fd91f1ef340da553f7a79da9540c3db711c937 (patch)
treeb11842027dc6641da63f4bcc524f8678263304a3 /drivers/hwspinlock
parent2a9b0348e685a63d97486f6749622b61e9e3292f (diff)
Linux-libre 4.2-gnu
Diffstat (limited to 'drivers/hwspinlock')
-rw-r--r--drivers/hwspinlock/Kconfig24
-rw-r--r--drivers/hwspinlock/Makefile2
-rw-r--r--drivers/hwspinlock/hwspinlock_core.c79
-rw-r--r--drivers/hwspinlock/omap_hwspinlock.c18
-rw-r--r--drivers/hwspinlock/qcom_hwspinlock.c181
-rw-r--r--drivers/hwspinlock/sirf_hwspinlock.c136
6 files changed, 436 insertions, 4 deletions
diff --git a/drivers/hwspinlock/Kconfig b/drivers/hwspinlock/Kconfig
index 3612cb5b3..73a401662 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 93eb64b66..6b59cb5a4 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 461a0d739..52f708bcf 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 47a275c6e..ad2f8cac8 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 000000000..c752447fb
--- /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 000000000..16018544d
--- /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>");