mirror of
https://github.com/Fishwaldo/u-boot.git
synced 2025-03-19 05:31:32 +00:00
Merge branch '2020-09-30-add-new-apis' into next
- SCMI firmware support - regmap, GPIO, reset API enhancements
This commit is contained in:
commit
097bbf1ba9
42 changed files with 3268 additions and 33 deletions
|
@ -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"
|
||||
|
|
|
@ -11,9 +11,12 @@
|
|||
struct udevice;
|
||||
|
||||
int sandbox_reset_query(struct udevice *dev, unsigned long id);
|
||||
int sandbox_reset_is_requested(struct udevice *dev, unsigned long id);
|
||||
|
||||
int sandbox_reset_test_get(struct udevice *dev);
|
||||
int sandbox_reset_test_get_devm(struct udevice *dev);
|
||||
int sandbox_reset_test_get_bulk(struct udevice *dev);
|
||||
int sandbox_reset_test_get_bulk_devm(struct udevice *dev);
|
||||
int sandbox_reset_test_assert(struct udevice *dev);
|
||||
int sandbox_reset_test_assert_bulk(struct udevice *dev);
|
||||
int sandbox_reset_test_deassert(struct udevice *dev);
|
||||
|
|
99
arch/sandbox/include/asm/scmi_test.h
Normal file
99
arch/sandbox/include/asm/scmi_test.h
Normal 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 */
|
|
@ -122,6 +122,7 @@ CONFIG_BUTTON=y
|
|||
CONFIG_BUTTON_GPIO=y
|
||||
CONFIG_CLK=y
|
||||
CONFIG_CLK_COMPOSITE_CCF=y
|
||||
CONFIG_CLK_SCMI=y
|
||||
CONFIG_SANDBOX_CLK_CCF=y
|
||||
CONFIG_CPU=y
|
||||
CONFIG_DM_DEMO=y
|
||||
|
@ -132,6 +133,8 @@ CONFIG_BOARD_SANDBOX=y
|
|||
CONFIG_DMA=y
|
||||
CONFIG_DMA_CHANNELS=y
|
||||
CONFIG_SANDBOX_DMA=y
|
||||
CONFIG_FIRMWARE=y
|
||||
CONFIG_SCMI_FIRMWARE=y
|
||||
CONFIG_GPIO_HOG=y
|
||||
CONFIG_DM_GPIO_LOOKUP_LABEL=y
|
||||
CONFIG_PM8916_GPIO=y
|
||||
|
@ -217,6 +220,7 @@ CONFIG_REMOTEPROC_SANDBOX=y
|
|||
CONFIG_DM_RESET=y
|
||||
CONFIG_SANDBOX_RESET=y
|
||||
CONFIG_RESET_SYSCON=y
|
||||
CONFIG_RESET_SCMI=y
|
||||
CONFIG_DM_RNG=y
|
||||
CONFIG_DM_RTC=y
|
||||
CONFIG_RTC_RV8803=y
|
||||
|
|
197
doc/device-tree-bindings/arm/arm,scmi.txt
Normal file
197
doc/device-tree-bindings/arm/arm,scmi.txt
Normal 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>;
|
||||
...
|
||||
};
|
||||
};
|
|
@ -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"
|
||||
|
|
|
@ -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
99
drivers/clk/clk_scmi.c
Normal 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,
|
||||
};
|
|
@ -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, ®_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);
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -3,3 +3,4 @@ obj-$(CONFIG_$(SPL_)ARM_PSCI_FW) += psci.o
|
|||
obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o
|
||||
obj-$(CONFIG_SANDBOX) += firmware-sandbox.o
|
||||
obj-$(CONFIG_ZYNQMP_FIRMWARE) += firmware-zynqmp.o
|
||||
obj-$(CONFIG_SCMI_FIRMWARE) += scmi/
|
||||
|
|
19
drivers/firmware/scmi/Kconfig
Normal file
19
drivers/firmware/scmi/Kconfig
Normal 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.
|
5
drivers/firmware/scmi/Makefile
Normal file
5
drivers/firmware/scmi/Makefile
Normal 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
|
102
drivers/firmware/scmi/mailbox_agent.c
Normal file
102
drivers/firmware/scmi/mailbox_agent.c
Normal 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,
|
||||
};
|
410
drivers/firmware/scmi/sandbox-scmi_agent.c
Normal file
410
drivers/firmware/scmi/sandbox-scmi_agent.c
Normal 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,
|
||||
};
|
113
drivers/firmware/scmi/sandbox-scmi_devices.c
Normal file
113
drivers/firmware/scmi/sandbox-scmi_devices.c
Normal 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,
|
||||
};
|
119
drivers/firmware/scmi/scmi_agent-uclass.c
Normal file
119
drivers/firmware/scmi/scmi_agent-uclass.c
Normal 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,
|
||||
};
|
89
drivers/firmware/scmi/smccc_agent.c
Normal file
89
drivers/firmware/scmi/smccc_agent.c
Normal 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
139
drivers/firmware/scmi/smt.c
Normal 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;
|
||||
}
|
86
drivers/firmware/scmi/smt.h
Normal file
86
drivers/firmware/scmi/smt.h
Normal 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 */
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -27,3 +27,4 @@ obj-$(CONFIG_RESET_IPQ419) += reset-ipq4019.o
|
|||
obj-$(CONFIG_RESET_SIFIVE) += reset-sifive.o
|
||||
obj-$(CONFIG_RESET_SYSCON) += reset-syscon.o
|
||||
obj-$(CONFIG_RESET_RASPBERRYPI) += reset-raspberrypi.o
|
||||
obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
|
||||
|
|
81
drivers/reset/reset-scmi.c
Normal file
81
drivers/reset/reset-scmi.c
Normal 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,
|
||||
};
|
|
@ -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",
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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 */
|
||||
|
|
205
include/regmap.h
205
include/regmap.h
|
@ -75,14 +75,41 @@ struct regmap_range {
|
|||
ulong size;
|
||||
};
|
||||
|
||||
struct regmap_bus;
|
||||
|
||||
/**
|
||||
* struct regmap_config - Configure the behaviour of a regmap
|
||||
*
|
||||
* @width: Width of the read/write operations. Defaults to
|
||||
* REGMAP_SIZE_32 if set to 0.
|
||||
* @reg_offset_shift Left shift the register offset by this value before
|
||||
* performing read or write.
|
||||
* @r_start: If specified, the regmap is created with one range
|
||||
* which starts at this address, instead of finding the
|
||||
* start from device tree.
|
||||
* @r_size: Same as above for the range size
|
||||
*/
|
||||
struct regmap_config {
|
||||
enum regmap_size_t width;
|
||||
u32 reg_offset_shift;
|
||||
ulong r_start;
|
||||
ulong r_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct regmap - a way of accessing hardware/bus registers
|
||||
*
|
||||
* @width: Width of the read/write operations. Defaults to
|
||||
* REGMAP_SIZE_32 if set to 0.
|
||||
* @reg_offset_shift Left shift the register offset by this value before
|
||||
* performing read or write.
|
||||
* @range_count: Number of ranges available within the map
|
||||
* @ranges: Array of ranges
|
||||
*/
|
||||
struct regmap {
|
||||
enum regmap_endianness_t endianness;
|
||||
enum regmap_size_t width;
|
||||
u32 reg_offset_shift;
|
||||
int range_count;
|
||||
struct regmap_range ranges[0];
|
||||
};
|
||||
|
@ -93,32 +120,24 @@ struct regmap {
|
|||
*/
|
||||
|
||||
/**
|
||||
* regmap_write() - Write a 32-bit value to a regmap
|
||||
* regmap_write() - Write a value to a regmap
|
||||
*
|
||||
* @map: Regmap to write to
|
||||
* @offset: Offset in the regmap to write to
|
||||
* @val: Data to write to the regmap at the specified offset
|
||||
*
|
||||
* Note that this function will only write values of 32 bit width to the
|
||||
* regmap; if the size of data to be read is different, the regmap_raw_write
|
||||
* function can be used.
|
||||
*
|
||||
* Return: 0 if OK, -ve on error
|
||||
*/
|
||||
int regmap_write(struct regmap *map, uint offset, uint val);
|
||||
|
||||
/**
|
||||
* regmap_read() - Read a 32-bit value from a regmap
|
||||
* regmap_read() - Read a value from a regmap
|
||||
*
|
||||
* @map: Regmap to read from
|
||||
* @offset: Offset in the regmap to read from
|
||||
* @valp: Pointer to the buffer to receive the data read from the regmap
|
||||
* at the specified offset
|
||||
*
|
||||
* Note that this function will only read values of 32 bit width from the
|
||||
* regmap; if the size of data to be read is different, the regmap_raw_read
|
||||
* function can be used.
|
||||
*
|
||||
* Return: 0 if OK, -ve on error
|
||||
*/
|
||||
int regmap_read(struct regmap *map, uint offset, uint *valp);
|
||||
|
@ -132,8 +151,9 @@ int regmap_read(struct regmap *map, uint offset, uint *valp);
|
|||
* @val_len: Length of the data to be written to the regmap
|
||||
*
|
||||
* Note that this function will, as opposed to regmap_write, write data of
|
||||
* arbitrary length to the regmap, and not just 32-bit values, and is thus a
|
||||
* generalized version of regmap_write.
|
||||
* arbitrary length to the regmap, and not just the size configured in the
|
||||
* regmap (defaults to 32-bit) and is thus a generalized version of
|
||||
* regmap_write.
|
||||
*
|
||||
* Return: 0 if OK, -ve on error
|
||||
*/
|
||||
|
@ -150,8 +170,9 @@ int regmap_raw_write(struct regmap *map, uint offset, const void *val,
|
|||
* @val_len: Length of the data to be read from the regmap
|
||||
*
|
||||
* Note that this function will, as opposed to regmap_read, read data of
|
||||
* arbitrary length from the regmap, and not just 32-bit values, and is thus a
|
||||
* generalized version of regmap_read.
|
||||
* arbitrary length from the regmap, and not just the size configured in the
|
||||
* regmap (defaults to 32-bit) and is thus a generalized version of
|
||||
* regmap_read.
|
||||
*
|
||||
* Return: 0 if OK, -ve on error
|
||||
*/
|
||||
|
@ -291,6 +312,43 @@ int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset,
|
|||
regmap_read_poll_timeout_test(map, addr, val, cond, sleep_us, \
|
||||
timeout_ms, 0) \
|
||||
|
||||
/**
|
||||
* regmap_field_read_poll_timeout - Poll until a condition is met or a timeout
|
||||
* occurs
|
||||
*
|
||||
* @field: Regmap field to read from
|
||||
* @val: Unsigned integer variable to read the value into
|
||||
* @cond: Break condition (usually involving @val)
|
||||
* @sleep_us: Maximum time to sleep between reads in us (0 tight-loops).
|
||||
* @timeout_ms: Timeout in ms, 0 means never timeout
|
||||
*
|
||||
* Returns 0 on success and -ETIMEDOUT upon a timeout or the regmap_field_read
|
||||
* error return value in case of a error read. In the two former cases,
|
||||
* the last read value at @addr is stored in @val.
|
||||
*
|
||||
* This is modelled after the regmap_read_poll_timeout macros in linux but
|
||||
* with millisecond timeout.
|
||||
*/
|
||||
#define regmap_field_read_poll_timeout(field, val, cond, sleep_us, timeout_ms) \
|
||||
({ \
|
||||
unsigned long __start = get_timer(0); \
|
||||
int __ret; \
|
||||
for (;;) { \
|
||||
__ret = regmap_field_read((field), &(val)); \
|
||||
if (__ret) \
|
||||
break; \
|
||||
if (cond) \
|
||||
break; \
|
||||
if ((timeout_ms) && get_timer(__start) > (timeout_ms)) { \
|
||||
__ret = regmap_field_read((field), &(val)); \
|
||||
break; \
|
||||
} \
|
||||
if ((sleep_us)) \
|
||||
udelay((sleep_us)); \
|
||||
} \
|
||||
__ret ?: ((cond) ? 0 : -ETIMEDOUT); \
|
||||
})
|
||||
|
||||
/**
|
||||
* regmap_update_bits() - Perform a read/modify/write using a mask
|
||||
*
|
||||
|
@ -335,6 +393,40 @@ int regmap_init_mem_platdata(struct udevice *dev, fdt_val_t *reg, int count,
|
|||
|
||||
int regmap_init_mem_index(ofnode node, struct regmap **mapp, int index);
|
||||
|
||||
/**
|
||||
* regmap_init_mem_range() - Set up a new memory region for ofnode with the
|
||||
* specified range.
|
||||
*
|
||||
* @node: The ofnode for the map.
|
||||
* @r_start: Start of the range.
|
||||
* @r_size: Size of the range.
|
||||
* @mapp: Returns allocated map.
|
||||
*
|
||||
* Return: 0 in success, -errno otherwise
|
||||
*
|
||||
* This creates a regmap with one range where instead of extracting the range
|
||||
* from 'node', it is created based on the parameters specified. This is
|
||||
* useful when a driver needs to calculate the base of the regmap at runtime,
|
||||
* and can't specify it in device tree.
|
||||
*/
|
||||
int regmap_init_mem_range(ofnode node, ulong r_start, ulong r_size,
|
||||
struct regmap **mapp);
|
||||
|
||||
/**
|
||||
* devm_regmap_init() - Initialise register map (device managed)
|
||||
*
|
||||
* @dev: Device that will be interacted with
|
||||
* @bus: Bus-specific callbacks to use with device (IGNORED)
|
||||
* @bus_context: Data passed to bus-specific callbacks (IGNORED)
|
||||
* @config: Configuration for register map
|
||||
*
|
||||
* @Return a valid pointer to a struct regmap or a ERR_PTR() on error.
|
||||
* The structure is automatically freed when the device is unbound
|
||||
*/
|
||||
struct regmap *devm_regmap_init(struct udevice *dev,
|
||||
const struct regmap_bus *bus,
|
||||
void *bus_context,
|
||||
const struct regmap_config *config);
|
||||
/**
|
||||
* regmap_get_range() - Obtain the base memory address of a regmap range
|
||||
*
|
||||
|
@ -352,4 +444,89 @@ void *regmap_get_range(struct regmap *map, unsigned int range_num);
|
|||
*/
|
||||
int regmap_uninit(struct regmap *map);
|
||||
|
||||
/**
|
||||
* struct reg_field - Description of an register field
|
||||
*
|
||||
* @reg: Offset of the register within the regmap bank
|
||||
* @lsb: lsb of the register field.
|
||||
* @msb: msb of the register field.
|
||||
*/
|
||||
struct reg_field {
|
||||
unsigned int reg;
|
||||
unsigned int lsb;
|
||||
unsigned int msb;
|
||||
};
|
||||
|
||||
struct regmap_field;
|
||||
|
||||
/**
|
||||
* REG_FIELD() - A convenient way to initialize a 'struct reg_feild'.
|
||||
*
|
||||
* @_reg: Offset of the register within the regmap bank
|
||||
* @_lsb: lsb of the register field.
|
||||
* @_msb: msb of the register field.
|
||||
*
|
||||
* Register fields are often described in terms of 3 things: the register it
|
||||
* belongs to, its LSB, and its MSB. This macro can be used by drivers to
|
||||
* clearly and easily initialize a 'struct regmap_field'.
|
||||
*
|
||||
* For example, say a device has a register at offset DEV_REG1 (0x100) and a
|
||||
* field of DEV_REG1 is on bits [7:3]. So a driver can initialize a regmap
|
||||
* field for this by doing:
|
||||
* struct reg_field field = REG_FIELD(DEV_REG1, 3, 7);
|
||||
*/
|
||||
#define REG_FIELD(_reg, _lsb, _msb) { \
|
||||
.reg = _reg, \
|
||||
.lsb = _lsb, \
|
||||
.msb = _msb, \
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_regmap_field_alloc() - Allocate and initialise a register field.
|
||||
*
|
||||
* @dev: Device that will be interacted with
|
||||
* @regmap: regmap bank in which this register field is located.
|
||||
* @reg_field: Register field with in the bank.
|
||||
*
|
||||
* The return value will be an ERR_PTR() on error or a valid pointer
|
||||
* to a struct regmap_field. The regmap_field will be automatically freed
|
||||
* by the device management code.
|
||||
*/
|
||||
struct regmap_field *devm_regmap_field_alloc(struct udevice *dev,
|
||||
struct regmap *regmap,
|
||||
struct reg_field reg_field);
|
||||
/**
|
||||
* devm_regmap_field_free() - Free a register field allocated using
|
||||
* devm_regmap_field_alloc.
|
||||
*
|
||||
* @dev: Device that will be interacted with
|
||||
* @field: regmap field which should be freed.
|
||||
*
|
||||
* Free register field allocated using devm_regmap_field_alloc(). Usually
|
||||
* drivers need not call this function, as the memory allocated via devm
|
||||
* will be freed as per device-driver life-cyle.
|
||||
*/
|
||||
void devm_regmap_field_free(struct udevice *dev, struct regmap_field *field);
|
||||
|
||||
/**
|
||||
* regmap_field_write() - Write a value to a regmap field
|
||||
*
|
||||
* @field: Regmap field to write to
|
||||
* @val: Data to write to the regmap at the specified offset
|
||||
*
|
||||
* Return: 0 if OK, -ve on error
|
||||
*/
|
||||
int regmap_field_write(struct regmap_field *field, unsigned int val);
|
||||
|
||||
/**
|
||||
* regmap_read() - Read a 32-bit value from a regmap
|
||||
*
|
||||
* @field: Regmap field to write to
|
||||
* @valp: Pointer to the buffer to receive the data read from the regmap
|
||||
* field
|
||||
*
|
||||
* Return: 0 if OK, -ve on error
|
||||
*/
|
||||
int regmap_field_read(struct regmap_field *field, unsigned int *val);
|
||||
|
||||
#endif
|
||||
|
|
135
include/reset.h
135
include/reset.h
|
@ -7,7 +7,7 @@
|
|||
#define _RESET_H
|
||||
|
||||
#include <dm/ofnode.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
/**
|
||||
* A reset is a hardware signal indicating that a HW module (or IP block, or
|
||||
|
@ -84,6 +84,98 @@ struct reset_ctl_bulk {
|
|||
};
|
||||
|
||||
#if CONFIG_IS_ENABLED(DM_RESET)
|
||||
|
||||
/**
|
||||
* devm_reset_control_get - resource managed reset_get_by_name()
|
||||
* @dev: device to be reset by the controller
|
||||
* @id: reset line name
|
||||
*
|
||||
* Managed reset_get_by_name(). For reset controllers returned
|
||||
* from this function, reset_free() is called automatically on driver
|
||||
* detach.
|
||||
*
|
||||
* Returns a struct reset_ctl or IS_ERR() condition containing errno.
|
||||
*/
|
||||
struct reset_ctl *devm_reset_control_get(struct udevice *dev, const char *id);
|
||||
|
||||
/**
|
||||
* devm_reset_control_get_optional - resource managed reset_get_by_name() that
|
||||
* can fail
|
||||
* @dev: The client device.
|
||||
* @id: reset line name
|
||||
*
|
||||
* Managed reset_get_by_name(). For reset controllers returned
|
||||
* from this function, reset_free() is called automatically on driver
|
||||
* detach.
|
||||
*
|
||||
* Returns a struct reset_ctl or a dummy reset controller if it failed.
|
||||
*/
|
||||
struct reset_ctl *devm_reset_control_get_optional(struct udevice *dev,
|
||||
const char *id);
|
||||
|
||||
/**
|
||||
* devm_reset_control_get - resource managed reset_get_by_index()
|
||||
* @dev: The client device.
|
||||
* @index: The index of the reset signal to request, within the client's
|
||||
* list of reset signals.
|
||||
*
|
||||
* Managed reset_get_by_index(). For reset controllers returned
|
||||
* from this function, reset_free() is called automatically on driver
|
||||
* detach.
|
||||
*
|
||||
* Returns a struct reset_ctl or IS_ERR() condition containing errno.
|
||||
*/
|
||||
struct reset_ctl *devm_reset_control_get_by_index(struct udevice *dev,
|
||||
int index);
|
||||
|
||||
/**
|
||||
* devm_reset_bulk_get - resource managed reset_get_bulk()
|
||||
* @dev: device to be reset by the controller
|
||||
*
|
||||
* Managed reset_get_bulk(). For reset controllers returned
|
||||
* from this function, reset_free() is called automatically on driver
|
||||
* detach.
|
||||
*
|
||||
* Returns a struct reset_ctl or IS_ERR() condition containing errno.
|
||||
*/
|
||||
struct reset_ctl_bulk *devm_reset_bulk_get(struct udevice *dev);
|
||||
|
||||
/**
|
||||
* devm_reset_bulk_get_optional - resource managed reset_get_bulk() that
|
||||
* can fail
|
||||
* @dev: The client device.
|
||||
*
|
||||
* Managed reset_get_bulk(). For reset controllers returned
|
||||
* from this function, reset_free() is called automatically on driver
|
||||
* detach.
|
||||
*
|
||||
* Returns a struct reset_ctl or NULL if it failed.
|
||||
*/
|
||||
struct reset_ctl_bulk *devm_reset_bulk_get_optional(struct udevice *dev);
|
||||
|
||||
/**
|
||||
* devm_reset_bulk_get_by_node - resource managed reset_get_bulk()
|
||||
* @dev: device to be reset by the controller
|
||||
* @node: ofnode where the "resets" property is. Usually a sub-node of
|
||||
* the dev's node.
|
||||
*
|
||||
* see devm_reset_bulk_get()
|
||||
*/
|
||||
struct reset_ctl_bulk *devm_reset_bulk_get_by_node(struct udevice *dev,
|
||||
ofnode node);
|
||||
|
||||
/**
|
||||
* devm_reset_bulk_get_optional_by_node - resource managed reset_get_bulk()
|
||||
* that can fail
|
||||
* @dev: device to be reset by the controller
|
||||
* @node: ofnode where the "resets" property is. Usually a sub-node of
|
||||
* the dev's node.
|
||||
*
|
||||
* see devm_reset_bulk_get_optional()
|
||||
*/
|
||||
struct reset_ctl_bulk *devm_reset_bulk_get_optional_by_node(struct udevice *dev,
|
||||
ofnode node);
|
||||
|
||||
/**
|
||||
* reset_get_by_index - Get/request a reset signal by integer index.
|
||||
*
|
||||
|
@ -265,7 +357,48 @@ static inline int reset_release_bulk(struct reset_ctl_bulk *bulk)
|
|||
{
|
||||
return reset_release_all(bulk->resets, bulk->count);
|
||||
}
|
||||
|
||||
#else
|
||||
static inline struct reset_ctl *devm_reset_control_get(struct udevice *dev,
|
||||
const char *id)
|
||||
{
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
}
|
||||
|
||||
static inline struct reset_ctl *devm_reset_control_get_optional(struct udevice *dev,
|
||||
const char *id)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct reset_ctl *devm_reset_control_get_by_index(struct udevice *dev,
|
||||
int index)
|
||||
{
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
}
|
||||
|
||||
static inline struct reset_ctl_bulk *devm_reset_bulk_get(struct udevice *dev)
|
||||
{
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
}
|
||||
|
||||
static inline struct reset_ctl_bulk *devm_reset_bulk_get_optional(struct udevice *dev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct reset_ctl_bulk *devm_reset_bulk_get_by_node(struct udevice *dev,
|
||||
ofnode node)
|
||||
{
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
}
|
||||
|
||||
static inline struct reset_ctl_bulk *devm_reset_bulk_get_optional_by_node(struct udevice *dev,
|
||||
ofnode node)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int reset_get_by_index(struct udevice *dev, int index,
|
||||
struct reset_ctl *reset_ctl)
|
||||
{
|
||||
|
|
24
include/scmi_agent-uclass.h
Normal file
24
include/scmi_agent-uclass.h
Normal 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
68
include/scmi_agent.h
Normal 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
179
include/scmi_protocols.h
Normal 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 */
|
|
@ -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
|
||||
|
|
|
@ -120,7 +120,7 @@ UCLASS_DRIVER(testbus) = {
|
|||
/* Test that we can probe for children */
|
||||
static int dm_test_bus_children(struct unit_test_state *uts)
|
||||
{
|
||||
int num_devices = 8;
|
||||
int num_devices = 9;
|
||||
struct udevice *bus;
|
||||
struct uclass *uc;
|
||||
|
||||
|
|
102
test/dm/gpio.c
102
test/dm/gpio.c
|
@ -10,6 +10,7 @@
|
|||
#include <malloc.h>
|
||||
#include <acpi/acpi_device.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/root.h>
|
||||
#include <dm/test.h>
|
||||
#include <dm/util.h>
|
||||
|
@ -480,3 +481,104 @@ static int dm_test_gpio_get_acpi_irq(struct unit_test_state *uts)
|
|||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_gpio_get_acpi_irq, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
|
||||
|
||||
/* Test that we can get/release GPIOs using managed API */
|
||||
static int dm_test_gpio_devm(struct unit_test_state *uts)
|
||||
{
|
||||
static const u32 flags = GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE;
|
||||
struct gpio_desc *desc1, *desc2, *desc3, *desc_err;
|
||||
struct udevice *dev;
|
||||
struct udevice *dev2;
|
||||
|
||||
ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test",
|
||||
&dev));
|
||||
ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "another-test",
|
||||
&dev2));
|
||||
|
||||
/* Get 3 GPIOs from 'a-test' dev */
|
||||
desc1 = devm_gpiod_get_index(dev, "test4", 0, flags);
|
||||
ut_assert(!IS_ERR(desc1));
|
||||
desc2 = devm_gpiod_get_index(dev, "test4", 1, flags);
|
||||
ut_assert(!IS_ERR(desc2));
|
||||
desc3 = devm_gpiod_get_index_optional(dev, "test5", 0, flags);
|
||||
ut_assert(!IS_ERR(desc3));
|
||||
ut_assert(desc3);
|
||||
|
||||
/*
|
||||
* Try get the same 3 GPIOs from 'a-test' and 'another-test' devices.
|
||||
* check that it fails
|
||||
*/
|
||||
desc_err = devm_gpiod_get_index(dev, "test4", 0, flags);
|
||||
ut_asserteq(-EBUSY, PTR_ERR(desc_err));
|
||||
desc_err = devm_gpiod_get_index(dev2, "test4", 0, flags);
|
||||
ut_asserteq(-EBUSY, PTR_ERR(desc_err));
|
||||
desc_err = devm_gpiod_get_index(dev, "test4", 1, flags);
|
||||
ut_asserteq(-EBUSY, PTR_ERR(desc_err));
|
||||
desc_err = devm_gpiod_get_index(dev2, "test4", 1, flags);
|
||||
ut_asserteq(-EBUSY, PTR_ERR(desc_err));
|
||||
desc_err = devm_gpiod_get_index_optional(dev, "test5", 0, flags);
|
||||
ut_asserteq_ptr(NULL, desc_err);
|
||||
desc_err = devm_gpiod_get_index_optional(dev2, "test5", 0, flags);
|
||||
ut_asserteq_ptr(NULL, desc_err);
|
||||
|
||||
/* Try get GPIOs outside of the list */
|
||||
desc_err = devm_gpiod_get_index(dev, "test4", 2, flags);
|
||||
ut_assert(IS_ERR(desc_err));
|
||||
desc_err = devm_gpiod_get_index_optional(dev, "test5", 1, flags);
|
||||
ut_asserteq_ptr(NULL, desc_err);
|
||||
|
||||
/* Manipulate the GPIOs */
|
||||
ut_assertok(dm_gpio_set_value(desc1, 1));
|
||||
ut_asserteq(1, dm_gpio_get_value(desc1));
|
||||
ut_assertok(dm_gpio_set_value(desc1, 0));
|
||||
ut_asserteq(0, dm_gpio_get_value(desc1));
|
||||
|
||||
ut_assertok(dm_gpio_set_value(desc2, 1));
|
||||
ut_asserteq(1, dm_gpio_get_value(desc2));
|
||||
ut_assertok(dm_gpio_set_value(desc2, 0));
|
||||
ut_asserteq(0, dm_gpio_get_value(desc2));
|
||||
|
||||
ut_assertok(dm_gpio_set_value(desc3, 1));
|
||||
ut_asserteq(1, dm_gpio_get_value(desc3));
|
||||
ut_assertok(dm_gpio_set_value(desc3, 0));
|
||||
ut_asserteq(0, dm_gpio_get_value(desc3));
|
||||
|
||||
/* Check that the GPIO cannot be owned by more than one device */
|
||||
desc_err = devm_gpiod_get_index(dev2, "test4", 0, flags);
|
||||
ut_asserteq(-EBUSY, PTR_ERR(desc_err));
|
||||
desc_err = devm_gpiod_get_index(dev2, "test4", 1, flags);
|
||||
ut_asserteq(-EBUSY, PTR_ERR(desc_err));
|
||||
desc_err = devm_gpiod_get_index_optional(dev2, "test5", 0, flags);
|
||||
ut_asserteq_ptr(NULL, desc_err);
|
||||
|
||||
/*
|
||||
* Release one GPIO and check that we can get it back using
|
||||
* 'another-test' and then 'a-test'
|
||||
*/
|
||||
devm_gpiod_put(dev, desc2);
|
||||
desc2 = devm_gpiod_get_index(dev2, "test4", 1, flags);
|
||||
ut_assert(!IS_ERR(desc2));
|
||||
|
||||
devm_gpiod_put(dev2, desc2);
|
||||
desc2 = devm_gpiod_get_index(dev, "test4", 1, flags);
|
||||
ut_assert(!IS_ERR(desc2));
|
||||
|
||||
/* Release one GPIO before removing the 'a-test' dev. */
|
||||
devm_gpiod_put(dev, desc2);
|
||||
device_remove(dev, DM_REMOVE_NORMAL);
|
||||
|
||||
/* All the GPIOs must have been freed. We should be able to claim
|
||||
* them with the 'another-test' device.
|
||||
*/
|
||||
desc1 = devm_gpiod_get_index(dev2, "test4", 0, flags);
|
||||
ut_assert(!IS_ERR(desc1));
|
||||
desc2 = devm_gpiod_get_index(dev2, "test4", 1, flags);
|
||||
ut_assert(!IS_ERR(desc2));
|
||||
desc3 = devm_gpiod_get_index_optional(dev2, "test5", 0, flags);
|
||||
ut_assert(!IS_ERR(desc3));
|
||||
ut_assert(desc3);
|
||||
|
||||
device_remove(dev2, DM_REMOVE_NORMAL);
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_gpio_devm, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
|
||||
|
|
198
test/dm/regmap.c
198
test/dm/regmap.c
|
@ -9,8 +9,10 @@
|
|||
#include <mapmem.h>
|
||||
#include <regmap.h>
|
||||
#include <syscon.h>
|
||||
#include <rand.h>
|
||||
#include <asm/test.h>
|
||||
#include <dm/test.h>
|
||||
#include <dm/devres.h>
|
||||
#include <linux/err.h>
|
||||
#include <test/test.h>
|
||||
#include <test/ut.h>
|
||||
|
@ -187,3 +189,199 @@ static int dm_test_regmap_poll(struct unit_test_state *uts)
|
|||
}
|
||||
|
||||
DM_TEST(dm_test_regmap_poll, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
|
||||
|
||||
struct regmaptest_priv {
|
||||
struct regmap *cfg_regmap; /* For testing regmap_config options. */
|
||||
struct regmap *fld_regmap; /* For testing regmap fields. */
|
||||
struct regmap_field **fields;
|
||||
};
|
||||
|
||||
static const struct reg_field field_cfgs[] = {
|
||||
{
|
||||
.reg = 0,
|
||||
.lsb = 0,
|
||||
.msb = 6,
|
||||
},
|
||||
{
|
||||
.reg = 2,
|
||||
.lsb = 4,
|
||||
.msb = 12,
|
||||
},
|
||||
{
|
||||
.reg = 2,
|
||||
.lsb = 12,
|
||||
.msb = 15,
|
||||
}
|
||||
};
|
||||
|
||||
#define REGMAP_TEST_BUF_START 0
|
||||
#define REGMAP_TEST_BUF_SZ 5
|
||||
|
||||
static int remaptest_probe(struct udevice *dev)
|
||||
{
|
||||
struct regmaptest_priv *priv = dev_get_priv(dev);
|
||||
struct regmap *regmap;
|
||||
struct regmap_field *field;
|
||||
struct regmap_config cfg;
|
||||
int i;
|
||||
static const int n = ARRAY_SIZE(field_cfgs);
|
||||
|
||||
/*
|
||||
* To exercise all the regmap config options, create a regmap that
|
||||
* points to a custom memory area instead of the one defined in device
|
||||
* tree. Use 2-byte elements. To allow directly indexing into the
|
||||
* elements, use an offset shift of 1. So, accessing offset 1 gets the
|
||||
* element at index 1 at memory location 2.
|
||||
*
|
||||
* REGMAP_TEST_BUF_SZ is the number of elements, so we need to multiply
|
||||
* it by 2 because r_size expects number of bytes.
|
||||
*/
|
||||
cfg.reg_offset_shift = 1;
|
||||
cfg.r_start = REGMAP_TEST_BUF_START;
|
||||
cfg.r_size = REGMAP_TEST_BUF_SZ * 2;
|
||||
cfg.width = REGMAP_SIZE_16;
|
||||
|
||||
regmap = devm_regmap_init(dev, NULL, NULL, &cfg);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
priv->cfg_regmap = regmap;
|
||||
|
||||
memset(&cfg, 0, sizeof(struct regmap_config));
|
||||
cfg.width = REGMAP_SIZE_16;
|
||||
|
||||
regmap = devm_regmap_init(dev, NULL, NULL, &cfg);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
priv->fld_regmap = regmap;
|
||||
|
||||
priv->fields = devm_kzalloc(dev, sizeof(struct regmap_field *) * n,
|
||||
GFP_KERNEL);
|
||||
if (!priv->fields)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0 ; i < n; i++) {
|
||||
field = devm_regmap_field_alloc(dev, priv->fld_regmap,
|
||||
field_cfgs[i]);
|
||||
if (IS_ERR(field))
|
||||
return PTR_ERR(field);
|
||||
priv->fields[i] = field;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id regmaptest_ids[] = {
|
||||
{ .compatible = "sandbox,regmap_test" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(regmap_test) = {
|
||||
.name = "regmaptest_drv",
|
||||
.of_match = regmaptest_ids,
|
||||
.id = UCLASS_NOP,
|
||||
.probe = remaptest_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct regmaptest_priv),
|
||||
};
|
||||
|
||||
static int dm_test_devm_regmap(struct unit_test_state *uts)
|
||||
{
|
||||
int i = 0;
|
||||
u32 val;
|
||||
u16 pattern[REGMAP_TEST_BUF_SZ];
|
||||
u16 *buffer;
|
||||
struct udevice *dev;
|
||||
struct regmaptest_priv *priv;
|
||||
|
||||
sandbox_set_enable_memio(true);
|
||||
|
||||
/*
|
||||
* Map the memory area the regmap should point to so we can make sure
|
||||
* the writes actually go to that location.
|
||||
*/
|
||||
buffer = map_physmem(REGMAP_TEST_BUF_START,
|
||||
REGMAP_TEST_BUF_SZ * 2, MAP_NOCACHE);
|
||||
|
||||
ut_assertok(uclass_get_device_by_name(UCLASS_NOP, "regmap-test_0",
|
||||
&dev));
|
||||
priv = dev_get_priv(dev);
|
||||
|
||||
srand(get_ticks() + rand());
|
||||
for (i = 0; i < REGMAP_TEST_BUF_SZ; i++) {
|
||||
pattern[i] = rand();
|
||||
ut_assertok(regmap_write(priv->cfg_regmap, i, pattern[i]));
|
||||
}
|
||||
for (i = 0; i < REGMAP_TEST_BUF_SZ; i++) {
|
||||
ut_assertok(regmap_read(priv->cfg_regmap, i, &val));
|
||||
ut_asserteq(val, buffer[i]);
|
||||
ut_asserteq(val, pattern[i]);
|
||||
}
|
||||
|
||||
ut_asserteq(-ERANGE, regmap_write(priv->cfg_regmap, REGMAP_TEST_BUF_SZ,
|
||||
val));
|
||||
ut_asserteq(-ERANGE, regmap_read(priv->cfg_regmap, REGMAP_TEST_BUF_SZ,
|
||||
&val));
|
||||
ut_asserteq(-ERANGE, regmap_write(priv->cfg_regmap, -1, val));
|
||||
ut_asserteq(-ERANGE, regmap_read(priv->cfg_regmap, -1, &val));
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_devm_regmap, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
|
||||
|
||||
static int test_one_field(struct unit_test_state *uts,
|
||||
struct regmap *regmap,
|
||||
struct regmap_field *field,
|
||||
struct reg_field field_cfg)
|
||||
{
|
||||
int j;
|
||||
unsigned int val;
|
||||
int mask = (1 << (field_cfg.msb - field_cfg.lsb + 1)) - 1;
|
||||
int shift = field_cfg.lsb;
|
||||
|
||||
ut_assertok(regmap_write(regmap, field_cfg.reg, 0));
|
||||
ut_assertok(regmap_read(regmap, field_cfg.reg, &val));
|
||||
ut_asserteq(0, val);
|
||||
|
||||
for (j = 0; j <= mask; j++) {
|
||||
ut_assertok(regmap_field_write(field, j));
|
||||
ut_assertok(regmap_field_read(field, &val));
|
||||
ut_asserteq(j, val);
|
||||
ut_assertok(regmap_read(regmap, field_cfg.reg, &val));
|
||||
ut_asserteq(j << shift, val);
|
||||
}
|
||||
|
||||
ut_assertok(regmap_field_write(field, mask + 1));
|
||||
ut_assertok(regmap_read(regmap, field_cfg.reg, &val));
|
||||
ut_asserteq(0, val);
|
||||
|
||||
ut_assertok(regmap_field_write(field, 0xFFFF));
|
||||
ut_assertok(regmap_read(regmap, field_cfg.reg, &val));
|
||||
ut_asserteq(mask << shift, val);
|
||||
|
||||
ut_assertok(regmap_write(regmap, field_cfg.reg, 0xFFFF));
|
||||
ut_assertok(regmap_field_write(field, 0));
|
||||
ut_assertok(regmap_read(regmap, field_cfg.reg, &val));
|
||||
ut_asserteq(0xFFFF & ~(mask << shift), val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dm_test_devm_regmap_field(struct unit_test_state *uts)
|
||||
{
|
||||
int i, rc;
|
||||
struct udevice *dev;
|
||||
struct regmaptest_priv *priv;
|
||||
|
||||
ut_assertok(uclass_get_device_by_name(UCLASS_NOP, "regmap-test_0",
|
||||
&dev));
|
||||
priv = dev_get_priv(dev);
|
||||
|
||||
sandbox_set_enable_memio(true);
|
||||
for (i = 0 ; i < ARRAY_SIZE(field_cfgs); i++) {
|
||||
rc = test_one_field(uts, priv->fld_regmap, priv->fields[i],
|
||||
field_cfgs[i]);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_devm_regmap_field, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
|
||||
|
|
|
@ -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
203
test/dm/scmi.c
Normal 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);
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Add table
Reference in a new issue