Browse Source

Merge branch '2020-09-30-add-new-apis' into next

- SCMI firmware support
- regmap, GPIO, reset API enhancements
Tom Rini 3 years ago
parent
commit
097bbf1ba9
42 changed files with 3268 additions and 33 deletions
  1. 60 0
      arch/sandbox/dts/test.dts
  2. 3 0
      arch/sandbox/include/asm/reset.h
  3. 99 0
      arch/sandbox/include/asm/scmi_test.h
  4. 4 0
      configs/sandbox_defconfig
  5. 197 0
      doc/device-tree-bindings/arm/arm,scmi.txt
  6. 8 0
      drivers/clk/Kconfig
  7. 1 0
      drivers/clk/Makefile
  8. 99 0
      drivers/clk/clk_scmi.c
  9. 160 3
      drivers/core/regmap.c
  10. 2 0
      drivers/firmware/Kconfig
  11. 1 0
      drivers/firmware/Makefile
  12. 19 0
      drivers/firmware/scmi/Kconfig
  13. 5 0
      drivers/firmware/scmi/Makefile
  14. 102 0
      drivers/firmware/scmi/mailbox_agent.c
  15. 410 0
      drivers/firmware/scmi/sandbox-scmi_agent.c
  16. 113 0
      drivers/firmware/scmi/sandbox-scmi_devices.c
  17. 119 0
      drivers/firmware/scmi/scmi_agent-uclass.c
  18. 89 0
      drivers/firmware/scmi/smccc_agent.c
  19. 139 0
      drivers/firmware/scmi/smt.c
  20. 86 0
      drivers/firmware/scmi/smt.h
  21. 71 0
      drivers/gpio/gpio-uclass.c
  22. 8 0
      drivers/reset/Kconfig
  23. 1 0
      drivers/reset/Makefile
  24. 81 0
      drivers/reset/reset-scmi.c
  25. 114 4
      drivers/reset/reset-uclass.c
  26. 45 6
      drivers/reset/sandbox-reset-test.c
  27. 19 0
      drivers/reset/sandbox-reset.c
  28. 47 0
      include/asm-generic/gpio.h
  29. 1 0
      include/dm/uclass-id.h
  30. 191 14
      include/regmap.h
  31. 134 1
      include/reset.h
  32. 24 0
      include/scmi_agent-uclass.h
  33. 68 0
      include/scmi_agent.h
  34. 179 0
      include/scmi_protocols.h
  35. 1 0
      test/dm/Makefile
  36. 1 1
      test/dm/bus.c
  37. 102 0
      test/dm/gpio.c
  38. 198 0
      test/dm/regmap.c
  39. 60 0
      test/dm/reset.c
  40. 203 0
      test/dm/scmi.c
  41. 3 3
      test/dm/test-fdt.c
  42. 1 1
      test/py/tests/test_bind.py

+ 60 - 0
arch/sandbox/dts/test.dts

@@ -121,6 +121,9 @@
 			<&gpio_c 5 GPIO_IN>,
 			<&gpio_c 6 (GPIO_ACTIVE_LOW|GPIO_OUT|GPIO_OPEN_DRAIN)>,
 			<&gpio_c 7 (GPIO_ACTIVE_LOW|GPIO_OUT|GPIO_OPEN_SOURCE)>;
+		test4-gpios = <&gpio_a 14>, <&gpio_b 4 1 3 2 1>;
+		test5-gpios = <&gpio_a 19>;
+
 		int-value = <1234>;
 		uint-value = <(-1234)>;
 		int64-value = /bits/ 64 <0x1111222233334444>;
@@ -270,6 +273,13 @@
 		compatible = "denx,u-boot-devres-test";
 	};
 
+	another-test {
+		reg = <0 2>;
+		compatible = "denx,u-boot-fdt-test";
+		test4-gpios = <&gpio_a 14>, <&gpio_b 4 1 3 2 1>;
+		test5-gpios = <&gpio_a 19>;
+	};
+
 	acpi_test1: acpi-test {
 		compatible = "denx,u-boot-acpi-test";
 		acpi-ssdt-test-data = "ab";
@@ -356,6 +366,37 @@
 		sandbox_firmware: sandbox-firmware {
 			compatible = "sandbox,firmware";
 		};
+
+		sandbox-scmi-agent@0 {
+			compatible = "sandbox,scmi-agent";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			clk_scmi0: protocol@14 {
+				reg = <0x14>;
+				#clock-cells = <1>;
+			};
+
+			reset_scmi0: protocol@16 {
+				reg = <0x16>;
+				#reset-cells = <1>;
+			};
+		};
+
+		sandbox-scmi-agent@1 {
+			compatible = "sandbox,scmi-agent";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			clk_scmi1: protocol@14 {
+				reg = <0x14>;
+				#clock-cells = <1>;
+			};
+
+			protocol@10 {
+				reg = <0x10>;
+			};
+		};
 	};
 
 	pinctrl-gpio {
@@ -1043,6 +1084,12 @@
 		compatible = "sandbox,virtio2";
 	};
 
+	sandbox_scmi {
+		compatible = "sandbox,scmi-devices";
+		clocks = <&clk_scmi0 7>, <&clk_scmi0 3>, <&clk_scmi1 1>;
+		resets = <&reset_scmi0 3>;
+	};
+
 	pinctrl {
 		compatible = "sandbox,pinctrl";
 
@@ -1129,6 +1176,19 @@
 		resets = <&resetc2 15>, <&resetc2 30>, <&resetc2 60>;
 		reset-names = "valid", "no_mask", "out_of_range";
 	};
+
+	some_regmapped-bus {
+		#address-cells = <0x1>;
+		#size-cells = <0x1>;
+
+		ranges = <0x0 0x0 0x10>;
+		compatible = "simple-bus";
+
+		regmap-test_0 {
+			reg = <0 0x10>;
+			compatible = "sandbox,regmap_test";
+		};
+	};
 };
 
 #include "sandbox_pmic.dtsi"

+ 3 - 0
arch/sandbox/include/asm/reset.h

@@ -11,9 +11,12 @@
 struct udevice;
 
 int sandbox_reset_query(struct udevice *dev, unsigned long id);
+int sandbox_reset_is_requested(struct udevice *dev, unsigned long id);
 
 int sandbox_reset_test_get(struct udevice *dev);
+int sandbox_reset_test_get_devm(struct udevice *dev);
 int sandbox_reset_test_get_bulk(struct udevice *dev);
+int sandbox_reset_test_get_bulk_devm(struct udevice *dev);
 int sandbox_reset_test_assert(struct udevice *dev);
 int sandbox_reset_test_assert_bulk(struct udevice *dev);
 int sandbox_reset_test_deassert(struct udevice *dev);

+ 99 - 0
arch/sandbox/include/asm/scmi_test.h

@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020, Linaro Limited
+ */
+
+#ifndef __SANDBOX_SCMI_TEST_H
+#define __SANDBOX_SCMI_TEST_H
+
+struct udevice;
+struct sandbox_scmi_agent;
+struct sandbox_scmi_service;
+
+/**
+ * struct sandbox_scmi_clk - Simulated clock exposed by SCMI
+ * @id:		Identifier of the clock used in the SCMI protocol
+ * @enabled:	Clock state: true if enabled, false if disabled
+ * @rate:	Clock rate in Hertz
+ */
+struct sandbox_scmi_clk {
+	uint id;
+	bool enabled;
+	ulong rate;
+};
+
+/**
+ * struct sandbox_scmi_reset - Simulated reset controller exposed by SCMI
+ * @asserted:	Reset control state: true if asserted, false if desasserted
+ */
+struct sandbox_scmi_reset {
+	uint id;
+	bool asserted;
+};
+
+/**
+ * struct sandbox_scmi_agent - Simulated SCMI service seen by SCMI agent
+ * @idx:	Identifier for the SCMI agent, its index
+ * @clk:	Simulated clocks
+ * @clk_count:	Simulated clocks array size
+ * @clk:	Simulated reset domains
+ * @clk_count:	Simulated reset domains array size
+ */
+struct sandbox_scmi_agent {
+	uint idx;
+	struct sandbox_scmi_clk *clk;
+	size_t clk_count;
+	struct sandbox_scmi_reset *reset;
+	size_t reset_count;
+};
+
+/**
+ * struct sandbox_scmi_service - Reference to simutaed SCMI agents/services
+ * @agent:		Pointer to SCMI sandbox agent pointers array
+ * @agent_count:	Number of emulated agents exposed in array @agent.
+ */
+struct sandbox_scmi_service {
+	struct sandbox_scmi_agent **agent;
+	size_t agent_count;
+};
+
+/**
+ * struct sandbox_scmi_devices - Reference to devices probed through SCMI
+ * @clk:		Array the clock devices
+ * @clk_count:		Number of clock devices probed
+ * @reset:		Array the reset controller devices
+ * @reset_count:	Number of reset controller devices probed
+ */
+struct sandbox_scmi_devices {
+	struct clk *clk;
+	size_t clk_count;
+	struct reset_ctl *reset;
+	size_t reset_count;
+};
+
+#ifdef CONFIG_SCMI_FIRMWARE
+/**
+ * sandbox_scmi_service_context - Get the simulated SCMI services context
+ * @return:	Reference to backend simulated resources state
+ */
+struct sandbox_scmi_service *sandbox_scmi_service_ctx(void);
+
+/**
+ * sandbox_scmi_devices_get_ref - Get references to devices accessed through SCMI
+ * @dev:	Reference to the test device used get test resources
+ * @return:	Reference to the devices probed by the SCMI test
+ */
+struct sandbox_scmi_devices *sandbox_scmi_devices_ctx(struct udevice *dev);
+#else
+static inline struct sandbox_scmi_service *sandbox_scmi_service_ctx(void)
+{
+	return NULL;
+}
+
+static inline
+struct sandbox_scmi_devices *sandbox_scmi_devices_ctx(struct udevice *dev)
+{
+	return NULL;
+}
+#endif /* CONFIG_SCMI_FIRMWARE */
+#endif /* __SANDBOX_SCMI_TEST_H */

+ 4 - 0
configs/sandbox_defconfig

@@ -122,6 +122,7 @@ CONFIG_BUTTON=y
 CONFIG_BUTTON_GPIO=y
 CONFIG_CLK=y
 CONFIG_CLK_COMPOSITE_CCF=y
+CONFIG_CLK_SCMI=y
 CONFIG_SANDBOX_CLK_CCF=y
 CONFIG_CPU=y
 CONFIG_DM_DEMO=y
@@ -132,6 +133,8 @@ CONFIG_BOARD_SANDBOX=y
 CONFIG_DMA=y
 CONFIG_DMA_CHANNELS=y
 CONFIG_SANDBOX_DMA=y
+CONFIG_FIRMWARE=y
+CONFIG_SCMI_FIRMWARE=y
 CONFIG_GPIO_HOG=y
 CONFIG_DM_GPIO_LOOKUP_LABEL=y
 CONFIG_PM8916_GPIO=y
@@ -217,6 +220,7 @@ CONFIG_REMOTEPROC_SANDBOX=y
 CONFIG_DM_RESET=y
 CONFIG_SANDBOX_RESET=y
 CONFIG_RESET_SYSCON=y
+CONFIG_RESET_SCMI=y
 CONFIG_DM_RNG=y
 CONFIG_DM_RTC=y
 CONFIG_RTC_RV8803=y

+ 197 - 0
doc/device-tree-bindings/arm/arm,scmi.txt

@@ -0,0 +1,197 @@
+System Control and Management Interface (SCMI) Message Protocol
+----------------------------------------------------------
+
+The SCMI is intended to allow agents such as OSPM to manage various functions
+that are provided by the hardware platform it is running on, including power
+and performance functions.
+
+This binding is intended to define the interface the firmware implementing
+the SCMI as described in ARM document number ARM DEN 0056A ("ARM System Control
+and Management Interface Platform Design Document")[0] provide for OSPM in
+the device tree.
+
+Required properties:
+
+The scmi node with the following properties shall be under the /firmware/ node.
+
+- compatible : shall be "arm,scmi" or "arm,scmi-smc" for smc/hvc transports
+- mboxes: List of phandle and mailbox channel specifiers. It should contain
+	  exactly one or two mailboxes, one for transmitting messages("tx")
+	  and another optional for receiving the notifications("rx") if
+	  supported.
+- shmem : List of phandle pointing to the shared memory(SHM) area as per
+	  generic mailbox client binding.
+- #address-cells : should be '1' if the device has sub-nodes, maps to
+	  protocol identifier for a given sub-node.
+- #size-cells : should be '0' as 'reg' property doesn't have any size
+	  associated with it.
+- arm,smc-id : SMC id required when using smc or hvc transports
+
+Optional properties:
+
+- mbox-names: shall be "tx" or "rx" depending on mboxes entries.
+
+See Documentation/devicetree/bindings/mailbox/mailbox.txt for more details
+about the generic mailbox controller and client driver bindings.
+
+The mailbox is the only permitted method of calling the SCMI firmware.
+Mailbox doorbell is used as a mechanism to alert the presence of a
+messages and/or notification.
+
+Each protocol supported shall have a sub-node with corresponding compatible
+as described in the following sections. If the platform supports dedicated
+communication channel for a particular protocol, the 3 properties namely:
+mboxes, mbox-names and shmem shall be present in the sub-node corresponding
+to that protocol.
+
+Clock/Performance bindings for the clocks/OPPs based on SCMI Message Protocol
+------------------------------------------------------------
+
+This binding uses the common clock binding[1].
+
+Required properties:
+- #clock-cells : Should be 1. Contains the Clock ID value used by SCMI commands.
+
+Power domain bindings for the power domains based on SCMI Message Protocol
+------------------------------------------------------------
+
+This binding for the SCMI power domain providers uses the generic power
+domain binding[2].
+
+Required properties:
+ - #power-domain-cells : Should be 1. Contains the device or the power
+			 domain ID value used by SCMI commands.
+
+Sensor bindings for the sensors based on SCMI Message Protocol
+--------------------------------------------------------------
+SCMI provides an API to access the various sensors on the SoC.
+
+Required properties:
+- #thermal-sensor-cells: should be set to 1. This property follows the
+			 thermal device tree bindings[3].
+
+			 Valid cell values are raw identifiers (Sensor ID)
+			 as used by the firmware. Refer to  platform details
+			 for your implementation for the IDs to use.
+
+Reset signal bindings for the reset domains based on SCMI Message Protocol
+------------------------------------------------------------
+
+This binding for the SCMI reset domain providers uses the generic reset
+signal binding[5].
+
+Required properties:
+ - #reset-cells : Should be 1. Contains the reset domain ID value used
+		  by SCMI commands.
+
+SRAM and Shared Memory for SCMI
+-------------------------------
+
+A small area of SRAM is reserved for SCMI communication between application
+processors and SCP.
+
+The properties should follow the generic mmio-sram description found in [4]
+
+Each sub-node represents the reserved area for SCMI.
+
+Required sub-node properties:
+- reg : The base offset and size of the reserved area with the SRAM
+- compatible : should be "arm,scmi-shmem" for Non-secure SRAM based
+	       shared memory
+
+[0] http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/index.html
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+[2] Documentation/devicetree/bindings/power/power-domain.yaml
+[3] Documentation/devicetree/bindings/thermal/thermal.txt
+[4] Documentation/devicetree/bindings/sram/sram.yaml
+[5] Documentation/devicetree/bindings/reset/reset.txt
+
+Example:
+
+sram@50000000 {
+	compatible = "mmio-sram";
+	reg = <0x0 0x50000000 0x0 0x10000>;
+
+	#address-cells = <1>;
+	#size-cells = <1>;
+	ranges = <0 0x0 0x50000000 0x10000>;
+
+	cpu_scp_lpri: scp-shmem@0 {
+		compatible = "arm,scmi-shmem";
+		reg = <0x0 0x200>;
+	};
+
+	cpu_scp_hpri: scp-shmem@200 {
+		compatible = "arm,scmi-shmem";
+		reg = <0x200 0x200>;
+	};
+};
+
+mailbox@40000000 {
+	....
+	#mbox-cells = <1>;
+	reg = <0x0 0x40000000 0x0 0x10000>;
+};
+
+firmware {
+
+	...
+
+	scmi {
+		compatible = "arm,scmi";
+		mboxes = <&mailbox 0 &mailbox 1>;
+		mbox-names = "tx", "rx";
+		shmem = <&cpu_scp_lpri &cpu_scp_hpri>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		scmi_devpd: protocol@11 {
+			reg = <0x11>;
+			#power-domain-cells = <1>;
+		};
+
+		scmi_dvfs: protocol@13 {
+			reg = <0x13>;
+			#clock-cells = <1>;
+		};
+
+		scmi_clk: protocol@14 {
+			reg = <0x14>;
+			#clock-cells = <1>;
+		};
+
+		scmi_sensors0: protocol@15 {
+			reg = <0x15>;
+			#thermal-sensor-cells = <1>;
+		};
+
+		scmi_reset: protocol@16 {
+			reg = <0x16>;
+			#reset-cells = <1>;
+		};
+	};
+};
+
+cpu@0 {
+	...
+	reg = <0 0>;
+	clocks = <&scmi_dvfs 0>;
+};
+
+hdlcd@7ff60000 {
+	...
+	reg = <0 0x7ff60000 0 0x1000>;
+	clocks = <&scmi_clk 4>;
+	power-domains = <&scmi_devpd 1>;
+	resets = <&scmi_reset 10>;
+};
+
+thermal-zones {
+	soc_thermal {
+		polling-delay-passive = <100>;
+		polling-delay = <1000>;
+					/* sensor ID */
+		thermal-sensors = <&scmi_sensors0 3>;
+		...
+	};
+};

+ 8 - 0
drivers/clk/Kconfig

@@ -159,6 +159,14 @@ config CLK_CDCE9XX
 	   Enable the clock synthesizer driver for CDCE913/925/937/949
 	   series of chips.
 
+config CLK_SCMI
+	bool "Enable SCMI clock driver"
+	depends on SCMI_FIRMWARE
+	help
+	  Enable this option if you want to support clock devices exposed
+	  by a SCMI agent based on SCMI clock protocol communication
+	  with a SCMI server.
+
 source "drivers/clk/analogbits/Kconfig"
 source "drivers/clk/at91/Kconfig"
 source "drivers/clk/exynos/Kconfig"

+ 1 - 0
drivers/clk/Makefile

@@ -32,6 +32,7 @@ obj-$(CONFIG_CLK_MPC83XX) += mpc83xx_clk.o
 obj-$(CONFIG_CLK_OCTEON) += clk_octeon.o
 obj-$(CONFIG_CLK_OWL) += owl/
 obj-$(CONFIG_CLK_RENESAS) += renesas/
+obj-$(CONFIG_CLK_SCMI) += clk_scmi.o
 obj-$(CONFIG_CLK_SIFIVE) += sifive/
 obj-$(CONFIG_ARCH_SUNXI) += sunxi/
 obj-$(CONFIG_CLK_STM32F) += clk_stm32f.o

+ 99 - 0
drivers/clk/clk_scmi.c

@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019-2020 Linaro Limited
+ */
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <scmi_agent.h>
+#include <scmi_protocols.h>
+#include <asm/types.h>
+
+static int scmi_clk_gate(struct clk *clk, int enable)
+{
+	struct scmi_clk_state_in in = {
+		.clock_id = clk->id,
+		.attributes = enable,
+	};
+	struct scmi_clk_state_out out;
+	struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
+					  SCMI_CLOCK_CONFIG_SET,
+					  in, out);
+	int ret;
+
+	ret = devm_scmi_process_msg(clk->dev->parent, &msg);
+	if (ret)
+		return ret;
+
+	return scmi_to_linux_errno(out.status);
+}
+
+static int scmi_clk_enable(struct clk *clk)
+{
+	return scmi_clk_gate(clk, 1);
+}
+
+static int scmi_clk_disable(struct clk *clk)
+{
+	return scmi_clk_gate(clk, 0);
+}
+
+static ulong scmi_clk_get_rate(struct clk *clk)
+{
+	struct scmi_clk_rate_get_in in = {
+		.clock_id = clk->id,
+	};
+	struct scmi_clk_rate_get_out out;
+	struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
+					  SCMI_CLOCK_RATE_GET,
+					  in, out);
+	int ret;
+
+	ret = devm_scmi_process_msg(clk->dev->parent, &msg);
+	if (ret < 0)
+		return ret;
+
+	ret = scmi_to_linux_errno(out.status);
+	if (ret < 0)
+		return ret;
+
+	return (ulong)(((u64)out.rate_msb << 32) | out.rate_lsb);
+}
+
+static ulong scmi_clk_set_rate(struct clk *clk, ulong rate)
+{
+	struct scmi_clk_rate_set_in in = {
+		.clock_id = clk->id,
+		.flags = SCMI_CLK_RATE_ROUND_CLOSEST,
+		.rate_lsb = (u32)rate,
+		.rate_msb = (u32)((u64)rate >> 32),
+	};
+	struct scmi_clk_rate_set_out out;
+	struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
+					  SCMI_CLOCK_RATE_SET,
+					  in, out);
+	int ret;
+
+	ret = devm_scmi_process_msg(clk->dev->parent, &msg);
+	if (ret < 0)
+		return ret;
+
+	ret = scmi_to_linux_errno(out.status);
+	if (ret < 0)
+		return ret;
+
+	return scmi_clk_get_rate(clk);
+}
+
+static const struct clk_ops scmi_clk_ops = {
+	.enable = scmi_clk_enable,
+	.disable = scmi_clk_disable,
+	.get_rate = scmi_clk_get_rate,
+	.set_rate = scmi_clk_set_rate,
+};
+
+U_BOOT_DRIVER(scmi_clock) = {
+	.name = "scmi_clk",
+	.id = UCLASS_CLK,
+	.ops = &scmi_clk_ops,
+};

