aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2020-09-30 16:11:11 -0400
committerTom Rini <trini@konsulko.com>2020-09-30 16:11:11 -0400
commit097bbf1ba97b8ece930deca663f05ea444e99e45 (patch)
treeae4db71e77556f34f9fa7023205e4284c1d72c38
parent01114adfc1e0bf3cf5e2f3da858bb2c8e9810c1c (diff)
parentc0dd177a9986b97dab42f07b3db0ed1d2fe6e540 (diff)
downloadu-boot-097bbf1ba97b8ece930deca663f05ea444e99e45.zip
u-boot-097bbf1ba97b8ece930deca663f05ea444e99e45.tar.gz
u-boot-097bbf1ba97b8ece930deca663f05ea444e99e45.tar.bz2
Merge branch '2020-09-30-add-new-apis' into next
- SCMI firmware support - regmap, GPIO, reset API enhancements
-rw-r--r--arch/sandbox/dts/test.dts60
-rw-r--r--arch/sandbox/include/asm/reset.h3
-rw-r--r--arch/sandbox/include/asm/scmi_test.h99
-rw-r--r--configs/sandbox_defconfig4
-rw-r--r--doc/device-tree-bindings/arm/arm,scmi.txt197
-rw-r--r--drivers/clk/Kconfig8
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/clk_scmi.c99
-rw-r--r--drivers/core/regmap.c163
-rw-r--r--drivers/firmware/Kconfig2
-rw-r--r--drivers/firmware/Makefile1
-rw-r--r--drivers/firmware/scmi/Kconfig19
-rw-r--r--drivers/firmware/scmi/Makefile5
-rw-r--r--drivers/firmware/scmi/mailbox_agent.c102
-rw-r--r--drivers/firmware/scmi/sandbox-scmi_agent.c410
-rw-r--r--drivers/firmware/scmi/sandbox-scmi_devices.c113
-rw-r--r--drivers/firmware/scmi/scmi_agent-uclass.c119
-rw-r--r--drivers/firmware/scmi/smccc_agent.c89
-rw-r--r--drivers/firmware/scmi/smt.c139
-rw-r--r--drivers/firmware/scmi/smt.h86
-rw-r--r--drivers/gpio/gpio-uclass.c71
-rw-r--r--drivers/reset/Kconfig8
-rw-r--r--drivers/reset/Makefile1
-rw-r--r--drivers/reset/reset-scmi.c81
-rw-r--r--drivers/reset/reset-uclass.c118
-rw-r--r--drivers/reset/sandbox-reset-test.c51
-rw-r--r--drivers/reset/sandbox-reset.c19
-rw-r--r--include/asm-generic/gpio.h47
-rw-r--r--include/dm/uclass-id.h1
-rw-r--r--include/regmap.h205
-rw-r--r--include/reset.h135
-rw-r--r--include/scmi_agent-uclass.h24
-rw-r--r--include/scmi_agent.h68
-rw-r--r--include/scmi_protocols.h179
-rw-r--r--test/dm/Makefile1
-rw-r--r--test/dm/bus.c2
-rw-r--r--test/dm/gpio.c102
-rw-r--r--test/dm/regmap.c198
-rw-r--r--test/dm/reset.c60
-rw-r--r--test/dm/scmi.c203
-rw-r--r--test/dm/test-fdt.c6
-rw-r--r--test/py/tests/test_bind.py2
42 files changed, 3268 insertions, 33 deletions
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index 2f55926..e6da47d 100644
--- a/arch/sandbox/dts/test.dts
+++ b/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"
diff --git a/arch/sandbox/include/asm/reset.h b/arch/sandbox/include/asm/reset.h
index c4205ea..40d3e61 100644
--- a/arch/sandbox/include/asm/reset.h
+++ b/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);
diff --git a/arch/sandbox/include/asm/scmi_test.h b/arch/sandbox/include/asm/scmi_test.h
new file mode 100644
index 0000000..3e8b006
--- /dev/null
+++ b/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 */
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index 6e9f029..a2ebb3c 100644
--- a/configs/sandbox_defconfig
+++ b/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
diff --git a/doc/device-tree-bindings/arm/arm,scmi.txt b/doc/device-tree-bindings/arm/arm,scmi.txt
new file mode 100644
index 0000000..1f293ea
--- /dev/null
+++ b/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>;
+ ...
+ };
+};
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 6003e14..4dfbad7 100644
--- a/drivers/clk/Kconfig
+++ b/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"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index cda4b4b..d1e295a 100644
--- a/drivers/clk/Makefile
+++ b/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
diff --git a/drivers/clk/clk_scmi.c b/drivers/clk/clk_scmi.c
new file mode 100644
index 0000000..93a4819
--- /dev/null
+++ b/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,
+};
diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c
index a67a237..c2bed88 100644
--- a/drivers/core/regmap.c
+++ b/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);
+}
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index b70a206..ef958b3 100644
--- a/drivers/firmware/Kconfig
+++ b/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"
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index a0c250a..7ce83d7 100644
--- a/drivers/firmware/Makefile
+++ b/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/
diff --git a/drivers/firmware/scmi/Kconfig b/drivers/firmware/scmi/Kconfig
new file mode 100644
index 0000000..c3a109b
--- /dev/null
+++ b/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.
diff --git a/drivers/firmware/scmi/Makefile b/drivers/firmware/scmi/Makefile
new file mode 100644
index 0000000..e1e0224
--- /dev/null
+++ b/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
diff --git a/drivers/firmware/scmi/mailbox_agent.c b/drivers/firmware/scmi/mailbox_agent.c
new file mode 100644
index 0000000..7d9fb36
--- /dev/null
+++ b/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,
+};
diff --git a/drivers/firmware/scmi/sandbox-scmi_agent.c b/drivers/firmware/scmi/sandbox-scmi_agent.c
new file mode 100644
index 0000000..5b6a423
--- /dev/null
+++ b/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,
+};
diff --git a/drivers/firmware/scmi/sandbox-scmi_devices.c b/drivers/firmware/scmi/sandbox-scmi_devices.c
new file mode 100644
index 0000000..c69967b
--- /dev/null
+++ b/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,
+};
diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c
new file mode 100644
index 0000000..77160b1
--- /dev/null
+++ b/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,
+};
diff --git a/drivers/firmware/scmi/smccc_agent.c b/drivers/firmware/scmi/smccc_agent.c
new file mode 100644
index 0000000..85dbf91
--- /dev/null
+++ b/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,
+};
diff --git a/drivers/firmware/scmi/smt.c b/drivers/firmware/scmi/smt.c
new file mode 100644
index 0000000..ce8fe49
--- /dev/null
+++ b/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;
+}
diff --git a/drivers/firmware/scmi/smt.h b/drivers/firmware/scmi/smt.h
new file mode 100644
index 0000000..a8c0987
--- /dev/null
+++ b/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 */
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c
index 9c53299..0c01413 100644
--- a/drivers/gpio/gpio-uclass.c
+++ b/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;
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index 3fdfe4a..b60e11f 100644
--- a/drivers/reset/Kconfig
+++ b/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
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 5176da5..10a7973 100644
--- a/drivers/reset/Makefile
+++ b/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
diff --git a/drivers/reset/reset-scmi.c b/drivers/reset/reset-scmi.c
new file mode 100644
index 0000000..1bff807
--- /dev/null
+++ b/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,
+};
diff --git a/drivers/reset/reset-uclass.c b/drivers/reset/reset-uclass.c
index 5e38ce5..e7e407c 100644
--- a/drivers/reset/reset-uclass.c
+++ b/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",
diff --git a/drivers/reset/sandbox-reset-test.c b/drivers/reset/sandbox-reset-test.c
index 9bc4a7e..10e02f1 100644
--- a/drivers/reset/sandbox-reset-test.c
+++ b/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[] = {
diff --git a/drivers/reset/sandbox-reset.c b/drivers/reset/sandbox-reset.c
index 7a6f7f6..08008d8 100644
--- a/drivers/reset/sandbox-reset.c
+++ b/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;
+}
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index a57dd26..3ae1894 100644
--- a/include/asm-generic/gpio.h
+++ b/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_ */
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 4ec5fa6..88f10c4 100644
--- a/include/dm/uclass-id.h
+++ b/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 */
diff --git a/include/regmap.h b/include/regmap.h
index 30183c5..c6258fa 100644
--- a/include/regmap.h
+++ b/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
*/
@@ -292,6 +313,43 @@ int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset,
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
*
* @map: The map returned by regmap_init_mem*()
@@ -336,6 +394,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
*
* @map: Regmap to query
@@ -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
diff --git a/include/reset.h b/include/reset.h
index 4fac4e6..cde2c4b 100644
--- a/include/reset.h
+++ b/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)
{
diff --git a/include/scmi_agent-uclass.h b/include/scmi_agent-uclass.h
new file mode 100644
index 0000000..a501d1b
--- /dev/null
+++ b/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 */
diff --git a/include/scmi_agent.h b/include/scmi_agent.h
new file mode 100644
index 0000000..f1be9ff
--- /dev/null
+++ b/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 */
diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h
new file mode 100644
index 0000000..ccab97c
--- /dev/null
+++ b/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 */
diff --git a/test/dm/Makefile b/test/dm/Makefile
index 864c8d0..70ba1b6 100644
--- a/test/dm/Makefile
+++ b/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
diff --git a/test/dm/bus.c b/test/dm/bus.c
index 865e8bd..27b7266 100644
--- a/test/dm/bus.c
+++ b/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;
diff --git a/test/dm/gpio.c b/test/dm/gpio.c
index 4848152..54e960b 100644
--- a/test/dm/gpio.c
+++ b/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);
diff --git a/test/dm/regmap.c b/test/dm/regmap.c
index bd21c83..2effef3 100644
--- a/test/dm/regmap.c
+++ b/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);
diff --git a/test/dm/reset.c b/test/dm/reset.c
index f5f3661..fc8e925 100644
--- a/test/dm/reset.c
+++ b/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);
diff --git a/test/dm/scmi.c b/test/dm/scmi.c
new file mode 100644
index 0000000..be60b44
--- /dev/null
+++ b/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);
diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c
index 04802de..26d57f4 100644
--- a/test/dm/test-fdt.c
+++ b/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;
}
diff --git a/test/py/tests/test_bind.py b/test/py/tests/test_bind.py
index 5e73d40..6703325 100644
--- a/test/py/tests/test_bind.py
+++ b/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: