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

- SCMI firmware support
- regmap, GPIO, reset API enhancements
This commit is contained in:
Tom Rini 2020-09-30 16:11:11 -04:00
commit 097bbf1ba9
42 changed files with 3268 additions and 33 deletions

View File

@ -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"

View File

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

View File

@ -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 */

View File

@ -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

View File

@ -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>;
...
};
};

View File

@ -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"

View File

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

99
drivers/clk/clk_scmi.c Normal file
View File

@ -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,
};

View File

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

View File

@ -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"

View File

@ -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/

View File

@ -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.

View File

@ -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

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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,
};

View File

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

139
drivers/firmware/scmi/smt.c Normal file
View File

@ -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;
}

View File

@ -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 */

View File

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

View File

@ -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

View File

@ -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

View File

@ -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,
};

View File

@ -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",

View File

@ -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[] = {

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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 */

View File

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

View File

@ -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)
{

View File

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

68
include/scmi_agent.h Normal file
View File

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

179
include/scmi_protocols.h Normal file
View File

@ -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 */

View File

@ -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

View File

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

View File

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

View File

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

View File

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

203
test/dm/scmi.c Normal file
View File

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

View File

@ -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;
}

View File

@ -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: