dm: Add Hardware Spinlock class
authorBenjamin Gaignard <benjamin.gaignard@linaro.org>
Tue, 27 Nov 2018 12:49:50 +0000 (13:49 +0100)
committerTom Rini <trini@konsulko.com>
Fri, 7 Dec 2018 04:26:32 +0000 (23:26 -0500)
This is uclass for Hardware Spinlocks.
It implements two mandatory operations: lock and unlock
and one optional relax operation.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
Reviewed-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Patrice Chotard <patrice.chotard@st.com>
13 files changed:
arch/sandbox/dts/test.dts
arch/sandbox/include/asm/state.h
configs/sandbox_defconfig
drivers/Kconfig
drivers/Makefile
drivers/hwspinlock/Kconfig [new file with mode: 0644]
drivers/hwspinlock/Makefile [new file with mode: 0644]
drivers/hwspinlock/hwspinlock-uclass.c [new file with mode: 0644]
drivers/hwspinlock/sandbox_hwspinlock.c [new file with mode: 0644]
include/dm/uclass-id.h
include/hwspinlock.h [new file with mode: 0644]
test/dm/Makefile
test/dm/hwspinlock.c [new file with mode: 0644]

index 6722e18bc39c035e8fe6eac593c2f039030edeef..12d39d8d5c1db889b79db2723fa964b96a1520a0 100644 (file)
        pinctrl {
                compatible = "sandbox,pinctrl";
        };
+
+       hwspinlock@0 {
+               compatible = "sandbox,hwspinlock";
+       };
 };
 
 #include "sandbox_pmic.dtsi"
index 5a1448510251d55297def6c7036a06055d377208..c724827f6cf1269df4c7fa9f6c9d23199a6ae139 100644 (file)
@@ -101,6 +101,7 @@ struct sandbox_state {
 
        ulong next_tag;                 /* Next address tag to allocate */
        struct list_head mapmem_head;   /* struct sandbox_mapmem_entry */
+       bool hwspinlock;                /* Hardware Spinlock status */
 };
 
 /* Minimum space we guarantee in the state FDT when calling read/write*/
index 1a767854984e5021ed72acbdf305a8dbcec5135c..2e761f9320bb99c102585c08248274d5959abc78 100644 (file)
@@ -96,6 +96,8 @@ CONFIG_BOARD=y
 CONFIG_BOARD_SANDBOX=y
 CONFIG_PM8916_GPIO=y
 CONFIG_SANDBOX_GPIO=y
+CONFIG_DM_HWSPINLOCK=y
+CONFIG_HWSPINLOCK_SANDBOX=y
 CONFIG_DM_I2C_COMPAT=y
 CONFIG_I2C_CROS_EC_TUNNEL=y
 CONFIG_I2C_CROS_EC_LDO=y
index 4ac823d962e855e149ab6ab1fce5fdc2557479b5..e9fbadd13d5a6eb3284dcc3adf1e6ac5549d2260 100644 (file)
@@ -40,6 +40,8 @@ source "drivers/fpga/Kconfig"
 
 source "drivers/gpio/Kconfig"
 
+source "drivers/hwspinlock/Kconfig"
+
 source "drivers/i2c/Kconfig"
 
 source "drivers/input/Kconfig"
index 55de10926ef423452f12a9c75a41a3a657e0df29..c425831b584f67b0de80e24f9e53b4b0d4d8ccbf 100644 (file)
@@ -113,4 +113,5 @@ obj-$(CONFIG_W1) += w1/
 obj-$(CONFIG_W1_EEPROM) += w1-eeprom/
 
 obj-$(CONFIG_MACH_PIC32) += ddr/microchip/
+obj-$(CONFIG_DM_HWSPINLOCK) += hwspinlock/
 endif