+ 160 - 3
drivers/core/regmap.c

@@ -14,7 +14,24 @@
 #include <regmap.h>
 #include <asm/io.h>
 #include <dm/of_addr.h>
+#include <dm/devres.h>
 #include <linux/ioport.h>
+#include <linux/compat.h>
+#include <linux/err.h>
+#include <linux/bitops.h>
+
+/*
+ * Internal representation of a regmap field. Instead of storing the MSB and
+ * LSB, store the shift and mask. This makes the code a bit cleaner and faster
+ * because the shift and mask don't have to be calculated every time.
+ */
+struct regmap_field {
+	struct regmap *regmap;
+	unsigned int mask;
+	/* lsb */
+	unsigned int shift;
+	unsigned int reg;
+};
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -22,16 +39,22 @@ DECLARE_GLOBAL_DATA_PTR;
  * regmap_alloc() - Allocate a regmap with a given number of ranges.
  *
  * @count: Number of ranges to be allocated for the regmap.
+ *
+ * The default regmap width is set to REGMAP_SIZE_32. Callers can override it
+ * if they need.
+ *
  * Return: A pointer to the newly allocated regmap, or NULL on error.
  */
 static struct regmap *regmap_alloc(int count)
 {
 	struct regmap *map;
+	size_t size = sizeof(*map) + sizeof(map->ranges[0]) * count;
 
-	map = malloc(sizeof(*map) + sizeof(map->ranges[0]) * count);
+	map = calloc(1, size);
 	if (!map)
 		return NULL;
 	map->range_count = count;
+	map->width = REGMAP_SIZE_32;
 
 	return map;
 }
@@ -155,6 +178,33 @@ err:
 	return ret;
 }
 
+int regmap_init_mem_range(ofnode node, ulong r_start, ulong r_size,
+			  struct regmap **mapp)
+{
+	struct regmap *map;
+	struct regmap_range *range;
+
+	map = regmap_alloc(1);
+	if (!map)
+		return -ENOMEM;
+
+	range = &map->ranges[0];
+	range->start = r_start;
+	range->size = r_size;
+
+	if (ofnode_read_bool(node, "little-endian"))
+		map->endianness = REGMAP_LITTLE_ENDIAN;
+	else if (ofnode_read_bool(node, "big-endian"))
+		map->endianness = REGMAP_BIG_ENDIAN;
+	else if (ofnode_read_bool(node, "native-endian"))
+		map->endianness = REGMAP_NATIVE_ENDIAN;
+	else /* Default: native endianness */
+		map->endianness = REGMAP_NATIVE_ENDIAN;
+
+	*mapp = map;
+	return 0;
+}
+
 int regmap_init_mem(ofnode node, struct regmap **mapp)
 {
 	struct regmap_range *range;
@@ -228,6 +278,42 @@ err:
 
 	return ret;
 }
+
+static void devm_regmap_release(struct udevice *dev, void *res)
+{
+	regmap_uninit(*(struct regmap **)res);
+}
+
+struct regmap *devm_regmap_init(struct udevice *dev,
+				const struct regmap_bus *bus,
+				void *bus_context,
+				const struct regmap_config *config)
+{
+	int rc;
+	struct regmap **mapp, *map;
+
+	mapp = devres_alloc(devm_regmap_release, sizeof(struct regmap *),
+			    __GFP_ZERO);
+	if (unlikely(!mapp))
+		return ERR_PTR(-ENOMEM);
+
+	if (config && config->r_size != 0)
+		rc = regmap_init_mem_range(dev_ofnode(dev), config->r_start,
+					   config->r_size, mapp);
+	else
+		rc = regmap_init_mem(dev_ofnode(dev), mapp);
+	if (rc)
+		return ERR_PTR(rc);
+
+	map = *mapp;
+	if (config) {
+		map->width = config->width;
+		map->reg_offset_shift = config->reg_offset_shift;
+	}
+
+	devres_add(dev, mapp);
+	return *mapp;
+}
 #endif
 
 void *regmap_get_range(struct regmap *map, unsigned int range_num)
@@ -310,6 +396,7 @@ int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset,
 	}
 	range = &map->ranges[range_num];
 
