[U-Boot] [PATCH v2 0/4] Add Hardware Spinlock class

version 2: - use -ETIMEDOUT and -ENOSYS for errors cases - do not test if ops is valid - remove useless include - add a private structure to store base address - be more verbose in configuration flag description and commit message - log the error after hwspinlock_get_by_index()
This series add a news class to support hardware spinlock. Hardware spinlock could be used to protect critical sections of code between multi-processors.
The proposed API remain simple with only 3 functions to be called client: - hwspinlock_get_by_index: get a hardware spinlock by integer index from device-tree node - hwspinlock_lock_timeout: lock the hardware spinlock - hwspinlock_unlock: unlock the hardware spinlock
Driver API offert 4 operations: - of_xlate: translate a client's device-tree (OF) hardware specifier - lock: lock the hardware spinlock - unlock: unlock the hardware spinlock - relax: wait time between two calls to lock operations
Benjamin Gaignard (4): dm: Add Hardware Spinlock class clk: stm32: add hardware spinlock clock hwspinlock: add stm32 hardware spinlock support pinctrl: stm32: make pinctrl use hwspinlock
arch/arm/dts/stm32mp157c-ed1.dts | 8 ++ arch/arm/dts/stm32mp157c.dtsi | 9 ++ arch/sandbox/dts/test.dts | 4 + arch/sandbox/include/asm/state.h | 1 + configs/sandbox_defconfig | 2 + configs/stm32mp15_basic_defconfig | 2 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/clk/clk_stm32mp1.c | 3 + drivers/hwspinlock/Kconfig | 24 ++++++ drivers/hwspinlock/Makefile | 7 ++ drivers/hwspinlock/hwspinlock-uclass.c | 143 ++++++++++++++++++++++++++++++++ drivers/hwspinlock/sandbox_hwspinlock.c | 56 +++++++++++++ drivers/hwspinlock/stm32_hwspinlock.c | 92 ++++++++++++++++++++ drivers/pinctrl/pinctrl_stm32.c | 27 ++++++ include/dm/uclass-id.h | 1 + include/hwspinlock.h | 140 +++++++++++++++++++++++++++++++ test/dm/Makefile | 1 + test/dm/hwspinlock.c | 40 +++++++++ 19 files changed, 563 insertions(+) create mode 100644 drivers/hwspinlock/Kconfig create mode 100644 drivers/hwspinlock/Makefile create mode 100644 drivers/hwspinlock/hwspinlock-uclass.c create mode 100644 drivers/hwspinlock/sandbox_hwspinlock.c create mode 100644 drivers/hwspinlock/stm32_hwspinlock.c create mode 100644 include/hwspinlock.h create mode 100644 test/dm/hwspinlock.c

From: Benjamin Gaignard benjamin.gaignard@linaro.org
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 --- version 2: - use -ETIMEDOUT and -ENOSYS for errors cases - do not test if ops is valid
arch/sandbox/dts/test.dts | 4 + arch/sandbox/include/asm/state.h | 1 + configs/sandbox_defconfig | 2 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/hwspinlock/Kconfig | 16 ++++ drivers/hwspinlock/Makefile | 6 ++ drivers/hwspinlock/hwspinlock-uclass.c | 143 ++++++++++++++++++++++++++++++++ drivers/hwspinlock/sandbox_hwspinlock.c | 56 +++++++++++++ include/dm/uclass-id.h | 1 + include/hwspinlock.h | 140 +++++++++++++++++++++++++++++++ test/dm/Makefile | 1 + test/dm/hwspinlock.c | 40 +++++++++ 13 files changed, 413 insertions(+) create mode 100644 drivers/hwspinlock/Kconfig create mode 100644 drivers/hwspinlock/Makefile create mode 100644 drivers/hwspinlock/hwspinlock-uclass.c create mode 100644 drivers/hwspinlock/sandbox_hwspinlock.c create mode 100644 include/hwspinlock.h create mode 100644 test/dm/hwspinlock.c
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 57e0dd7663..50cd2f89e0 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -712,6 +712,10 @@ sandbox_tee { compatible = "sandbox,tee"; }; + + hwspinlock@0 { + compatible = "sandbox,hwspinlock"; + }; };
#include "sandbox_pmic.dtsi" diff --git a/arch/sandbox/include/asm/state.h b/arch/sandbox/include/asm/state.h index dcb6d5f568..a5d7c6aaf3 100644 --- a/arch/sandbox/include/asm/state.h +++ b/arch/sandbox/include/asm/state.h @@ -99,6 +99,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*/ diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 2ce336fc81..36b67be2df 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -97,6 +97,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 diff --git a/drivers/Kconfig b/drivers/Kconfig index 927a2b87f6..7e6ca7cd3e 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -40,6 +40,8 @@ source "drivers/fpga/Kconfig"
source "drivers/gpio/Kconfig"
+source "drivers/hwspinlock/Kconfig" + source "drivers/i2c/Kconfig"
source "drivers/input/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index fb38b67541..0ef56fb416 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -111,4 +111,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 index 0000000000..de367fd2a9 --- /dev/null +++ b/drivers/hwspinlock/Kconfig @@ -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 index 0000000000..2704d6814f --- /dev/null +++ b/drivers/hwspinlock/Makefile @@ -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 index 0000000000..353bd77154 --- /dev/null +++ b/drivers/hwspinlock/hwspinlock-uclass.c @@ -0,0 +1,143 @@ +// 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; + + for (start = get_timer(0); get_timer(start) < timeout;) { + ret = ops->lock(hws->dev, hws->id); + if (!ret) + return ret; + + if (ops->relax) + ops->relax(hws->dev); + } + + 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 index 0000000000..be920f5f99 --- /dev/null +++ b/drivers/hwspinlock/sandbox_hwspinlock.c @@ -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, +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 269a2c6e72..6193017432 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -40,6 +40,7 @@ enum uclass_id { UCLASS_ETH, /* Ethernet device */ UCLASS_FS_FIRMWARE_LOADER, /* Generic loader */ UCLASS_GPIO, /* Bank of general-purpose I/O pins */ + UCLASS_HWSPINLOCK, /* Hardware semaphores */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_I2C, /* I2C bus */ UCLASS_I2C_EEPROM, /* I2C EEPROM device */ diff --git a/include/hwspinlock.h b/include/hwspinlock.h new file mode 100644 index 0000000000..99389c13c2 --- /dev/null +++ b/include/hwspinlock.h @@ -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_ */ diff --git a/test/dm/Makefile b/test/dm/Makefile index b490cf2862..9b3b0bf202 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -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 index 0000000000..09ec38b4f3 --- /dev/null +++ b/test/dm/hwspinlock.c @@ -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);

