diff mbox series

[v2,1/4] dm: Add Hardware Spinlock class

Message ID 20181114090114.7727-2-benjamin.gaignard@st.com
State Superseded
Headers show
Series Add Hardware Spinlock class | expand

Commit Message

Benjamin Gaignard Nov. 14, 2018, 9:01 a.m. UTC
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

Comments

Simon Glass Nov. 16, 2018, 12:07 a.m. UTC | #1
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>
Patrice CHOTARD Nov. 16, 2018, 8:20 a.m. UTC | #2
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
diff mbox series

Patch

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);