[U-Boot] [Uboot-stm32] [PATCH v2 1/4] dm: Add Hardware Spinlock class
Patrice CHOTARD
patrice.chotard at st.com
Fri Nov 16 08:20:39 UTC 2018
Hi Benjamin
On 11/14/18 10:01 AM, Benjamin Gaignard wrote:
> From: Benjamin Gaignard <benjamin.gaignard at 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 at 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 at 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 at st.com>
Thanks
More information about the U-Boot
mailing list