On 14 November 2018 at 01:01, Benjamin Gaignard benjamin.gaignard@linaro.org wrote:
From: Benjamin Gaignard benjamin.gaignard@linaro.org
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
version 2:
- use -ETIMEDOUT and -ENOSYS for errors cases
- do not test if ops is valid
arch/sandbox/dts/test.dts | 4 + arch/sandbox/include/asm/state.h | 1 + configs/sandbox_defconfig | 2 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/hwspinlock/Kconfig | 16 ++++ drivers/hwspinlock/Makefile | 6 ++ drivers/hwspinlock/hwspinlock-uclass.c | 143 ++++++++++++++++++++++++++++++++ drivers/hwspinlock/sandbox_hwspinlock.c | 56 +++++++++++++ include/dm/uclass-id.h | 1 + include/hwspinlock.h | 140 +++++++++++++++++++++++++++++++ test/dm/Makefile | 1 + test/dm/hwspinlock.c | 40 +++++++++ 13 files changed, 413 insertions(+) create mode 100644 drivers/hwspinlock/Kconfig create mode 100644 drivers/hwspinlock/Makefile create mode 100644 drivers/hwspinlock/hwspinlock-uclass.c create mode 100644 drivers/hwspinlock/sandbox_hwspinlock.c create mode 100644 include/hwspinlock.h create mode 100644 test/dm/hwspinlock.c
Reviewed-by: Simon Glass sjg@chromium.org

Hi Benjamin
On 11/14/18 10:01 AM, Benjamin Gaignard wrote:
From: Benjamin Gaignard benjamin.gaignard@linaro.org
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
version 2:
- use -ETIMEDOUT and -ENOSYS for errors cases
- do not test if ops is valid
arch/sandbox/dts/test.dts | 4 + arch/sandbox/include/asm/state.h | 1 + configs/sandbox_defconfig | 2 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/hwspinlock/Kconfig | 16 ++++ drivers/hwspinlock/Makefile | 6 ++ drivers/hwspinlock/hwspinlock-uclass.c | 143 ++++++++++++++++++++++++++++++++ drivers/hwspinlock/sandbox_hwspinlock.c | 56 +++++++++++++ include/dm/uclass-id.h | 1 + include/hwspinlock.h | 140 +++++++++++++++++++++++++++++++ test/dm/Makefile | 1 + test/dm/hwspinlock.c | 40 +++++++++ 13 files changed, 413 insertions(+) create mode 100644 drivers/hwspinlock/Kconfig create mode 100644 drivers/hwspinlock/Makefile create mode 100644 drivers/hwspinlock/hwspinlock-uclass.c create mode 100644 drivers/hwspinlock/sandbox_hwspinlock.c create mode 100644 include/hwspinlock.h create mode 100644 test/dm/hwspinlock.c
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 57e0dd7663..50cd2f89e0 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -712,6 +712,10 @@ sandbox_tee { compatible = "sandbox,tee"; };
- hwspinlock@0 {
compatible = "sandbox,hwspinlock";
- };
};
#include "sandbox_pmic.dtsi" diff --git a/arch/sandbox/include/asm/state.h b/arch/sandbox/include/asm/state.h index dcb6d5f568..a5d7c6aaf3 100644 --- a/arch/sandbox/include/asm/state.h +++ b/arch/sandbox/include/asm/state.h @@ -99,6 +99,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*/ diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 2ce336fc81..36b67be2df 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -97,6 +97,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 diff --git a/drivers/Kconfig b/drivers/Kconfig index 927a2b87f6..7e6ca7cd3e 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -40,6 +40,8 @@ source "drivers/fpga/Kconfig"
source "drivers/gpio/Kconfig"
+source "drivers/hwspinlock/Kconfig"
source "drivers/i2c/Kconfig"
source "drivers/input/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index fb38b67541..0ef56fb416 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -111,4 +111,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 index 0000000000..de367fd2a9 --- /dev/null +++ b/drivers/hwspinlock/Kconfig @@ -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 index 0000000000..2704d6814f --- /dev/null +++ b/drivers/hwspinlock/Makefile @@ -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 index 0000000000..353bd77154 --- /dev/null +++ b/drivers/hwspinlock/hwspinlock-uclass.c @@ -0,0 +1,143 @@ +// 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;
- for (start = get_timer(0); get_timer(start) < timeout;) {
ret = ops->lock(hws->dev, hws->id);
if (!ret)
return ret;
if (ops->relax)
ops->relax(hws->dev);
- }
- 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 index 0000000000..be920f5f99 --- /dev/null +++ b/drivers/hwspinlock/sandbox_hwspinlock.c @@ -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,
+}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 269a2c6e72..6193017432 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -40,6 +40,7 @@ enum uclass_id { UCLASS_ETH, /* Ethernet device */ UCLASS_FS_FIRMWARE_LOADER, /* Generic loader */ UCLASS_GPIO, /* Bank of general-purpose I/O pins */
- UCLASS_HWSPINLOCK, /* Hardware semaphores */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_I2C, /* I2C bus */ UCLASS_I2C_EEPROM, /* I2C EEPROM device */
diff --git a/include/hwspinlock.h b/include/hwspinlock.h new file mode 100644 index 0000000000..99389c13c2 --- /dev/null +++ b/include/hwspinlock.h @@ -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_ */ diff --git a/test/dm/Makefile b/test/dm/Makefile index b490cf2862..9b3b0bf202 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -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 index 0000000000..09ec38b4f3 --- /dev/null +++ b/test/dm/hwspinlock.c @@ -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);
Reviewed-by: Patrice Chotard patrice.chotard@st.com
Thanks