+	offset <<= map->reg_offset_shift;
 	if (offset + val_len > range->size) {
 		debug("%s: offset/size combination invalid\n", __func__);
 		return -ERANGE;
@@ -347,7 +434,7 @@ int regmap_raw_read(struct regmap *map, uint offset, void *valp, size_t val_len)
 
 int regmap_read(struct regmap *map, uint offset, uint *valp)
 {
-	return regmap_raw_read(map, offset, valp, REGMAP_SIZE_32);
+	return regmap_raw_read(map, offset, valp, map->width);
 }
 
 static inline void __write_8(u8 *addr, const u8 *val,
@@ -419,6 +506,7 @@ int regmap_raw_write_range(struct regmap *map, uint range_num, uint offset,
 	}
 	range = &map->ranges[range_num];
 
+	offset <<= map->reg_offset_shift;
 	if (offset + val_len > range->size) {
 		debug("%s: offset/size combination invalid\n", __func__);
 		return -ERANGE;
@@ -457,7 +545,7 @@ int regmap_raw_write(struct regmap *map, uint offset, const void *val,
 
 int regmap_write(struct regmap *map, uint offset, uint val)
 {
-	return regmap_raw_write(map, offset, &val, REGMAP_SIZE_32);
+	return regmap_raw_write(map, offset, &val, map->width);
 }
 
 int regmap_update_bits(struct regmap *map, uint offset, uint mask, uint val)
@@ -473,3 +561,72 @@ int regmap_update_bits(struct regmap *map, uint offset, uint mask, uint val)
 
 	return regmap_write(map, offset, reg | (val & mask));
 }
+
+int regmap_field_read(struct regmap_field *field, unsigned int *val)
+{
+	int ret;
+	unsigned int reg_val;
+
+	ret = regmap_read(field->regmap, field->reg, &reg_val);
+	if (ret != 0)
+		return ret;
+
+	reg_val &= field->mask;
+	reg_val >>= field->shift;
+	*val = reg_val;
+
+	return ret;
+}
+
+int regmap_field_write(struct regmap_field *field, unsigned int val)
+{
+	return regmap_update_bits(field->regmap, field->reg, field->mask,
+				  val << field->shift);
+}
+
+static void regmap_field_init(struct regmap_field *rm_field,
+			      struct regmap *regmap,
+			      struct reg_field reg_field)
+{
+	rm_field->regmap = regmap;
+	rm_field->reg = reg_field.reg;
+	rm_field->shift = reg_field.lsb;
+	rm_field->mask = GENMASK(reg_field.msb, reg_field.lsb);
+}
+
+struct regmap_field *devm_regmap_field_alloc(struct udevice *dev,
+					     struct regmap *regmap,
+					     struct reg_field reg_field)
+{
+	struct regmap_field *rm_field = devm_kzalloc(dev, sizeof(*rm_field),
+						     GFP_KERNEL);
+	if (!rm_field)
+		return ERR_PTR(-ENOMEM);
+
+	regmap_field_init(rm_field, regmap, reg_field);
+
+	return rm_field;
+}
+
+void devm_regmap_field_free(struct udevice *dev, struct regmap_field *field)
+{
+	devm_kfree(dev, field);
+}
+
+struct regmap_field *regmap_field_alloc(struct regmap *regmap,
+					struct reg_field reg_field)
+{
+	struct regmap_field *rm_field = kzalloc(sizeof(*rm_field), GFP_KERNEL);
+
+	if (!rm_field)
+		return ERR_PTR(-ENOMEM);
+
+	regmap_field_init(rm_field, regmap, reg_field);
+
+	return rm_field;
+}
+
+void regmap_field_free(struct regmap_field *field)
+{
+	kfree(field);
+}

+ 2 - 0
drivers/firmware/Kconfig

@@ -36,3 +36,5 @@ config ZYNQMP_FIRMWARE
 	  various platform management services.
 	  Say yes to enable ZynqMP firmware interface driver.
 	  If in doubt, say N.
+
+source "drivers/firmware/scmi/Kconfig"

+ 1 - 0
drivers/firmware/Makefile

@@ -3,3 +3,4 @@ obj-$(CONFIG_$(SPL_)ARM_PSCI_FW)	+= psci.o
 obj-$(CONFIG_TI_SCI_PROTOCOL)	+= ti_sci.o
 obj-$(CONFIG_SANDBOX)		+= firmware-sandbox.o
 obj-$(CONFIG_ZYNQMP_FIRMWARE)	+= firmware-zynqmp.o
+obj-$(CONFIG_SCMI_FIRMWARE)	+= scmi/

+ 19 - 0
drivers/firmware/scmi/Kconfig

@@ -0,0 +1,19 @@
+config SCMI_FIRMWARE
+	bool "Enable SCMI support"
+	select FIRMWARE
+	select OF_TRANSLATE
+	depends on SANDBOX || DM_MAILBOX || ARM_SMCCC
+	help
+	  System Control and Management Interface (SCMI) is a communication
+	  protocol that defines standard interfaces for power, performance
+	  and system management. The SCMI specification is available at
+	  https://developer.arm.com/architectures/system-architectures/software-standards/scmi
+
+	  An SCMI agent communicates with a related SCMI server firmware
+	  located in another sub-system, as a companion micro controller
+	  or a companion host in the CPU system.
+
+	  Communications between agent (client) and the SCMI server are
+	  based on message exchange. Messages can be exchange over tranport
+	  channels as a mailbox device or an Arm SMCCC service with some
+	  piece of identified shared memory.

+ 5 - 0
drivers/firmware/scmi/Makefile

@@ -0,0 +1,5 @@
+obj-y	+= scmi_agent-uclass.o
+obj-y	+= smt.o
+obj-$(CONFIG_ARM_SMCCC) 	+= smccc_agent.o
+obj-$(CONFIG_DM_MAILBOX)	+= mailbox_agent.o
+obj-$(CONFIG_SANDBOX)		+= sandbox-scmi_agent.o sandbox-scmi_devices.o

+ 102 - 0
drivers/firmware/scmi/mailbox_agent.c

@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Linaro Limited.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <mailbox.h>
+#include <scmi_agent.h>
+#include <scmi_agent-uclass.h>
+#include <dm/devres.h>
+#include <linux/compat.h>
+
+#include "smt.h"
+
+#define TIMEOUT_US_10MS			10000
+
+/**
+ * struct scmi_mbox_channel - Description of an SCMI mailbox transport
+ * @smt:	Shared memory buffer
+ * @mbox:	Mailbox channel description
+ * @timeout_us:	Timeout in microseconds for the mailbox transfer
+ */
+struct scmi_mbox_channel {
+	struct scmi_smt smt;
+	struct mbox_chan mbox;
+	ulong timeout_us;
+};
+
+static int scmi_mbox_process_msg(struct udevice *dev, struct scmi_msg *msg)
+{
+	struct scmi_mbox_channel *chan = dev_get_priv(dev);
+	int ret;
+
+	ret = scmi_write_msg_to_smt(dev, &chan->smt, msg);
+	if (ret)
+		return ret;
+
+	/* Give shm addr to mbox in case it is meaningful */
+	ret = mbox_send(&chan->mbox, chan->smt.buf);
+	if (ret) {
+		dev_err(dev, "Message send failed: %d\n", ret);
+		goto out;
+	}
+
+	/* Receive the response */
+	ret = mbox_recv(&chan->mbox, chan->smt.buf, chan->timeout_us);
+	if (ret) {
+		dev_err(dev, "Response failed: %d, abort\n", ret);
+		goto out;
+	}
+
+	ret = scmi_read_resp_from_smt(dev, &chan->smt, msg);
+
+out:
+	scmi_clear_smt_channel(&chan->smt);
+
+	return ret;
+}
+
+int scmi_mbox_probe(struct udevice *dev)
+{
+	struct scmi_mbox_channel *chan = dev_get_priv(dev);
+	int ret;
+
+	chan->timeout_us = TIMEOUT_US_10MS;
+
+	ret = mbox_get_by_index(dev, 0, &chan->mbox);
+	if (ret) {
+		dev_err(dev, "Failed to find mailbox: %d\n", ret);
+		goto out;
+	}
+
+	ret = scmi_dt_get_smt_buffer(dev, &chan->smt);
+	if (ret)
+		dev_err(dev, "Failed to get shm resources: %d\n", ret);
+
+out:
+	if (ret)
+		devm_kfree(dev, chan);
+
+	return ret;
+}
+
+static const struct udevice_id scmi_mbox_ids[] = {
+	{ .compatible = "arm,scmi" },
+	{ }
+};
+
+static const struct scmi_agent_ops scmi_mbox_ops = {
+	.process_msg = scmi_mbox_process_msg,
+};
+
+U_BOOT_DRIVER(scmi_mbox) = {
+	.name		= "scmi-over-mailbox",
+	.id		= UCLASS_SCMI_AGENT,
+	.of_match	= scmi_mbox_ids,
+	.priv_auto_alloc_size = sizeof(struct scmi_mbox_channel),
+	.probe		= scmi_mbox_probe,
+	.ops		= &scmi_mbox_ops,
+};

+ 410 - 0
drivers/firmware/scmi/sandbox-scmi_agent.c

@@ -0,0 +1,410 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020, Linaro Limited
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <malloc.h>
+#include <scmi_agent.h>
+#include <scmi_agent-uclass.h>
+#include <scmi_protocols.h>
+#include <asm/io.h>
+#include <asm/scmi_test.h>
+#include <dm/device_compat.h>
+
+/*
+ * The sandbox SCMI agent driver simulates to some extend a SCMI message
+ * processing. It simulates few of the SCMI services for some of the
+ * SCMI protocols embedded in U-Boot. Currently:
+ * - SCMI clock protocol: emulate 2 agents each exposing few clocks
+ * - SCMI reset protocol: emulate 1 agents each exposing a reset
+ *
+ * Agent #0 simulates 2 clocks and 1 reset domain.
+ * See IDs in scmi0_clk[]/scmi0_reset[] and "sandbox-scmi-agent@0" in test.dts.
+ *
+ * Agent #1 simulates 1 clock.
+ * See IDs in scmi1_clk[] and "sandbox-scmi-agent@1" in test.dts.
+ *
+ * All clocks are default disabled and reset levels down.
+ *
+ * This Driver exports sandbox_scmi_service_ct() for the test sequence to
+ * get the state of the simulated services (clock state, rate, ...) and
+ * check back-end device state reflects the request send through the
+ * various uclass devices, as clocks and reset controllers.
+ */
+
+#define SANDBOX_SCMI_AGENT_COUNT	2
+
+static struct sandbox_scmi_clk scmi0_clk[] = {
+	{ .id = 7, .rate = 1000 },
+	{ .id = 3, .rate = 333 },
+};
+
+static struct sandbox_scmi_reset scmi0_reset[] = {
+	{ .id = 3 },
+};
+
+static struct sandbox_scmi_clk scmi1_clk[] = {
+	{ .id = 1, .rate = 44 },
+};
+
+/* The list saves to simulted end devices references for test purpose */
+struct sandbox_scmi_agent *sandbox_scmi_agent_list[SANDBOX_SCMI_AGENT_COUNT];
+
+static struct sandbox_scmi_service sandbox_scmi_service_state = {
+	.agent = sandbox_scmi_agent_list,
+	.agent_count = SANDBOX_SCMI_AGENT_COUNT,
+};
+
+struct sandbox_scmi_service *sandbox_scmi_service_ctx(void)
+{
+	return &sandbox_scmi_service_state;
+}
+
+static void debug_print_agent_state(struct udevice *dev, char *str)
+{
+	struct sandbox_scmi_agent *agent = dev_get_priv(dev);
+
+	dev_dbg(dev, "Dump sandbox_scmi_agent %u: %s\n", agent->idx, str);
+	dev_dbg(dev, " scmi%u_clk   (%zu): %d/%ld, %d/%ld, %d/%ld, ...\n",
+		agent->idx,
+		agent->clk_count,
+		agent->clk_count ? agent->clk[0].enabled : -1,
+		agent->clk_count ? agent->clk[0].rate : -1,
+		agent->clk_count > 1 ? agent->clk[1].enabled : -1,
+		agent->clk_count > 1 ? agent->clk[1].rate : -1,
+		agent->clk_count > 2 ? agent->clk[2].enabled : -1,
+		agent->clk_count > 2 ? agent->clk[2].rate : -1);
+	dev_dbg(dev, " scmi%u_reset (%zu): %d, %d, ...\n",
+		agent->idx,
+		agent->reset_count,
+		agent->reset_count ? agent->reset[0].asserted : -1,
+		agent->reset_count > 1 ? agent->reset[1].asserted : -1);
+};
+
+static struct sandbox_scmi_clk *get_scmi_clk_state(uint agent_id, uint clock_id)
+{
+	struct sandbox_scmi_clk *target = NULL;
+	size_t target_count = 0;
+	size_t n;
+
+	switch (agent_id) {
+	case 0:
+		target = scmi0_clk;
+		target_count = ARRAY_SIZE(scmi0_clk);
+		break;
+	case 1:
+		target = scmi1_clk;
+		target_count = ARRAY_SIZE(scmi1_clk);
+		break;
+	default:
+		return NULL;
+	}
+
+	for (n = 0; n < target_count; n++)
+		if (target[n].id == clock_id)
+			return target + n;
+
+	return NULL;
+}
+
+static struct sandbox_scmi_reset *get_scmi_reset_state(uint agent_id,
+						       uint reset_id)
+{
+	size_t n;
+
+	if (agent_id == 0) {
+		for (n = 0; n < ARRAY_SIZE(scmi0_reset); n++)
+			if (scmi0_reset[n].id == reset_id)
+				return scmi0_reset + n;
+	}
+
+	return NULL;
+}
+
+/*
+ * Sandbox SCMI agent ops
+ */
+
+static int sandbox_scmi_clock_rate_set(struct udevice *dev,
+				       struct scmi_msg *msg)
+{
+	struct sandbox_scmi_agent *agent = dev_get_priv(dev);
+	struct scmi_clk_rate_set_in *in = NULL;
+	struct scmi_clk_rate_set_out *out = NULL;
+	struct sandbox_scmi_clk *clk_state = NULL;
+
+	if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) ||
+	    !msg->out_msg || msg->out_msg_sz < sizeof(*out))
+		return -EINVAL;
+
+	in = (struct scmi_clk_rate_set_in *)msg->in_msg;
+	out = (struct scmi_clk_rate_set_out *)msg->out_msg;
+
+	clk_state = get_scmi_clk_state(agent->idx, in->clock_id);
+	if (!clk_state) {
+		dev_err(dev, "Unexpected clock ID %u\n", in->clock_id);
+
+		out->status = SCMI_NOT_FOUND;
+	} else {
+		u64 rate = ((u64)in->rate_msb << 32) + in->rate_lsb;
+
+		clk_state->rate = (ulong)rate;
+
+		out->status = SCMI_SUCCESS;
+	}
+
+	return 0;
+}
+
+static int sandbox_scmi_clock_rate_get(struct udevice *dev,
+				       struct scmi_msg *msg)
+{
+	struct sandbox_scmi_agent *agent = dev_get_priv(dev);
+	struct scmi_clk_rate_get_in *in = NULL;
+	struct scmi_clk_rate_get_out *out = NULL;
+	struct sandbox_scmi_clk *clk_state = NULL;
+
+	if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) ||
+	    !msg->out_msg || msg->out_msg_sz < sizeof(*out))
+		return -EINVAL;
+
+	in = (struct scmi_clk_rate_get_in *)msg->in_msg;
+	out = (struct scmi_clk_rate_get_out *)msg->out_msg;
+
+	clk_state = get_scmi_clk_state(agent->idx, in->clock_id);
+	if (!clk_state) {
+		dev_err(dev, "Unexpected clock ID %u\n", in->clock_id);
+
+		out->status = SCMI_NOT_FOUND;
+	} else {
+		out->rate_msb = (u32)((u64)clk_state->rate >> 32);
+		out->rate_lsb = (u32)clk_state->rate;
+
+		out->status = SCMI_SUCCESS;
+	}
+
+	return 0;
+}
+
+static int sandbox_scmi_clock_gate(struct udevice *dev, struct scmi_msg *msg)
+{
+	struct sandbox_scmi_agent *agent = dev_get_priv(dev);
+	struct scmi_clk_state_in *in = NULL;
+	struct scmi_clk_state_out *out = NULL;
+	struct sandbox_scmi_clk *clk_state = NULL;
+
+	if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) ||
+	    !msg->out_msg || msg->out_msg_sz < sizeof(*out))
+		return -EINVAL;
+
+	in = (struct scmi_clk_state_in *)msg->in_msg;
+	out = (struct scmi_clk_state_out *)msg->out_msg;
+
+	clk_state = get_scmi_clk_state(agent->idx, in->clock_id);
+	if (!clk_state) {
+		dev_err(dev, "Unexpected clock ID %u\n", in->clock_id);
+
+		out->status = SCMI_NOT_FOUND;
+	} else if (in->attributes > 1) {
+		out->status = SCMI_PROTOCOL_ERROR;
+	} else {
+		clk_state->enabled = in->attributes;
+
+		out->status = SCMI_SUCCESS;
+	}
+
+	return 0;
+}
+
+static int sandbox_scmi_rd_attribs(struct udevice *dev, struct scmi_msg *msg)
+{
+	struct sandbox_scmi_agent *agent = dev_get_priv(dev);
+	struct scmi_rd_attr_in *in = NULL;
+	struct scmi_rd_attr_out *out = NULL;
+	struct sandbox_scmi_reset *reset_state = NULL;
+
+	if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) ||
+	    !msg->out_msg || msg->out_msg_sz < sizeof(*out))
+		return -EINVAL;
+
+	in = (struct scmi_rd_attr_in *)msg->in_msg;
+	out = (struct scmi_rd_attr_out *)msg->out_msg;
+
+	reset_state = get_scmi_reset_state(agent->idx, in->domain_id);
+	if (!reset_state) {
+		dev_err(dev, "Unexpected reset domain ID %u\n", in->domain_id);
+
+		out->status = SCMI_NOT_FOUND;
+	} else {
+		memset(out, 0, sizeof(*out));
+		snprintf(out->name, sizeof(out->name), "rd%u", in->domain_id);
+
+		out->status = SCMI_SUCCESS;
+	}
+
+	return 0;
+}
+
+static int sandbox_scmi_rd_reset(struct udevice *dev, struct scmi_msg *msg)
+{
+	struct sandbox_scmi_agent *agent = dev_get_priv(dev);
+	struct scmi_rd_reset_in *in = NULL;
+	struct scmi_rd_reset_out *out = NULL;
+	struct sandbox_scmi_reset *reset_state = NULL;
+
+	if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) ||
+	    !msg->out_msg || msg->out_msg_sz < sizeof(*out))
+		return -EINVAL;
+
+	in = (struct scmi_rd_reset_in *)msg->in_msg;
+	out = (struct scmi_rd_reset_out *)msg->out_msg;
+
+	reset_state = get_scmi_reset_state(agent->idx, in->domain_id);
+	if (!reset_state) {
+		dev_err(dev, "Unexpected reset domain ID %u\n", in->domain_id);
+
+		out->status = SCMI_NOT_FOUND;
+	} else if (in->reset_state > 1) {
+		dev_err(dev, "Invalid reset domain input attribute value\n");
+
+		out->status = SCMI_INVALID_PARAMETERS;
+	} else {
+		if (in->flags & SCMI_RD_RESET_FLAG_CYCLE) {
+			if (in->flags & SCMI_RD_RESET_FLAG_ASYNC) {
+				out->status = SCMI_NOT_SUPPORTED;
+			} else {
+				/* Ends deasserted whatever current state */
+				reset_state->asserted = false;
+				out->status = SCMI_SUCCESS;
+			}
+		} else {
+			reset_state->asserted = in->flags &
+						SCMI_RD_RESET_FLAG_ASSERT;
+
+			out->status = SCMI_SUCCESS;
+		}
+	}
+
+	return 0;
+}
+
+static int sandbox_scmi_test_process_msg(struct udevice *dev,
+					 struct scmi_msg *msg)
+{
+	switch (msg->protocol_id) {
+	case SCMI_PROTOCOL_ID_CLOCK:
+		switch (msg->message_id) {
+		case SCMI_CLOCK_RATE_SET:
+			return sandbox_scmi_clock_rate_set(dev, msg);
+		case SCMI_CLOCK_RATE_GET:
+			return sandbox_scmi_clock_rate_get(dev, msg);
+		case SCMI_CLOCK_CONFIG_SET:
+			return sandbox_scmi_clock_gate(dev, msg);
+		default:
+			break;
+		}
+		break;
+	case SCMI_PROTOCOL_ID_RESET_DOMAIN:
+		switch (msg->message_id) {
+		case SCMI_RESET_DOMAIN_ATTRIBUTES:
+			return sandbox_scmi_rd_attribs(dev, msg);
+		case SCMI_RESET_DOMAIN_RESET:
+			return sandbox_scmi_rd_reset(dev, msg);
+		default:
+			break;
+		}
+		break;
+	case SCMI_PROTOCOL_ID_BASE:
+	case SCMI_PROTOCOL_ID_POWER_DOMAIN:
+	case SCMI_PROTOCOL_ID_SYSTEM:
+	case SCMI_PROTOCOL_ID_PERF:
+	case SCMI_PROTOCOL_ID_SENSOR:
+		*(u32 *)msg->out_msg = SCMI_NOT_SUPPORTED;
+		return 0;
+	default:
+		break;
+	}
+
+	dev_err(dev, "%s(%s): Unhandled protocol_id %#x/message_id %#x\n",
+		__func__, dev->name, msg->protocol_id, msg->message_id);
+
+	if (msg->out_msg_sz < sizeof(u32))
+		return -EINVAL;
+
+	/* Intentionnaly report unhandled IDs through the SCMI return code */
+	*(u32 *)msg->out_msg = SCMI_PROTOCOL_ERROR;
+	return 0;
+}
+
+static int sandbox_scmi_test_remove(struct udevice *dev)
+{
+	struct sandbox_scmi_agent *agent = dev_get_priv(dev);
+
+	debug_print_agent_state(dev, "removed");
+
+	/* We only need to dereference the agent in the context */
+	sandbox_scmi_service_ctx()->agent[agent->idx] = NULL;
+
+	return 0;
+}
+
+static int sandbox_scmi_test_probe(struct udevice *dev)
+{
+	static const char basename[] = "sandbox-scmi-agent@";
+	struct sandbox_scmi_agent *agent = dev_get_priv(dev);
+	const size_t basename_size = sizeof(basename) - 1;
+
+	if (strncmp(basename, dev->name, basename_size))
+		return -ENOENT;
+
+	switch (dev->name[basename_size]) {
+	case '0':
+		*agent = (struct sandbox_scmi_agent){
+			.idx = 0,
+			.clk = scmi0_clk,
+			.clk_count = ARRAY_SIZE(scmi0_clk),
+			.reset = scmi0_reset,
+			.reset_count = ARRAY_SIZE(scmi0_reset),
+		};
+		break;
+	case '1':
+		*agent = (struct sandbox_scmi_agent){
+			.idx = 1,
+			.clk = scmi1_clk,
+			.clk_count = ARRAY_SIZE(scmi1_clk),
+		};
+		break;
+	default:
+		dev_err(dev, "%s(): Unexpected agent ID %s\n",
+			__func__, dev->name + basename_size);
+		return -ENOENT;
+	}
+
+	debug_print_agent_state(dev, "probed");
+
+	/* Save reference for tests purpose */
+	sandbox_scmi_service_ctx()->agent[agent->idx] = agent;
+
+	return 0;
+};
+
+static const struct udevice_id sandbox_scmi_test_ids[] = {
+	{ .compatible = "sandbox,scmi-agent" },
+	{ }
+};
+
+struct scmi_agent_ops sandbox_scmi_test_ops = {
+	.process_msg = sandbox_scmi_test_process_msg,
+};
+
+U_BOOT_DRIVER(sandbox_scmi_agent) = {
+	.name = "sandbox-scmi_agent",
+	.id = UCLASS_SCMI_AGENT,
+	.of_match = sandbox_scmi_test_ids,
+	.priv_auto_alloc_size = sizeof(struct sandbox_scmi_agent),
+	.probe = sandbox_scmi_test_probe,
+	.remove = sandbox_scmi_test_remove,
+	.ops = &sandbox_scmi_test_ops,
+};

+ 113 - 0
drivers/firmware/scmi/sandbox-scmi_devices.c

@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020, Linaro Limited
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <malloc.h>
+#include <reset.h>
+#include <asm/io.h>
+#include <asm/scmi_test.h>
+#include <dm/device_compat.h>
+
+/*
+ * Simulate to some extent a SCMI exchange.
+ * This drivers gets SCMI resources and offers API function to the
+ * SCMI test sequence manipulate the resources, currently clock
+ * and reset controllers.
+ */
+
+#define SCMI_TEST_DEVICES_CLK_COUNT		3
+#define SCMI_TEST_DEVICES_RD_COUNT		1
+
+/*
+ * struct sandbox_scmi_device_priv - Storage for device handles used by test
+ * @clk:		Array of clock instances used by tests
+ * @reset_clt:		Array of the reset controller instances used by tests
+ * @devices:		Resources exposed by sandbox_scmi_devices_ctx()
+ */
+struct sandbox_scmi_device_priv {
+	struct clk clk[SCMI_TEST_DEVICES_CLK_COUNT];
+	struct reset_ctl reset_ctl[SCMI_TEST_DEVICES_RD_COUNT];
+	struct sandbox_scmi_devices devices;
+};
+
+struct sandbox_scmi_devices *sandbox_scmi_devices_ctx(struct udevice *dev)
+{
+	struct sandbox_scmi_device_priv *priv = dev_get_priv(dev);
+
+	if (priv)
+		return &priv->devices;
+
+	return NULL;
+}
+
+static int sandbox_scmi_devices_remove(struct udevice *dev)
+{
+	struct sandbox_scmi_devices *devices = sandbox_scmi_devices_ctx(dev);
+	int ret = 0;
+	size_t n;
+
+	for (n = 0; n < SCMI_TEST_DEVICES_RD_COUNT; n++) {
+		int ret2 = reset_free(devices->reset + n);
+
+		if (ret2 && !ret)
+			ret = ret2;
+	}
+
+	return ret;
+}
+
+static int sandbox_scmi_devices_probe(struct udevice *dev)
+{
+	struct sandbox_scmi_device_priv *priv = dev_get_priv(dev);
+	int ret;
+	size_t n;
+
+	priv->devices = (struct sandbox_scmi_devices){
+		.clk = priv->clk,
+		.clk_count = SCMI_TEST_DEVICES_CLK_COUNT,
+		.reset = priv->reset_ctl,
+		.reset_count = SCMI_TEST_DEVICES_RD_COUNT,
+	};
+
+	for (n = 0; n < SCMI_TEST_DEVICES_CLK_COUNT; n++) {
+		ret = clk_get_by_index(dev, n, priv->devices.clk + n);
+		if (ret) {
+			dev_err(dev, "%s: Failed on clk %zu\n", __func__, n);
+			return ret;
+		}
+	}
+
+	for (n = 0; n < SCMI_TEST_DEVICES_RD_COUNT; n++) {
+		ret = reset_get_by_index(dev, n, priv->devices.reset + n);
+		if (ret) {
+			dev_err(dev, "%s: Failed on reset %zu\n", __func__, n);
+			goto err_reset;
+		}
+	}
+
+	return 0;
+
+err_reset:
+	for (; n > 0; n--)
+		reset_free(priv->devices.reset + n - 1);
+
+	return ret;
+}
+
+static const struct udevice_id sandbox_scmi_devices_ids[] = {
+	{ .compatible = "sandbox,scmi-devices" },
+	{ }
+};
+
+U_BOOT_DRIVER(sandbox_scmi_devices) = {
+	.name = "sandbox-scmi_devices",
+	.id = UCLASS_MISC,
+	.of_match = sandbox_scmi_devices_ids,
+	.priv_auto_alloc_size = sizeof(struct sandbox_scmi_device_priv),
+	.remove = sandbox_scmi_devices_remove,
+	.probe = sandbox_scmi_devices_probe,
+};