diff --git a/drivers/hwspinlock/Kconfig b/drivers/hwspinlock/Kconfig
new file mode 100644 (file)
index 0000000..de367fd
--- /dev/null
@@ -0,0 +1,16 @@
+menu "Hardware Spinlock Support"
+
+config DM_HWSPINLOCK
+       bool "Enable U-Boot hardware spinlock support"
+       help
+         This option enables U-Boot hardware spinlock support
+
+config HWSPINLOCK_SANDBOX
+       bool "Enable Hardware Spinlock support for Sandbox"
+       depends on SANDBOX && DM_HWSPINLOCK
+       help
+         Enable hardware spinlock support in Sandbox. This is a dummy device that
+         can be probed and support all the methods of HWSPINLOCK, but does not
+         really do anything.
+
+endmenu
diff --git a/drivers/hwspinlock/Makefile b/drivers/hwspinlock/Makefile
new file mode 100644 (file)
index 0000000..2704d68
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+#
+# Copyright (C) 2018, STMicroelectronics - All Rights Reserved
+
+obj-$(CONFIG_DM_HWSPINLOCK) += hwspinlock-uclass.o
+obj-$(CONFIG_HWSPINLOCK_SANDBOX) += sandbox_hwspinlock.o
diff --git a/drivers/hwspinlock/hwspinlock-uclass.c b/drivers/hwspinlock/hwspinlock-uclass.c
new file mode 100644 (file)
index 0000000..195f079
--- /dev/null
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <hwspinlock.h>
+#include <dm/device-internal.h>
+
+static inline const struct hwspinlock_ops *
+hwspinlock_dev_ops(struct udevice *dev)
+{
+       return (const struct hwspinlock_ops *)dev->driver->ops;
+}
+
+static int hwspinlock_of_xlate_default(struct hwspinlock *hws,
+                                      struct ofnode_phandle_args *args)
+{
+       if (args->args_count > 1) {
+               debug("Invaild args_count: %d\n", args->args_count);
+               return -EINVAL;
+       }
+
+       if (args->args_count)
+               hws->id = args->args[0];
+       else
+               hws->id = 0;
+
+       return 0;
+}
+
+int hwspinlock_get_by_index(struct udevice *dev, int index,
+                           struct hwspinlock *hws)
+{
+       int ret;
+       struct ofnode_phandle_args args;
+       struct udevice *dev_hws;
+       const struct hwspinlock_ops *ops;
+
+       assert(hws);
+       hws->dev = NULL;
+
+       ret = dev_read_phandle_with_args(dev, "hwlocks", "#hwlock-cells", 1,
+                                        index, &args);
+       if (ret) {
+               dev_dbg(dev, "%s: dev_read_phandle_with_args: err=%d\n",
+                       __func__, ret);
+               return ret;
+       }
+
+       ret = uclass_get_device_by_ofnode(UCLASS_HWSPINLOCK,
+                                         args.node, &dev_hws);
+       if (ret) {
+               dev_dbg(dev,
+                       "%s: uclass_get_device_by_of_offset failed: err=%d\n",
+                       __func__, ret);
+               return ret;
+       }
+
+       hws->dev = dev_hws;
+
+       ops = hwspinlock_dev_ops(dev_hws);
+
+       if (ops->of_xlate)
+               ret = ops->of_xlate(hws, &args);
+       else
+               ret = hwspinlock_of_xlate_default(hws, &args);
+       if (ret)
+               dev_dbg(dev, "of_xlate() failed: %d\n", ret);
+
+       return ret;
+}
+
+int hwspinlock_lock_timeout(struct hwspinlock *hws, unsigned int timeout)
+{
+       const struct hwspinlock_ops *ops;
+       ulong start;
+       int ret;
+
+       assert(hws);
+
+       if (!hws->dev)
+               return -EINVAL;
+
+       ops = hwspinlock_dev_ops(hws->dev);
+       if (!ops->lock)
+               return -ENOSYS;
+
+       start = get_timer(0);
+       do {
+               ret = ops->lock(hws->dev, hws->id);
+               if (!ret)
+                       return ret;
+
+               if (ops->relax)
+                       ops->relax(hws->dev);
+       } while (get_timer(start) < timeout);
+
+       return -ETIMEDOUT;
+}
+
+int hwspinlock_unlock(struct hwspinlock *hws)
+{
+       const struct hwspinlock_ops *ops;
+
+       assert(hws);
+
+       if (!hws->dev)
+               return -EINVAL;
+
+       ops = hwspinlock_dev_ops(hws->dev);
+       if (!ops->unlock)
+               return -ENOSYS;
+
+       return ops->unlock(hws->dev, hws->id);
+}
+
+static int hwspinlock_post_bind(struct udevice *dev)
+{
+#if defined(CONFIG_NEEDS_MANUAL_RELOC)
+       struct hwspinlock_ops *ops = device_get_ops(dev);
+       static int reloc_done;
+
+       if (!reloc_done) {
+               if (ops->lock)
+                       ops->lock += gd->reloc_off;
+               if (ops->unlock)
+                       ops->unlock += gd->reloc_off;
+               if (ops->relax)
+                       ops->relax += gd->reloc_off;
+
+               reloc_done++;
+       }
+#endif
+       return 0;
+}
+
+UCLASS_DRIVER(hwspinlock) = {
+       .id             = UCLASS_HWSPINLOCK,
+       .name           = "hwspinlock",
+       .post_bind      = hwspinlock_post_bind,
+};
diff --git a/drivers/hwspinlock/sandbox_hwspinlock.c b/drivers/hwspinlock/sandbox_hwspinlock.c
new file mode 100644 (file)
index 0000000..be920f5
--- /dev/null
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <hwspinlock.h>
+#include <asm/state.h>
+
+static int sandbox_lock(struct udevice *dev, int index)
+{
+       struct sandbox_state *state = state_get_current();
+
+       if (index != 0)
+               return -1;
+
+       if (state->hwspinlock)
+               return -1;
+
+       state->hwspinlock = true;
+
+       return 0;
+}
+
+static int sandbox_unlock(struct udevice *dev, int index)
+{
+       struct sandbox_state *state = state_get_current();
+
+       if (index != 0)
+               return -1;
+
+       if (!state->hwspinlock)
+               return -1;
+
+       state->hwspinlock = false;
+
+       return 0;
+}
+
+static const struct hwspinlock_ops sandbox_hwspinlock_ops = {
+       .lock = sandbox_lock,
+       .unlock = sandbox_unlock,
+};
+
+static const struct udevice_id sandbox_hwspinlock_ids[] = {
+       { .compatible = "sandbox,hwspinlock" },
+       {}
+};
+
+U_BOOT_DRIVER(hwspinlock_sandbox) = {
+       .name = "hwspinlock_sandbox",
+       .id = UCLASS_HWSPINLOCK,
+       .of_match = sandbox_hwspinlock_ids,
+       .ops = &sandbox_hwspinlock_ops,
+};
index 62d9e2f404aad3cb0fc49649f8a40b04a61949b4..037af0460c6d2e901fe448df7039255ebd12f2b7 100644 (file)
@@ -42,6 +42,7 @@ enum uclass_id {
        UCLASS_FIRMWARE,        /* Firmware */
        UCLASS_FS_FIRMWARE_LOADER,              /* Generic loader */
        UCLASS_GPIO,            /* Bank of general-purpose I/O pins */
+       UCLASS_HWSPINLOCK,      /* Hardware semaphores */
        UCLASS_I2C,             /* I2C bus */
        UCLASS_I2C_EEPROM,      /* I2C EEPROM device */
        UCLASS_I2C_GENERIC,     /* Generic I2C device */
diff --git a/include/hwspinlock.h b/include/hwspinlock.h
new file mode 100644 (file)
index 0000000..99389c1
--- /dev/null
@@ -0,0 +1,140 @@
+/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */
+/*
+ * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
+ */
+
+#ifndef _HWSPINLOCK_H_
+#define _HWSPINLOCK_H_
+
+/**
+ * Implement a hwspinlock uclass.
+ * Hardware spinlocks are used to perform hardware protection of
+ * critical sections and synchronisation between multiprocessors.
+ */
+
+struct udevice;
+
+/**
+ * struct hwspinlock - A handle to (allowing control of) a single hardware
+ * spinlock.
+ *
+ * @dev: The device which implements the hardware spinlock.
+ * @id: The hardware spinlock ID within the provider.
+ */
+struct hwspinlock {
+       struct udevice *dev;
+       unsigned long id;
+};
+
+#if CONFIG_IS_ENABLED(DM_HWSPINLOCK)
+
+/**
+ * hwspinlock_get_by_index - Get a hardware spinlock by integer index
+ *
+ * This looks up and request a hardware spinlock. The index is relative to the
+ * client device; each device is assumed to have n hardware spinlock associated
+ * with it somehow, and this function finds and requests one of them.
+ *
+ * @dev:       The client device.
+ * @index:     The index of the hardware spinlock to request, within the
+ *             client's list of hardware spinlock.
+ * @hws:       A pointer to a hardware spinlock struct to initialize.
+ * @return 0 if OK, or a negative error code.
+ */
+int hwspinlock_get_by_index(struct udevice *dev,
+                           int index, struct hwspinlock *hws);
+
+/**
+ * Lock the hardware spinlock
+ *
+ * @hws:       A hardware spinlock struct that previously requested by
+ *             hwspinlock_get_by_index
+ * @timeout:   Timeout value in msecs
+ * @return: 0 if OK, -ETIMEDOUT if timeout, -ve on other errors
+ */
+int hwspinlock_lock_timeout(struct hwspinlock *hws, unsigned int timeout);
+
+/**
+ * Unlock the hardware spinlock
+ *
+ * @hws:       A hardware spinlock struct that previously requested by
+ *             hwspinlock_get_by_index
+ * @return: 0 if OK, -ve on error
+ */
+int hwspinlock_unlock(struct hwspinlock *hws);
+
+#else
+
+static inline int hwspinlock_get_by_index(struct udevice *dev,
+                                         int index,
+                                         struct hwspinlock *hws)
+{
+       return -ENOSYS;
+}
+
+static inline int hwspinlock_lock_timeout(struct hwspinlock *hws,
+                                         int timeout)
+{
+       return -ENOSYS;
+}
+
+static inline int hwspinlock_unlock(struct hwspinlock *hws)
+{
+       return -ENOSYS;
+}
+
+#endif /* CONFIG_DM_HWSPINLOCK */
+
+struct ofnode_phandle_args;
+
+/**
+ * struct hwspinlock_ops - Driver model hwspinlock operations
+ *
+ * The uclass interface is implemented by all hwspinlock devices which use
+ * driver model.
+ */
+struct hwspinlock_ops {
+       /**
+        * of_xlate - Translate a client's device-tree (OF) hardware specifier.
+        *
+        * The hardware core calls this function as the first step in
+        * implementing a client's hwspinlock_get_by_*() call.
+        *
+        * @hws:        The hardware spinlock struct to hold the translation
+        *                      result.
+        * @args:       The hardware spinlock specifier values from device tree.
+        * @return 0 if OK, or a negative error code.
+        */
+       int (*of_xlate)(struct hwspinlock *hws,
+                       struct ofnode_phandle_args *args);
+
+       /**
+        * Lock the hardware spinlock
+        *
+        * @dev:        hwspinlock Device
+        * @index:      index of the lock to be used
+        * @return 0 if OK, -ve on error
+        */
+       int (*lock)(struct udevice *dev, int index);
+
+       /**
+        * Unlock the hardware spinlock
+        *
+        * @dev:        hwspinlock Device
+        * @index:      index of the lock to be unlocked
+        * @return 0 if OK, -ve on error
+        */
+       int (*unlock)(struct udevice *dev, int index);
+
+       /**
+        * Relax - optional
+        *       Platform-specific relax method, called by hwspinlock core
+        *       while spinning on a lock, between two successive call to
+        *       lock
+        *
+        * @dev:        hwspinlock Device
+        */
+       void (*relax)(struct udevice *dev);
+};
+
+#endif /* _HWSPINLOCK_H_ */
index 213e0fda946a0d521d9cdfed3ae1d6cabded6404..7355fe18e26f554802fe24ad12182e917b996ae0 100644 (file)
@@ -19,6 +19,7 @@ obj-$(CONFIG_CLK) += clk.o
 obj-$(CONFIG_DM_ETH) += eth.o
 obj-$(CONFIG_FIRMWARE) += firmware.o
 obj-$(CONFIG_DM_GPIO) += gpio.o
+obj-$(CONFIG_DM_HWSPINLOCK) += hwspinlock.o
 obj-$(CONFIG_DM_I2C) += i2c.o
 obj-$(CONFIG_LED) += led.o
 obj-$(CONFIG_DM_MAILBOX) += mailbox.o
diff --git a/test/dm/hwspinlock.c b/test/dm/hwspinlock.c
new file mode 100644 (file)
index 0000000..09ec38b
--- /dev/null
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <hwspinlock.h>
+#include <asm/state.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/ut.h>
+
+/* Test that hwspinlock driver functions are called */
+static int dm_test_hwspinlock_base(struct unit_test_state *uts)
+{
+       struct sandbox_state *state = state_get_current();
+       struct hwspinlock hws;
+
+       ut_assertok(uclass_get_device(UCLASS_HWSPINLOCK, 0, &hws.dev));
+       ut_assertnonnull(hws.dev);
+       ut_asserteq(false, state->hwspinlock);
+
+       hws.id = 0;
+       ut_assertok(hwspinlock_lock_timeout(&hws, 1));
+       ut_asserteq(true, state->hwspinlock);
+
+       ut_assertok(hwspinlock_unlock(&hws));
+       ut_asserteq(false, state->hwspinlock);
+
+       ut_assertok(hwspinlock_lock_timeout(&hws, 1));
+       ut_assertok(!hwspinlock_lock_timeout(&hws, 1));
+
+       ut_assertok(hwspinlock_unlock(&hws));
+       ut_assertok(!hwspinlock_unlock(&hws));
+
+       return 0;
+}
+
+DM_TEST(dm_test_hwspinlock_base, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);