From: Benjamin Gaignard benjamin.gaignard@linaro.org
Add hardware spinlock in the list of the clocks.
Signed-off-by: Benjamin Gaignard benjamin.gaignard@linaro.org --- drivers/clk/clk_stm32mp1.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/drivers/clk/clk_stm32mp1.c b/drivers/clk/clk_stm32mp1.c index 6a8c7b754f..b7c5d34fe0 100644 --- a/drivers/clk/clk_stm32mp1.c +++ b/drivers/clk/clk_stm32mp1.c @@ -104,6 +104,7 @@ #define RCC_MP_APB2ENSETR 0XA08 #define RCC_MP_APB3ENSETR 0xA10 #define RCC_MP_AHB2ENSETR 0xA18 +#define RCC_MP_AHB3ENSETR 0xA20 #define RCC_MP_AHB4ENSETR 0xA28
/* used for most of SELR register */ @@ -534,6 +535,8 @@ static const struct stm32mp1_clk_gate stm32mp1_clk_gate[] = { STM32MP1_CLK_SET_CLR(RCC_MP_AHB2ENSETR, 8, USBO_K, _USBO_SEL), STM32MP1_CLK_SET_CLR(RCC_MP_AHB2ENSETR, 16, SDMMC3_K, _SDMMC3_SEL),
+ STM32MP1_CLK_SET_CLR(RCC_MP_AHB3ENSETR, 11, HSEM, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 0, GPIOA, _UNKNOWN_SEL), STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 1, GPIOB, _UNKNOWN_SEL), STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 2, GPIOC, _UNKNOWN_SEL),

On 14 November 2018 at 01:01, Benjamin Gaignard benjamin.gaignard@linaro.org wrote:
From: Benjamin Gaignard benjamin.gaignard@linaro.org
Add hardware spinlock in the list of the clocks.
Signed-off-by: Benjamin Gaignard benjamin.gaignard@linaro.org
drivers/clk/clk_stm32mp1.c | 3 +++ 1 file changed, 3 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org

Hi Benjamin
On 11/14/18 10:01 AM, Benjamin Gaignard wrote:
From: Benjamin Gaignard benjamin.gaignard@linaro.org
Add hardware spinlock in the list of the clocks.
Signed-off-by: Benjamin Gaignard benjamin.gaignard@linaro.org
drivers/clk/clk_stm32mp1.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/drivers/clk/clk_stm32mp1.c b/drivers/clk/clk_stm32mp1.c index 6a8c7b754f..b7c5d34fe0 100644 --- a/drivers/clk/clk_stm32mp1.c +++ b/drivers/clk/clk_stm32mp1.c @@ -104,6 +104,7 @@ #define RCC_MP_APB2ENSETR 0XA08 #define RCC_MP_APB3ENSETR 0xA10 #define RCC_MP_AHB2ENSETR 0xA18 +#define RCC_MP_AHB3ENSETR 0xA20 #define RCC_MP_AHB4ENSETR 0xA28
/* used for most of SELR register */ @@ -534,6 +535,8 @@ static const struct stm32mp1_clk_gate stm32mp1_clk_gate[] = { STM32MP1_CLK_SET_CLR(RCC_MP_AHB2ENSETR, 8, USBO_K, _USBO_SEL), STM32MP1_CLK_SET_CLR(RCC_MP_AHB2ENSETR, 16, SDMMC3_K, _SDMMC3_SEL),
- STM32MP1_CLK_SET_CLR(RCC_MP_AHB3ENSETR, 11, HSEM, _UNKNOWN_SEL),
- STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 0, GPIOA, _UNKNOWN_SEL), STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 1, GPIOB, _UNKNOWN_SEL), STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 2, GPIOC, _UNKNOWN_SEL),
Reviewed-by: Patrice Chotard patrice.chotard@st.com
Thanks