+ 119 - 0
drivers/firmware/scmi/scmi_agent-uclass.c

@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Linaro Limited.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <scmi_agent-uclass.h>
+#include <scmi_protocols.h>
+
+#include <dm/device-internal.h>
+#include <linux/compat.h>
+
+/**
+ * struct error_code - Helper structure for SCMI error code conversion
+ * @scmi:	SCMI error code
+ * @errno:	Related standard error number
+ */
+struct error_code {
+	int scmi;
+	int errno;
+};
+
+static const struct error_code scmi_linux_errmap[] = {
+	{ .scmi = SCMI_NOT_SUPPORTED, .errno = -EOPNOTSUPP, },
+	{ .scmi = SCMI_INVALID_PARAMETERS, .errno = -EINVAL, },
+	{ .scmi = SCMI_DENIED, .errno = -EACCES, },
+	{ .scmi = SCMI_NOT_FOUND, .errno = -ENOENT, },
+	{ .scmi = SCMI_OUT_OF_RANGE, .errno = -ERANGE, },
+	{ .scmi = SCMI_BUSY, .errno = -EBUSY, },
+	{ .scmi = SCMI_COMMS_ERROR, .errno = -ECOMM, },
+	{ .scmi = SCMI_GENERIC_ERROR, .errno = -EIO, },
+	{ .scmi = SCMI_HARDWARE_ERROR, .errno = -EREMOTEIO, },
+	{ .scmi = SCMI_PROTOCOL_ERROR, .errno = -EPROTO, },
+};
+
+int scmi_to_linux_errno(s32 scmi_code)
+{
+	int n;
+
+	if (!scmi_code)
+		return 0;
+
+	for (n = 0; n < ARRAY_SIZE(scmi_linux_errmap); n++)
+		if (scmi_code == scmi_linux_errmap[n].scmi)
+			return scmi_linux_errmap[1].errno;
+
+	return -EPROTO;
+}
+
+/*
+ * SCMI agent devices binds devices of various uclasses depeding on
+ * the FDT description. scmi_bind_protocol() is a generic bind sequence
+ * called by the uclass at bind stage, that is uclass post_bind.
+ */
+static int scmi_bind_protocols(struct udevice *dev)
+{
+	int ret = 0;
+	ofnode node;
+
+	dev_for_each_subnode(node, dev) {
+		struct driver *drv = NULL;
+		u32 protocol_id;
+
+		if (!ofnode_is_available(node))
+			continue;
+
+		if (ofnode_read_u32(node, "reg", &protocol_id))
+			continue;
+
+		switch (protocol_id) {
+		case SCMI_PROTOCOL_ID_CLOCK:
+			if (IS_ENABLED(CONFIG_CLK_SCMI))
+				drv = DM_GET_DRIVER(scmi_clock);
+			break;
+		case SCMI_PROTOCOL_ID_RESET_DOMAIN:
+			if (IS_ENABLED(CONFIG_RESET_SCMI))
+				drv = DM_GET_DRIVER(scmi_reset_domain);
+			break;
+		default:
+			break;
+		}
+
+		if (!drv) {
+			dev_dbg(dev, "Ignore unsupported SCMI protocol %#x\n",
+				protocol_id);
+			continue;
+		}
+
+		ret = device_bind_ofnode(dev, drv, ofnode_get_name(node),
+					 NULL, node, NULL);
+		if (ret)
+			break;
+	}
+
+	return ret;
+}
+
+static const struct scmi_agent_ops *transport_dev_ops(struct udevice *dev)
+{
+	return (const struct scmi_agent_ops *)dev->driver->ops;
+}
+
+int devm_scmi_process_msg(struct udevice *dev, struct scmi_msg *msg)
+{
+	const struct scmi_agent_ops *ops = transport_dev_ops(dev);
+
+	if (ops->process_msg)
+		return ops->process_msg(dev, msg);
+
+	return -EPROTONOSUPPORT;
+}
+
+UCLASS_DRIVER(scmi_agent) = {
+	.id		= UCLASS_SCMI_AGENT,
+	.name		= "scmi_agent",
+	.post_bind	= scmi_bind_protocols,
+};

+ 89 - 0
drivers/firmware/scmi/smccc_agent.c

@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Linaro Limited.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <scmi_agent.h>
+#include <scmi_agent-uclass.h>
+#include <dm/devres.h>
+#include <dm/device-internal.h>
+#include <linux/arm-smccc.h>
+#include <linux/compat.h>
+
+#include "smt.h"
+
+#define SMCCC_RET_NOT_SUPPORTED         ((unsigned long)-1)
+
+/**
+ * struct scmi_smccc_channel - Description of an SCMI SMCCC transport
+ * @func_id:	SMCCC function ID used by the SCMI transport
+ * @smt:	Shared memory buffer
+ */
+struct scmi_smccc_channel {
+	ulong func_id;
+	struct scmi_smt smt;
+};
+
+static int scmi_smccc_process_msg(struct udevice *dev, struct scmi_msg *msg)
+{
+	struct scmi_smccc_channel *chan = dev_get_priv(dev);
+	struct arm_smccc_res res;
+	int ret;
+
+	ret = scmi_write_msg_to_smt(dev, &chan->smt, msg);
+	if (ret)
+		return ret;
+
+	arm_smccc_smc(chan->func_id, 0, 0, 0, 0, 0, 0, 0, &res);
+	if (res.a0 == SMCCC_RET_NOT_SUPPORTED)
+		ret = -ENXIO;
+	else
+		ret = scmi_read_resp_from_smt(dev, &chan->smt, msg);
+
+	scmi_clear_smt_channel(&chan->smt);
+
+	return ret;
+}
+
+static int scmi_smccc_probe(struct udevice *dev)
+{
+	struct scmi_smccc_channel *chan = dev_get_priv(dev);
+	u32 func_id;
+	int ret;
+
+	if (dev_read_u32(dev, "arm,smc-id", &func_id)) {
+		dev_err(dev, "Missing property func-id\n");
+		return -EINVAL;
+	}
+
+	chan->func_id = func_id;
+
+	ret = scmi_dt_get_smt_buffer(dev, &chan->smt);
+	if (ret) {
+		dev_err(dev, "Failed to get smt resources: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct udevice_id scmi_smccc_ids[] = {
+	{ .compatible = "arm,scmi-smc" },
+	{ }
+};
+
+static const struct scmi_agent_ops scmi_smccc_ops = {
+	.process_msg = scmi_smccc_process_msg,
+};
+
+U_BOOT_DRIVER(scmi_smccc) = {
+	.name		= "scmi-over-smccc",
+	.id		= UCLASS_SCMI_AGENT,
+	.of_match	= scmi_smccc_ids,
+	.priv_auto_alloc_size = sizeof(struct scmi_smccc_channel),
+	.probe		= scmi_smccc_probe,
+	.ops		= &scmi_smccc_ops,
+};

+ 139 - 0
drivers/firmware/scmi/smt.c

@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
+ * Copyright (C) 2019-2020 Linaro Limited.
+ */
+
+#include <common.h>
+#include <cpu_func.h>
+#include <dm.h>
+#include <errno.h>
+#include <scmi_agent.h>
+#include <asm/cache.h>
+#include <asm/system.h>
+#include <dm/ofnode.h>
+#include <linux/compat.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+
+#include "smt.h"
+
+/**
+ * Get shared memory configuration defined by the referred DT phandle
+ * Return with a errno compliant value.
+ */
+int scmi_dt_get_smt_buffer(struct udevice *dev, struct scmi_smt *smt)
+{
+	int ret;
+	struct ofnode_phandle_args args;
+	struct resource resource;
+	fdt32_t faddr;
+	phys_addr_t paddr;
+
+	ret = dev_read_phandle_with_args(dev, "shmem", NULL, 0, 0, &args);
+	if (ret)
+		return ret;
+
+	ret = ofnode_read_resource(args.node, 0, &resource);
+	if (ret)
+		return ret;
+
+	faddr = cpu_to_fdt32(resource.start);
+	paddr = ofnode_translate_address(args.node, &faddr);
+
+	smt->size = resource_size(&resource);
+	if (smt->size < sizeof(struct scmi_smt_header)) {
+		dev_err(dev, "Shared memory buffer too small\n");
+		return -EINVAL;
+	}
+
+	smt->buf = devm_ioremap(dev, paddr, smt->size);
+	if (!smt->buf)
+		return -ENOMEM;
+
+#ifdef CONFIG_ARM
+	if (dcache_status())
+		mmu_set_region_dcache_behaviour((uintptr_t)smt->buf,
+						smt->size, DCACHE_OFF);
+#endif
+
+	return 0;
+}
+
+/**
+ * Write SCMI message @msg into a SMT shared buffer @smt.
+ * Return 0 on success and with a negative errno in case of error.
+ */
+int scmi_write_msg_to_smt(struct udevice *dev, struct scmi_smt *smt,
+			  struct scmi_msg *msg)
+{
+	struct scmi_smt_header *hdr = (void *)smt->buf;
+
+	if ((!msg->in_msg && msg->in_msg_sz) ||
+	    (!msg->out_msg && msg->out_msg_sz))
+		return -EINVAL;
+
+	if (!(hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE)) {
+		dev_dbg(dev, "Channel busy\n");
+		return -EBUSY;
+	}
+
+	if (smt->size < (sizeof(*hdr) + msg->in_msg_sz) ||
+	    smt->size < (sizeof(*hdr) + msg->out_msg_sz)) {
+		dev_dbg(dev, "Buffer too small\n");
+		return -ETOOSMALL;
+	}
+
+	/* Load message in shared memory */
+	hdr->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE;
+	hdr->length = msg->in_msg_sz + sizeof(hdr->msg_header);
+	hdr->msg_header = SMT_HEADER_TOKEN(0) |
+			  SMT_HEADER_MESSAGE_TYPE(0) |
+			  SMT_HEADER_PROTOCOL_ID(msg->protocol_id) |
+			  SMT_HEADER_MESSAGE_ID(msg->message_id);
+
+	memcpy_toio(hdr->msg_payload, msg->in_msg, msg->in_msg_sz);
+
+	return 0;
+}
+
+/**
+ * Read SCMI message from a SMT shared buffer @smt and copy it into @msg.
+ * Return 0 on success and with a negative errno in case of error.
+ */
+int scmi_read_resp_from_smt(struct udevice *dev, struct scmi_smt *smt,
+			    struct scmi_msg *msg)
+{
+	struct scmi_smt_header *hdr = (void *)smt->buf;
+
+	if (!(hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE)) {
+		dev_err(dev, "Channel unexpectedly busy\n");
+		return -EBUSY;
+	}
+
+	if (hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR) {
+		dev_err(dev, "Channel error reported, reset channel\n");
+		return -ECOMM;
+	}
+
+	if (hdr->length > msg->out_msg_sz + sizeof(hdr->msg_header)) {
+		dev_err(dev, "Buffer to small\n");
+		return -ETOOSMALL;
+	}
+
+	/* Get the data */
+	msg->out_msg_sz = hdr->length - sizeof(hdr->msg_header);
+	memcpy_fromio(msg->out_msg, hdr->msg_payload, msg->out_msg_sz);
+
+	return 0;
+}
+
+/**
+ * Clear SMT flags in shared buffer to allow further message exchange
+ */
+void scmi_clear_smt_channel(struct scmi_smt *smt)
+{
+	struct scmi_smt_header *hdr = (void *)smt->buf;
+
+	hdr->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR;
+}

+ 86 - 0
drivers/firmware/scmi/smt.h

@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
+ * Copyright (C) 2019-2020 Linaro Limited.
+ */
+#ifndef SCMI_SMT_H
+#define SCMI_SMT_H
+
+#include <asm/types.h>
+
+/**
+ * struct scmi_smt_header - Description of the shared memory message buffer
+ *
+ * SMT stands for Shared Memory based Transport.
+ * SMT uses 28 byte header prior message payload to handle the state of
+ * the communication channel realized by the shared memory area and
+ * to define SCMI protocol information the payload relates to.
+ */
+struct scmi_smt_header {
+	__le32 reserved;
+	__le32 channel_status;
+#define SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR	BIT(1)
+#define SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE	BIT(0)
+	__le32 reserved1[2];
+	__le32 flags;
+#define SCMI_SHMEM_FLAG_INTR_ENABLED		BIT(0)
+	__le32 length;
+	__le32 msg_header;
+	u8 msg_payload[0];
+};
+
+#define SMT_HEADER_TOKEN(token)		(((token) << 18) & GENMASK(31, 18))
+#define SMT_HEADER_PROTOCOL_ID(proto)	(((proto) << 10) & GENMASK(17, 10))
+#define SMT_HEADER_MESSAGE_TYPE(type)	(((type) << 18) & GENMASK(9, 8))
+#define SMT_HEADER_MESSAGE_ID(id)	((id) & GENMASK(7, 0))
+
+/**
+ * struct scmi_smt - Description of a SMT memory buffer
+ * @buf:	Shared memory base address
+ * @size:	Shared memory byte size
+ */
+struct scmi_smt {
+	u8 *buf;
+	size_t size;
+};
+
+static inline bool scmi_smt_channel_is_free(struct scmi_smt *smt)
+{
+	struct scmi_smt_header *hdr = (void *)smt->buf;
+
+	return hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE;
+}
+
+static inline bool scmi_smt_channel_reports_error(struct scmi_smt *smt)
+{
+	struct scmi_smt_header *hdr = (void *)smt->buf;
+
+	return hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR;
+}
+
+static inline void scmi_smt_get_channel(struct scmi_smt *smt)
+{
+	struct scmi_smt_header *hdr = (void *)smt->buf;
+
+	hdr->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE;
+}
+
+static inline void scmi_smt_put_channel(struct scmi_smt *smt)
+{
+	struct scmi_smt_header *hdr = (void *)smt->buf;
+
+	hdr->channel_status |= SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE;
+	hdr->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR;
+}
+
+int scmi_dt_get_smt_buffer(struct udevice *dev, struct scmi_smt *smt);
+
+int scmi_write_msg_to_smt(struct udevice *dev, struct scmi_smt *smt,
+			  struct scmi_msg *msg);
+
+int scmi_read_resp_from_smt(struct udevice *dev, struct scmi_smt *smt,
+			    struct scmi_msg *msg);
+
+void scmi_clear_smt_channel(struct scmi_smt *smt);
+
+#endif /* SCMI_SMT_H */

+ 71 - 0
drivers/gpio/gpio-uclass.c

@@ -6,6 +6,8 @@
 #include <common.h>
 #include <dm.h>
 #include <log.h>
+#include <dm/devres.h>
+#include <dm/device_compat.h>
 #include <dm/device-internal.h>
 #include <dm/lists.h>
 #include <dm/uclass-internal.h>
@@ -1209,6 +1211,75 @@ int gpio_dev_request_index(struct udevice *dev, const char *nodename,
 				 flags, 0, dev);
 }
 
+static void devm_gpiod_release(struct udevice *dev, void *res)
+{
+	dm_gpio_free(dev, res);
+}
+
+static int devm_gpiod_match(struct udevice *dev, void *res, void *data)
+{
+	return res == data;
+}
+
+struct gpio_desc *devm_gpiod_get_index(struct udevice *dev, const char *id,
+				       unsigned int index, int flags)
+{
+	int rc;
+	struct gpio_desc *desc;
+	char *propname;
+	static const char suffix[] = "-gpios";
+
+	propname = malloc(strlen(id) + sizeof(suffix));
+	if (!propname) {
+		rc = -ENOMEM;
+		goto end;
+	}
+
+	strcpy(propname, id);
+	strcat(propname, suffix);
+
+	desc = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc),
+			    __GFP_ZERO);
+	if (unlikely(!desc)) {
+		rc = -ENOMEM;
+		goto end;
+	}
+
+	rc = gpio_request_by_name(dev, propname, index, desc, flags);
+
+end:
+	if (propname)
+		free(propname);
+
+	if (rc)
+		return ERR_PTR(rc);
+
+	devres_add(dev, desc);
+
+	return desc;
+}
+
+struct gpio_desc *devm_gpiod_get_index_optional(struct udevice *dev,
+						const char *id,
+						unsigned int index,
+						int flags)
+{
+	struct gpio_desc *desc = devm_gpiod_get_index(dev, id, index, flags);
+
+	if (IS_ERR(desc))
+		return NULL;
+
+	return desc;
+}
+
+void devm_gpiod_put(struct udevice *dev, struct gpio_desc *desc)
+{
+	int rc;
+
+	rc = devres_release(dev, devm_gpiod_release, devm_gpiod_match, desc);
+	WARN_ON(rc);
+}
+
 static int gpio_post_bind(struct udevice *dev)
 {
 	struct udevice *child;

+ 8 - 0
drivers/reset/Kconfig

@@ -181,4 +181,12 @@ config RESET_RASPBERRYPI
 	  relevant. This driver provides a reset controller capable of
 	  interfacing with RPi4's co-processor and model these firmware
 	  initialization routines as reset lines.
+
+config RESET_SCMI
+	bool "Enable SCMI reset domain driver"
+	select SCMI_FIRMWARE
+	help
+	  Enable this option if you want to support reset controller
+	  devices exposed by a SCMI agent based on SCMI reset domain
+	  protocol communication with a SCMI server.
 endmenu

+ 1 - 0
drivers/reset/Makefile

@@ -27,3 +27,4 @@ obj-$(CONFIG_RESET_IPQ419) += reset-ipq4019.o
 obj-$(CONFIG_RESET_SIFIVE) += reset-sifive.o
 obj-$(CONFIG_RESET_SYSCON) += reset-syscon.o
 obj-$(CONFIG_RESET_RASPBERRYPI) += reset-raspberrypi.o
+obj-$(CONFIG_RESET_SCMI) += reset-scmi.o

+ 81 - 0
drivers/reset/reset-scmi.c

@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019-2020 Linaro Limited
+ */
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <reset-uclass.h>
+#include <scmi_agent.h>
+#include <scmi_protocols.h>
+#include <asm/types.h>
+
+static int scmi_reset_set_level(struct reset_ctl *rst, bool assert_not_deassert)
+{
+	struct scmi_rd_reset_in in = {
+		.domain_id = rst->id,
+		.flags = assert_not_deassert ? SCMI_RD_RESET_FLAG_ASSERT : 0,
+		.reset_state = 0,
+	};
+	struct scmi_rd_reset_out out;
+	struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_RESET_DOMAIN,
+					  SCMI_RESET_DOMAIN_RESET,
+					  in, out);
+	int ret;
+
+	ret = devm_scmi_process_msg(rst->dev->parent, &msg);
+	if (ret)
+		return ret;
+
+	return scmi_to_linux_errno(out.status);
+}
+
+static int scmi_reset_assert(struct reset_ctl *rst)
+{
+	return scmi_reset_set_level(rst, true);
+}
+
+static int scmi_reset_deassert(struct reset_ctl *rst)
+{
+	return scmi_reset_set_level(rst, false);
+}
+
+static int scmi_reset_request(struct reset_ctl *rst)
+{
+	struct scmi_rd_attr_in in = {
+		.domain_id = rst->id,
+	};
+	struct scmi_rd_attr_out out;
+	struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_RESET_DOMAIN,
+					  SCMI_RESET_DOMAIN_ATTRIBUTES,
+					  in, out);
+	int ret;
+
+	/*
+	 * We don't really care about the attribute, just check
+	 * the reset domain exists.
+	 */
+	ret = devm_scmi_process_msg(rst->dev->parent, &msg);
+	if (ret)
+		return ret;
+
+	return scmi_to_linux_errno(out.status);
+}
+
+static int scmi_reset_rfree(struct reset_ctl *rst)
+{
+	return 0;
+}
+
+static const struct reset_ops scmi_reset_domain_ops = {
+	.request	= scmi_reset_request,
+	.rfree		= scmi_reset_rfree,
+	.rst_assert	= scmi_reset_assert,
+	.rst_deassert	= scmi_reset_deassert,
+};
+
+U_BOOT_DRIVER(scmi_reset_domain) = {
+	.name = "scmi_reset_domain",
+	.id = UCLASS_RESET,
+	.ops = &scmi_reset_domain_ops,
+};

