Merge tag 'hwspinlock-4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 3 Jul 2015 21:52:25 +0000 (14:52 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 3 Jul 2015 21:52:25 +0000 (14:52 -0700)
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

15 files changed:
Documentation/devicetree/bindings/hwlock/hwlock.txt [new file with mode: 0644]
Documentation/devicetree/bindings/hwlock/omap-hwspinlock.txt [new file with mode: 0644]
Documentation/devicetree/bindings/hwlock/qcom-hwspinlock.txt [new file with mode: 0644]
Documentation/devicetree/bindings/hwlock/sirf,hwspinlock.txt [new file with mode: 0644]
Documentation/hwspinlock.txt
MAINTAINERS
arch/arm/mach-omap2/Makefile
arch/arm/mach-omap2/hwspinlock.c [deleted file]
drivers/hwspinlock/Kconfig
drivers/hwspinlock/Makefile
drivers/hwspinlock/hwspinlock_core.c
drivers/hwspinlock/omap_hwspinlock.c
drivers/hwspinlock/qcom_hwspinlock.c [new file with mode: 0644]
drivers/hwspinlock/sirf_hwspinlock.c [new file with mode: 0644]
include/linux/hwspinlock.h

diff --git a/Documentation/devicetree/bindings/hwlock/hwlock.txt b/Documentation/devicetree/bindings/hwlock/hwlock.txt
new file mode 100644 (file)
index 0000000..085d1f5
--- /dev/null
@@ -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 (file)
index 0000000..2c9804f
--- /dev/null
@@ -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 (file)
index 0000000..4563f52
--- /dev/null
@@ -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 (file)
index 0000000..9bb1240
--- /dev/null
@@ -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>;
+               ...
+       };
index 62f7d4ea6e26459b74cdccbca9f7a3532ab4900b..61c1ee98e59f2137b8b250d2b469d4d949cca9b3 100644 (file)
@@ -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
index 86ea2084bc581d3ad7992da40c01c88157e9ce84..e23ff1cbd12b5f53fe19ce4398b1940d330d423a 100644 (file)
@@ -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>
index f1a68c63dc9933c8be7c2bcd2f30fb1973db5f65..903c85be28972e6e7d28aaf9459f5bc87b4ac87c 100644 (file)
@@ -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 (file)
index ef175ac..0000000
+++ /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);
index 3612cb5b30b206a066c75d835402654cacf7d002..73a40166285362f49326f3c2069721e2d061e3aa 100644 (file)
@@ -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
index 93eb64b664863b58b3cb1fb0c32c5c04ead0dad1..6b59cb5a4f3a857cf26d4dc7eebfd014f338414f 100644 (file)
@@ -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
index 461a0d739d75b0d646d9197a89ed6852f932be07..52f708bcf77f397952ea0f278ce5b161780e076a 100644 (file)
@@ -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;
index 47a275c6ece1d24487ca0f832f2913a597b8a87b..ad2f8cac8487c0a6edde5d6847dc57f97409210d 100644 (file)
@@ -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 (file)
index 0000000..c752447
--- /dev/null
@@ -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 (file)
index 0000000..1601854
--- /dev/null
@@ -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>");
index 3343298e40e83453e84dad4d8704af95ef57b5f7..859d673d98c80239715df9ef56c32672647e47a1 100644 (file)
@@ -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;