Implement hardware spinlock support for STM32MP1.
Signed-off-by: Benjamin Gaignard benjamin.gaignard@st.com --- version 2: - remove useless include - add a private structure to store base address - be more verbose in configuration flag description
arch/arm/dts/stm32mp157c-ed1.dts | 4 ++ arch/arm/dts/stm32mp157c.dtsi | 9 ++++ configs/stm32mp15_basic_defconfig | 2 + drivers/hwspinlock/Kconfig | 8 +++ drivers/hwspinlock/Makefile | 1 + drivers/hwspinlock/stm32_hwspinlock.c | 92 +++++++++++++++++++++++++++++++++++ 6 files changed, 116 insertions(+) create mode 100644 drivers/hwspinlock/stm32_hwspinlock.c
diff --git a/arch/arm/dts/stm32mp157c-ed1.dts b/arch/arm/dts/stm32mp157c-ed1.dts index f8b7701167..fc277dd7d2 100644 --- a/arch/arm/dts/stm32mp157c-ed1.dts +++ b/arch/arm/dts/stm32mp157c-ed1.dts @@ -365,6 +365,10 @@ usb33d-supply = <&usb33>; };
+&hwspinlock { + status = "okay"; +}; + &usbphyc_port0 { phy-supply = <&vdd_usb>; vdda1v1-supply = <®11>; diff --git a/arch/arm/dts/stm32mp157c.dtsi b/arch/arm/dts/stm32mp157c.dtsi index 33c5981869..37cadfa30c 100644 --- a/arch/arm/dts/stm32mp157c.dtsi +++ b/arch/arm/dts/stm32mp157c.dtsi @@ -690,6 +690,15 @@ status = "disabled"; };
+ hwspinlock: hwspinlock@4c000000 { + compatible = "st,stm32-hwspinlock"; + #hwlock-cells = <1>; + reg = <0x4c000000 0x400>; + clocks = <&rcc HSEM>; + clock-names = "hwspinlock"; + status = "disabled"; + }; + rcc: rcc@50000000 { compatible = "st,stm32mp1-rcc", "syscon"; reg = <0x50000000 0x1000>; diff --git a/configs/stm32mp15_basic_defconfig b/configs/stm32mp15_basic_defconfig index 3bf7538089..c8409fd04e 100644 --- a/configs/stm32mp15_basic_defconfig +++ b/configs/stm32mp15_basic_defconfig @@ -32,6 +32,8 @@ CONFIG_CMD_EXT4_WRITE=y # CONFIG_SPL_DOS_PARTITION is not set CONFIG_DEFAULT_DEVICE_TREE="stm32mp157c-ev1" CONFIG_STM32_ADC=y +CONFIG_DM_HWSPINLOCK=y +CONFIG_HWSPINLOCK_STM32=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_STM32F7=y CONFIG_LED=y diff --git a/drivers/hwspinlock/Kconfig b/drivers/hwspinlock/Kconfig index de367fd2a9..96d4f5d6ca 100644 --- a/drivers/hwspinlock/Kconfig +++ b/drivers/hwspinlock/Kconfig @@ -13,4 +13,12 @@ config HWSPINLOCK_SANDBOX can be probed and support all the methods of HWSPINLOCK, but does not really do anything.
+config HWSPINLOCK_STM32 + bool "Enable Hardware Spinlock support for STM32" + depends on ARCH_STM32MP && DM_HWSPINLOCK + help + Enable hardware spinlock support in STM32MP. Hardware spinlocks are + hardware mutex which provide a synchronisation mechanism for the + various processors on the SoC. + endmenu diff --git a/drivers/hwspinlock/Makefile b/drivers/hwspinlock/Makefile index 2704d6814f..289b12a256 100644 --- a/drivers/hwspinlock/Makefile +++ b/drivers/hwspinlock/Makefile @@ -4,3 +4,4 @@
obj-$(CONFIG_DM_HWSPINLOCK) += hwspinlock-uclass.o obj-$(CONFIG_HWSPINLOCK_SANDBOX) += sandbox_hwspinlock.o +obj-$(CONFIG_HWSPINLOCK_STM32) += stm32_hwspinlock.o diff --git a/drivers/hwspinlock/stm32_hwspinlock.c b/drivers/hwspinlock/stm32_hwspinlock.c new file mode 100644 index 0000000000..a32bde4906 --- /dev/null +++ b/drivers/hwspinlock/stm32_hwspinlock.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <hwspinlock.h> +#include <asm/io.h> + +#define STM32_MUTEX_COREID BIT(8) +#define STM32_MUTEX_LOCK_BIT BIT(31) +#define STM32_MUTEX_NUM_LOCKS 32 + +struct stm32mp1_hws_priv { + fdt_addr_t base; +}; + +static int stm32mp1_lock(struct udevice *dev, int index) +{ + struct stm32mp1_hws_priv *priv = dev_get_priv(dev); + u32 status; + + if (index >= STM32_MUTEX_NUM_LOCKS) + return -EINVAL; + + status = readl(priv->base + index * sizeof(u32)); + if (status == (STM32_MUTEX_LOCK_BIT | STM32_MUTEX_COREID)) + return -EBUSY; + + writel(STM32_MUTEX_LOCK_BIT | STM32_MUTEX_COREID, + priv->base + index * sizeof(u32)); + + status = readl(priv->base + index * sizeof(u32)); + if (status != (STM32_MUTEX_LOCK_BIT | STM32_MUTEX_COREID)) + return -EINVAL; + + return 0; +} + +static int stm32mp1_unlock(struct udevice *dev, int index) +{ + struct stm32mp1_hws_priv *priv = dev_get_priv(dev); + + if (index >= STM32_MUTEX_NUM_LOCKS) + return -EINVAL; + + writel(STM32_MUTEX_COREID, priv->base + index * sizeof(u32)); + + return 0; +} + +static int stm32mp1_hwspinlock_probe(struct udevice *dev) +{ + struct stm32mp1_hws_priv *priv = dev_get_priv(dev); + struct clk clk; + int ret; + + priv->base = dev_read_addr(dev); + if (priv->base == FDT_ADDR_T_NONE) + return -EINVAL; + + ret = clk_get_by_index(dev, 0, &clk); + if (ret) + return ret; + + ret = clk_enable(&clk); + if (ret) + clk_free(&clk); + + return ret; +} + +static const struct hwspinlock_ops stm32mp1_hwspinlock_ops = { + .lock = stm32mp1_lock, + .unlock = stm32mp1_unlock, +}; + +static const struct udevice_id stm32mp1_hwspinlock_ids[] = { + { .compatible = "st,stm32-hwspinlock" }, + {} +}; + +U_BOOT_DRIVER(hwspinlock_stm32mp1) = { + .name = "hwspinlock_stm32mp1", + .id = UCLASS_HWSPINLOCK, + .of_match = stm32mp1_hwspinlock_ids, + .ops = &stm32mp1_hwspinlock_ops, + .probe = stm32mp1_hwspinlock_probe, + .priv_auto_alloc_size = sizeof(struct stm32mp1_hws_priv), +};

On 14 November 2018 at 01:01, Benjamin Gaignard benjamin.gaignard@linaro.org wrote:
Implement hardware spinlock support for STM32MP1.
Signed-off-by: Benjamin Gaignard benjamin.gaignard@st.com
version 2:
- remove useless include
- add a private structure to store base address
- be more verbose in configuration flag description
arch/arm/dts/stm32mp157c-ed1.dts | 4 ++ arch/arm/dts/stm32mp157c.dtsi | 9 ++++ configs/stm32mp15_basic_defconfig | 2 + drivers/hwspinlock/Kconfig | 8 +++ drivers/hwspinlock/Makefile | 1 + drivers/hwspinlock/stm32_hwspinlock.c | 92 +++++++++++++++++++++++++++++++++++ 6 files changed, 116 insertions(+) create mode 100644 drivers/hwspinlock/stm32_hwspinlock.c
Reviewed-by: Simon Glass sjg@chromium.org

Hi Benjamin
On 11/14/18 10:01 AM, Benjamin Gaignard wrote:
Implement hardware spinlock support for STM32MP1.
Signed-off-by: Benjamin Gaignard benjamin.gaignard@st.com
version 2:
- remove useless include
- add a private structure to store base address
- be more verbose in configuration flag description
arch/arm/dts/stm32mp157c-ed1.dts | 4 ++ arch/arm/dts/stm32mp157c.dtsi | 9 ++++ configs/stm32mp15_basic_defconfig | 2 + drivers/hwspinlock/Kconfig | 8 +++ drivers/hwspinlock/Makefile | 1 + drivers/hwspinlock/stm32_hwspinlock.c | 92 +++++++++++++++++++++++++++++++++++ 6 files changed, 116 insertions(+) create mode 100644 drivers/hwspinlock/stm32_hwspinlock.c
diff --git a/arch/arm/dts/stm32mp157c-ed1.dts b/arch/arm/dts/stm32mp157c-ed1.dts index f8b7701167..fc277dd7d2 100644 --- a/arch/arm/dts/stm32mp157c-ed1.dts +++ b/arch/arm/dts/stm32mp157c-ed1.dts @@ -365,6 +365,10 @@ usb33d-supply = <&usb33>; };
+&hwspinlock {
- status = "okay";
+};
&usbphyc_port0 { phy-supply = <&vdd_usb>; vdda1v1-supply = <®11>; diff --git a/arch/arm/dts/stm32mp157c.dtsi b/arch/arm/dts/stm32mp157c.dtsi index 33c5981869..37cadfa30c 100644 --- a/arch/arm/dts/stm32mp157c.dtsi +++ b/arch/arm/dts/stm32mp157c.dtsi @@ -690,6 +690,15 @@ status = "disabled"; };
hwspinlock: hwspinlock@4c000000 {
compatible = "st,stm32-hwspinlock";
#hwlock-cells = <1>;
reg = <0x4c000000 0x400>;
clocks = <&rcc HSEM>;
clock-names = "hwspinlock";
status = "disabled";
};
- rcc: rcc@50000000 { compatible = "st,stm32mp1-rcc", "syscon"; reg = <0x50000000 0x1000>;
diff --git a/configs/stm32mp15_basic_defconfig b/configs/stm32mp15_basic_defconfig index 3bf7538089..c8409fd04e 100644 --- a/configs/stm32mp15_basic_defconfig +++ b/configs/stm32mp15_basic_defconfig @@ -32,6 +32,8 @@ CONFIG_CMD_EXT4_WRITE=y # CONFIG_SPL_DOS_PARTITION is not set CONFIG_DEFAULT_DEVICE_TREE="stm32mp157c-ev1" CONFIG_STM32_ADC=y +CONFIG_DM_HWSPINLOCK=y +CONFIG_HWSPINLOCK_STM32=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_STM32F7=y CONFIG_LED=y diff --git a/drivers/hwspinlock/Kconfig b/drivers/hwspinlock/Kconfig index de367fd2a9..96d4f5d6ca 100644 --- a/drivers/hwspinlock/Kconfig +++ b/drivers/hwspinlock/Kconfig @@ -13,4 +13,12 @@ config HWSPINLOCK_SANDBOX can be probed and support all the methods of HWSPINLOCK, but does not really do anything.
+config HWSPINLOCK_STM32
- bool "Enable Hardware Spinlock support for STM32"
- depends on ARCH_STM32MP && DM_HWSPINLOCK
- help
Enable hardware spinlock support in STM32MP. Hardware spinlocks are
hardware mutex which provide a synchronisation mechanism for the
various processors on the SoC.
endmenu diff --git a/drivers/hwspinlock/Makefile b/drivers/hwspinlock/Makefile index 2704d6814f..289b12a256 100644 --- a/drivers/hwspinlock/Makefile +++ b/drivers/hwspinlock/Makefile @@ -4,3 +4,4 @@
obj-$(CONFIG_DM_HWSPINLOCK) += hwspinlock-uclass.o obj-$(CONFIG_HWSPINLOCK_SANDBOX) += sandbox_hwspinlock.o +obj-$(CONFIG_HWSPINLOCK_STM32) += stm32_hwspinlock.o diff --git a/drivers/hwspinlock/stm32_hwspinlock.c b/drivers/hwspinlock/stm32_hwspinlock.c new file mode 100644 index 0000000000..a32bde4906 --- /dev/null +++ b/drivers/hwspinlock/stm32_hwspinlock.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/*
- Copyright (C) 2018, STMicroelectronics - All Rights Reserved
- */
+#include <common.h> +#include <clk.h> +#include <dm.h> +#include <hwspinlock.h> +#include <asm/io.h>
+#define STM32_MUTEX_COREID BIT(8) +#define STM32_MUTEX_LOCK_BIT BIT(31) +#define STM32_MUTEX_NUM_LOCKS 32
+struct stm32mp1_hws_priv {
- fdt_addr_t base;
+};
+static int stm32mp1_lock(struct udevice *dev, int index) +{
- struct stm32mp1_hws_priv *priv = dev_get_priv(dev);
- u32 status;
- if (index >= STM32_MUTEX_NUM_LOCKS)
return -EINVAL;
- status = readl(priv->base + index * sizeof(u32));
- if (status == (STM32_MUTEX_LOCK_BIT | STM32_MUTEX_COREID))
return -EBUSY;
- writel(STM32_MUTEX_LOCK_BIT | STM32_MUTEX_COREID,
priv->base + index * sizeof(u32));
- status = readl(priv->base + index * sizeof(u32));
- if (status != (STM32_MUTEX_LOCK_BIT | STM32_MUTEX_COREID))
return -EINVAL;
- return 0;
+}
+static int stm32mp1_unlock(struct udevice *dev, int index) +{
- struct stm32mp1_hws_priv *priv = dev_get_priv(dev);
- if (index >= STM32_MUTEX_NUM_LOCKS)
return -EINVAL;
- writel(STM32_MUTEX_COREID, priv->base + index * sizeof(u32));
- return 0;
+}
+static int stm32mp1_hwspinlock_probe(struct udevice *dev) +{
- struct stm32mp1_hws_priv *priv = dev_get_priv(dev);
- struct clk clk;
- int ret;
- priv->base = dev_read_addr(dev);
- if (priv->base == FDT_ADDR_T_NONE)
return -EINVAL;
- ret = clk_get_by_index(dev, 0, &clk);
- if (ret)
return ret;
- ret = clk_enable(&clk);
- if (ret)
clk_free(&clk);
- return ret;
+}
+static const struct hwspinlock_ops stm32mp1_hwspinlock_ops = {
- .lock = stm32mp1_lock,
- .unlock = stm32mp1_unlock,
+};
+static const struct udevice_id stm32mp1_hwspinlock_ids[] = {
- { .compatible = "st,stm32-hwspinlock" },
- {}
+};
+U_BOOT_DRIVER(hwspinlock_stm32mp1) = {
- .name = "hwspinlock_stm32mp1",
- .id = UCLASS_HWSPINLOCK,
- .of_match = stm32mp1_hwspinlock_ids,
- .ops = &stm32mp1_hwspinlock_ops,
- .probe = stm32mp1_hwspinlock_probe,
- .priv_auto_alloc_size = sizeof(struct stm32mp1_hws_priv),
+};
Reviewed-by: Patrice Chotard patrice.chotard@st.com
Thanks

From: Benjamin Gaignard benjamin.gaignard@linaro.org
Protect configuration registers with a hardware spinlock.
If a hwspinlock is defined in the device-tree node used it to be sure that none of the others processors on the SoC could change the configuration at the same time.
Signed-off-by: Benjamin Gaignard benjamin.gaignard@linaro.org --- version 2: - be more verbose in commit message - log the error after hwspinlock_get_by_index()
arch/arm/dts/stm32mp157c-ed1.dts | 4 ++++ drivers/pinctrl/pinctrl_stm32.c | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+)
diff --git a/arch/arm/dts/stm32mp157c-ed1.dts b/arch/arm/dts/stm32mp157c-ed1.dts index fc277dd7d2..7a9b742d36 100644 --- a/arch/arm/dts/stm32mp157c-ed1.dts +++ b/arch/arm/dts/stm32mp157c-ed1.dts @@ -369,6 +369,10 @@ status = "okay"; };
+&pinctrl { + hwlocks = <&hwspinlock 0>; +}; + &usbphyc_port0 { phy-supply = <&vdd_usb>; vdda1v1-supply = <®11>; diff --git a/drivers/pinctrl/pinctrl_stm32.c b/drivers/pinctrl/pinctrl_stm32.c index 31285cdd57..5b63a2de15 100644 --- a/drivers/pinctrl/pinctrl_stm32.c +++ b/drivers/pinctrl/pinctrl_stm32.c @@ -1,6 +1,7 @@ #include <common.h> #include <dm.h> #include <dm/pinctrl.h> +#include <hwspinlock.h> #include <asm/arch/gpio.h> #include <asm/gpio.h> #include <asm/io.h> @@ -19,12 +20,20 @@ static int stm32_gpio_config(struct gpio_desc *desc, { struct stm32_gpio_priv *priv = dev_get_priv(desc->dev); struct stm32_gpio_regs *regs = priv->regs; + struct hwspinlock *hws = dev_get_priv(desc->dev->parent); u32 index; + int ret;
if (!ctl || ctl->af > 15 || ctl->mode > 3 || ctl->otype > 1 || ctl->pupd > 2 || ctl->speed > 3) return -EINVAL;
+ ret = hwspinlock_lock_timeout(hws, 1); + if (ret == -ETIME) { + dev_err(desc->dev, "HWSpinlock timeout\n"); + return ret; + } + index = (desc->offset & 0x07) * 4; clrsetbits_le32(®s->afr[desc->offset >> 3], AFR_MASK << index, ctl->af << index); @@ -39,6 +48,8 @@ static int stm32_gpio_config(struct gpio_desc *desc, index = desc->offset; clrsetbits_le32(®s->otyper, OTYPE_MSK << index, ctl->otype << index);
+ hwspinlock_unlock(hws); + return 0; }
@@ -176,6 +187,20 @@ static int stm32_pinctrl_set_state_simple(struct udevice *dev, } #endif /* PINCTRL_FULL */
+static int stm32_pinctrl_probe(struct udevice *dev) +{ + struct hwspinlock *hws = dev_get_priv(dev); + int err; + + /* hwspinlock property is optional, just log the error */ + err = hwspinlock_get_by_index(dev, 0, hws); + if (err) + debug("%s: hwspinlock_get_by_index may have failed (%d)\n", + __func__, err); + + return 0; +} + static struct pinctrl_ops stm32_pinctrl_ops = { #if CONFIG_IS_ENABLED(PINCTRL_FULL) .set_state = stm32_pinctrl_set_state, @@ -200,4 +225,6 @@ U_BOOT_DRIVER(pinctrl_stm32) = { .of_match = stm32_pinctrl_ids, .ops = &stm32_pinctrl_ops, .bind = dm_scan_fdt_dev, + .probe = stm32_pinctrl_probe, + .priv_auto_alloc_size = sizeof(struct hwspinlock), };

On 14 November 2018 at 01:01, Benjamin Gaignard benjamin.gaignard@linaro.org wrote:
From: Benjamin Gaignard benjamin.gaignard@linaro.org
Protect configuration registers with a hardware spinlock.
If a hwspinlock is defined in the device-tree node used it to be sure that none of the others processors on the SoC could change the configuration at the same time.
Signed-off-by: Benjamin Gaignard benjamin.gaignard@linaro.org
version 2:
- be more verbose in commit message
- log the error after hwspinlock_get_by_index()
arch/arm/dts/stm32mp157c-ed1.dts | 4 ++++ drivers/pinctrl/pinctrl_stm32.c | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org
with nit below
diff --git a/arch/arm/dts/stm32mp157c-ed1.dts b/arch/arm/dts/stm32mp157c-ed1.dts index fc277dd7d2..7a9b742d36 100644 --- a/arch/arm/dts/stm32mp157c-ed1.dts +++ b/arch/arm/dts/stm32mp157c-ed1.dts @@ -369,6 +369,10 @@ status = "okay"; };
+&pinctrl {
hwlocks = <&hwspinlock 0>;
+};
&usbphyc_port0 { phy-supply = <&vdd_usb>; vdda1v1-supply = <®11>; diff --git a/drivers/pinctrl/pinctrl_stm32.c b/drivers/pinctrl/pinctrl_stm32.c index 31285cdd57..5b63a2de15 100644 --- a/drivers/pinctrl/pinctrl_stm32.c +++ b/drivers/pinctrl/pinctrl_stm32.c @@ -1,6 +1,7 @@ #include <common.h> #include <dm.h> #include <dm/pinctrl.h> +#include <hwspinlock.h> #include <asm/arch/gpio.h> #include <asm/gpio.h> #include <asm/io.h> @@ -19,12 +20,20 @@ static int stm32_gpio_config(struct gpio_desc *desc, { struct stm32_gpio_priv *priv = dev_get_priv(desc->dev); struct stm32_gpio_regs *regs = priv->regs;
struct hwspinlock *hws = dev_get_priv(desc->dev->parent);
dev_get_parent()
u32 index;
int ret; if (!ctl || ctl->af > 15 || ctl->mode > 3 || ctl->otype > 1 || ctl->pupd > 2 || ctl->speed > 3) return -EINVAL;
ret = hwspinlock_lock_timeout(hws, 1);
if (ret == -ETIME) {
dev_err(desc->dev, "HWSpinlock timeout\n");
return ret;
}
index = (desc->offset & 0x07) * 4; clrsetbits_le32(®s->afr[desc->offset >> 3], AFR_MASK << index, ctl->af << index);
@@ -39,6 +48,8 @@ static int stm32_gpio_config(struct gpio_desc *desc, index = desc->offset; clrsetbits_le32(®s->otyper, OTYPE_MSK << index, ctl->otype << index);
hwspinlock_unlock(hws);
return 0;
}
@@ -176,6 +187,20 @@ static int stm32_pinctrl_set_state_simple(struct udevice *dev, } #endif /* PINCTRL_FULL */
+static int stm32_pinctrl_probe(struct udevice *dev) +{
struct hwspinlock *hws = dev_get_priv(dev);
int err;
/* hwspinlock property is optional, just log the error */
err = hwspinlock_get_by_index(dev, 0, hws);
if (err)
debug("%s: hwspinlock_get_by_index may have failed (%d)\n",
__func__, err);
return 0;
+}
static struct pinctrl_ops stm32_pinctrl_ops = { #if CONFIG_IS_ENABLED(PINCTRL_FULL) .set_state = stm32_pinctrl_set_state, @@ -200,4 +225,6 @@ U_BOOT_DRIVER(pinctrl_stm32) = { .of_match = stm32_pinctrl_ids, .ops = &stm32_pinctrl_ops, .bind = dm_scan_fdt_dev,
.probe = stm32_pinctrl_probe,
.priv_auto_alloc_size = sizeof(struct hwspinlock),
};
2.15.0

Hi Benjamin
On 11/14/18 10:01 AM, Benjamin Gaignard wrote:
From: Benjamin Gaignard benjamin.gaignard@linaro.org
Protect configuration registers with a hardware spinlock.
If a hwspinlock is defined in the device-tree node used it to be sure that none of the others processors on the SoC could change the configuration at the same time.
Signed-off-by: Benjamin Gaignard benjamin.gaignard@linaro.org
version 2:
- be more verbose in commit message
- log the error after hwspinlock_get_by_index()
arch/arm/dts/stm32mp157c-ed1.dts | 4 ++++ drivers/pinctrl/pinctrl_stm32.c | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+)
diff --git a/arch/arm/dts/stm32mp157c-ed1.dts b/arch/arm/dts/stm32mp157c-ed1.dts index fc277dd7d2..7a9b742d36 100644 --- a/arch/arm/dts/stm32mp157c-ed1.dts +++ b/arch/arm/dts/stm32mp157c-ed1.dts @@ -369,6 +369,10 @@ status = "okay"; };
+&pinctrl {
- hwlocks = <&hwspinlock 0>;
+};
&usbphyc_port0 { phy-supply = <&vdd_usb>; vdda1v1-supply = <®11>; diff --git a/drivers/pinctrl/pinctrl_stm32.c b/drivers/pinctrl/pinctrl_stm32.c index 31285cdd57..5b63a2de15 100644 --- a/drivers/pinctrl/pinctrl_stm32.c +++ b/drivers/pinctrl/pinctrl_stm32.c @@ -1,6 +1,7 @@ #include <common.h> #include <dm.h> #include <dm/pinctrl.h> +#include <hwspinlock.h> #include <asm/arch/gpio.h> #include <asm/gpio.h> #include <asm/io.h> @@ -19,12 +20,20 @@ static int stm32_gpio_config(struct gpio_desc *desc, { struct stm32_gpio_priv *priv = dev_get_priv(desc->dev); struct stm32_gpio_regs *regs = priv->regs;
struct hwspinlock *hws = dev_get_priv(desc->dev->parent); u32 index;
int ret;
if (!ctl || ctl->af > 15 || ctl->mode > 3 || ctl->otype > 1 || ctl->pupd > 2 || ctl->speed > 3) return -EINVAL;
ret = hwspinlock_lock_timeout(hws, 1);
if (ret == -ETIME) {
dev_err(desc->dev, "HWSpinlock timeout\n");
return ret;
}
index = (desc->offset & 0x07) * 4; clrsetbits_le32(®s->afr[desc->offset >> 3], AFR_MASK << index, ctl->af << index);
@@ -39,6 +48,8 @@ static int stm32_gpio_config(struct gpio_desc *desc, index = desc->offset; clrsetbits_le32(®s->otyper, OTYPE_MSK << index, ctl->otype << index);
- hwspinlock_unlock(hws);
- return 0;
}
@@ -176,6 +187,20 @@ static int stm32_pinctrl_set_state_simple(struct udevice *dev, } #endif /* PINCTRL_FULL */
+static int stm32_pinctrl_probe(struct udevice *dev) +{
- struct hwspinlock *hws = dev_get_priv(dev);
- int err;
- /* hwspinlock property is optional, just log the error */
- err = hwspinlock_get_by_index(dev, 0, hws);
- if (err)
debug("%s: hwspinlock_get_by_index may have failed (%d)\n",
__func__, err);
- return 0;
+}
static struct pinctrl_ops stm32_pinctrl_ops = { #if CONFIG_IS_ENABLED(PINCTRL_FULL) .set_state = stm32_pinctrl_set_state, @@ -200,4 +225,6 @@ U_BOOT_DRIVER(pinctrl_stm32) = { .of_match = stm32_pinctrl_ids, .ops = &stm32_pinctrl_ops, .bind = dm_scan_fdt_dev,
- .probe = stm32_pinctrl_probe,
- .priv_auto_alloc_size = sizeof(struct hwspinlock),
};
Reviewed-by: Patrice Chotard patrice.chotard@st.com
Thanks
participants (3)
-
Benjamin Gaignard
-
Patrice CHOTARD
-
Simon Glass