+ 114 - 4
drivers/reset/reset-uclass.c

@@ -11,6 +11,7 @@
 #include <reset.h>
 #include <reset-uclass.h>
 #include <dm/devres.h>
+#include <dm/lists.h>
 
 static inline struct reset_ops *reset_dev_ops(struct udevice *dev)
 {
@@ -100,13 +101,14 @@ int reset_get_by_index_nodev(ofnode node, int index,
 				       index > 0, reset_ctl);
 }
 
-int reset_get_bulk(struct udevice *dev, struct reset_ctl_bulk *bulk)
+static int __reset_get_bulk(struct udevice *dev, ofnode node,
+			    struct reset_ctl_bulk *bulk)
 {
 	int i, ret, err, count;
-	
+
 	bulk->count = 0;
 
-	count = dev_count_phandle_with_args(dev, "resets", "#reset-cells");
+	count = ofnode_count_phandle_with_args(node, "resets", "#reset-cells");
 	if (count < 1)
 		return count;
 
@@ -116,7 +118,7 @@ int reset_get_bulk(struct udevice *dev, struct reset_ctl_bulk *bulk)
 		return -ENOMEM;
 
 	for (i = 0; i < count; i++) {
-		ret = reset_get_by_index(dev, i, &bulk->resets[i]);
+		ret = reset_get_by_index_nodev(node, i, &bulk->resets[i]);
 		if (ret < 0)
 			goto bulk_get_err;
 
@@ -134,6 +136,11 @@ bulk_get_err:
 	return ret;
 }
 
+int reset_get_bulk(struct udevice *dev, struct reset_ctl_bulk *bulk)
+{
+	return __reset_get_bulk(dev, dev_ofnode(dev), bulk);
+}
+
 int reset_get_by_name(struct udevice *dev, const char *name,
 		     struct reset_ctl *reset_ctl)
 {
@@ -246,6 +253,109 @@ int reset_release_all(struct reset_ctl *reset_ctl, int count)
 	return 0;
 }
 
+static void devm_reset_release(struct udevice *dev, void *res)
+{
+	reset_free(res);
+}
+
+struct reset_ctl *devm_reset_control_get_by_index(struct udevice *dev,
+						  int index)
+{
+	int rc;
+	struct reset_ctl *reset_ctl;
+
+	reset_ctl = devres_alloc(devm_reset_release, sizeof(struct reset_ctl),
+				 __GFP_ZERO);
+	if (unlikely(!reset_ctl))
+		return ERR_PTR(-ENOMEM);
+
+	rc = reset_get_by_index(dev, index, reset_ctl);
+	if (rc)
+		return ERR_PTR(rc);
+
+	devres_add(dev, reset_ctl);
+	return reset_ctl;
+}
+
+struct reset_ctl *devm_reset_control_get(struct udevice *dev, const char *id)
+{
+	int rc;
+	struct reset_ctl *reset_ctl;
+
+	reset_ctl = devres_alloc(devm_reset_release, sizeof(struct reset_ctl),
+				 __GFP_ZERO);
+	if (unlikely(!reset_ctl))
+		return ERR_PTR(-ENOMEM);
+
+	rc = reset_get_by_name(dev, id, reset_ctl);
+	if (rc)
+		return ERR_PTR(rc);
+
+	devres_add(dev, reset_ctl);
+	return reset_ctl;
+}
+
+struct reset_ctl *devm_reset_control_get_optional(struct udevice *dev,
+						  const char *id)
+{
+	struct reset_ctl *r = devm_reset_control_get(dev, id);
+
+	if (IS_ERR(r))
+		return NULL;
+
+	return r;
+}
+
+static void devm_reset_bulk_release(struct udevice *dev, void *res)
+{
+	struct reset_ctl_bulk *bulk = res;
+
+	reset_release_all(bulk->resets, bulk->count);
+}
+
+struct reset_ctl_bulk *devm_reset_bulk_get_by_node(struct udevice *dev,
+						   ofnode node)
+{
+	int rc;
+	struct reset_ctl_bulk *bulk;
+
+	bulk = devres_alloc(devm_reset_bulk_release,
+			    sizeof(struct reset_ctl_bulk),
+			    __GFP_ZERO);
+	if (unlikely(!bulk))
+		return ERR_PTR(-ENOMEM);
+
+	rc = __reset_get_bulk(dev, node, bulk);
+	if (rc)
+		return ERR_PTR(rc);
+
+	devres_add(dev, bulk);
+	return bulk;
+}
+
+struct reset_ctl_bulk *devm_reset_bulk_get_optional_by_node(struct udevice *dev,
+							    ofnode node)
+{
+	struct reset_ctl_bulk *bulk;
+
+	bulk = devm_reset_bulk_get_by_node(dev, node);
+
+	if (IS_ERR(bulk))
+		return NULL;
+
+	return bulk;
+}
+
+struct reset_ctl_bulk *devm_reset_bulk_get(struct udevice *dev)
+{
+	return devm_reset_bulk_get_by_node(dev, dev_ofnode(dev));
+}
+
+struct reset_ctl_bulk *devm_reset_bulk_get_optional(struct udevice *dev)
+{
+	return devm_reset_bulk_get_optional_by_node(dev, dev_ofnode(dev));
+}
+
 UCLASS_DRIVER(reset) = {
 	.id		= UCLASS_RESET,
 	.name		= "reset",

+ 45 - 6
drivers/reset/sandbox-reset-test.c

@@ -10,66 +10,105 @@
 #include <reset.h>
 #include <asm/io.h>
 #include <asm/reset.h>
+#include <linux/err.h>
 
 struct sandbox_reset_test {
 	struct reset_ctl ctl;
 	struct reset_ctl_bulk bulk;
+
+	struct reset_ctl *ctlp;
+	struct reset_ctl_bulk *bulkp;
 };
 
 int sandbox_reset_test_get(struct udevice *dev)
 {
 	struct sandbox_reset_test *sbrt = dev_get_priv(dev);
 
+	sbrt->ctlp = &sbrt->ctl;
 	return reset_get_by_name(dev, "test", &sbrt->ctl);
 }
 
+int sandbox_reset_test_get_devm(struct udevice *dev)
+{
+	struct sandbox_reset_test *sbrt = dev_get_priv(dev);
+	struct reset_ctl *r;
+
+	r = devm_reset_control_get(dev, "not-a-valid-reset-ctl");
+	if (!IS_ERR(r))
+		return -EINVAL;
+
+	r = devm_reset_control_get_optional(dev, "not-a-valid-reset-ctl");
+	if (r)
+		return -EINVAL;
+
+	sbrt->ctlp = devm_reset_control_get(dev, "test");
+	if (IS_ERR(sbrt->ctlp))
+		return PTR_ERR(sbrt->ctlp);
+
+	return 0;
+}
+
 int sandbox_reset_test_get_bulk(struct udevice *dev)
 {
 	struct sandbox_reset_test *sbrt = dev_get_priv(dev);
 
+	sbrt->bulkp = &sbrt->bulk;
 	return reset_get_bulk(dev, &sbrt->bulk);
 }
 
+int sandbox_reset_test_get_bulk_devm(struct udevice *dev)
+{
+	struct sandbox_reset_test *sbrt = dev_get_priv(dev);
+	struct reset_ctl_bulk *r;
+
+	r = devm_reset_bulk_get_optional(dev);
+	if (IS_ERR(r))
+		return PTR_ERR(r);
+
+	sbrt->bulkp = r;
+	return 0;
+}
+
 int sandbox_reset_test_assert(struct udevice *dev)
 {
 	struct sandbox_reset_test *sbrt = dev_get_priv(dev);
 
-	return reset_assert(&sbrt->ctl);
+	return reset_assert(sbrt->ctlp);
 }
 
 int sandbox_reset_test_assert_bulk(struct udevice *dev)
 {
 	struct sandbox_reset_test *sbrt = dev_get_priv(dev);
 
-	return reset_assert_bulk(&sbrt->bulk);
+	return reset_assert_bulk(sbrt->bulkp);
 }
 
 int sandbox_reset_test_deassert(struct udevice *dev)
 {
 	struct sandbox_reset_test *sbrt = dev_get_priv(dev);
 
-	return reset_deassert(&sbrt->ctl);
+	return reset_deassert(sbrt->ctlp);
 }
 
 int sandbox_reset_test_deassert_bulk(struct udevice *dev)
 {
 	struct sandbox_reset_test *sbrt = dev_get_priv(dev);
 
-	return reset_deassert_bulk(&sbrt->bulk);
+	return reset_deassert_bulk(sbrt->bulkp);
 }
 
 int sandbox_reset_test_free(struct udevice *dev)
 {
 	struct sandbox_reset_test *sbrt = dev_get_priv(dev);
 
-	return reset_free(&sbrt->ctl);
+	return reset_free(sbrt->ctlp);
 }
 
 int sandbox_reset_test_release_bulk(struct udevice *dev)
 {
 	struct sandbox_reset_test *sbrt = dev_get_priv(dev);
 
-	return reset_release_bulk(&sbrt->bulk);
+	return reset_release_bulk(sbrt->bulkp);
 }
 
 static const struct udevice_id sandbox_reset_test_ids[] = {

+ 19 - 0
drivers/reset/sandbox-reset.c

@@ -15,6 +15,7 @@
 
 struct sandbox_reset_signal {
 	bool asserted;
+	bool requested;
 };
 
 struct sandbox_reset {
@@ -23,18 +24,24 @@ struct sandbox_reset {
 
 static int sandbox_reset_request(struct reset_ctl *reset_ctl)
 {
+	struct sandbox_reset *sbr = dev_get_priv(reset_ctl->dev);
+
 	debug("%s(reset_ctl=%p)\n", __func__, reset_ctl);
 
 	if (reset_ctl->id >= SANDBOX_RESET_SIGNALS)
 		return -EINVAL;
 
+	sbr->signals[reset_ctl->id].requested = true;
 	return 0;
 }
 
 static int sandbox_reset_free(struct reset_ctl *reset_ctl)
 {
+	struct sandbox_reset *sbr = dev_get_priv(reset_ctl->dev);
+
 	debug("%s(reset_ctl=%p)\n", __func__, reset_ctl);
 
+	sbr->signals[reset_ctl->id].requested = false;
 	return 0;
 }
 
@@ -107,3 +114,15 @@ int sandbox_reset_query(struct udevice *dev, unsigned long id)
 
 	return sbr->signals[id].asserted;
 }
+
+int sandbox_reset_is_requested(struct udevice *dev, unsigned long id)
+{
+	struct sandbox_reset *sbr = dev_get_priv(dev);
+
+	debug("%s(dev=%p, id=%ld)\n", __func__, dev, id);
+
+	if (id >= SANDBOX_RESET_SIGNALS)
+		return -EINVAL;
+
+	return sbr->signals[id].requested;
+}

+ 47 - 0
include/asm-generic/gpio.h

@@ -701,4 +701,51 @@ int gpio_get_number(const struct gpio_desc *desc);
  */
 int gpio_get_acpi(const struct gpio_desc *desc, struct acpi_gpio *gpio);
 
+/**
+ * devm_gpiod_get_index - Resource-managed gpiod_get()
+ * @dev:	GPIO consumer
+ * @con_id:	function within the GPIO consumer
+ * @index:	index of the GPIO to obtain in the consumer
+ * @flags:	optional GPIO initialization flags
+ *
+ * Managed gpiod_get(). GPIO descriptors returned from this function are
+ * automatically disposed on device unbind.
+ * Return the GPIO descriptor corresponding to the function con_id of device
+ * dev, -ENOENT if no GPIO has been assigned to the requested function, or
+ * another IS_ERR() code if an error occurred while trying to acquire the GPIO.
+ */
+struct gpio_desc *devm_gpiod_get_index(struct udevice *dev, const char *id,
+				       unsigned int index, int flags);
+
+#define devm_gpiod_get(dev, id, flags) devm_gpiod_get_index(dev, id, 0, flags)
+/**
+ * gpiod_get_optional - obtain an optional GPIO for a given GPIO function
+ * @dev: GPIO consumer, can be NULL for system-global GPIOs
+ * @con_id: function within the GPIO consumer
+ * @index:	index of the GPIO to obtain in the consumer
+ * @flags: optional GPIO initialization flags
+ *
+ * This is equivalent to devm_gpiod_get(), except that when no GPIO was
+ * assigned to the requested function it will return NULL. This is convenient
+ * for drivers that need to handle optional GPIOs.
+ */
+struct gpio_desc *devm_gpiod_get_index_optional(struct udevice *dev,
+						const char *id,
+						unsigned int index,
+						int flags);
+
+#define devm_gpiod_get_optional(dev, id, flags) \
+	devm_gpiod_get_index_optional(dev, id, 0, flags)
+
+/**
+ * devm_gpiod_put - Resource-managed gpiod_put()
+ * @dev:	GPIO consumer
+ * @desc:	GPIO descriptor to dispose of
+ *
+ * Dispose of a GPIO descriptor obtained with devm_gpiod_get() or
+ * devm_gpiod_get_index(). Normally this function will not be called as the GPIO
+ * will be disposed of by the resource management code.
+ */
+void devm_gpiod_put(struct udevice *dev, struct gpio_desc *desc);
+
 #endif	/* _ASM_GENERIC_GPIO_H_ */

+ 1 - 0
include/dm/uclass-id.h

@@ -94,6 +94,7 @@ enum uclass_id {
 	UCLASS_RESET,		/* Reset controller device */
 	UCLASS_RNG,		/* Random Number Generator */
 	UCLASS_RTC,		/* Real time clock device */
+	UCLASS_SCMI_AGENT,	/* Interface with an SCMI server */
 	UCLASS_SCSI,		/* SCSI device */
 	UCLASS_SERIAL,		/* Serial UART */
 	UCLASS_SIMPLE_BUS,	/* Bus with child devices */

+ 191 - 14
include/regmap.h

@@ -75,14 +75,41 @@ struct regmap_range {
 	ulong size;
 };
 
+struct regmap_bus;
+
+/**
+ * struct regmap_config - Configure the behaviour of a regmap
+ *
+ * @width:		Width of the read/write operations. Defaults to
+ *			REGMAP_SIZE_32 if set to 0.
+ * @reg_offset_shift	Left shift the register offset by this value before
+ *			performing read or write.
+ * @r_start:		If specified, the regmap is created with one range
+ *			which starts at this address, instead of finding the
+ *			start from device tree.
+ * @r_size:		Same as above for the range size
+ */
+struct regmap_config {
+	enum regmap_size_t width;
+	u32 reg_offset_shift;
+	ulong r_start;
+	ulong r_size;
+};
+
 /**
  * struct regmap - a way of accessing hardware/bus registers
  *
+ * @width:		Width of the read/write operations. Defaults to
+ *			REGMAP_SIZE_32 if set to 0.
+ * @reg_offset_shift	Left shift the register offset by this value before
+ *			performing read or write.
  * @range_count:	Number of ranges available within the map
  * @ranges:		Array of ranges
  */
 struct regmap {
 	enum regmap_endianness_t endianness;
+	enum regmap_size_t width;
+	u32 reg_offset_shift;
 	int range_count;
 	struct regmap_range ranges[0];
 };
@@ -93,32 +120,24 @@ struct regmap {
  */
 
 /**
- * regmap_write() - Write a 32-bit value to a regmap
+ * regmap_write() - Write a value to a regmap
  *
  * @map:	Regmap to write to
  * @offset:	Offset in the regmap to write to
  * @val:	Data to write to the regmap at the specified offset
  *
- * Note that this function will only write values of 32 bit width to the
- * regmap; if the size of data to be read is different, the regmap_raw_write
- * function can be used.
- *
  * Return: 0 if OK, -ve on error
  */
 int regmap_write(struct regmap *map, uint offset, uint val);
 
 /**
- * regmap_read() - Read a 32-bit value from a regmap
+ * regmap_read() - Read a value from a regmap
  *
  * @map:	Regmap to read from
  * @offset:	Offset in the regmap to read from
  * @valp:	Pointer to the buffer to receive the data read from the regmap
  *		at the specified offset
  *
- * Note that this function will only read values of 32 bit width from the
- * regmap; if the size of data to be read is different, the regmap_raw_read
- * function can be used.
- *
  * Return: 0 if OK, -ve on error
  */
 int regmap_read(struct regmap *map, uint offset, uint *valp);
@@ -132,8 +151,9 @@ int regmap_read(struct regmap *map, uint offset, uint *valp);
  * @val_len:	Length of the data to be written to the regmap
  *
  * Note that this function will, as opposed to regmap_write, write data of
- * arbitrary length to the regmap, and not just 32-bit values, and is thus a
- * generalized version of regmap_write.
+ * arbitrary length to the regmap, and not just the size configured in the
+ * regmap (defaults to 32-bit) and is thus a generalized version of
+ * regmap_write.
  *
  * Return: 0 if OK, -ve on error
  */
@@ -150,8 +170,9 @@ int regmap_raw_write(struct regmap *map, uint offset, const void *val,
  * @val_len:	Length of the data to be read from the regmap
  *
  * Note that this function will, as opposed to regmap_read, read data of
- * arbitrary length from the regmap, and not just 32-bit values, and is thus a
- * generalized version of regmap_read.
+ * arbitrary length from the regmap, and not just the size configured in the
+ * regmap (defaults to 32-bit) and is thus a generalized version of
+ * regmap_read.
  *
  * Return: 0 if OK, -ve on error
  */
@@ -291,6 +312,43 @@ int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset,
 	regmap_read_poll_timeout_test(map, addr, val, cond, sleep_us, \
 				      timeout_ms, 0) \
 
+/**
+ * regmap_field_read_poll_timeout - Poll until a condition is met or a timeout
+ *				    occurs
+ *
+ * @field:	Regmap field to read from
+ * @val:	Unsigned integer variable to read the value into
+ * @cond:	Break condition (usually involving @val)
+ * @sleep_us:	Maximum time to sleep between reads in us (0 tight-loops).
+ * @timeout_ms:	Timeout in ms, 0 means never timeout
+ *
+ * Returns 0 on success and -ETIMEDOUT upon a timeout or the regmap_field_read
+ * error return value in case of a error read. In the two former cases,
+ * the last read value at @addr is stored in @val.
+ *
+ * This is modelled after the regmap_read_poll_timeout macros in linux but
+ * with millisecond timeout.
+ */
+#define regmap_field_read_poll_timeout(field, val, cond, sleep_us, timeout_ms) \
+({ \
+	unsigned long __start = get_timer(0); \
+	int __ret; \
+	for (;;) { \
+		__ret = regmap_field_read((field), &(val)); \
+		if (__ret) \
+			break; \
+		if (cond) \
+			break; \
+		if ((timeout_ms) && get_timer(__start) > (timeout_ms)) { \
+			__ret = regmap_field_read((field), &(val)); \
+			break; \
+		} \
+		if ((sleep_us)) \
+			udelay((sleep_us)); \
+	} \
+	__ret ?: ((cond) ? 0 : -ETIMEDOUT); \
+})
+
 /**
  * regmap_update_bits() - Perform a read/modify/write using a mask
  *
@@ -335,6 +393,40 @@ int regmap_init_mem_platdata(struct udevice *dev, fdt_val_t *reg, int count,
 
 int regmap_init_mem_index(ofnode node, struct regmap **mapp, int index);
 
+/**
+ * regmap_init_mem_range() - Set up a new memory region for ofnode with the
+ *			     specified range.
+ *
+ * @node:	The ofnode for the map.
+ * @r_start:	Start of the range.
+ * @r_size:	Size of the range.
+ * @mapp:	Returns allocated map.
+ *
+ * Return: 0 in success, -errno otherwise
+ *
+ * This creates a regmap with one range where instead of extracting the range
+ * from 'node', it is created based on the parameters specified. This is
+ * useful when a driver needs to calculate the base of the regmap at runtime,
+ * and can't specify it in device tree.
+ */
+int regmap_init_mem_range(ofnode node, ulong r_start, ulong r_size,
+			  struct regmap **mapp);
+
+/**
+ * devm_regmap_init() - Initialise register map (device managed)
+ *
+ * @dev: Device that will be interacted with
+ * @bus: Bus-specific callbacks to use with device (IGNORED)
+ * @bus_context: Data passed to bus-specific callbacks (IGNORED)
+ * @config: Configuration for register map
+ *
+ * @Return a valid pointer to a struct regmap or a ERR_PTR() on error.
+ * The structure is automatically freed when the device is unbound
+ */
+struct regmap *devm_regmap_init(struct udevice *dev,
+				const struct regmap_bus *bus,
+				void *bus_context,
+				const struct regmap_config *config);
 /**
  * regmap_get_range() - Obtain the base memory address of a regmap range
  *
@@ -352,4 +444,89 @@ void *regmap_get_range(struct regmap *map, unsigned int range_num);
  */
 int regmap_uninit(struct regmap *map);
 
+/**
+ * struct reg_field - Description of an register field
+ *
+ * @reg: Offset of the register within the regmap bank
+ * @lsb: lsb of the register field.
+ * @msb: msb of the register field.
+ */
+struct reg_field {
+	unsigned int reg;
+	unsigned int lsb;
+	unsigned int msb;
+};
+
+struct regmap_field;
+
+/**
+ * REG_FIELD() - A convenient way to initialize a 'struct reg_feild'.
+ *
+ * @_reg: Offset of the register within the regmap bank
+ * @_lsb: lsb of the register field.
+ * @_msb: msb of the register field.
+ *
+ * Register fields are often described in terms of 3 things: the register it
+ * belongs to, its LSB, and its MSB. This macro can be used by drivers to
+ * clearly and easily initialize a 'struct regmap_field'.
+ *
+ * For example, say a device has a register at offset DEV_REG1 (0x100) and a
+ * field of DEV_REG1 is on bits [7:3]. So a driver can initialize a regmap
+ * field for this by doing:
+ *     struct reg_field field = REG_FIELD(DEV_REG1, 3, 7);
+ */
+#define REG_FIELD(_reg, _lsb, _msb) {		\
+				.reg = _reg,	\
+				.lsb = _lsb,	\
+				.msb = _msb,	\
+				}
+
+/**
+ * devm_regmap_field_alloc() - Allocate and initialise a register field.
+ *
+ * @dev: Device that will be interacted with
+ * @regmap: regmap bank in which this register field is located.
+ * @reg_field: Register field with in the bank.
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct regmap_field. The regmap_field will be automatically freed
+ * by the device management code.
+ */
+struct regmap_field *devm_regmap_field_alloc(struct udevice *dev,
+					     struct regmap *regmap,
+					     struct reg_field reg_field);
+/**
+ * devm_regmap_field_free() - Free a register field allocated using
+ *                            devm_regmap_field_alloc.
+ *
+ * @dev: Device that will be interacted with
+ * @field: regmap field which should be freed.
+ *
+ * Free register field allocated using devm_regmap_field_alloc(). Usually
+ * drivers need not call this function, as the memory allocated via devm
+ * will be freed as per device-driver life-cyle.
+ */
+void devm_regmap_field_free(struct udevice *dev, struct regmap_field *field);
+
+/**
+ * regmap_field_write() - Write a value to a regmap field
+ *
+ * @field:	Regmap field to write to
+ * @val:	Data to write to the regmap at the specified offset
+ *
+ * Return: 0 if OK, -ve on error
+ */
+int regmap_field_write(struct regmap_field *field, unsigned int val);
+
+/**
+ * regmap_read() - Read a 32-bit value from a regmap
+ *
+ * @field:	Regmap field to write to
+ * @valp:	Pointer to the buffer to receive the data read from the regmap
+ *		field
+ *
+ * Return: 0 if OK, -ve on error
+ */
+int regmap_field_read(struct regmap_field *field, unsigned int *val);
+
 #endif

+ 134 - 1
include/reset.h

@@ -7,7 +7,7 @@
 #define _RESET_H
 
 #include <dm/ofnode.h>
-#include <linux/errno.h>
+#include <linux/err.h>
 
 /**
  * A reset is a hardware signal indicating that a HW module (or IP block, or
@@ -84,6 +84,98 @@ struct reset_ctl_bulk {
 };
 
 #if CONFIG_IS_ENABLED(DM_RESET)
+
+/**
+ * devm_reset_control_get - resource managed reset_get_by_name()
+ * @dev: device to be reset by the controller
+ * @id: reset line name
+ *
+ * Managed reset_get_by_name(). For reset controllers returned
+ * from this function, reset_free() is called automatically on driver
+ * detach.
+ *
+ * Returns a struct reset_ctl or IS_ERR() condition containing errno.
+ */
+struct reset_ctl *devm_reset_control_get(struct udevice *dev, const char *id);
+
+/**
+ * devm_reset_control_get_optional - resource managed reset_get_by_name() that
+ *                                   can fail
+ * @dev:	The client device.
+ * @id:		reset line name
+ *
+ * Managed reset_get_by_name(). For reset controllers returned
+ * from this function, reset_free() is called automatically on driver
+ * detach.
+ *
+ * Returns a struct reset_ctl or a dummy reset controller if it failed.
+ */
+struct reset_ctl *devm_reset_control_get_optional(struct udevice *dev,
+						  const char *id);
+
+/**
+ * devm_reset_control_get - resource managed reset_get_by_index()
+ * @dev:	The client device.
+ * @index:	The index of the reset signal to request, within the client's
+ *		list of reset signals.
+ *
+ * Managed reset_get_by_index(). For reset controllers returned
+ * from this function, reset_free() is called automatically on driver
+ * detach.
+ *
+ * Returns a struct reset_ctl or IS_ERR() condition containing errno.
+ */
+struct reset_ctl *devm_reset_control_get_by_index(struct udevice *dev,
+						  int index);
+
+/**
+ * devm_reset_bulk_get - resource managed reset_get_bulk()
+ * @dev: device to be reset by the controller
+ *
+ * Managed reset_get_bulk(). For reset controllers returned
+ * from this function, reset_free() is called automatically on driver
+ * detach.
+ *
+ * Returns a struct reset_ctl or IS_ERR() condition containing errno.
+ */
+struct reset_ctl_bulk *devm_reset_bulk_get(struct udevice *dev);
+
+/**
+ * devm_reset_bulk_get_optional - resource managed reset_get_bulk() that
+ *                                can fail
+ * @dev:	The client device.
+ *
+ * Managed reset_get_bulk(). For reset controllers returned
+ * from this function, reset_free() is called automatically on driver
+ * detach.
+ *
+ * Returns a struct reset_ctl or NULL if it failed.
+ */
+struct reset_ctl_bulk *devm_reset_bulk_get_optional(struct udevice *dev);
+
+/**
+ * devm_reset_bulk_get_by_node - resource managed reset_get_bulk()
+ * @dev: device to be reset by the controller
+ * @node: ofnode where the "resets" property is. Usually a sub-node of
+ *        the dev's node.
+ *
+ * see devm_reset_bulk_get()
+ */
+struct reset_ctl_bulk *devm_reset_bulk_get_by_node(struct udevice *dev,
+						   ofnode node);
+
+/**
+ * devm_reset_bulk_get_optional_by_node - resource managed reset_get_bulk()
+ *                                        that can fail
+ * @dev: device to be reset by the controller
+ * @node: ofnode where the "resets" property is. Usually a sub-node of
+ *        the dev's node.
+ *
+ * see devm_reset_bulk_get_optional()
+ */
+struct reset_ctl_bulk *devm_reset_bulk_get_optional_by_node(struct udevice *dev,
+							    ofnode node);
+
 /**
  * reset_get_by_index - Get/request a reset signal by integer index.
  *
@@ -265,7 +357,48 @@ static inline int reset_release_bulk(struct reset_ctl_bulk *bulk)
 {
 	return reset_release_all(bulk->resets, bulk->count);
 }
+
 #else
+static inline struct reset_ctl *devm_reset_control_get(struct udevice *dev,
+						       const char *id)
+{
+	return ERR_PTR(-ENOTSUPP);
+}
+
+static inline struct reset_ctl *devm_reset_control_get_optional(struct udevice *dev,
+								const char *id)
+{
+	return NULL;
+}
+
+static inline struct reset_ctl *devm_reset_control_get_by_index(struct udevice *dev,
+								int index)
+{
+	return ERR_PTR(-ENOTSUPP);
+}
+
+static inline struct reset_ctl_bulk *devm_reset_bulk_get(struct udevice *dev)
+{
+	return ERR_PTR(-ENOTSUPP);
+}
+
+static inline struct reset_ctl_bulk *devm_reset_bulk_get_optional(struct udevice *dev)
+{
+	return NULL;
+}
+
+static inline struct reset_ctl_bulk *devm_reset_bulk_get_by_node(struct udevice *dev,
+								 ofnode node)
+{
+	return ERR_PTR(-ENOTSUPP);
+}
+
+static inline struct reset_ctl_bulk *devm_reset_bulk_get_optional_by_node(struct udevice *dev,
+									  ofnode node)
+{
+	return NULL;
+}
+
 static inline int reset_get_by_index(struct udevice *dev, int index,
 				     struct reset_ctl *reset_ctl)
 {

+ 24 - 0
include/scmi_agent-uclass.h

@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2019-2020 Linaro Limited.
+ */
+#ifndef _SCMI_AGENT_UCLASS_H
+#define _SCMI_AGENT_UCLASS_H
+
+struct udevice;
+struct scmi_msg;
+
+/**
+ * struct scmi_transport_ops - The functions that a SCMI transport layer must implement.
+ */
+struct scmi_agent_ops {
+	/*
+	 * process_msg - Request transport to get the SCMI message processed
+	 *
+	 * @agent:		Agent using the transport
+	 * @msg:		SCMI message to be transmitted
+	 */
+	int (*process_msg)(struct udevice *dev, struct scmi_msg *msg);
+};
+
+#endif /* _SCMI_TRANSPORT_UCLASS_H */

+ 68 - 0
include/scmi_agent.h

@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
+ * Copyright (C) 2019-2020, Linaro Limited
+ *
+ * An SCMI agent device represent on communication path from a
+ * device driver to the remote SCMI server which driver sends
+ * messages to and receives response messages from.
+ */
+#ifndef SCMI_AGENT_H
+#define SCMI_AGENT_H
+
+#include <asm/types.h>
+
+struct udevice;
+
+/*
+ * struct scmi_msg - Context of a SCMI message sent and the response received
+ *
+ * @protocol_id:	SCMI protocol ID
+ * @message_id:		SCMI message ID for a defined protocol ID
+ * @in_msg:		Pointer to the message payload sent by the driver
+ * @in_msg_sz:		Byte size of the message payload sent
+ * @out_msg:		Pointer to buffer to store response message payload
+ * @out_msg_sz:		Byte size of the response buffer and response payload
+ */
+struct scmi_msg {
+	unsigned int protocol_id;
+	unsigned int message_id;
+	u8 *in_msg;
+	size_t in_msg_sz;
+	u8 *out_msg;
+	size_t out_msg_sz;
+};
+
+/* Helper macro to match a message on input/output array references */
+#define SCMI_MSG_IN(_protocol, _message, _in_array, _out_array) \
+	(struct scmi_msg){			\
+		.protocol_id = (_protocol),	\
+		.message_id = (_message),	\
+		.in_msg = (uint8_t *)&(_in_array),	\
+		.in_msg_sz = sizeof(_in_array),	\
+		.out_msg = (uint8_t *)&(_out_array),	\
+		.out_msg_sz = sizeof(_out_array),	\
+	}
+
+/**
+ * scmi_send_and_process_msg() - send and process a SCMI message
+ *
+ * Send a message to a SCMI server through a target SCMI agent device.
+ * Caller sets scmi_msg::out_msg_sz to the output message buffer size.
+ * On return, scmi_msg::out_msg_sz stores the response payload size.
+ *
+ * @dev:	SCMI agent device
+ * @msg:	Message structure reference
+ * @return 0 on success and a negative errno on failure
+ */
+int devm_scmi_process_msg(struct udevice *dev, struct scmi_msg *msg);
+
+/**
+ * scmi_to_linux_errno() - Convert an SCMI error code into a Linux errno code
+ *
+ * @scmi_errno:	SCMI error code value
+ * @return 0 for successful status and a negative errno otherwise
+ */
+int scmi_to_linux_errno(s32 scmi_errno);
+
+#endif /* SCMI_H */

+ 179 - 0
include/scmi_protocols.h

@@ -0,0 +1,179 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
+ * Copyright (C) 2019-2020, Linaro Limited
+ */
+#ifndef _SCMI_PROTOCOLS_H
+#define _SCMI_PROTOCOLS_H
+
+#include <linux/bitops.h>
+#include <asm/types.h>
+
+/*
+ * Subset the SCMI protocols definition
+ * based on SCMI specification v2.0 (DEN0056B)
+ * https://developer.arm.com/docs/den0056/b
+ */
+
+enum scmi_std_protocol {
+	SCMI_PROTOCOL_ID_BASE = 0x10,
+	SCMI_PROTOCOL_ID_POWER_DOMAIN = 0x11,
+	SCMI_PROTOCOL_ID_SYSTEM = 0x12,
+	SCMI_PROTOCOL_ID_PERF = 0x13,
+	SCMI_PROTOCOL_ID_CLOCK = 0x14,
+	SCMI_PROTOCOL_ID_SENSOR = 0x15,
+	SCMI_PROTOCOL_ID_RESET_DOMAIN = 0x16,
+};
+
+enum scmi_status_code {
+	SCMI_SUCCESS =  0,
+	SCMI_NOT_SUPPORTED = -1,
+	SCMI_INVALID_PARAMETERS = -2,
+	SCMI_DENIED = -3,
+	SCMI_NOT_FOUND = -4,
+	SCMI_OUT_OF_RANGE = -5,
+	SCMI_BUSY = -6,
+	SCMI_COMMS_ERROR = -7,
+	SCMI_GENERIC_ERROR = -8,
+	SCMI_HARDWARE_ERROR = -9,
+	SCMI_PROTOCOL_ERROR = -10,
+};
+
+/*
+ * SCMI Clock Protocol
+ */
+
+enum scmi_clock_message_id {
+	SCMI_CLOCK_RATE_SET = 0x5,
+	SCMI_CLOCK_RATE_GET = 0x6,
+	SCMI_CLOCK_CONFIG_SET = 0x7,
+};
+
+#define SCMI_CLK_RATE_ASYNC_NOTIFY	BIT(0)
+#define SCMI_CLK_RATE_ASYNC_NORESP	(BIT(0) | BIT(1))
+#define SCMI_CLK_RATE_ROUND_DOWN	0
+#define SCMI_CLK_RATE_ROUND_UP		BIT(2)
+#define SCMI_CLK_RATE_ROUND_CLOSEST	BIT(3)
+
+/**
+ * struct scmi_clk_state_in - Message payload for CLOCK_CONFIG_SET command
+ * @clock_id:	SCMI clock ID
+ * @attributes:	Attributes of the targets clock state
+ */
+struct scmi_clk_state_in {
+	u32 clock_id;
+	u32 attributes;
+};
+
+/**
+ * struct scmi_clk_state_out - Response payload for CLOCK_CONFIG_SET command
+ * @status:	SCMI command status
+ */
+struct scmi_clk_state_out {
+	s32 status;
+};
+
+/**
+ * struct scmi_clk_state_in - Message payload for CLOCK_RATE_GET command
+ * @clock_id:	SCMI clock ID
+ * @attributes:	Attributes of the targets clock state
+ */
+struct scmi_clk_rate_get_in {
+	u32 clock_id;
+};
+
+/**
+ * struct scmi_clk_rate_get_out - Response payload for CLOCK_RATE_GET command
+ * @status:	SCMI command status
+ * @rate_lsb:	32bit LSB of the clock rate in Hertz
+ * @rate_msb:	32bit MSB of the clock rate in Hertz
+ */
+struct scmi_clk_rate_get_out {
+	s32 status;
+	u32 rate_lsb;
+	u32 rate_msb;
+};
+
+/**
+ * struct scmi_clk_state_in - Message payload for CLOCK_RATE_SET command
+ * @clock_id:	SCMI clock ID
+ * @flags:	Flags for the clock rate set request
+ * @rate_lsb:	32bit LSB of the clock rate in Hertz
+ * @rate_msb:	32bit MSB of the clock rate in Hertz
+ */
+struct scmi_clk_rate_set_in {
+	u32 clock_id;
+	u32 flags;
+	u32 rate_lsb;
+	u32 rate_msb;
+};
+
+/**
+ * struct scmi_clk_rate_set_out - Response payload for CLOCK_RATE_SET command
+ * @status:	SCMI command status
+ */
+struct scmi_clk_rate_set_out {
+	s32 status;
+};
+
+/*
+ * SCMI Reset Domain Protocol
+ */
+
+enum scmi_reset_domain_message_id {
+	SCMI_RESET_DOMAIN_ATTRIBUTES = 0x3,
+	SCMI_RESET_DOMAIN_RESET = 0x4,
+};
+
+#define SCMI_RD_NAME_LEN		16
+
+#define SCMI_RD_ATTRIBUTES_FLAG_ASYNC	BIT(31)
+#define SCMI_RD_ATTRIBUTES_FLAG_NOTIF	BIT(30)
+
+#define SCMI_RD_RESET_FLAG_ASYNC	BIT(2)
+#define SCMI_RD_RESET_FLAG_ASSERT	BIT(1)
+#define SCMI_RD_RESET_FLAG_CYCLE	BIT(0)
+
+/**
+ * struct scmi_rd_attr_in - Payload for RESET_DOMAIN_ATTRIBUTES message
+ * @domain_id:	SCMI reset domain ID
+ */
+struct scmi_rd_attr_in {
+	u32 domain_id;
+};
+
+/**
+ * struct scmi_rd_attr_out - Payload for RESET_DOMAIN_ATTRIBUTES response
+ * @status:	SCMI command status
+ * @attributes:	Retrieved attributes of the reset domain
+ * @latency:	Reset cycle max lantency
+ * @name:	Reset domain name
+ */
+struct scmi_rd_attr_out {
+	s32 status;
+	u32 attributes;
+	u32 latency;
+	char name[SCMI_RD_NAME_LEN];
+};
+
+/**
+ * struct scmi_rd_reset_in - Message payload for RESET command
+ * @domain_id:		SCMI reset domain ID
+ * @flags:		Flags for the reset request
+ * @reset_state:	Reset target state
+ */
+struct scmi_rd_reset_in {
+	u32 domain_id;
+	u32 flags;
+	u32 reset_state;
+};
+
+/**
+ * struct scmi_rd_reset_out - Response payload for RESET command
+ * @status:	SCMI command status
+ */
+struct scmi_rd_reset_out {
+	s32 status;
+};
+
+#endif /* _SCMI_PROTOCOLS_H */

+ 1 - 0
test/dm/Makefile

@@ -80,4 +80,5 @@ obj-$(CONFIG_DM_RNG) += rng.o
 obj-$(CONFIG_CLK_K210_SET_RATE) += k210_pll.o
 obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o
 obj-$(CONFIG_RESET_SYSCON) += syscon-reset.o
+obj-$(CONFIG_SCMI_FIRMWARE) += scmi.o
 endif

+ 1 - 1
test/dm/bus.c

@@ -120,7 +120,7 @@ UCLASS_DRIVER(testbus) = {
 /* Test that we can probe for children */
 static int dm_test_bus_children(struct unit_test_state *uts)
 {
-	int num_devices = 8;
+	int num_devices = 9;
 	struct udevice *bus;
 	struct uclass *uc;
 

+ 102 - 0
test/dm/gpio.c

@@ -10,6 +10,7 @@
 #include <malloc.h>
 #include <acpi/acpi_device.h>
 #include <asm/gpio.h>
+#include <dm/device-internal.h>
 #include <dm/root.h>
 #include <dm/test.h>
 #include <dm/util.h>
@@ -480,3 +481,104 @@ static int dm_test_gpio_get_acpi_irq(struct unit_test_state *uts)
 	return 0;
 }
 DM_TEST(dm_test_gpio_get_acpi_irq, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that we can get/release GPIOs using managed API */
+static int dm_test_gpio_devm(struct unit_test_state *uts)
+{
+	static const u32 flags = GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE;
+	struct gpio_desc *desc1, *desc2, *desc3, *desc_err;
+	struct udevice *dev;
+	struct udevice *dev2;
+
+	ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test",
+					      &dev));
+	ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "another-test",
+					      &dev2));
+
+	/* Get 3 GPIOs from 'a-test' dev */
+	desc1 = devm_gpiod_get_index(dev, "test4", 0, flags);
+	ut_assert(!IS_ERR(desc1));
+	desc2 = devm_gpiod_get_index(dev, "test4", 1, flags);
+	ut_assert(!IS_ERR(desc2));
+	desc3 = devm_gpiod_get_index_optional(dev, "test5", 0, flags);
+	ut_assert(!IS_ERR(desc3));
+	ut_assert(desc3);
+
+	/*
+	 * Try get the same 3 GPIOs from 'a-test' and 'another-test' devices.
+	 * check that it fails
+	 */
+	desc_err = devm_gpiod_get_index(dev, "test4", 0, flags);
+	ut_asserteq(-EBUSY, PTR_ERR(desc_err));
+	desc_err = devm_gpiod_get_index(dev2, "test4", 0, flags);
+	ut_asserteq(-EBUSY, PTR_ERR(desc_err));
+	desc_err = devm_gpiod_get_index(dev, "test4", 1, flags);
+	ut_asserteq(-EBUSY, PTR_ERR(desc_err));
+	desc_err = devm_gpiod_get_index(dev2, "test4", 1, flags);
+	ut_asserteq(-EBUSY, PTR_ERR(desc_err));
+	desc_err = devm_gpiod_get_index_optional(dev, "test5", 0, flags);
+	ut_asserteq_ptr(NULL, desc_err);
+	desc_err = devm_gpiod_get_index_optional(dev2, "test5", 0, flags);
+	ut_asserteq_ptr(NULL, desc_err);
+
+	/* Try get GPIOs outside of the list */
+	desc_err = devm_gpiod_get_index(dev, "test4", 2, flags);
+	ut_assert(IS_ERR(desc_err));
+	desc_err = devm_gpiod_get_index_optional(dev, "test5", 1, flags);
+	ut_asserteq_ptr(NULL, desc_err);
+
+	/* Manipulate the GPIOs */
+	ut_assertok(dm_gpio_set_value(desc1, 1));
+	ut_asserteq(1, dm_gpio_get_value(desc1));
+	ut_assertok(dm_gpio_set_value(desc1, 0));
+	ut_asserteq(0, dm_gpio_get_value(desc1));
+
+	ut_assertok(dm_gpio_set_value(desc2, 1));
+	ut_asserteq(1, dm_gpio_get_value(desc2));
+	ut_assertok(dm_gpio_set_value(desc2, 0));
+	ut_asserteq(0, dm_gpio_get_value(desc2));
+
+	ut_assertok(dm_gpio_set_value(desc3, 1));
+	ut_asserteq(1, dm_gpio_get_value(desc3));
+	ut_assertok(dm_gpio_set_value(desc3, 0));
+	ut_asserteq(0, dm_gpio_get_value(desc3));
+
+	/* Check that the GPIO cannot be owned by more than one device */
+	desc_err = devm_gpiod_get_index(dev2, "test4", 0, flags);
+	ut_asserteq(-EBUSY, PTR_ERR(desc_err));
+	desc_err = devm_gpiod_get_index(dev2, "test4", 1, flags);
+	ut_asserteq(-EBUSY, PTR_ERR(desc_err));
+	desc_err = devm_gpiod_get_index_optional(dev2, "test5", 0, flags);
+	ut_asserteq_ptr(NULL, desc_err);
+
+	/*
+	 * Release one GPIO and check that we can get it back using
+	 * 'another-test' and then 'a-test'
+	 */
+	devm_gpiod_put(dev, desc2);
+	desc2 = devm_gpiod_get_index(dev2, "test4", 1, flags);
+	ut_assert(!IS_ERR(desc2));
+
+	devm_gpiod_put(dev2, desc2);
+	desc2 = devm_gpiod_get_index(dev, "test4", 1, flags);
+	ut_assert(!IS_ERR(desc2));
+
+	/* Release one GPIO before removing the 'a-test' dev. */
+	devm_gpiod_put(dev, desc2);
+	device_remove(dev, DM_REMOVE_NORMAL);
+
+	/* All the GPIOs must have been freed. We should be able to claim
+	 * them with the 'another-test' device.
+	 */
+	desc1 = devm_gpiod_get_index(dev2, "test4", 0, flags);
+	ut_assert(!IS_ERR(desc1));
+	desc2 = devm_gpiod_get_index(dev2, "test4", 1, flags);
+	ut_assert(!IS_ERR(desc2));
+	desc3 = devm_gpiod_get_index_optional(dev2, "test5", 0, flags);
+	ut_assert(!IS_ERR(desc3));
+	ut_assert(desc3);
+
+	device_remove(dev2, DM_REMOVE_NORMAL);
+	return 0;
+}
+DM_TEST(dm_test_gpio_devm, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);

+ 198 - 0
test/dm/regmap.c

@@ -9,8 +9,10 @@
 #include <mapmem.h>
 #include <regmap.h>
 #include <syscon.h>
+#include <rand.h>
 #include <asm/test.h>
 #include <dm/test.h>
+#include <dm/devres.h>
 #include <linux/err.h>
 #include <test/test.h>
 #include <test/ut.h>
@@ -187,3 +189,199 @@ static int dm_test_regmap_poll(struct unit_test_state *uts)
 }
 
 DM_TEST(dm_test_regmap_poll, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+struct regmaptest_priv {
+	struct regmap *cfg_regmap; /* For testing regmap_config options. */
+	struct regmap *fld_regmap; /* For testing regmap fields. */
+	struct regmap_field **fields;
+};
+
+static const struct reg_field field_cfgs[] = {
+	{
+		.reg = 0,
+		.lsb = 0,
+		.msb = 6,
+	},
+	{
+		.reg = 2,
+		.lsb = 4,
+		.msb = 12,
+	},
+	{
+		.reg = 2,
+		.lsb = 12,
+		.msb = 15,
+	}
+};
+
+#define REGMAP_TEST_BUF_START 0
+#define REGMAP_TEST_BUF_SZ 5
+
+static int remaptest_probe(struct udevice *dev)
+{
+	struct regmaptest_priv *priv = dev_get_priv(dev);
+	struct regmap *regmap;
+	struct regmap_field *field;
+	struct regmap_config cfg;
+	int i;
+	static const int n = ARRAY_SIZE(field_cfgs);
+
+	/*
+	 * To exercise all the regmap config options, create a regmap that
+	 * points to a custom memory area instead of the one defined in device
+	 * tree. Use 2-byte elements. To allow directly indexing into the
+	 * elements, use an offset shift of 1. So, accessing offset 1 gets the
+	 * element at index 1 at memory location 2.
+	 *
+	 * REGMAP_TEST_BUF_SZ is the number of elements, so we need to multiply
+	 * it by 2 because r_size expects number of bytes.
+	 */
+	cfg.reg_offset_shift = 1;
+	cfg.r_start = REGMAP_TEST_BUF_START;
+	cfg.r_size = REGMAP_TEST_BUF_SZ * 2;
+	cfg.width = REGMAP_SIZE_16;
+
+	regmap = devm_regmap_init(dev, NULL, NULL, &cfg);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+	priv->cfg_regmap = regmap;
+
+	memset(&cfg, 0, sizeof(struct regmap_config));
+	cfg.width = REGMAP_SIZE_16;
+
+	regmap = devm_regmap_init(dev, NULL, NULL, &cfg);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+	priv->fld_regmap = regmap;
+
+	priv->fields = devm_kzalloc(dev, sizeof(struct regmap_field *) * n,
+				    GFP_KERNEL);
+	if (!priv->fields)
+		return -ENOMEM;
+
+	for (i = 0 ; i < n; i++) {
+		field = devm_regmap_field_alloc(dev, priv->fld_regmap,
+						field_cfgs[i]);
+		if (IS_ERR(field))
+			return PTR_ERR(field);
+		priv->fields[i] = field;
+	}
+
+	return 0;
+}
+
+static const struct udevice_id regmaptest_ids[] = {
+	{ .compatible = "sandbox,regmap_test" },
+	{ }
+};
+
+U_BOOT_DRIVER(regmap_test) = {
+	.name	= "regmaptest_drv",
+	.of_match	= regmaptest_ids,
+	.id	= UCLASS_NOP,
+	.probe = remaptest_probe,
+	.priv_auto_alloc_size = sizeof(struct regmaptest_priv),
+};
+
+static int dm_test_devm_regmap(struct unit_test_state *uts)
+{
+	int i = 0;
+	u32 val;
+	u16 pattern[REGMAP_TEST_BUF_SZ];
+	u16 *buffer;
+	struct udevice *dev;
+	struct regmaptest_priv *priv;
+
+	sandbox_set_enable_memio(true);
+
+	/*
+	 * Map the memory area the regmap should point to so we can make sure
+	 * the writes actually go to that location.
+	 */
+	buffer = map_physmem(REGMAP_TEST_BUF_START,
+			     REGMAP_TEST_BUF_SZ * 2, MAP_NOCACHE);
+
+	ut_assertok(uclass_get_device_by_name(UCLASS_NOP, "regmap-test_0",
+					      &dev));
+	priv = dev_get_priv(dev);
+
+	srand(get_ticks() + rand());
+	for (i = 0; i < REGMAP_TEST_BUF_SZ; i++) {
+		pattern[i] = rand();
+		ut_assertok(regmap_write(priv->cfg_regmap, i, pattern[i]));
+	}
+	for (i = 0; i < REGMAP_TEST_BUF_SZ; i++) {
+		ut_assertok(regmap_read(priv->cfg_regmap, i, &val));
+		ut_asserteq(val, buffer[i]);
+		ut_asserteq(val, pattern[i]);
+	}
+
+	ut_asserteq(-ERANGE, regmap_write(priv->cfg_regmap, REGMAP_TEST_BUF_SZ,
+					  val));
+	ut_asserteq(-ERANGE, regmap_read(priv->cfg_regmap, REGMAP_TEST_BUF_SZ,
+					 &val));
+	ut_asserteq(-ERANGE, regmap_write(priv->cfg_regmap, -1, val));
+	ut_asserteq(-ERANGE, regmap_read(priv->cfg_regmap, -1, &val));
+
+	return 0;
+}
+DM_TEST(dm_test_devm_regmap, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int test_one_field(struct unit_test_state *uts,
+			  struct regmap *regmap,
+			  struct regmap_field *field,
+			  struct reg_field field_cfg)
+{
+	int j;
+	unsigned int val;
+	int mask = (1 << (field_cfg.msb - field_cfg.lsb + 1)) - 1;
+	int shift = field_cfg.lsb;
+
+	ut_assertok(regmap_write(regmap, field_cfg.reg, 0));
+	ut_assertok(regmap_read(regmap, field_cfg.reg, &val));
+	ut_asserteq(0, val);
+
+	for (j = 0; j <= mask; j++) {
+		ut_assertok(regmap_field_write(field, j));
+		ut_assertok(regmap_field_read(field, &val));
+		ut_asserteq(j, val);
+		ut_assertok(regmap_read(regmap, field_cfg.reg, &val));
+		ut_asserteq(j << shift, val);
+	}
+
+	ut_assertok(regmap_field_write(field, mask + 1));
+	ut_assertok(regmap_read(regmap, field_cfg.reg, &val));
+	ut_asserteq(0, val);
+
+	ut_assertok(regmap_field_write(field, 0xFFFF));
+	ut_assertok(regmap_read(regmap, field_cfg.reg, &val));
+	ut_asserteq(mask << shift, val);
+
+	ut_assertok(regmap_write(regmap, field_cfg.reg, 0xFFFF));
+	ut_assertok(regmap_field_write(field, 0));
+	ut_assertok(regmap_read(regmap, field_cfg.reg, &val));
+	ut_asserteq(0xFFFF & ~(mask << shift), val);
+	return 0;
+}
+
+static int dm_test_devm_regmap_field(struct unit_test_state *uts)
+{
+	int i, rc;
+	struct udevice *dev;
+	struct regmaptest_priv *priv;
+
+	ut_assertok(uclass_get_device_by_name(UCLASS_NOP, "regmap-test_0",
+					      &dev));
+	priv = dev_get_priv(dev);
+
+	sandbox_set_enable_memio(true);
+	for (i = 0 ; i < ARRAY_SIZE(field_cfgs); i++) {
+		rc = test_one_field(uts, priv->fld_regmap, priv->fields[i],
+				    field_cfgs[i]);
+		if (rc)
+			break;
+	}
+
+	return 0;
+}
+DM_TEST(dm_test_devm_regmap_field, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);

+ 60 - 0
test/dm/reset.c

@@ -5,6 +5,7 @@
 
 #include <common.h>
 #include <dm.h>
+#include <dm/device-internal.h>
 #include <log.h>
 #include <malloc.h>
 #include <reset.h>
@@ -60,12 +61,39 @@ static int dm_test_reset(struct unit_test_state *uts)
 	ut_assertok(sandbox_reset_test_deassert(dev_test));
 	ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID));
 
+	ut_asserteq(1, sandbox_reset_is_requested(dev_reset, TEST_RESET_ID));
 	ut_assertok(sandbox_reset_test_free(dev_test));
+	ut_asserteq(0, sandbox_reset_is_requested(dev_reset, TEST_RESET_ID));
 
 	return 0;
 }
 DM_TEST(dm_test_reset, UT_TESTF_SCAN_FDT);
 
+static int dm_test_reset_devm(struct unit_test_state *uts)
+{
+	struct udevice *dev_reset;
+	struct udevice *dev_test;
+
+	ut_assertok(uclass_get_device_by_name(UCLASS_RESET, "reset-ctl",
+					      &dev_reset));
+	ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+	ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "reset-ctl-test",
+					      &dev_test));
+	ut_assertok(sandbox_reset_test_get_devm(dev_test));
+
+	ut_assertok(sandbox_reset_test_assert(dev_test));
+	ut_asserteq(1, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+	ut_assertok(sandbox_reset_test_deassert(dev_test));
+	ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+
+	ut_asserteq(1, sandbox_reset_is_requested(dev_reset, TEST_RESET_ID));
+	ut_assertok(device_remove(dev_test, DM_REMOVE_NORMAL));
+	ut_asserteq(0, sandbox_reset_is_requested(dev_reset, TEST_RESET_ID));
+
+	return 0;
+}
+DM_TEST(dm_test_reset_devm, UT_TESTF_SCAN_FDT);
+
 static int dm_test_reset_bulk(struct unit_test_state *uts)
 {
 	struct udevice *dev_reset;
@@ -95,3 +123,35 @@ static int dm_test_reset_bulk(struct unit_test_state *uts)
 	return 0;
 }
 DM_TEST(dm_test_reset_bulk, UT_TESTF_SCAN_FDT);
+
+static int dm_test_reset_bulk_devm(struct unit_test_state *uts)
+{
+	struct udevice *dev_reset;
+	struct udevice *dev_test;
+
+	ut_assertok(uclass_get_device_by_name(UCLASS_RESET, "reset-ctl",
+					      &dev_reset));
+	ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+	ut_asserteq(0, sandbox_reset_query(dev_reset, OTHER_RESET_ID));
+
+	ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "reset-ctl-test",
+					      &dev_test));
+	ut_assertok(sandbox_reset_test_get_bulk_devm(dev_test));
+
+	ut_assertok(sandbox_reset_test_assert_bulk(dev_test));
+	ut_asserteq(1, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+	ut_asserteq(1, sandbox_reset_query(dev_reset, OTHER_RESET_ID));
+
+	ut_assertok(sandbox_reset_test_deassert_bulk(dev_test));
+	ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+	ut_asserteq(0, sandbox_reset_query(dev_reset, OTHER_RESET_ID));
+
+	ut_asserteq(1, sandbox_reset_is_requested(dev_reset, OTHER_RESET_ID));
+	ut_asserteq(1, sandbox_reset_is_requested(dev_reset, TEST_RESET_ID));
+	ut_assertok(device_remove(dev_test, DM_REMOVE_NORMAL));
+	ut_asserteq(0, sandbox_reset_is_requested(dev_reset, TEST_RESET_ID));
+	ut_asserteq(0, sandbox_reset_is_requested(dev_reset, OTHER_RESET_ID));
+
+	return 0;
+}
+DM_TEST(dm_test_reset_bulk_devm, UT_TESTF_SCAN_FDT);

+ 203 - 0
test/dm/scmi.c

@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020, Linaro Limited
+ *
+ * Tests scmi_agent uclass and the SCMI drivers implemented in other
+ * uclass devices probe when a SCMI server exposes resources.
+ *
+ * Note in test.dts the protocol@10 node in agent 1. Protocol 0x10 is not
+ * implemented in U-Boot SCMI components but the implementation is exepected
+ * to not complain on unknown protocol IDs, as long as it is not used. Note
+ * in test.dts tests that SCMI drivers probing does not fail for such an
+ * unknown SCMI protocol ID.
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <reset.h>
+#include <asm/scmi_test.h>
+#include <dm/device-internal.h>
+#include <dm/test.h>
+#include <linux/kconfig.h>
+#include <test/ut.h>
+
+static int ut_assert_scmi_state_preprobe(struct unit_test_state *uts)
+{
+	struct sandbox_scmi_service *scmi_ctx = sandbox_scmi_service_ctx();
+
+	ut_assertnonnull(scmi_ctx);
+	if (scmi_ctx->agent_count)
+		ut_asserteq(2, scmi_ctx->agent_count);
+
+	return 0;
+}
+
+static int ut_assert_scmi_state_postprobe(struct unit_test_state *uts,
+					  struct udevice *dev)
+{
+	struct sandbox_scmi_devices *scmi_devices;
+	struct sandbox_scmi_service *scmi_ctx;
+
+	/* Device references to check context against test sequence */
+	scmi_devices = sandbox_scmi_devices_ctx(dev);
+
+	ut_assertnonnull(scmi_devices);
+	if (IS_ENABLED(CONFIG_CLK_SCMI))
+		ut_asserteq(3, scmi_devices->clk_count);
+	if (IS_ENABLED(CONFIG_RESET_SCMI))
+		ut_asserteq(1, scmi_devices->reset_count);
+
+	/* State of the simulated SCMI server exposed */
+	scmi_ctx = sandbox_scmi_service_ctx();
+
+	ut_asserteq(2, scmi_ctx->agent_count);
+
+	ut_assertnonnull(scmi_ctx->agent[0]);
+	ut_asserteq(2, scmi_ctx->agent[0]->clk_count);
+	ut_assertnonnull(scmi_ctx->agent[0]->clk);
+	ut_asserteq(1, scmi_ctx->agent[0]->reset_count);
+	ut_assertnonnull(scmi_ctx->agent[0]->reset);
+
+	ut_assertnonnull(scmi_ctx->agent[1]);
+	ut_assertnonnull(scmi_ctx->agent[1]->clk);
+	ut_asserteq(1, scmi_ctx->agent[1]->clk_count);
+
+	return 0;
+}
+
+static int load_sandbox_scmi_test_devices(struct unit_test_state *uts,
+					  struct udevice **dev)
+{
+	int ret;
+
+	ret = ut_assert_scmi_state_preprobe(uts);
+	if (ret)
+		return ret;
+
+	ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "sandbox_scmi",
+					      dev));
+	ut_assertnonnull(*dev);
+
+	return ut_assert_scmi_state_postprobe(uts, *dev);
+}
+
+static int release_sandbox_scmi_test_devices(struct unit_test_state *uts,
+					     struct udevice *dev)
+{
+	ut_assertok(device_remove(dev, DM_REMOVE_NORMAL));
+
+	/* Not sure test devices are fully removed, agent may not be visible */
+	return 0;
+}
+
+/*
+ * Test SCMI states when loading and releasing resources
+ * related to SCMI drivers.
+ */
+static int dm_test_scmi_sandbox_agent(struct unit_test_state *uts)
+{
+	struct udevice *dev = NULL;
+	int ret;
+
+	ret = load_sandbox_scmi_test_devices(uts, &dev);
+	if (!ret)
+		ret = release_sandbox_scmi_test_devices(uts, dev);
+
+	return ret;
+}
+
+DM_TEST(dm_test_scmi_sandbox_agent, UT_TESTF_SCAN_FDT);
+
+static int dm_test_scmi_clocks(struct unit_test_state *uts)
+{
+	struct sandbox_scmi_devices *scmi_devices;
+	struct sandbox_scmi_service *scmi_ctx;
+	struct udevice *dev = NULL;
+	int ret_dev;
+	int ret;
+
+	if (!IS_ENABLED(CONFIG_CLK_SCMI))
+		return 0;
+
+	ret = load_sandbox_scmi_test_devices(uts, &dev);
+	if (ret)
+		return ret;
+
+	scmi_devices = sandbox_scmi_devices_ctx(dev);
+	scmi_ctx = sandbox_scmi_service_ctx();
+
+	/* Test SCMI clocks rate manipulation */
+	ut_asserteq(1000, clk_get_rate(&scmi_devices->clk[0]));
+	ut_asserteq(333, clk_get_rate(&scmi_devices->clk[1]));
+	ut_asserteq(44, clk_get_rate(&scmi_devices->clk[2]));
+
+	ret_dev = clk_set_rate(&scmi_devices->clk[1], 1088);
+	ut_assert(!ret_dev || ret_dev == 1088);
+
+	ut_asserteq(1000, scmi_ctx->agent[0]->clk[0].rate);
+	ut_asserteq(1088, scmi_ctx->agent[0]->clk[1].rate);
+	ut_asserteq(44, scmi_ctx->agent[1]->clk[0].rate);
+
+	ut_asserteq(1000, clk_get_rate(&scmi_devices->clk[0]));
+	ut_asserteq(1088, clk_get_rate(&scmi_devices->clk[1]));
+	ut_asserteq(44, clk_get_rate(&scmi_devices->clk[2]));
+
+	/* restore original rate for further tests */
+	ret_dev = clk_set_rate(&scmi_devices->clk[1], 333);
+	ut_assert(!ret_dev || ret_dev == 333);
+
+	/* Test SCMI clocks gating manipulation */
+	ut_assert(!scmi_ctx->agent[0]->clk[0].enabled);
+	ut_assert(!scmi_ctx->agent[0]->clk[1].enabled);
+	ut_assert(!scmi_ctx->agent[1]->clk[0].enabled);
+
+	ut_asserteq(0, clk_enable(&scmi_devices->clk[1]));
+	ut_asserteq(0, clk_enable(&scmi_devices->clk[2]));
+
+	ut_assert(!scmi_ctx->agent[0]->clk[0].enabled);
+	ut_assert(scmi_ctx->agent[0]->clk[1].enabled);
+	ut_assert(scmi_ctx->agent[1]->clk[0].enabled);
+
+	ut_assertok(clk_disable(&scmi_devices->clk[1]));
+	ut_assertok(clk_disable(&scmi_devices->clk[2]));
+
+	ut_assert(!scmi_ctx->agent[0]->clk[0].enabled);
+	ut_assert(!scmi_ctx->agent[0]->clk[1].enabled);
+	ut_assert(!scmi_ctx->agent[1]->clk[0].enabled);
+
+	return release_sandbox_scmi_test_devices(uts, dev);
+}
+
+DM_TEST(dm_test_scmi_clocks, UT_TESTF_SCAN_FDT);
+
+static int dm_test_scmi_resets(struct unit_test_state *uts)
+{
+	struct sandbox_scmi_devices *scmi_devices;
+	struct sandbox_scmi_service *scmi_ctx;
+	struct udevice *dev = NULL;
+	int ret;
+
+	if (!IS_ENABLED(CONFIG_RESET_SCMI))
+		return 0;
+
+	ret = load_sandbox_scmi_test_devices(uts, &dev);
+	if (ret)
+		return ret;
+
+	scmi_devices = sandbox_scmi_devices_ctx(dev);
+	scmi_ctx = sandbox_scmi_service_ctx();
+
+	/* Test SCMI resect controller manipulation */
+	ut_assert(!scmi_ctx->agent[0]->reset[0].asserted)
+
+	ut_assertok(reset_assert(&scmi_devices->reset[0]));
+	ut_assert(scmi_ctx->agent[0]->reset[0].asserted)
+
+	ut_assertok(reset_deassert(&scmi_devices->reset[0]));
+	ut_assert(!scmi_ctx->agent[0]->reset[0].asserted);
+
+	return release_sandbox_scmi_test_devices(uts, dev);
+}
+
+DM_TEST(dm_test_scmi_resets, UT_TESTF_SCAN_FDT);

+ 3 - 3
test/dm/test-fdt.c

@@ -251,7 +251,7 @@ int dm_check_devices(struct unit_test_state *uts, int num_devices)
 /* Test that FDT-based binding works correctly */
 static int dm_test_fdt(struct unit_test_state *uts)
 {
-	const int num_devices = 8;
+	const int num_devices = 9;
 	struct udevice *dev;
 	struct uclass *uc;
 	int ret;
@@ -473,12 +473,12 @@ static int dm_test_uclass_foreach(struct unit_test_state *uts)
 	count = 0;
 	uclass_id_foreach_dev(UCLASS_TEST_FDT, dev, uc)
 		count++;
-	ut_asserteq(8, count);
+	ut_asserteq(9, count);
 
 	count = 0;
 	uclass_foreach_dev(dev, uc)
 		count++;
-	ut_asserteq(8, count);
+	ut_asserteq(9, count);
 
 	return 0;
 }

+ 1 - 1
test/py/tests/test_bind.py

@@ -16,7 +16,7 @@ def in_tree(response, name, uclass, drv, depth, last_child):
                         leaf = leaf + '`'
 
 	leaf = leaf + '-- ' + name
-	line = (r' *{:10.10}    [0-9]*  \[ [ +] \]   {:20.20}  [` |]{}$'
+	line = (r' *{:10.10} *[0-9]*  \[ [ +] \]   {:20.20}  [` |]{}$'
 	        .format(uclass, drv, leaf))
 	prog = re.compile(line)
 	for l in lines: