* [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support
@ 2022-02-20 12:47 Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 01/24] PBL: fdt: factor reg property parsing into helper Ahmad Fatoum
` (24 more replies)
0 siblings, 25 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium
ST calls trusted firmware + SCMI for accessing some secure resources the
trusted boot chain (as opposed to the basic bootchain). This is
independent of cryptographically secured boot. So far, barebox generated
images with a STM32 header prepended, which could be booted after
upstream TF-A. This extends barebox to generate an extra image suitable
for inclusion in A TF-A Firmware Image Package and teaches barebox to
support firmware clocks and resets provided by TF-A over secure monitor
calls. There are no upstream DTs yet with SCMI and intention seems to be
to have boot firmware fix up the nodes. The SCMI changes here have been
tested by taking OpenSTLinux with its downstream TF-A, U-Boot, OP-TEE
and Linux components and just replacing U-Boot with barebox via fiptool
update.
Cheers,
Ahmad Fatoum (24):
PBL: fdt: factor reg property parsing into helper
pinctrl: stm32: use gpio-ranges instead of alias
ARM: stm32mp: simplify with build_stm32mp_image macro
ARM: stm32mp: change stm32image extension to .stm32
filetype: detect TF-A Firmware Image Packages (FIP)
scripts: add tool to adjust bl33 load address in existing FIP
ARM: stm32mp: build extra barebox-stm32mp-generic-bl33.img
ARM: stm32mp: ddrctrl: fix wrong register field widths
reset: stm32: drop stm32mp1_reset_ops indirection
reset: move stm32 reset code to drivers/power/reset
ARM: smccc: sync header with upstream
firmware: import Linux v5.13 SCMI support
reset: add SCMI support
clk: add SCMI clock driver
regulator: add SCMI regulator driver
clk: accept const arguments in clk_to_clk_hw/clk_hw_to_clk
serial: stm32: bail if clock_get_rate returns zero
clk: implement of_clk_hw_{onecell,simple}_get
clk: implement clk_hw_reparent
reset: add support for reset_control_status
clk: stm32mp1: sync with Linux v5.17-rc1
regulator: core: fall back to node name if no regulator-name property
ARM: dts: stm32mp: remove regulator-name override in stm32mp151.dtsi
ARM: stm32mp: enable more config options
Documentation/boards/stm32mp.rst | 74 +-
arch/arm/configs/stm32mp_defconfig | 8 +-
arch/arm/dts/stm32mp151.dtsi | 16 -
arch/arm/include/asm/opcodes-sec.h | 17 +
arch/arm/mach-stm32mp/Kconfig | 6 +-
arch/arm/mach-stm32mp/Makefile | 1 +
arch/arm/mach-stm32mp/bl33-generic.c | 24 +
arch/arm/mach-stm32mp/ddrctrl.c | 10 +-
common/filetype.c | 4 +
drivers/base/driver.c | 28 +
drivers/clk/Kconfig | 11 +
drivers/clk/Makefile | 1 +
drivers/clk/clk-scmi.c | 192 +++
drivers/clk/clk-stm32mp1.c | 886 ++++++++----
drivers/clk/clk.c | 22 +
drivers/firmware/Kconfig | 10 +
drivers/firmware/Makefile | 1 +
drivers/firmware/arm_scmi/Makefile | 10 +
drivers/firmware/arm_scmi/base.c | 281 ++++
drivers/firmware/arm_scmi/bus.c | 226 +++
drivers/firmware/arm_scmi/clock.c | 374 +++++
drivers/firmware/arm_scmi/common.h | 333 +++++
drivers/firmware/arm_scmi/driver.c | 1279 +++++++++++++++++
drivers/firmware/arm_scmi/reset.c | 229 +++
drivers/firmware/arm_scmi/shmem.c | 89 ++
drivers/firmware/arm_scmi/smc.c | 137 ++
drivers/firmware/arm_scmi/voltage.c | 379 +++++
drivers/pinctrl/pinctrl-stm32.c | 14 +-
drivers/power/reset/Kconfig | 7 +
drivers/power/reset/Makefile | 1 +
.../reset/stm32-reboot.c} | 143 +-
drivers/regulator/Kconfig | 9 +
drivers/regulator/Makefile | 1 +
drivers/regulator/core.c | 2 +
drivers/regulator/scmi-regulator.c | 391 +++++
drivers/reset/Kconfig | 17 +-
drivers/reset/Makefile | 2 +-
drivers/reset/core.c | 21 +
drivers/reset/reset-scmi.c | 130 ++
drivers/reset/reset-simple.c | 3 +-
drivers/serial/serial_stm32.c | 2 +
images/Makefile.stm32mp | 59 +-
include/driver.h | 8 +
include/filetype.h | 1 +
include/linux/arm-smccc.h | 363 ++++-
include/linux/clk.h | 33 +-
include/linux/idr.h | 79 +
include/linux/processor.h | 29 +
include/linux/reset-controller.h | 2 +
include/linux/reset.h | 6 +
include/linux/scmi_protocol.h | 654 +++++++++
include/linux/slab.h | 3 +
include/soc/stm32/reboot.h | 16 +
pbl/fdt.c | 23 +-
scripts/fiptool_fwconfig | 39 +
55 files changed, 6218 insertions(+), 488 deletions(-)
create mode 100644 arch/arm/include/asm/opcodes-sec.h
create mode 100644 arch/arm/mach-stm32mp/bl33-generic.c
create mode 100644 drivers/clk/clk-scmi.c
create mode 100644 drivers/firmware/arm_scmi/Makefile
create mode 100644 drivers/firmware/arm_scmi/base.c
create mode 100644 drivers/firmware/arm_scmi/bus.c
create mode 100644 drivers/firmware/arm_scmi/clock.c
create mode 100644 drivers/firmware/arm_scmi/common.h
create mode 100644 drivers/firmware/arm_scmi/driver.c
create mode 100644 drivers/firmware/arm_scmi/reset.c
create mode 100644 drivers/firmware/arm_scmi/shmem.c
create mode 100644 drivers/firmware/arm_scmi/smc.c
create mode 100644 drivers/firmware/arm_scmi/voltage.c
rename drivers/{reset/reset-stm32.c => power/reset/stm32-reboot.c} (53%)
create mode 100644 drivers/regulator/scmi-regulator.c
create mode 100644 drivers/reset/reset-scmi.c
create mode 100644 include/linux/idr.h
create mode 100644 include/linux/processor.h
create mode 100644 include/linux/scmi_protocol.h
create mode 100644 include/soc/stm32/reboot.h
create mode 100755 scripts/fiptool_fwconfig
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 01/24] PBL: fdt: factor reg property parsing into helper
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
@ 2022-02-20 12:47 ` Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 02/24] pinctrl: stm32: use gpio-ranges instead of alias Ahmad Fatoum
` (23 subsequent siblings)
24 siblings, 0 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium, Ahmad Fatoum
Instead of duplicating the loop for each of base and size, move it into
a helper function. This may come in handy later when extending the
function, e.g. to have the generic-dt-2nd image take /reserved-memory
entries into account and not rely on CONFIG_OPTEE_SIZE.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
pbl/fdt.c | 23 +++++++++++++++--------
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/pbl/fdt.c b/pbl/fdt.c
index 7a913c546af6..51719698f269 100644
--- a/pbl/fdt.c
+++ b/pbl/fdt.c
@@ -3,12 +3,24 @@
#include <pbl.h>
#include <linux/printk.h>
+static const __be32 *fdt_parse_reg(const __be32 *reg, uint32_t n,
+ uint64_t *val)
+{
+ int i;
+
+ *val = 0;
+ for (i = 0; i < n; i++)
+ *val = (*val << 32) | fdt32_to_cpu(*reg++);
+
+ return reg;
+}
+
void fdt_find_mem(const void *fdt, unsigned long *membase, unsigned long *memsize)
{
const __be32 *nap, *nsp, *reg;
uint32_t na, ns;
uint64_t memsize64, membase64;
- int node, size, i;
+ int node, size;
/* Make sure FDT blob is sane */
if (fdt_check_header(fdt) != 0) {
@@ -51,14 +63,9 @@ void fdt_find_mem(const void *fdt, unsigned long *membase, unsigned long *memsiz
goto err;
}
- membase64 = 0;
- for (i = 0; i < na; i++)
- membase64 = (membase64 << 32) | fdt32_to_cpu(*reg++);
-
/* get the memsize and truncate it to under 4G on 32 bit machines */
- memsize64 = 0;
- for (i = 0; i < ns; i++)
- memsize64 = (memsize64 << 32) | fdt32_to_cpu(*reg++);
+ reg = fdt_parse_reg(reg, na, &membase64);
+ reg = fdt_parse_reg(reg, ns, &memsize64);
*membase = membase64;
*memsize = memsize64;
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 02/24] pinctrl: stm32: use gpio-ranges instead of alias
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 01/24] PBL: fdt: factor reg property parsing into helper Ahmad Fatoum
@ 2022-02-20 12:47 ` Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 03/24] ARM: stm32mp: simplify with build_stm32mp_image macro Ahmad Fatoum
` (22 subsequent siblings)
24 siblings, 0 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium, Ahmad Fatoum
Upstream device tree doesn't feature aliases and we don't really need
it, as the gpio-ranges property has a GPIO controller offset cell.
Use it instead.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
arch/arm/dts/stm32mp151.dtsi | 12 ------------
drivers/pinctrl/pinctrl-stm32.c | 14 ++------------
2 files changed, 2 insertions(+), 24 deletions(-)
diff --git a/arch/arm/dts/stm32mp151.dtsi b/arch/arm/dts/stm32mp151.dtsi
index f1fd888fa1c6..aac2907bc6b5 100644
--- a/arch/arm/dts/stm32mp151.dtsi
+++ b/arch/arm/dts/stm32mp151.dtsi
@@ -1,18 +1,6 @@
/ {
aliases {
- gpio0 = &gpioa;
- gpio1 = &gpiob;
- gpio2 = &gpioc;
- gpio3 = &gpiod;
- gpio4 = &gpioe;
- gpio5 = &gpiof;
- gpio6 = &gpiog;
- gpio7 = &gpioh;
- gpio8 = &gpioi;
- gpio9 = &gpioj;
- gpio10 = &gpiok;
- gpio25 = &gpioz;
mmc0 = &sdmmc1;
mmc1 = &sdmmc2;
mmc2 = &sdmmc3;
diff --git a/drivers/pinctrl/pinctrl-stm32.c b/drivers/pinctrl/pinctrl-stm32.c
index ceaa4254c4d0..cee10636cef9 100644
--- a/drivers/pinctrl/pinctrl-stm32.c
+++ b/drivers/pinctrl/pinctrl-stm32.c
@@ -303,7 +303,7 @@ static int stm32_gpiochip_add(struct stm32_gpio_bank *bank,
enum { PINCTRL_PHANDLE, GPIOCTRL_OFFSET, PINCTRL_OFFSET, PINCOUNT, GPIO_RANGE_NCELLS };
const __be32 *gpio_ranges;
u32 ngpios;
- int id, ret, size;
+ int ret, size;
dev = of_platform_device_create(np, parent);
if (!dev)
@@ -347,17 +347,7 @@ static int stm32_gpiochip_add(struct stm32_gpio_bank *bank,
bank->base = IOMEM(iores->start);
- if (dev->id >= 0) {
- id = dev->id;
- } else {
- id = of_alias_get_id(np, "gpio");
- if (id < 0) {
- dev_err(dev, "Failed to get GPIO alias\n");
- return id;
- }
- }
-
- bank->chip.base = id * STM32_GPIO_PINS_PER_BANK;
+ bank->chip.base = be32_to_cpu(gpio_ranges[PINCTRL_OFFSET]);
bank->chip.ops = &stm32_gpio_ops;
bank->chip.dev = dev;
bank->clk = clk_get(dev, NULL);
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 03/24] ARM: stm32mp: simplify with build_stm32mp_image macro
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 01/24] PBL: fdt: factor reg property parsing into helper Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 02/24] pinctrl: stm32: use gpio-ranges instead of alias Ahmad Fatoum
@ 2022-02-20 12:47 ` Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 04/24] ARM: stm32mp: change stm32image extension to .stm32 Ahmad Fatoum
` (21 subsequent siblings)
24 siblings, 0 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium, Ahmad Fatoum
Reduce duplication by adding a common macro to get the four lines of
boilerplate down to a single line. No functional change.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
images/Makefile.stm32mp | 49 +++++++++++++++++------------------------
1 file changed, 20 insertions(+), 29 deletions(-)
diff --git a/images/Makefile.stm32mp b/images/Makefile.stm32mp
index 17f03908b0d0..558f8c585984 100644
--- a/images/Makefile.stm32mp
+++ b/images/Makefile.stm32mp
@@ -9,35 +9,26 @@
$(obj)/%.stm32: $(obj)/% FORCE
$(call if_changed,stm32_image)
-STM32MP1_OPTS = -a 0xc0100000 -e 0xc0100000 -v1
+define build_stm32mp_image =
+$(eval
+ifeq ($($(strip $(1))), y)
+ pblb-y += $(strip $(2))
+ FILE_barebox-$(strip $(3)).img = $(strip $(2)).pblb.stm32
+ OPTS_$(strip $(2)).pblb.stm32 = -a 0xc0100000 -e 0xc0100000 -v1
+ image-y += barebox-$(strip $(3)).img
+endif
+)
+endef
# --------------------------------------
-pblb-$(CONFIG_MACH_STM32MP15XX_DKX) += start_stm32mp15xx_dkx
-FILE_barebox-stm32mp15xx-dkx.img = start_stm32mp15xx_dkx.pblb.stm32
-OPTS_start_stm32mp15xx_dkx.pblb.stm32 = $(STM32MP1_OPTS)
-image-$(CONFIG_MACH_STM32MP15XX_DKX) += barebox-stm32mp15xx-dkx.img
-
-pblb-$(CONFIG_MACH_LXA_MC1) += start_stm32mp157c_lxa_mc1
-FILE_barebox-stm32mp157c-lxa-mc1.img = start_stm32mp157c_lxa_mc1.pblb.stm32
-OPTS_start_stm32mp157c_lxa_mc1.pblb.stm32 = $(STM32MP1_OPTS)
-image-$(CONFIG_MACH_LXA_MC1) += barebox-stm32mp157c-lxa-mc1.img
-
-pblb-$(CONFIG_MACH_PROTONIC_STM32MP1) += start_prtt1a start_prtt1s start_prtt1c
-FILE_barebox-prtt1a.img = start_prtt1a.pblb.stm32
-FILE_barebox-prtt1c.img = start_prtt1c.pblb.stm32
-FILE_barebox-prtt1s.img = start_prtt1s.pblb.stm32
-OPTS_start_prtt1a.pblb.stm32 = $(STM32MP1_OPTS)
-OPTS_start_prtt1c.pblb.stm32 = $(STM32MP1_OPTS)
-OPTS_start_prtt1s.pblb.stm32 = $(STM32MP1_OPTS)
-image-$(CONFIG_MACH_PROTONIC_STM32MP1) += barebox-prtt1a.img barebox-prtt1s.img barebox-prtt1c.img
-
-pblb-$(CONFIG_MACH_SEEED_ODYSSEY) += start_stm32mp157c_seeed_odyssey
-FILE_barebox-stm32mp157c-seeed-odyssey.img = start_stm32mp157c_seeed_odyssey.pblb.stm32
-OPTS_start_stm32mp157c_seeed_odyssey.pblb.stm32 = $(STM32MP1_OPTS)
-image-$(CONFIG_MACH_SEEED_ODYSSEY) += barebox-stm32mp157c-seeed-odyssey.img
-
-pblb-$(CONFIG_MACH_STM32MP15X_EV1) += start_stm32mp15x_ev1
-FILE_barebox-stm32mp15x-ev1.img = start_stm32mp15x_ev1.pblb.stm32
-OPTS_start_stm32mp15x_ev1.pblb.stm32 = $(STM32MP1_OPTS)
-image-$(CONFIG_MACH_STM32MP15X_EV1) += barebox-stm32mp15x-ev1.img
+$(call build_stm32mp_image, CONFIG_MACH_STM32MP15XX_DKX, start_stm32mp15xx_dkx, stm32mp15xx-dkx)
+$(call build_stm32mp_image, CONFIG_MACH_STM32MP15X_EV1, start_stm32mp15x_ev1, stm32mp15x-ev1)
+
+$(call build_stm32mp_image, CONFIG_MACH_LXA_MC1, start_stm32mp157c_lxa_mc1, stm32mp157c-lxa-mc1)
+
+$(call build_stm32mp_image, CONFIG_MACH_PROTONIC_STM32MP1, start_prtt1a, prtt1a)
+$(call build_stm32mp_image, CONFIG_MACH_PROTONIC_STM32MP1, start_prtt1s, prtt1s)
+$(call build_stm32mp_image, CONFIG_MACH_PROTONIC_STM32MP1, start_prtt1c, prtt1c)
+
+$(call build_stm32mp_image, CONFIG_MACH_SEEED_ODYSSEY, start_stm32mp157c_seeed_odyssey, stm32mp157c-seeed-odyssey)
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 04/24] ARM: stm32mp: change stm32image extension to .stm32
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
` (2 preceding siblings ...)
2022-02-20 12:47 ` [PATCH 03/24] ARM: stm32mp: simplify with build_stm32mp_image macro Ahmad Fatoum
@ 2022-02-20 12:47 ` Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 05/24] filetype: detect TF-A Firmware Image Packages (FIP) Ahmad Fatoum
` (20 subsequent siblings)
24 siblings, 0 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium, Ahmad Fatoum
The .img extension for stm32mp1 images is unfortunate. The format is
deprecated and its header makes it not directly executable and thus not
suitable as-is for use in a FIP image where the BL33 is run from offset 0.
To make existence of the STM32 header evident, rename the extension
from .img to .stm32. As it's still supported by TF-A, have symlinks, so
user build script can use the old names for now.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
Documentation/boards/stm32mp.rst | 29 +++++++++++++++++------------
arch/arm/mach-stm32mp/Kconfig | 6 +++---
images/Makefile.stm32mp | 11 +++++++----
3 files changed, 27 insertions(+), 19 deletions(-)
diff --git a/Documentation/boards/stm32mp.rst b/Documentation/boards/stm32mp.rst
index 24b7a19ceb96..2b9dc01b551a 100644
--- a/Documentation/boards/stm32mp.rst
+++ b/Documentation/boards/stm32mp.rst
@@ -10,7 +10,7 @@ The first stage boot loader (FSBL) is loaded by the ROM code into the built-in
SYSRAM and executed. The FSBL sets up the SDRAM, install a secure monitor and
then the second stage boot loader (SSBL) is loaded into DRAM.
-When building barebox, the resulting ``barebox-${board}.img`` file has the STM32
+When building barebox, the resulting ``barebox-${board}.stm32`` file has the STM32
header preprended, so it can be loaded directly as SSBL by the ARM TF-A
(https://github.com/ARM-software/arm-trusted-firmware). Each entry point has a
header-less image ending in ``*.pblb`` as well.
@@ -25,18 +25,23 @@ as sole defconfig for all STM32MP boards::
make ARCH=arm stm32mp_defconfig
-The resulting images will be placed under ``images/``:
+The resulting images will be placed under ``images/``::
-::
+ barebox-stm32mp15xx-dkx.stm32
+ barebox-stm32mp15x-ev1.stm32
+ barebox-stm32mp157c-lxa-mc1.stm32
+ barebox-prtt1a.stm32
+ barebox-prtt1s.stm32
+ barebox-prtt1c.stm32
+ barebox-stm32mp157c-seeed-odyssey.stm32
+ barebox-dt-2nd.img
- barebox-stm32mp15xx-dkx.img # both DK1 and DK2
- barebox-stm32mp157c-lxa-mc1.img
- barebox-stm32mp157c-seeed-odyssey.img
- barebox-stm32mp15x-ev1.img # stm32mp157c-ev1 and friends
+In the above output, images with a ``.stm32`` extension feature the (legacy)
+stm32image header. ``barebox-dt-2nd.img`` is a generic barebox image that
+received an external device tree.
-
-Flashing barebox
-----------------
+Flashing barebox (legacy stm32image)
+------------------------------------
An appropriate image for a SD-Card can be generated with following
``genimage(1)`` config::
@@ -55,7 +60,7 @@ An appropriate image for a SD-Card can be generated with following
size = 256K
}
partition ssbl {
- image = "barebox-@STM32MP_BOARD@.img"
+ image = "barebox-@STM32MP_BOARD@.stm32"
size = 1M
}
partition barebox-environment {
@@ -69,7 +74,7 @@ partitions may look like this::
image @STM32MP_BOARD@.img {
partition ssbl {
- image = "barebox-@STM32MP_BOARD@.img"
+ image = "barebox-@STM32MP_BOARD@.stm32"
size = 1M
}
partition barebox-environment {
diff --git a/arch/arm/mach-stm32mp/Kconfig b/arch/arm/mach-stm32mp/Kconfig
index d059dbda56a9..38c1a44770c9 100644
--- a/arch/arm/mach-stm32mp/Kconfig
+++ b/arch/arm/mach-stm32mp/Kconfig
@@ -14,7 +14,7 @@ config MACH_STM32MP15XX_DKX
select ARCH_STM32MP157
bool "STM32MP157 DK1 and DK2 boards"
help
- builds a single barebox-stm32mp15xx-dkx.img that can be deployed
+ builds a single barebox-stm32mp15xx-dkx.stm32 that can be deployed
as SSBL on both the stm32mp157a-dk1 and stm32mp157c-dk2
config MACH_LXA_MC1
@@ -29,7 +29,7 @@ config MACH_STM32MP15X_EV1
select ARCH_STM32MP157
bool "STM32MP15X-EV1 board"
help
- builds a single barebox-stm32mp15x-ev1.img that can be deployed
+ builds a single barebox-stm32mp15x-ev1.stm32 that can be deployed
as SSBL on any STM32MP15X-EVAL platform, like the
STM32MP157C-EV1
@@ -37,7 +37,7 @@ config MACH_PROTONIC_STM32MP1
select ARCH_STM32MP157
bool "Protonic PRTT1L family of boards"
help
- Builds all barebox-prtt1*.img that can be deployed as SSBL
+ Builds all barebox-prtt1*.stm32 that can be deployed as SSBL
on the respective PRTT1L family board
endif
diff --git a/images/Makefile.stm32mp b/images/Makefile.stm32mp
index 558f8c585984..eeb5d9ecf6d3 100644
--- a/images/Makefile.stm32mp
+++ b/images/Makefile.stm32mp
@@ -6,16 +6,19 @@
# %.stm32 - convert into STM32MP image
# --------------------------------------
-$(obj)/%.stm32: $(obj)/% FORCE
+.SECONDEXPANSION:
+$(obj)/%.stm32: $(obj)/$$(FILE_$$(@F)) FORCE
+ $(Q)if [ -z $(FILE_$(@F)) ]; then echo "FILE_$(@F) empty!"; false; fi
+ @(cd $(obj) && ln -fs $(notdir $@) $(basename $(notdir $@)).img)
$(call if_changed,stm32_image)
define build_stm32mp_image =
$(eval
ifeq ($($(strip $(1))), y)
pblb-y += $(strip $(2))
- FILE_barebox-$(strip $(3)).img = $(strip $(2)).pblb.stm32
- OPTS_$(strip $(2)).pblb.stm32 = -a 0xc0100000 -e 0xc0100000 -v1
- image-y += barebox-$(strip $(3)).img
+ FILE_barebox-$(strip $(3)).stm32 = $(strip $(2)).pblb
+ OPTS_barebox-$(strip $(3)).stm32 = -a 0xc0100000 -e 0xc0100000 -v1
+ image-y += barebox-$(strip $(3)).stm32
endif
)
endef
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 05/24] filetype: detect TF-A Firmware Image Packages (FIP)
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
` (3 preceding siblings ...)
2022-02-20 12:47 ` [PATCH 04/24] ARM: stm32mp: change stm32image extension to .stm32 Ahmad Fatoum
@ 2022-02-20 12:47 ` Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 06/24] scripts: add tool to adjust bl33 load address in existing FIP Ahmad Fatoum
` (19 subsequent siblings)
24 siblings, 0 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium, Ahmad Fatoum
FIP is the new format for firmware loaded by ARM Trusted Firmware. It
can contain non-secure firmware, hardware config (device tree), firmware
config (configuration DT) and optionally OP-TEE.
In future, we may want to mount FIP to chain boot barebox out of it, but
for now, just let barebox filetype detect it correctly.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
common/filetype.c | 4 ++++
include/filetype.h | 1 +
2 files changed, 5 insertions(+)
diff --git a/common/filetype.c b/common/filetype.c
index 53517da70d39..0ded64b83c00 100644
--- a/common/filetype.c
+++ b/common/filetype.c
@@ -75,6 +75,7 @@ static const struct filetype_str filetype_str[] = {
[filetype_zynq_image] = { "Zynq image", "zynq-image" },
[filetype_mxs_sd_image] = { "i.MX23/28 SD card image", "mxs-sd-image" },
[filetype_rockchip_rkns_image] = { "Rockchip boot image", "rk-image" },
+ [filetype_fip] = { "TF-A Firmware Image Package", "fip" },
};
const char *file_type_to_string(enum filetype f)
@@ -315,6 +316,9 @@ enum filetype file_detect_type(const void *_buf, size_t bufsize)
return filetype_riscv_barebox_image;
if (strncmp(buf8, "RKNS", 4) == 0)
return filetype_rockchip_rkns_image;
+ if (le32_to_cpu(buf[0]) == le32_to_cpu(0xaa640001))
+ return filetype_fip;
+
if ((buf8[0] == 0x5a || buf8[0] == 0x69 || buf8[0] == 0x78 ||
buf8[0] == 0x8b || buf8[0] == 0x9c) &&
buf8[0x1] == 0 && buf8[0x2] == 0 && buf8[0x3] == 0 &&
diff --git a/include/filetype.h b/include/filetype.h
index 8bc179ac0817..9b7499fdf307 100644
--- a/include/filetype.h
+++ b/include/filetype.h
@@ -56,6 +56,7 @@ enum filetype {
filetype_zynq_image,
filetype_mxs_sd_image,
filetype_rockchip_rkns_image,
+ filetype_fip,
filetype_max,
};
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 06/24] scripts: add tool to adjust bl33 load address in existing FIP
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
` (4 preceding siblings ...)
2022-02-20 12:47 ` [PATCH 05/24] filetype: detect TF-A Firmware Image Packages (FIP) Ahmad Fatoum
@ 2022-02-20 12:47 ` Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 07/24] ARM: stm32mp: build extra barebox-stm32mp-generic-bl33.img Ahmad Fatoum
` (18 subsequent siblings)
24 siblings, 0 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium, Ahmad Fatoum
Instead of rebuilding TF-A with a (newer) barebox, an existing image can
be repacked:
fiptool update fip.bin --nt-fw images/barebox-dt-2nd.img \
--hw-config build/arch/arm/dts/stm32mp157c-dk2.dtb
This may fail at runtime though, because the STM32MP default is to place
BL33 at the very start of RAM. The script introduced here offers
an alternative to rebuilding TF-A with adjust FW_CONFIG:
./scripts/fiptool_fwconfig -l 0xc0001000 mmcblk0p3
This will have barebox loaded 4096 bytes into the STM32MP SDRAM.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
scripts/fiptool_fwconfig | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
create mode 100755 scripts/fiptool_fwconfig
diff --git a/scripts/fiptool_fwconfig b/scripts/fiptool_fwconfig
new file mode 100755
index 000000000000..f76f450b0473
--- /dev/null
+++ b/scripts/fiptool_fwconfig
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+set -e
+
+# adjust bl33 load address in existing FIP
+
+FDTGET=${FDTGET:-fdtget}
+FDTPUT=${FDTPUT:-fdtput}
+
+if [ "$1" != "-l" ] || [ "$#" -ne 3 ]; then
+ echo "USAGE: $0 -l NT_LOAD_ADDR FIP" 1>&2
+ exit 1
+fi
+
+NEW_LOAD_ADDR=$(($2))
+FIP=$3
+FWCONFIG=".$FIP.fw-config.bin"
+
+fiptool unpack --fw-config "$FWCONFIG" --force "$FIP"
+
+MAX_SIZE="$($FDTGET -t u $FWCONFIG /dtb-registry/nt_fw max-size)"
+set $($FDTGET -t u $FWCONFIG /dtb-registry/nt_fw load-address)
+ENTRY=$1
+OLD_LOAD_ADDR=$2
+
+if [ $NEW_LOAD_ADDR -lt $OLD_LOAD_ADDR ] ||
+ [ $NEW_LOAD_ADDR -ge $((OLD_LOAD_ADDR + MAX_SIZE)) ]; then
+ printf "New load address 0x%08x out of bounds [0x%08x-0x%08x)\n" \
+ $NEW_LOAD_ADDR $OLD_LOAD_ADDR $((OLD_LOAD_ADDR + MAX_SIZE)) 1>&2
+ exit 1
+fi
+
+$FDTPUT -t u $FWCONFIG /dtb-registry/nt_fw load-address $ENTRY $NEW_LOAD_ADDR
+$FDTPUT -t u $FWCONFIG /dtb-registry/nt_fw max-size \
+ $((MAX_SIZE + OLD_LOAD_ADDR - NEW_LOAD_ADDR))
+
+fiptool update $FIP --fw-config $FWCONFIG
+
+rm $FWCONFIG
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 07/24] ARM: stm32mp: build extra barebox-stm32mp-generic-bl33.img
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
` (5 preceding siblings ...)
2022-02-20 12:47 ` [PATCH 06/24] scripts: add tool to adjust bl33 load address in existing FIP Ahmad Fatoum
@ 2022-02-20 12:47 ` Ahmad Fatoum
2022-02-21 10:35 ` [PATCH] fixup! " Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 08/24] ARM: stm32mp: ddrctrl: fix wrong register field widths Ahmad Fatoum
` (17 subsequent siblings)
24 siblings, 1 reply; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium, Ahmad Fatoum
barebox-dt-2nd.img expects being loaded at an offset, so the stack can
grow down from entry point. The STM32MP TF-A default is to not have an
offset. Avoid this issue by having a stm32mp specific entry point that
sets up a 64 byte stack after end of barebox. As it's stm32mp-specific
anyway, we can skip the early FDT parsing and ask the SDRAM controller
about RAM size.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
Documentation/boards/stm32mp.rst | 51 ++++++++++++++++++++++++----
arch/arm/mach-stm32mp/Makefile | 1 +
arch/arm/mach-stm32mp/bl33-generic.c | 24 +++++++++++++
images/Makefile.stm32mp | 5 +++
4 files changed, 74 insertions(+), 7 deletions(-)
create mode 100644 arch/arm/mach-stm32mp/bl33-generic.c
diff --git a/Documentation/boards/stm32mp.rst b/Documentation/boards/stm32mp.rst
index 2b9dc01b551a..55bdafe78579 100644
--- a/Documentation/boards/stm32mp.rst
+++ b/Documentation/boards/stm32mp.rst
@@ -13,20 +13,26 @@ then the second stage boot loader (SSBL) is loaded into DRAM.
When building barebox, the resulting ``barebox-${board}.stm32`` file has the STM32
header preprended, so it can be loaded directly as SSBL by the ARM TF-A
(https://github.com/ARM-software/arm-trusted-firmware). Each entry point has a
-header-less image ending in ``*.pblb`` as well.
+header-less image ending in ``*.pblb`` as well. Additionally, there is
+a ``barebox-stm32mp-generic.img``, which is a header-less image for
+use as part of a Firmware Image Package (FIP).
-Use of barebox as FSBL is not supported.
+barebox images are meant to be loaded by the ARM TF-A
+(https://github.com/ARM-software/arm-trusted-firmware). FIP images are
+mandatory for STM32MP1 since TF-A v2.7.
+
+Use of barebox as FSBL is not implemented.
Building barebox
----------------
-With multi-image and device trees, it's expected to have ``stm32mp_defconfig``
-as sole defconfig for all STM32MP boards::
+There's a single ``stm32mp_defconfig`` for all STM32MP boards::
make ARCH=arm stm32mp_defconfig
The resulting images will be placed under ``images/``::
+ barebox-stm32mp-generic-bl33.img
barebox-stm32mp15xx-dkx.stm32
barebox-stm32mp15x-ev1.stm32
barebox-stm32mp157c-lxa-mc1.stm32
@@ -37,13 +43,44 @@ The resulting images will be placed under ``images/``::
barebox-dt-2nd.img
In the above output, images with a ``.stm32`` extension feature the (legacy)
-stm32image header. ``barebox-dt-2nd.img`` is a generic barebox image that
-received an external device tree.
+stm32image header. ``barebox-dt-2nd.img`` and ``barebox-stm32mp-generic-bl33.img``
+are board-generic barebox images that receive an external device tree.
+
+Flashing barebox (FIP)
+----------------------
+
+After building barebox in ``$BAREBOX_BUILDDIR``, change directory to ARM
+Trusted Firmware to build a FIP image. Example building STM32MP157C-DK2
+with SP_min (no OP-TEE):
+
+.. code:: bash
+
+ make CROSS_COMPILE=arm-none-eabi- PLAT=stm32mp1 ARCH=aarch32 ARM_ARCH_MAJOR=7 \
+ STM32MP_EMMC=1 STM32MP_EMMC_BOOT=1 STM32MP_SDMMC=1 STM32MP_SPI_NOR=1 \
+ AARCH32_SP=sp_min \
+ DTB_FILE_NAME=stm32mp157c-dk2.dtb \
+ BL33=$BAREBOX_BUILDDIR/images/barebox-stm32mp-generic-bl33.img \
+ BL33_CFG=$BAREBOX_BUILDDIR/arch/arm/dts/stm32mp157c-dk2.dtb \
+ fip
+
+For different boards, adjust ``DTB_FILENAME`` and ``BL33_CFG`` as appropriate.
+
+If OP-TEE is used, ensure ``CONFIG_OPTEE_SIZE`` is set appropriately, so
+early barebox code does not attempt accessing secure memory.
+
+barebox can also be patched into an existing FIP image with ``fiptool``:
+
+.. code:: bash
+
+ fiptool update mmcblk0p3 \
+ --nt-fw $BAREBOX_BUILDDIR/images/barebox-stm32mp-generic-bl33.img \
+ --hw-config $BAREBOX_BUILDDIR/arch/arm/dts/stm32mp135f-dk.dtb
Flashing barebox (legacy stm32image)
------------------------------------
-An appropriate image for a SD-Card can be generated with following
+After building ARM Trusted Firmware with ``STM32MP_USE_STM32IMAGE=1``,
+an appropriate image for a SD-Card can be generated with following
``genimage(1)`` config::
image @STM32MP_BOARD@.img {
diff --git a/arch/arm/mach-stm32mp/Makefile b/arch/arm/mach-stm32mp/Makefile
index 4163bd176b1f..86c13b8fca8a 100644
--- a/arch/arm/mach-stm32mp/Makefile
+++ b/arch/arm/mach-stm32mp/Makefile
@@ -2,4 +2,5 @@
obj-y := init.o
obj-pbl-y := ddrctrl.o
+pbl-y := bl33-generic.o
obj-$(CONFIG_BOOTM) += stm32image.o
diff --git a/arch/arm/mach-stm32mp/bl33-generic.c b/arch/arm/mach-stm32mp/bl33-generic.c
new file mode 100644
index 000000000000..6f779b19cf4d
--- /dev/null
+++ b/arch/arm/mach-stm32mp/bl33-generic.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <mach/entry.h>
+#include <debug_ll.h>
+
+/*
+ * barebox-dt-2nd.img expects being loaded at an offset, so the
+ * stack can grow down from entry point. The STM32MP TF-A default
+ * is to not have an offset. This stm32mp specific entry points
+ * avoids this issue by setting up a 64 byte stack after end of
+ * barebox and by asking the memory controller about RAM size
+ * instead of parsing it out of the DT.
+ *
+ * When using OP-TEE, ensure CONFIG_OPTEE_SIZE is appopriately set.
+ */
+
+ENTRY_FUNCTION(start_stm32mp_bl33, r0, r1, r2)
+{
+ stm32mp_cpu_lowlevel_init();
+
+ putc_ll('>');
+
+ stm32mp1_barebox_entry((void *)r2);
+}
diff --git a/images/Makefile.stm32mp b/images/Makefile.stm32mp
index eeb5d9ecf6d3..862e955dbc59 100644
--- a/images/Makefile.stm32mp
+++ b/images/Makefile.stm32mp
@@ -25,6 +25,11 @@ endef
# --------------------------------------
+# For use as --nt-fw (BL33) in FIP images
+pblb-y += start_stm32mp_bl33
+FILE_barebox-stm32mp-generic-bl33.img = start_stm32mp_bl33.pblb
+image-y += barebox-stm32mp-generic-bl33.img
+
$(call build_stm32mp_image, CONFIG_MACH_STM32MP15XX_DKX, start_stm32mp15xx_dkx, stm32mp15xx-dkx)
$(call build_stm32mp_image, CONFIG_MACH_STM32MP15X_EV1, start_stm32mp15x_ev1, stm32mp15x-ev1)
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 08/24] ARM: stm32mp: ddrctrl: fix wrong register field widths
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
` (6 preceding siblings ...)
2022-02-20 12:47 ` [PATCH 07/24] ARM: stm32mp: build extra barebox-stm32mp-generic-bl33.img Ahmad Fatoum
@ 2022-02-20 12:47 ` Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 09/24] reset: stm32: drop stm32mp1_reset_ops indirection Ahmad Fatoum
` (16 subsequent siblings)
24 siblings, 0 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium, Ahmad Fatoum
Consulting the reference manual shows that column fields are 4
bits each, but some of them were treated as 5-bit wide. Fix it.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
arch/arm/mach-stm32mp/ddrctrl.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/arch/arm/mach-stm32mp/ddrctrl.c b/arch/arm/mach-stm32mp/ddrctrl.c
index 7f2013c22d79..93996d0afc79 100644
--- a/arch/arm/mach-stm32mp/ddrctrl.c
+++ b/arch/arm/mach-stm32mp/ddrctrl.c
@@ -24,12 +24,12 @@
#define ADDRMAP2_COL_B5 GENMASK(27, 24)
#define ADDRMAP3_COL_B6 GENMASK( 3, 0)
-#define ADDRMAP3_COL_B7 GENMASK(12, 8)
-#define ADDRMAP3_COL_B8 GENMASK(20, 16)
-#define ADDRMAP3_COL_B9 GENMASK(28, 24)
+#define ADDRMAP3_COL_B7 GENMASK(11, 8)
+#define ADDRMAP3_COL_B8 GENMASK(19, 16)
+#define ADDRMAP3_COL_B9 GENMASK(27, 24)
-#define ADDRMAP4_COL_B10 GENMASK( 4, 0)
-#define ADDRMAP4_COL_B11 GENMASK(12, 8)
+#define ADDRMAP4_COL_B10 GENMASK( 3, 0)
+#define ADDRMAP4_COL_B11 GENMASK(11, 8)
#define ADDRMAP5_ROW_B0 GENMASK( 3, 0)
#define ADDRMAP5_ROW_B1 GENMASK(11, 8)
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 09/24] reset: stm32: drop stm32mp1_reset_ops indirection
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
` (7 preceding siblings ...)
2022-02-20 12:47 ` [PATCH 08/24] ARM: stm32mp: ddrctrl: fix wrong register field widths Ahmad Fatoum
@ 2022-02-20 12:47 ` Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 10/24] reset: move stm32 reset code to drivers/power/reset Ahmad Fatoum
` (15 subsequent siblings)
24 siblings, 0 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium, Ahmad Fatoum
The driver used to support both STM32 MCUs and the STM32MP1. STM32 MCU
support is now handled by the reset-simple driver, so the indirection to
support both is no longer necessary. Remove it and simplify the code.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
drivers/reset/reset-stm32.c | 97 ++++++++++++++-----------------------
1 file changed, 37 insertions(+), 60 deletions(-)
diff --git a/drivers/reset/reset-stm32.c b/drivers/reset/reset-stm32.c
index 186b2a8bc654..e625ba27fff6 100644
--- a/drivers/reset/reset-stm32.c
+++ b/drivers/reset/reset-stm32.c
@@ -44,13 +44,6 @@ struct stm32_reset {
void __iomem *base;
struct reset_controller_dev rcdev;
struct restart_handler restart;
- const struct stm32_reset_ops *ops;
-};
-
-struct stm32_reset_ops {
- void (*reset)(void __iomem *reg, unsigned offset, bool assert);
- void __noreturn (*sys_reset)(struct restart_handler *rst);
- const struct stm32_reset_reason *reset_reasons;
};
static struct stm32_reset *to_stm32_reset(struct reset_controller_dev *rcdev)
@@ -58,14 +51,6 @@ static struct stm32_reset *to_stm32_reset(struct reset_controller_dev *rcdev)
return container_of(rcdev, struct stm32_reset, rcdev);
}
-static void stm32mp_reset(void __iomem *reg, unsigned offset, bool assert)
-{
- if (!assert)
- reg += RCC_CL;
-
- writel(BIT(offset), reg);
-}
-
static u32 stm32_reset_status(struct stm32_reset *priv, unsigned long bank)
{
return readl(priv->base + bank);
@@ -75,10 +60,38 @@ static void stm32_reset(struct stm32_reset *priv, unsigned long id, bool assert)
{
int bank = (id / 32) * 4;
int offset = id % 32;
+ void __iomem *reg = priv->base + bank;
- priv->ops->reset(priv->base + bank, offset, assert);
+ if (!assert)
+ reg += RCC_CL;
+
+ writel(BIT(offset), reg);
}
+static void __noreturn stm32mp_rcc_restart_handler(struct restart_handler *rst)
+{
+ struct stm32_reset *priv = container_of(rst, struct stm32_reset, restart);
+
+ stm32_reset(priv, RCC_MP_GRSTCSETR * BITS_PER_BYTE, true);
+
+ mdelay(1000);
+ hang();
+}
+
+static const struct stm32_reset_reason stm32mp_reset_reasons[] = {
+ { STM32MP_RCC_RSTF_POR, RESET_POR, 0 },
+ { STM32MP_RCC_RSTF_BOR, RESET_BROWNOUT, 0 },
+ { STM32MP_RCC_RSTF_STDBY, RESET_WKE, 0 },
+ { STM32MP_RCC_RSTF_CSTDBY, RESET_WKE, 1 },
+ { STM32MP_RCC_RSTF_MPSYS, RESET_RST, 2 },
+ { STM32MP_RCC_RSTF_MPUP0, RESET_RST, 0 },
+ { STM32MP_RCC_RSTF_MPUP1, RESET_RST, 1 },
+ { STM32MP_RCC_RSTF_IWDG1, RESET_WDG, 0 },
+ { STM32MP_RCC_RSTF_IWDG2, RESET_WDG, 1 },
+ { STM32MP_RCC_RSTF_PAD, RESET_EXT, 1 },
+ { /* sentinel */ }
+};
+
static void stm32_set_reset_reason(struct stm32_reset *priv,
const struct stm32_reset_reason *reasons)
{
@@ -128,9 +141,6 @@ static int stm32_reset_probe(struct device_d *dev)
int ret;
priv = xzalloc(sizeof(*priv));
- ret = dev_get_drvdata(dev, (const void **)&priv->ops);
- if (ret)
- return ret;
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores))
@@ -141,54 +151,21 @@ static int stm32_reset_probe(struct device_d *dev)
priv->rcdev.ops = &stm32_reset_ops;
priv->rcdev.of_node = dev->device_node;
- if (priv->ops->sys_reset) {
- priv->restart.name = "stm32-rcc";
- priv->restart.restart = priv->ops->sys_reset;
- priv->restart.priority = 200;
+ priv->restart.name = "stm32-rcc";
+ priv->restart.restart = stm32mp_rcc_restart_handler;
+ priv->restart.priority = 200;
- ret = restart_handler_register(&priv->restart);
- if (ret)
- dev_warn(dev, "Cannot register restart handler\n");
- }
+ ret = restart_handler_register(&priv->restart);
+ if (ret)
+ dev_warn(dev, "Cannot register restart handler\n");
- if (priv->ops->reset_reasons)
- stm32_set_reset_reason(priv, priv->ops->reset_reasons);
+ stm32_set_reset_reason(priv, stm32mp_reset_reasons);
return reset_controller_register(&priv->rcdev);
}
-static void __noreturn stm32mp_rcc_restart_handler(struct restart_handler *rst)
-{
- struct stm32_reset *priv = container_of(rst, struct stm32_reset, restart);
-
- stm32_reset(priv, RCC_MP_GRSTCSETR * BITS_PER_BYTE, true);
-
- mdelay(1000);
- hang();
-}
-
-static const struct stm32_reset_reason stm32mp_reset_reasons[] = {
- { STM32MP_RCC_RSTF_POR, RESET_POR, 0 },
- { STM32MP_RCC_RSTF_BOR, RESET_BROWNOUT, 0 },
- { STM32MP_RCC_RSTF_STDBY, RESET_WKE, 0 },
- { STM32MP_RCC_RSTF_CSTDBY, RESET_WKE, 1 },
- { STM32MP_RCC_RSTF_MPSYS, RESET_RST, 2 },
- { STM32MP_RCC_RSTF_MPUP0, RESET_RST, 0 },
- { STM32MP_RCC_RSTF_MPUP1, RESET_RST, 1 },
- { STM32MP_RCC_RSTF_IWDG1, RESET_WDG, 0 },
- { STM32MP_RCC_RSTF_IWDG2, RESET_WDG, 1 },
- { STM32MP_RCC_RSTF_PAD, RESET_EXT, 1 },
- { /* sentinel */ }
-};
-
-static const struct stm32_reset_ops stm32mp1_reset_ops = {
- .reset = stm32mp_reset,
- .sys_reset = stm32mp_rcc_restart_handler,
- .reset_reasons = stm32mp_reset_reasons,
-};
-
static const struct of_device_id stm32_rcc_reset_dt_ids[] = {
- { .compatible = "st,stm32mp1-rcc", .data = &stm32mp1_reset_ops },
+ { .compatible = "st,stm32mp1-rcc" },
{ /* sentinel */ },
};
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 10/24] reset: move stm32 reset code to drivers/power/reset
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
` (8 preceding siblings ...)
2022-02-20 12:47 ` [PATCH 09/24] reset: stm32: drop stm32mp1_reset_ops indirection Ahmad Fatoum
@ 2022-02-20 12:47 ` Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 11/24] ARM: smccc: sync header with upstream Ahmad Fatoum
` (14 subsequent siblings)
24 siblings, 0 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium, Ahmad Fatoum
We will gut the STM32 reset controller parts in a follow-up commit,
because that will be handled together with clocking in a single RCC
driver. This will leave only restart and reset reason support in the
driver, so move it to drivers/power/reset, so it's not out-of-place.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
drivers/power/reset/Kconfig | 6 ++++++
drivers/power/reset/Makefile | 1 +
drivers/{reset/reset-stm32.c => power/reset/stm32-reboot.c} | 0
drivers/reset/Kconfig | 6 ------
drivers/reset/Makefile | 1 -
5 files changed, 7 insertions(+), 7 deletions(-)
rename drivers/{reset/reset-stm32.c => power/reset/stm32-reboot.c} (100%)
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index 564b77ecba90..40cbc4a3dffd 100644
--- a/drivers/power/reset/Kconfig
+++ b/drivers/power/reset/Kconfig
@@ -62,3 +62,9 @@ config POWER_RESET_HTIF_POWEROFF
help
Adds poweroff support via the syscall device on systems
supporting the UC Berkely Host/Target Interface (HTIF).
+
+config RESET_STM32
+ bool "STM32 Reset Driver"
+ depends on ARCH_STM32MP || COMPILE_TEST
+ help
+ This enables the reset controller driver for STM32MP1.
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
index c581382be86d..347da50d767c 100644
--- a/drivers/power/reset/Makefile
+++ b/drivers/power/reset/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_POWER_RESET_SYSCON_POWEROFF) += syscon-poweroff.o
obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o
obj-$(CONFIG_POWER_RESET_HTIF_POWEROFF) += htif-poweroff.o
+obj-$(CONFIG_RESET_STM32) += stm32-reboot.o
diff --git a/drivers/reset/reset-stm32.c b/drivers/power/reset/stm32-reboot.c
similarity index 100%
rename from drivers/reset/reset-stm32.c
rename to drivers/power/reset/stm32-reboot.c
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index b12159094d88..21a6e1a50d8f 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -39,12 +39,6 @@ config RESET_IMX7
help
This enables the reset controller driver for i.MX7 SoCs.
-config RESET_STM32
- bool "STM32 Reset Driver"
- depends on ARCH_STM32MP || COMPILE_TEST
- help
- This enables the reset controller driver for STM32MP1.
-
config RESET_STARFIVE
bool "StarFive Controller Driver" if COMPILE_TEST
default SOC_STARFIVE
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index b4270411fdaf..6d0cd51f86ce 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -3,5 +3,4 @@ obj-$(CONFIG_RESET_CONTROLLER) += core.o
obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o
obj-$(CONFIG_RESET_IMX7) += reset-imx7.o
-obj-$(CONFIG_RESET_STM32) += reset-stm32.o
obj-$(CONFIG_RESET_STARFIVE) += reset-starfive-vic.o
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 11/24] ARM: smccc: sync header with upstream
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
` (9 preceding siblings ...)
2022-02-20 12:47 ` [PATCH 10/24] reset: move stm32 reset code to drivers/power/reset Ahmad Fatoum
@ 2022-02-20 12:47 ` Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 12/24] firmware: import Linux v5.13 SCMI support Ahmad Fatoum
` (13 subsequent siblings)
24 siblings, 0 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium, Ahmad Fatoum
For upcoming SCMI over SMC support, we'll need a newer version of the
header. Import it from v5.13-rc1.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
arch/arm/include/asm/opcodes-sec.h | 17 ++
include/linux/arm-smccc.h | 363 +++++++++++++++++++++++++++--
2 files changed, 366 insertions(+), 14 deletions(-)
create mode 100644 arch/arm/include/asm/opcodes-sec.h
diff --git a/arch/arm/include/asm/opcodes-sec.h b/arch/arm/include/asm/opcodes-sec.h
new file mode 100644
index 000000000000..b6f4b35024b8
--- /dev/null
+++ b/arch/arm/include/asm/opcodes-sec.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ *
+ * Copyright (C) 2012 ARM Limited
+ */
+
+#ifndef __ASM_ARM_OPCODES_SEC_H
+#define __ASM_ARM_OPCODES_SEC_H
+
+#include <asm/opcodes.h>
+
+#define __SMC(imm4) __inst_arm_thumb32( \
+ 0xE1600070 | (((imm4) & 0xF) << 0), \
+ 0xF7F08000 | (((imm4) & 0xF) << 16) \
+)
+
+#endif /* __ASM_ARM_OPCODES_SEC_H */
diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
index 1b38b7b3728b..64ee3863a637 100644
--- a/include/linux/arm-smccc.h
+++ b/include/linux/arm-smccc.h
@@ -1,28 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015, Linaro Limited
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#ifndef __LINUX_ARM_SMCCC_H
#define __LINUX_ARM_SMCCC_H
+#include <linux/const.h>
+
/*
* This file provides common defines for ARM SMC Calling Convention as
* specified in
- * http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html
+ * https://developer.arm.com/docs/den0028/latest
+ *
+ * This code is up-to-date with version DEN 0028 C
*/
-/* This constant is shifted by 31, make sure it's of an unsigned type */
-#define ARM_SMCCC_STD_CALL 0UL
-#define ARM_SMCCC_FAST_CALL 1UL
+#define ARM_SMCCC_STD_CALL _AC(0,U)
+#define ARM_SMCCC_FAST_CALL _AC(1,U)
#define ARM_SMCCC_TYPE_SHIFT 31
#define ARM_SMCCC_SMC_32 0
@@ -53,18 +47,178 @@
#define ARM_SMCCC_OWNER_SIP 2
#define ARM_SMCCC_OWNER_OEM 3
#define ARM_SMCCC_OWNER_STANDARD 4
+#define ARM_SMCCC_OWNER_STANDARD_HYP 5
+#define ARM_SMCCC_OWNER_VENDOR_HYP 6
#define ARM_SMCCC_OWNER_TRUSTED_APP 48
#define ARM_SMCCC_OWNER_TRUSTED_APP_END 49
#define ARM_SMCCC_OWNER_TRUSTED_OS 50
#define ARM_SMCCC_OWNER_TRUSTED_OS_END 63
+#define ARM_SMCCC_FUNC_QUERY_CALL_UID 0xff01
+
#define ARM_SMCCC_QUIRK_NONE 0
#define ARM_SMCCC_QUIRK_QCOM_A6 1 /* Save/restore register a6 */
+#define ARM_SMCCC_VERSION_1_0 0x10000
+#define ARM_SMCCC_VERSION_1_1 0x10001
+#define ARM_SMCCC_VERSION_1_2 0x10002
+
+#define ARM_SMCCC_VERSION_FUNC_ID \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_32, \
+ 0, 0)
+
+#define ARM_SMCCC_ARCH_FEATURES_FUNC_ID \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_32, \
+ 0, 1)
+
+#define ARM_SMCCC_ARCH_SOC_ID \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_32, \
+ 0, 2)
+
+#define ARM_SMCCC_ARCH_WORKAROUND_1 \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_32, \
+ 0, 0x8000)
+
+#define ARM_SMCCC_ARCH_WORKAROUND_2 \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_32, \
+ 0, 0x7fff)
+
+#define ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_32, \
+ ARM_SMCCC_OWNER_VENDOR_HYP, \
+ ARM_SMCCC_FUNC_QUERY_CALL_UID)
+
+/* KVM UID value: 28b46fb6-2ec5-11e9-a9ca-4b564d003a74 */
+#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_0 0xb66fb428U
+#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_1 0xe911c52eU
+#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_2 0x564bcaa9U
+#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3 0x743a004dU
+
+/* KVM "vendor specific" services */
+#define ARM_SMCCC_KVM_FUNC_FEATURES 0
+#define ARM_SMCCC_KVM_FUNC_PTP 1
+#define ARM_SMCCC_KVM_FUNC_FEATURES_2 127
+#define ARM_SMCCC_KVM_NUM_FUNCS 128
+
+#define ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_32, \
+ ARM_SMCCC_OWNER_VENDOR_HYP, \
+ ARM_SMCCC_KVM_FUNC_FEATURES)
+
+#define SMCCC_ARCH_WORKAROUND_RET_UNAFFECTED 1
+
+/*
+ * ptp_kvm is a feature used for time sync between vm and host.
+ * ptp_kvm module in guest kernel will get service from host using
+ * this hypercall ID.
+ */
+#define ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_32, \
+ ARM_SMCCC_OWNER_VENDOR_HYP, \
+ ARM_SMCCC_KVM_FUNC_PTP)
+
+/* ptp_kvm counter type ID */
+#define KVM_PTP_VIRT_COUNTER 0
+#define KVM_PTP_PHYS_COUNTER 1
+
+/* Paravirtualised time calls (defined by ARM DEN0057A) */
+#define ARM_SMCCC_HV_PV_TIME_FEATURES \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_64, \
+ ARM_SMCCC_OWNER_STANDARD_HYP, \
+ 0x20)
+
+#define ARM_SMCCC_HV_PV_TIME_ST \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_64, \
+ ARM_SMCCC_OWNER_STANDARD_HYP, \
+ 0x21)
+
+/* TRNG entropy source calls (defined by ARM DEN0098) */
+#define ARM_SMCCC_TRNG_VERSION \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_32, \
+ ARM_SMCCC_OWNER_STANDARD, \
+ 0x50)
+
+#define ARM_SMCCC_TRNG_FEATURES \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_32, \
+ ARM_SMCCC_OWNER_STANDARD, \
+ 0x51)
+
+#define ARM_SMCCC_TRNG_GET_UUID \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_32, \
+ ARM_SMCCC_OWNER_STANDARD, \
+ 0x52)
+
+#define ARM_SMCCC_TRNG_RND32 \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_32, \
+ ARM_SMCCC_OWNER_STANDARD, \
+ 0x53)
+
+#define ARM_SMCCC_TRNG_RND64 \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_64, \
+ ARM_SMCCC_OWNER_STANDARD, \
+ 0x53)
+
+/*
+ * Return codes defined in ARM DEN 0070A
+ * ARM DEN 0070A is now merged/consolidated into ARM DEN 0028 C
+ */
+#define SMCCC_RET_SUCCESS 0
+#define SMCCC_RET_NOT_SUPPORTED -1
+#define SMCCC_RET_NOT_REQUIRED -2
+#define SMCCC_RET_INVALID_PARAMETER -3
+
#ifndef __ASSEMBLY__
#include <linux/linkage.h>
#include <linux/types.h>
+
+enum arm_smccc_conduit {
+ SMCCC_CONDUIT_NONE,
+ SMCCC_CONDUIT_SMC,
+ SMCCC_CONDUIT_HVC,
+};
+
+/**
+ * arm_smccc_1_1_get_conduit()
+ *
+ * Returns the conduit to be used for SMCCCv1.1 or later.
+ *
+ * When SMCCCv1.1 is not present, returns SMCCC_CONDUIT_NONE.
+ */
+static inline enum arm_smccc_conduit arm_smccc_1_1_get_conduit(void)
+{
+ /* No HVC support yet */
+ return SMCCC_CONDUIT_SMC;
+}
+
+/**
+ * arm_smccc_get_version()
+ *
+ * Returns the version to be used for SMCCCv1.1 or later.
+ *
+ * When SMCCCv1.1 or above is not present, returns SMCCCv1.0, but this
+ * does not imply the presence of firmware or a valid conduit. Caller
+ * handling SMCCCv1.0 must determine the conduit by other means.
+ */
+u32 arm_smccc_get_version(void);
+
+void arm_smccc_version_init(u32 version, enum arm_smccc_conduit conduit);
+
/**
* struct arm_smccc_res - Result from SMC/HVC call
* @a0-a3 result values from registers 0 to 3
@@ -131,5 +285,186 @@ asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1,
#define arm_smccc_hvc_quirk(...) __arm_smccc_hvc(__VA_ARGS__)
+/* SMCCC v1.1 implementation madness follows */
+#ifdef CONFIG_ARM64
+
+#define SMCCC_SMC_INST "smc #0"
+#define SMCCC_HVC_INST "hvc #0"
+
+#elif defined(CONFIG_ARM)
+#include <asm/opcodes-sec.h>
+#include <asm/opcodes-virt.h>
+
+#define SMCCC_SMC_INST __SMC(0)
+#define SMCCC_HVC_INST __HVC(0)
+
+#endif
+
+#define ___count_args(_0, _1, _2, _3, _4, _5, _6, _7, _8, x, ...) x
+
+#define __count_args(...) \
+ ___count_args(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0)
+
+#define __constraint_read_0 "r" (arg0)
+#define __constraint_read_1 __constraint_read_0, "r" (arg1)
+#define __constraint_read_2 __constraint_read_1, "r" (arg2)
+#define __constraint_read_3 __constraint_read_2, "r" (arg3)
+#define __constraint_read_4 __constraint_read_3, "r" (arg4)
+#define __constraint_read_5 __constraint_read_4, "r" (arg5)
+#define __constraint_read_6 __constraint_read_5, "r" (arg6)
+#define __constraint_read_7 __constraint_read_6, "r" (arg7)
+
+#define __declare_arg_0(a0, res) \
+ struct arm_smccc_res *___res = res; \
+ register unsigned long arg0 asm("r0") = (u32)a0
+
+#define __declare_arg_1(a0, a1, res) \
+ typeof(a1) __a1 = a1; \
+ struct arm_smccc_res *___res = res; \
+ register unsigned long arg0 asm("r0") = (u32)a0; \
+ register typeof(a1) arg1 asm("r1") = __a1
+
+#define __declare_arg_2(a0, a1, a2, res) \
+ typeof(a1) __a1 = a1; \
+ typeof(a2) __a2 = a2; \
+ struct arm_smccc_res *___res = res; \
+ register unsigned long arg0 asm("r0") = (u32)a0; \
+ register typeof(a1) arg1 asm("r1") = __a1; \
+ register typeof(a2) arg2 asm("r2") = __a2
+
+#define __declare_arg_3(a0, a1, a2, a3, res) \
+ typeof(a1) __a1 = a1; \
+ typeof(a2) __a2 = a2; \
+ typeof(a3) __a3 = a3; \
+ struct arm_smccc_res *___res = res; \
+ register unsigned long arg0 asm("r0") = (u32)a0; \
+ register typeof(a1) arg1 asm("r1") = __a1; \
+ register typeof(a2) arg2 asm("r2") = __a2; \
+ register typeof(a3) arg3 asm("r3") = __a3
+
+#define __declare_arg_4(a0, a1, a2, a3, a4, res) \
+ typeof(a4) __a4 = a4; \
+ __declare_arg_3(a0, a1, a2, a3, res); \
+ register typeof(a4) arg4 asm("r4") = __a4
+
+#define __declare_arg_5(a0, a1, a2, a3, a4, a5, res) \
+ typeof(a5) __a5 = a5; \
+ __declare_arg_4(a0, a1, a2, a3, a4, res); \
+ register typeof(a5) arg5 asm("r5") = __a5
+
+#define __declare_arg_6(a0, a1, a2, a3, a4, a5, a6, res) \
+ typeof(a6) __a6 = a6; \
+ __declare_arg_5(a0, a1, a2, a3, a4, a5, res); \
+ register typeof(a6) arg6 asm("r6") = __a6
+
+#define __declare_arg_7(a0, a1, a2, a3, a4, a5, a6, a7, res) \
+ typeof(a7) __a7 = a7; \
+ __declare_arg_6(a0, a1, a2, a3, a4, a5, a6, res); \
+ register typeof(a7) arg7 asm("r7") = __a7
+
+#define ___declare_args(count, ...) __declare_arg_ ## count(__VA_ARGS__)
+#define __declare_args(count, ...) ___declare_args(count, __VA_ARGS__)
+
+#define ___constraints(count) \
+ : __constraint_read_ ## count \
+ : "memory"
+#define __constraints(count) ___constraints(count)
+
+/*
+ * We have an output list that is not necessarily used, and GCC feels
+ * entitled to optimise the whole sequence away. "volatile" is what
+ * makes it stick.
+ */
+#define __arm_smccc_1_1(inst, ...) \
+ do { \
+ register unsigned long r0 asm("r0"); \
+ register unsigned long r1 asm("r1"); \
+ register unsigned long r2 asm("r2"); \
+ register unsigned long r3 asm("r3"); \
+ __declare_args(__count_args(__VA_ARGS__), __VA_ARGS__); \
+ asm volatile(inst "\n" : \
+ "=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3) \
+ __constraints(__count_args(__VA_ARGS__))); \
+ if (___res) \
+ *___res = (typeof(*___res)){r0, r1, r2, r3}; \
+ } while (0)
+
+/*
+ * arm_smccc_1_1_smc() - make an SMCCC v1.1 compliant SMC call
+ *
+ * This is a variadic macro taking one to eight source arguments, and
+ * an optional return structure.
+ *
+ * @a0-a7: arguments passed in registers 0 to 7
+ * @res: result values from registers 0 to 3
+ *
+ * This macro is used to make SMC calls following SMC Calling Convention v1.1.
+ * The content of the supplied param are copied to registers 0 to 7 prior
+ * to the SMC instruction. The return values are updated with the content
+ * from register 0 to 3 on return from the SMC instruction if not NULL.
+ */
+#define arm_smccc_1_1_smc(...) __arm_smccc_1_1(SMCCC_SMC_INST, __VA_ARGS__)
+
+/*
+ * arm_smccc_1_1_hvc() - make an SMCCC v1.1 compliant HVC call
+ *
+ * This is a variadic macro taking one to eight source arguments, and
+ * an optional return structure.
+ *
+ * @a0-a7: arguments passed in registers 0 to 7
+ * @res: result values from registers 0 to 3
+ *
+ * This macro is used to make HVC calls following SMC Calling Convention v1.1.
+ * The content of the supplied param are copied to registers 0 to 7 prior
+ * to the HVC instruction. The return values are updated with the content
+ * from register 0 to 3 on return from the HVC instruction if not NULL.
+ */
+#define arm_smccc_1_1_hvc(...) __arm_smccc_1_1(SMCCC_HVC_INST, __VA_ARGS__)
+
+/*
+ * Like arm_smccc_1_1* but always returns SMCCC_RET_NOT_SUPPORTED.
+ * Used when the SMCCC conduit is not defined. The empty asm statement
+ * avoids compiler warnings about unused variables.
+ */
+#define __fail_smccc_1_1(...) \
+ do { \
+ __declare_args(__count_args(__VA_ARGS__), __VA_ARGS__); \
+ asm ("" : __constraints(__count_args(__VA_ARGS__))); \
+ if (___res) \
+ ___res->a0 = SMCCC_RET_NOT_SUPPORTED; \
+ } while (0)
+
+/*
+ * arm_smccc_1_1_invoke() - make an SMCCC v1.1 compliant call
+ *
+ * This is a variadic macro taking one to eight source arguments, and
+ * an optional return structure.
+ *
+ * @a0-a7: arguments passed in registers 0 to 7
+ * @res: result values from registers 0 to 3
+ *
+ * This macro will make either an HVC call or an SMC call depending on the
+ * current SMCCC conduit. If no valid conduit is available then -1
+ * (SMCCC_RET_NOT_SUPPORTED) is returned in @res.a0 (if supplied).
+ *
+ * The return value also provides the conduit that was used.
+ */
+#define arm_smccc_1_1_invoke(...) ({ \
+ int method = arm_smccc_1_1_get_conduit(); \
+ switch (method) { \
+ case SMCCC_CONDUIT_HVC: \
+ arm_smccc_1_1_hvc(__VA_ARGS__); \
+ break; \
+ case SMCCC_CONDUIT_SMC: \
+ arm_smccc_1_1_smc(__VA_ARGS__); \
+ break; \
+ default: \
+ __fail_smccc_1_1(__VA_ARGS__); \
+ method = SMCCC_CONDUIT_NONE; \
+ break; \
+ } \
+ method; \
+ })
+
#endif /*__ASSEMBLY__*/
#endif /*__LINUX_ARM_SMCCC_H*/
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 12/24] firmware: import Linux v5.13 SCMI support
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
` (10 preceding siblings ...)
2022-02-20 12:47 ` [PATCH 11/24] ARM: smccc: sync header with upstream Ahmad Fatoum
@ 2022-02-20 12:47 ` Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 13/24] reset: add " Ahmad Fatoum
` (12 subsequent siblings)
24 siblings, 0 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium, Ahmad Fatoum
The ARM System Control and Management Interface (SCMI) is a standard way
to offload handling of system resources like clocks, resets and power
domain handling to the firmware. This should replace Silicon Provider
specific conduits for new SoCs. Systems where this is already relevant:
- STM32MP1: For trusted boot, the clock and reset controller are
managed from secure world. This is not yet supported by barebox and
must be disabled in TF-A
- RK3568: At least the CPU clock is only controllable via SCMI
- i.MX8X: Communication with the SCU occurs via SCMI
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
drivers/base/driver.c | 28 +
drivers/firmware/Kconfig | 10 +
drivers/firmware/Makefile | 1 +
drivers/firmware/arm_scmi/Makefile | 10 +
drivers/firmware/arm_scmi/base.c | 281 ++++++
drivers/firmware/arm_scmi/bus.c | 226 +++++
drivers/firmware/arm_scmi/common.h | 330 +++++++
drivers/firmware/arm_scmi/driver.c | 1275 ++++++++++++++++++++++++++++
drivers/firmware/arm_scmi/shmem.c | 89 ++
drivers/firmware/arm_scmi/smc.c | 137 +++
include/driver.h | 3 +
include/linux/idr.h | 79 ++
include/linux/processor.h | 29 +
include/linux/scmi_protocol.h | 654 ++++++++++++++
include/linux/slab.h | 3 +
15 files changed, 3155 insertions(+)
create mode 100644 drivers/firmware/arm_scmi/Makefile
create mode 100644 drivers/firmware/arm_scmi/base.c
create mode 100644 drivers/firmware/arm_scmi/bus.c
create mode 100644 drivers/firmware/arm_scmi/common.h
create mode 100644 drivers/firmware/arm_scmi/driver.c
create mode 100644 drivers/firmware/arm_scmi/shmem.c
create mode 100644 drivers/firmware/arm_scmi/smc.c
create mode 100644 include/linux/idr.h
create mode 100644 include/linux/processor.h
create mode 100644 include/linux/scmi_protocol.h
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
index 2347b5c71fac..303ca061ceb6 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -619,3 +619,31 @@ int dev_err_probe(const struct device_d *dev, int err, const char *fmt, ...)
return err;
}
EXPORT_SYMBOL_GPL(dev_err_probe);
+
+/*
+ * device_find_child - device iterator for locating a particular device.
+ * @parent: parent struct device_d
+ * @match: Callback function to check device
+ * @data: Data to pass to match function
+ *
+ * The callback should return 0 if the device doesn't match and non-zero
+ * if it does. If the callback returns non-zero and a reference to the
+ * current device can be obtained, this function will return to the caller
+ * and not iterate over any more devices.
+ */
+struct device_d *device_find_child(struct device_d *parent, void *data,
+ int (*match)(struct device_d *dev, void *data))
+{
+ struct device_d *child;
+
+ if (!parent)
+ return NULL;
+
+ list_for_each_entry(child, &parent->children, sibling) {
+ if (match(child, data))
+ return child;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(device_find_child);
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index ffba7146f742..d3cca41a7e8f 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -22,4 +22,14 @@ config FIRMWARE_ZYNQMP_FPGA
select FIRMWARE
help
Load a bitstream to the PL of Zynq Ultrascale+
+
+config ARM_SCMI_PROTOCOL
+ tristate "ARM System Control and Management Interface (SCMI) Message Protocol"
+ depends on ARM || COMPILE_TEST
+ depends on ARM_SMCCC
+ help
+ ARM System Control and Management Interface (SCMI) protocol is a
+ set of operating system-independent software interfaces that are
+ used in system management.
+
endmenu
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index d16469cf72ec..26d6f3275ae1 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -2,3 +2,4 @@
obj-$(CONFIG_FIRMWARE_ALTERA_SERIAL) += altera_serial.o
obj-$(CONFIG_FIRMWARE_ALTERA_SOCFPGA) += socfpga.o socfpga_sdr.o
obj-$(CONFIG_FIRMWARE_ZYNQMP_FPGA) += zynqmp-fpga.o
+obj-y += arm_scmi/
diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
new file mode 100644
index 000000000000..8bb25484b9b2
--- /dev/null
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+scmi-bus-y = bus.o
+scmi-driver-y = driver.o
+scmi-transport-y = shmem.o
+scmi-transport-$(CONFIG_ARM_SMCCC) += smc.o
+scmi-protocols-y = base.o
+
+scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \
+ $(scmi-transport-y)
+obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o
diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
new file mode 100644
index 000000000000..d4af40c40cf1
--- /dev/null
+++ b/drivers/firmware/arm_scmi/base.c
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface (SCMI) Base Protocol
+ *
+ * Copyright (C) 2018-2021 ARM Ltd.
+ */
+
+#define pr_fmt(fmt) "SCMI BASE - " fmt
+
+#include <common.h>
+#include <linux/scmi_protocol.h>
+
+#include "common.h"
+
+#define SCMI_BASE_NUM_SOURCES 1
+#define SCMI_BASE_MAX_CMD_ERR_COUNT 1024
+
+struct scmi_msg_resp_base_attributes {
+ u8 num_protocols;
+ u8 num_agents;
+ __le16 reserved;
+};
+
+enum scmi_base_protocol_cmd {
+ BASE_DISCOVER_VENDOR = 0x3,
+ BASE_DISCOVER_SUB_VENDOR = 0x4,
+ BASE_DISCOVER_IMPLEMENT_VERSION = 0x5,
+ BASE_DISCOVER_LIST_PROTOCOLS = 0x6,
+ BASE_DISCOVER_AGENT = 0x7,
+ BASE_NOTIFY_ERRORS = 0x8,
+ BASE_SET_DEVICE_PERMISSIONS = 0x9,
+ BASE_SET_PROTOCOL_PERMISSIONS = 0xa,
+ BASE_RESET_AGENT_CONFIGURATION = 0xb,
+};
+
+/**
+ * scmi_base_attributes_get() - gets the implementation details
+ * that are associated with the base protocol.
+ *
+ * @ph: SCMI protocol handle
+ *
+ * Return: 0 on success, else appropriate SCMI error.
+ */
+static int scmi_base_attributes_get(const struct scmi_protocol_handle *ph)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_resp_base_attributes *attr_info;
+ struct scmi_revision_info *rev = ph->get_priv(ph);
+
+ ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES,
+ 0, sizeof(*attr_info), &t);
+ if (ret)
+ return ret;
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret) {
+ attr_info = t->rx.buf;
+ rev->num_protocols = attr_info->num_protocols;
+ rev->num_agents = attr_info->num_agents;
+ }
+
+ ph->xops->xfer_put(ph, t);
+
+ return ret;
+}
+
+/**
+ * scmi_base_vendor_id_get() - gets vendor/subvendor identifier ASCII string.
+ *
+ * @ph: SCMI protocol handle
+ * @sub_vendor: specify true if sub-vendor ID is needed
+ *
+ * Return: 0 on success, else appropriate SCMI error.
+ */
+static int
+scmi_base_vendor_id_get(const struct scmi_protocol_handle *ph, bool sub_vendor)
+{
+ u8 cmd;
+ int ret, size;
+ char *vendor_id;
+ struct scmi_xfer *t;
+ struct scmi_revision_info *rev = ph->get_priv(ph);
+
+
+ if (sub_vendor) {
+ cmd = BASE_DISCOVER_SUB_VENDOR;
+ vendor_id = rev->sub_vendor_id;
+ size = ARRAY_SIZE(rev->sub_vendor_id);
+ } else {
+ cmd = BASE_DISCOVER_VENDOR;
+ vendor_id = rev->vendor_id;
+ size = ARRAY_SIZE(rev->vendor_id);
+ }
+
+ ret = ph->xops->xfer_get_init(ph, cmd, 0, size, &t);
+ if (ret)
+ return ret;
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret)
+ memcpy(vendor_id, t->rx.buf, size);
+
+ ph->xops->xfer_put(ph, t);
+
+ return ret;
+}
+
+/**
+ * scmi_base_implementation_version_get() - gets a vendor-specific
+ * implementation 32-bit version. The format of the version number is
+ * vendor-specific
+ *
+ * @ph: SCMI protocol handle
+ *
+ * Return: 0 on success, else appropriate SCMI error.
+ */
+static int
+scmi_base_implementation_version_get(const struct scmi_protocol_handle *ph)
+{
+ int ret;
+ __le32 *impl_ver;
+ struct scmi_xfer *t;
+ struct scmi_revision_info *rev = ph->get_priv(ph);
+
+ ret = ph->xops->xfer_get_init(ph, BASE_DISCOVER_IMPLEMENT_VERSION,
+ 0, sizeof(*impl_ver), &t);
+ if (ret)
+ return ret;
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret) {
+ impl_ver = t->rx.buf;
+ rev->impl_ver = le32_to_cpu(*impl_ver);
+ }
+
+ ph->xops->xfer_put(ph, t);
+
+ return ret;
+}
+
+/**
+ * scmi_base_implementation_list_get() - gets the list of protocols it is
+ * OSPM is allowed to access
+ *
+ * @ph: SCMI protocol handle
+ * @protocols_imp: pointer to hold the list of protocol identifiers
+ *
+ * Return: 0 on success, else appropriate SCMI error.
+ */
+static int
+scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
+ u8 *protocols_imp)
+{
+ u8 *list;
+ int ret, loop;
+ struct scmi_xfer *t;
+ __le32 *num_skip, *num_ret;
+ u32 tot_num_ret = 0, loop_num_ret;
+ struct device_d *dev = ph->dev;
+
+ ret = ph->xops->xfer_get_init(ph, BASE_DISCOVER_LIST_PROTOCOLS,
+ sizeof(*num_skip), 0, &t);
+ if (ret)
+ return ret;
+
+ num_skip = t->tx.buf;
+ num_ret = t->rx.buf;
+ list = t->rx.buf + sizeof(*num_ret);
+
+ do {
+ /* Set the number of protocols to be skipped/already read */
+ *num_skip = cpu_to_le32(tot_num_ret);
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (ret)
+ break;
+
+ loop_num_ret = le32_to_cpu(*num_ret);
+ if (tot_num_ret + loop_num_ret > MAX_PROTOCOLS_IMP) {
+ dev_err(dev, "No. of Protocol > MAX_PROTOCOLS_IMP");
+ break;
+ }
+
+ for (loop = 0; loop < loop_num_ret; loop++)
+ protocols_imp[tot_num_ret + loop] = *(list + loop);
+
+ tot_num_ret += loop_num_ret;
+
+ ph->xops->reset_rx_to_maxsz(ph, t);
+ } while (loop_num_ret);
+
+ ph->xops->xfer_put(ph, t);
+
+ return ret;
+}
+
+/**
+ * scmi_base_discover_agent_get() - discover the name of an agent
+ *
+ * @ph: SCMI protocol handle
+ * @id: Agent identifier
+ * @name: Agent identifier ASCII string
+ *
+ * An agent id of 0 is reserved to identify the platform itself.
+ * Generally operating system is represented as "OSPM"
+ *
+ * Return: 0 on success, else appropriate SCMI error.
+ */
+static int scmi_base_discover_agent_get(const struct scmi_protocol_handle *ph,
+ int id, char *name)
+{
+ int ret;
+ struct scmi_xfer *t;
+
+ ret = ph->xops->xfer_get_init(ph, BASE_DISCOVER_AGENT,
+ sizeof(__le32), SCMI_MAX_STR_SIZE, &t);
+ if (ret)
+ return ret;
+
+ put_unaligned_le32(id, t->tx.buf);
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret)
+ strlcpy(name, t->rx.buf, SCMI_MAX_STR_SIZE);
+
+ ph->xops->xfer_put(ph, t);
+
+ return ret;
+}
+
+static int scmi_base_protocol_init(const struct scmi_protocol_handle *ph)
+{
+ int id, ret;
+ u8 *prot_imp;
+ u32 version;
+ char name[SCMI_MAX_STR_SIZE];
+ struct device_d *dev = ph->dev;
+ struct scmi_revision_info *rev = scmi_revision_area_get(ph);
+
+ ret = ph->xops->version_get(ph, &version);
+ if (ret)
+ return ret;
+
+ prot_imp = kcalloc(MAX_PROTOCOLS_IMP, sizeof(u8), GFP_KERNEL);
+ if (!prot_imp)
+ return -ENOMEM;
+
+ rev->major_ver = PROTOCOL_REV_MAJOR(version),
+ rev->minor_ver = PROTOCOL_REV_MINOR(version);
+ ph->set_priv(ph, rev);
+
+ scmi_base_attributes_get(ph);
+ scmi_base_vendor_id_get(ph, false);
+ scmi_base_vendor_id_get(ph, true);
+ scmi_base_implementation_version_get(ph);
+ scmi_base_implementation_list_get(ph, prot_imp);
+
+ scmi_setup_protocol_implemented(ph, prot_imp);
+
+ dev_info(dev, "SCMI Protocol v%d.%d '%s:%s' Firmware version 0x%x\n",
+ rev->major_ver, rev->minor_ver, rev->vendor_id,
+ rev->sub_vendor_id, rev->impl_ver);
+ dev_dbg(dev, "Found %d protocol(s) %d agent(s)\n", rev->num_protocols,
+ rev->num_agents);
+
+ for (id = 0; id < rev->num_agents; id++) {
+ scmi_base_discover_agent_get(ph, id, name);
+ dev_dbg(dev, "Agent %d: %s\n", id, name);
+ }
+
+ return 0;
+}
+
+static const struct scmi_protocol scmi_base = {
+ .id = SCMI_PROTOCOL_BASE,
+ .instance_init = &scmi_base_protocol_init,
+ .ops = NULL,
+};
+
+DEFINE_SCMI_PROTOCOL_REGISTER(base, scmi_base)
diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c
new file mode 100644
index 000000000000..e8297be4d22e
--- /dev/null
+++ b/drivers/firmware/arm_scmi/bus.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface (SCMI) Message Protocol bus layer
+ *
+ * Copyright (C) 2018-2021 ARM Ltd.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <common.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/idr.h>
+#include <driver.h>
+
+#include "common.h"
+
+static DEFINE_IDR(scmi_protocols);
+
+static const struct scmi_device_id *
+scmi_dev_match_id(struct scmi_device *scmi_dev, struct scmi_driver *scmi_drv)
+{
+ const struct scmi_device_id *id = scmi_drv->id_table;
+
+ if (!id)
+ return NULL;
+
+ for (; id->protocol_id; id++)
+ if (id->protocol_id == scmi_dev->protocol_id) {
+ if (!id->name)
+ return id;
+ else if (!strcmp(id->name, scmi_dev->name))
+ return id;
+ }
+
+ return NULL;
+}
+
+static int scmi_dev_match(struct device_d *dev, struct driver_d *drv)
+{
+ struct scmi_driver *scmi_drv = to_scmi_driver(drv);
+ struct scmi_device *scmi_dev = to_scmi_dev(dev);
+ const struct scmi_device_id *id;
+
+ id = scmi_dev_match_id(scmi_dev, scmi_drv);
+ if (id)
+ return 0;
+
+ return -1;
+}
+
+static int scmi_match_by_id_table(struct device_d *dev, void *data)
+{
+ struct scmi_device *sdev = to_scmi_dev(dev);
+ struct scmi_device_id *id_table = data;
+
+ return sdev->protocol_id == id_table->protocol_id &&
+ !strcmp(sdev->name, id_table->name);
+}
+
+struct scmi_device *scmi_child_dev_find(struct device_d *parent,
+ int prot_id, const char *name)
+{
+ struct scmi_device_id id_table;
+ struct device_d *dev;
+
+ id_table.protocol_id = prot_id;
+ id_table.name = name;
+
+ dev = device_find_child(parent, &id_table, scmi_match_by_id_table);
+ if (!dev)
+ return NULL;
+
+ return to_scmi_dev(dev);
+}
+
+const struct scmi_protocol *scmi_protocol_get(int protocol_id)
+{
+ const struct scmi_protocol *proto;
+
+ proto = idr_find(&scmi_protocols, protocol_id);
+ if (!proto) {
+ pr_warn("SCMI Protocol 0x%x not found!\n", protocol_id);
+ return NULL;
+ }
+
+ pr_debug("Found SCMI Protocol 0x%x\n", protocol_id);
+
+ return proto;
+}
+
+static int scmi_dev_probe(struct device_d *dev)
+{
+ struct scmi_driver *scmi_drv = to_scmi_driver(dev->driver);
+ struct scmi_device *scmi_dev = to_scmi_dev(dev);
+ const struct scmi_device_id *id;
+
+ id = scmi_dev_match_id(scmi_dev, scmi_drv);
+ if (!id)
+ return -ENODEV;
+
+ if (!scmi_dev->handle)
+ return -EPROBE_DEFER;
+
+ return scmi_drv->probe(scmi_dev);
+}
+
+static void scmi_dev_remove(struct device_d *dev)
+{
+ struct scmi_driver *scmi_drv = to_scmi_driver(dev->driver);
+ struct scmi_device *scmi_dev = to_scmi_dev(dev);
+
+ if (scmi_drv->remove)
+ scmi_drv->remove(scmi_dev);
+}
+
+static struct bus_type scmi_bus_type = {
+ .name = "scmi_protocol",
+ .match = scmi_dev_match,
+ .probe = scmi_dev_probe,
+ .remove = scmi_dev_remove,
+};
+
+int scmi_driver_register(struct scmi_driver *driver)
+{
+ int retval;
+
+ retval = scmi_protocol_device_request(driver->id_table);
+ if (retval)
+ return retval;
+
+ driver->driver.bus = &scmi_bus_type;
+ driver->driver.name = driver->name;
+
+ retval = register_driver(&driver->driver);
+ if (!retval)
+ pr_debug("registered new scmi driver %s\n", driver->name);
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(scmi_driver_register);
+
+struct scmi_device *
+scmi_device_alloc(struct device_node *np, struct device_d *parent, int protocol,
+ const char *name)
+{
+ struct scmi_device *scmi_dev;
+
+ scmi_dev = kzalloc(sizeof(*scmi_dev), GFP_KERNEL);
+ if (!scmi_dev)
+ return NULL;
+
+ scmi_dev->name = kstrdup_const(name ?: "unknown", GFP_KERNEL);
+ if (!scmi_dev->name) {
+ kfree(scmi_dev);
+ return NULL;
+ }
+
+ scmi_dev->dev.id = DEVICE_ID_DYNAMIC;
+ scmi_dev->protocol_id = protocol;
+ scmi_dev->dev.parent = parent;
+ scmi_dev->dev.device_node = np;
+ scmi_dev->dev.bus = &scmi_bus_type;
+ dev_set_name(&scmi_dev->dev, "scmi_dev");
+
+ return scmi_dev;
+}
+
+void scmi_device_destroy(struct scmi_device *scmi_dev)
+{
+ kfree_const(scmi_dev->name);
+ unregister_device(&scmi_dev->dev);
+}
+
+void scmi_set_handle(struct scmi_device *scmi_dev)
+{
+ scmi_dev->handle = scmi_handle_get(&scmi_dev->dev);
+}
+
+int scmi_protocol_register(const struct scmi_protocol *proto)
+{
+ int ret;
+
+ if (!proto) {
+ pr_err("invalid protocol\n");
+ return -EINVAL;
+ }
+
+ if (!proto->instance_init) {
+ pr_err("missing init for protocol 0x%x\n", proto->id);
+ return -EINVAL;
+ }
+
+ ret = idr_alloc_one(&scmi_protocols, (void *)proto, proto->id);
+ if (ret != proto->id) {
+ pr_err("unable to allocate SCMI idr slot for 0x%x - err %d\n",
+ proto->id, ret);
+ return ret;
+ }
+
+ pr_debug("Registered SCMI Protocol 0x%x\n", proto->id);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(scmi_protocol_register);
+
+void scmi_protocol_unregister(const struct scmi_protocol *proto)
+{
+ idr_remove(&scmi_protocols, proto->id);
+
+ pr_debug("Unregistered SCMI Protocol 0x%x\n", proto->id);
+
+ return;
+}
+EXPORT_SYMBOL_GPL(scmi_protocol_unregister);
+
+int __init scmi_bus_init(void)
+{
+ int retval;
+
+ retval = bus_register(&scmi_bus_type);
+ if (retval)
+ pr_err("scmi protocol bus register failed (%d)\n", retval);
+
+ return retval;
+}
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
new file mode 100644
index 000000000000..9e48c0e7747d
--- /dev/null
+++ b/drivers/firmware/arm_scmi/common.h
@@ -0,0 +1,330 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * System Control and Management Interface (SCMI) Message Protocol
+ * driver common header file containing some definitions, structures
+ * and function prototypes used in all the different SCMI protocols.
+ *
+ * Copyright (C) 2018-2021 ARM Ltd.
+ */
+#ifndef _SCMI_COMMON_H
+#define _SCMI_COMMON_H
+
+#include <linux/bitfield.h>
+#include <driver.h>
+#include <errno.h>
+#include <linux/kernel.h>
+#include <linux/scmi_protocol.h>
+#include <linux/types.h>
+#include <linux/idr.h>
+
+#include <asm/unaligned.h>
+
+#define PROTOCOL_REV_MINOR_MASK GENMASK(15, 0)
+#define PROTOCOL_REV_MAJOR_MASK GENMASK(31, 16)
+#define PROTOCOL_REV_MAJOR(x) (u16)(FIELD_GET(PROTOCOL_REV_MAJOR_MASK, (x)))
+#define PROTOCOL_REV_MINOR(x) (u16)(FIELD_GET(PROTOCOL_REV_MINOR_MASK, (x)))
+#define MAX_PROTOCOLS_IMP 16
+#define MAX_OPPS 16
+
+enum scmi_common_cmd {
+ PROTOCOL_VERSION = 0x0,
+ PROTOCOL_ATTRIBUTES = 0x1,
+ PROTOCOL_MESSAGE_ATTRIBUTES = 0x2,
+};
+
+/**
+ * struct scmi_msg_resp_prot_version - Response for a message
+ *
+ * @minor_version: Minor version of the ABI that firmware supports
+ * @major_version: Major version of the ABI that firmware supports
+ *
+ * In general, ABI version changes follow the rule that minor version increments
+ * are backward compatible. Major revision changes in ABI may not be
+ * backward compatible.
+ *
+ * Response to a generic message with message type SCMI_MSG_VERSION
+ */
+struct scmi_msg_resp_prot_version {
+ __le16 minor_version;
+ __le16 major_version;
+};
+
+#define MSG_ID_MASK GENMASK(7, 0)
+#define MSG_XTRACT_ID(hdr) FIELD_GET(MSG_ID_MASK, (hdr))
+#define MSG_TYPE_MASK GENMASK(9, 8)
+#define MSG_XTRACT_TYPE(hdr) FIELD_GET(MSG_TYPE_MASK, (hdr))
+#define MSG_TYPE_COMMAND 0
+#define MSG_TYPE_DELAYED_RESP 2
+#define MSG_TYPE_NOTIFICATION 3
+#define MSG_PROTOCOL_ID_MASK GENMASK(17, 10)
+#define MSG_XTRACT_PROT_ID(hdr) FIELD_GET(MSG_PROTOCOL_ID_MASK, (hdr))
+#define MSG_TOKEN_ID_MASK GENMASK(27, 18)
+#define MSG_XTRACT_TOKEN(hdr) FIELD_GET(MSG_TOKEN_ID_MASK, (hdr))
+#define MSG_TOKEN_MAX (MSG_XTRACT_TOKEN(MSG_TOKEN_ID_MASK) + 1)
+
+/**
+ * struct scmi_msg_hdr - Message(Tx/Rx) header
+ *
+ * @id: The identifier of the message being sent
+ * @protocol_id: The identifier of the protocol used to send @id message
+ * @seq: The token to identify the message. When a message returns, the
+ * platform returns the whole message header unmodified including the
+ * token
+ * @status: Status of the transfer once it's complete
+ * @poll_completion: Indicate if the transfer needs to be polled for
+ * completion or interrupt mode is used
+ */
+struct scmi_msg_hdr {
+ u8 id;
+ u8 protocol_id;
+ u16 seq;
+ u32 status;
+ bool poll_completion;
+};
+
+/**
+ * pack_scmi_header() - packs and returns 32-bit header
+ *
+ * @hdr: pointer to header containing all the information on message id,
+ * protocol id and sequence id.
+ *
+ * Return: 32-bit packed message header to be sent to the platform.
+ */
+static inline u32 pack_scmi_header(struct scmi_msg_hdr *hdr)
+{
+ return FIELD_PREP(MSG_ID_MASK, hdr->id) |
+ FIELD_PREP(MSG_TOKEN_ID_MASK, hdr->seq) |
+ FIELD_PREP(MSG_PROTOCOL_ID_MASK, hdr->protocol_id);
+}
+
+/**
+ * unpack_scmi_header() - unpacks and records message and protocol id
+ *
+ * @msg_hdr: 32-bit packed message header sent from the platform
+ * @hdr: pointer to header to fetch message and protocol id.
+ */
+static inline void unpack_scmi_header(u32 msg_hdr, struct scmi_msg_hdr *hdr)
+{
+ hdr->id = MSG_XTRACT_ID(msg_hdr);
+ hdr->protocol_id = MSG_XTRACT_PROT_ID(msg_hdr);
+}
+
+/**
+ * struct scmi_msg - Message(Tx/Rx) structure
+ *
+ * @buf: Buffer pointer
+ * @len: Length of data in the Buffer
+ */
+struct scmi_msg {
+ void *buf;
+ size_t len;
+};
+
+/**
+ * struct scmi_xfer - Structure representing a message flow
+ *
+ * @transfer_id: Unique ID for debug & profiling purpose
+ * @hdr: Transmit message header
+ * @tx: Transmit message
+ * @rx: Receive message, the buffer should be pre-allocated to store
+ * message. If request-ACK protocol is used, we can reuse the same
+ * buffer for the rx path as we use for the tx path.
+ * @done: command message transmit completion event
+ * @async_done: pointer to delayed response message received event completion
+ */
+struct scmi_xfer {
+ int transfer_id;
+ struct scmi_msg_hdr hdr;
+ struct scmi_msg tx;
+ struct scmi_msg rx;
+ bool done;
+ bool *async_done;
+};
+
+struct scmi_xfer_ops;
+
+/**
+ * struct scmi_protocol_handle - Reference to an initialized protocol instance
+ *
+ * @dev: A reference to the associated SCMI instance device (handle->dev).
+ * @xops: A reference to a struct holding refs to the core xfer operations that
+ * can be used by the protocol implementation to generate SCMI messages.
+ * @set_priv: A method to set protocol private data for this instance.
+ * @get_priv: A method to get protocol private data previously set.
+ *
+ * This structure represents a protocol initialized against specific SCMI
+ * instance and it will be used as follows:
+ * - as a parameter fed from the core to the protocol initialization code so
+ * that it can access the core xfer operations to build and generate SCMI
+ * messages exclusively for the specific underlying protocol instance.
+ * - as an opaque handle fed by an SCMI driver user when it tries to access
+ * this protocol through its own protocol operations.
+ * In this case this handle will be returned as an opaque object together
+ * with the related protocol operations when the SCMI driver tries to access
+ * the protocol.
+ */
+struct scmi_protocol_handle {
+ struct device_d *dev;
+ const struct scmi_xfer_ops *xops;
+ int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv);
+ void *(*get_priv)(const struct scmi_protocol_handle *ph);
+};
+
+/**
+ * struct scmi_xfer_ops - References to the core SCMI xfer operations.
+ * @version_get: Get this version protocol.
+ * @xfer_get_init: Initialize one struct xfer if any xfer slot is free.
+ * @reset_rx_to_maxsz: Reset rx size to max transport size.
+ * @do_xfer: Do the SCMI transfer.
+ * @do_xfer_with_response: Do the SCMI transfer waiting for a response.
+ * @xfer_put: Free the xfer slot.
+ *
+ * Note that all this operations expect a protocol handle as first parameter;
+ * they then internally use it to infer the underlying protocol number: this
+ * way is not possible for a protocol implementation to forge messages for
+ * another protocol.
+ */
+struct scmi_xfer_ops {
+ int (*version_get)(const struct scmi_protocol_handle *ph, u32 *version);
+ int (*xfer_get_init)(const struct scmi_protocol_handle *ph, u8 msg_id,
+ size_t tx_size, size_t rx_size,
+ struct scmi_xfer **p);
+ void (*reset_rx_to_maxsz)(const struct scmi_protocol_handle *ph,
+ struct scmi_xfer *xfer);
+ int (*do_xfer)(const struct scmi_protocol_handle *ph,
+ struct scmi_xfer *xfer);
+ int (*do_xfer_with_response)(const struct scmi_protocol_handle *ph,
+ struct scmi_xfer *xfer);
+ void (*xfer_put)(const struct scmi_protocol_handle *ph,
+ struct scmi_xfer *xfer);
+};
+
+struct scmi_revision_info *
+scmi_revision_area_get(const struct scmi_protocol_handle *ph);
+int scmi_handle_put(const struct scmi_handle *handle);
+struct scmi_handle *scmi_handle_get(struct device_d *dev);
+void scmi_set_handle(struct scmi_device *scmi_dev);
+void scmi_setup_protocol_implemented(const struct scmi_protocol_handle *ph,
+ u8 *prot_imp);
+
+typedef int (*scmi_prot_init_ph_fn_t)(const struct scmi_protocol_handle *);
+
+/**
+ * struct scmi_protocol - Protocol descriptor
+ * @id: Protocol ID.
+ * @instance_init: Mandatory protocol initialization function.
+ * @instance_deinit: Optional protocol de-initialization function.
+ * @ops: Optional reference to the operations provided by the protocol and
+ * exposed in scmi_protocol.h.
+ * @events: An optional reference to the events supported by this protocol.
+ */
+struct scmi_protocol {
+ const u8 id;
+ const scmi_prot_init_ph_fn_t instance_init;
+ const scmi_prot_init_ph_fn_t instance_deinit;
+ const void *ops;
+ const struct scmi_protocol_events *events;
+};
+
+int __init scmi_bus_init(void);
+void __exit scmi_bus_exit(void);
+
+#define DECLARE_SCMI_REGISTER(func) \
+ int __init scmi_##func##_register(void);
+DECLARE_SCMI_REGISTER(base);
+
+#define DEFINE_SCMI_PROTOCOL_REGISTER(name, proto) \
+static const struct scmi_protocol *__this_proto = &(proto); \
+ \
+int __init scmi_##name##_register(void) \
+{ \
+ return scmi_protocol_register(__this_proto); \
+}
+
+const struct scmi_protocol *scmi_protocol_get(int protocol_id);
+
+int scmi_protocol_acquire(const struct scmi_handle *handle, u8 protocol_id);
+void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id);
+
+/* SCMI Transport */
+/**
+ * struct scmi_chan_info - Structure representing a SCMI channel information
+ *
+ * @dev: Reference to device in the SCMI hierarchy corresponding to this
+ * channel
+ * @handle: Pointer to SCMI entity handle
+ * @transport_info: Transport layer related information
+ */
+struct scmi_chan_info {
+ struct device_d *dev;
+ struct scmi_handle *handle;
+ void *transport_info;
+};
+
+/**
+ * struct scmi_transport_ops - Structure representing a SCMI transport ops
+ *
+ * @chan_available: Callback to check if channel is available or not
+ * @chan_setup: Callback to allocate and setup a channel
+ * @chan_free: Callback to free a channel
+ * @send_message: Callback to send a message
+ * @mark_txdone: Callback to mark tx as done
+ * @fetch_response: Callback to fetch response
+ * @clear_channel: Callback to clear a channel
+ * @poll_done: Callback to poll transfer status
+ */
+struct scmi_transport_ops {
+ bool (*chan_available)(struct device_d *dev, int idx);
+ int (*chan_setup)(struct scmi_chan_info *cinfo, struct device_d *dev,
+ bool tx);
+ int (*chan_free)(int id, void *p, void *data);
+ int (*send_message)(struct scmi_chan_info *cinfo,
+ struct scmi_xfer *xfer);
+ void (*mark_txdone)(struct scmi_chan_info *cinfo, int ret);
+ void (*fetch_response)(struct scmi_chan_info *cinfo,
+ struct scmi_xfer *xfer);
+ void (*clear_channel)(struct scmi_chan_info *cinfo);
+ bool (*poll_done)(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer);
+};
+
+int scmi_protocol_device_request(const struct scmi_device_id *id_table);
+void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table);
+struct scmi_device *scmi_child_dev_find(struct device_d *parent,
+ int prot_id, const char *name);
+
+/**
+ * struct scmi_desc - Description of SoC integration
+ *
+ * @ops: Pointer to the transport specific ops structure
+ * @max_rx_timeout_ms: Timeout for communication with SoC (in Milliseconds)
+ * @max_msg: Maximum number of messages that can be pending
+ * simultaneously in the system
+ * @max_msg_size: Maximum size of data per message that can be handled.
+ */
+struct scmi_desc {
+ const struct scmi_transport_ops *ops;
+ int max_rx_timeout_ms;
+ int max_msg;
+ int max_msg_size;
+};
+
+#ifdef CONFIG_ARM_SMCCC
+extern const struct scmi_desc scmi_smc_desc;
+#endif
+
+void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr);
+void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id);
+
+/* shmem related declarations */
+struct scmi_shared_mem;
+
+void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem,
+ struct scmi_xfer *xfer);
+u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem);
+void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem,
+ struct scmi_xfer *xfer);
+void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem);
+bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem,
+ struct scmi_xfer *xfer);
+
+#endif /* _SCMI_COMMON_H */
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
new file mode 100644
index 000000000000..c9819e7c2aa3
--- /dev/null
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -0,0 +1,1275 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface (SCMI) Message Protocol driver
+ *
+ * SCMI Message Protocol is used between the System Control Processor(SCP)
+ * and the Application Processors(AP). The Message Handling Unit(MHU)
+ * provides a mechanism for inter-processor communication between SCP's
+ * Cortex M3 and AP.
+ *
+ * SCP offers control and management of the core/cluster power states,
+ * various power domain DVFS including the core/cluster, certain system
+ * clocks configuration, thermal sensors and many others.
+ *
+ * Copyright (C) 2018-2021 ARM Ltd.
+ */
+
+#define pr_fmt(fmt) "SCMI DRIVER - " fmt
+
+#include <common.h>
+#include <linux/bitmap.h>
+#include <driver.h>
+#include <linux/export.h>
+#include <io.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <of_address.h>
+#include <of_device.h>
+#include <linux/slab.h>
+#include <linux/idr.h>
+#include <linux/processor.h>
+
+#include "common.h"
+
+enum scmi_error_codes {
+ SCMI_SUCCESS = 0, /* Success */
+ SCMI_ERR_SUPPORT = -1, /* Not supported */
+ SCMI_ERR_PARAMS = -2, /* Invalid Parameters */
+ SCMI_ERR_ACCESS = -3, /* Invalid access/permission denied */
+ SCMI_ERR_ENTRY = -4, /* Not found */
+ SCMI_ERR_RANGE = -5, /* Value out of range */
+ SCMI_ERR_BUSY = -6, /* Device busy */
+ SCMI_ERR_COMMS = -7, /* Communication Error */
+ SCMI_ERR_GENERIC = -8, /* Generic Error */
+ SCMI_ERR_HARDWARE = -9, /* Hardware Error */
+ SCMI_ERR_PROTOCOL = -10,/* Protocol Error */
+ SCMI_ERR_MAX
+};
+
+/* List of all SCMI devices active in system */
+static LIST_HEAD(scmi_list);
+/* Protection for the entire list */
+/* Track the unique id for the transfers for debug & profiling purpose */
+static unsigned transfer_last_id;
+
+static DEFINE_IDR(scmi_requested_devices);
+
+struct scmi_requested_dev {
+ const struct scmi_device_id *id_table;
+ struct list_head node;
+};
+
+/**
+ * struct scmi_xfers_info - Structure to manage transfer information
+ *
+ * @xfer_block: Preallocated Message array
+ * @xfer_alloc_table: Bitmap table for allocated messages.
+ * Index of this bitmap table is also used for message
+ * sequence identifier.
+ */
+struct scmi_xfers_info {
+ struct scmi_xfer *xfer_block;
+ unsigned long *xfer_alloc_table;
+};
+
+/**
+ * struct scmi_protocol_instance - Describe an initialized protocol instance.
+ * @handle: Reference to the SCMI handle associated to this protocol instance.
+ * @proto: A reference to the protocol descriptor.
+ * @users: A refcount to track effective users of this protocol.
+ * @priv: Reference for optional protocol private data.
+ * @ph: An embedded protocol handle that will be passed down to protocol
+ * initialization code to identify this instance.
+ *
+ * Each protocol is initialized independently once for each SCMI platform in
+ * which is defined by DT and implemented by the SCMI server fw.
+ */
+struct scmi_protocol_instance {
+ const struct scmi_handle *handle;
+ const struct scmi_protocol *proto;
+ int users;
+ void *priv;
+ struct scmi_protocol_handle ph;
+};
+
+#define ph_to_pi(h) container_of(h, struct scmi_protocol_instance, ph)
+
+/**
+ * struct scmi_info - Structure representing a SCMI instance
+ *
+ * @dev: Device pointer
+ * @desc: SoC description for this instance
+ * @version: SCMI revision information containing protocol version,
+ * implementation version and (sub-)vendor identification.
+ * @handle: Instance of SCMI handle to send to clients
+ * @tx_minfo: Universal Transmit Message management info
+ * @rx_minfo: Universal Receive Message management info
+ * @tx_idr: IDR object to map protocol id to Tx channel info pointer
+ * @rx_idr: IDR object to map protocol id to Rx channel info pointer
+ * @protocols: IDR for protocols' instance descriptors initialized for
+ * this SCMI instance: populated on protocol's first attempted
+ * usage.
+ * @protocols_imp: List of protocols implemented, currently maximum of
+ * MAX_PROTOCOLS_IMP elements allocated by the base protocol
+ * @active_protocols: IDR storing device_nodes for protocols actually defined
+ * in the DT and confirmed as implemented by fw.
+ * @node: List head
+ * @users: Number of users of this instance
+ */
+struct scmi_info {
+ struct device_d *dev;
+ const struct scmi_desc *desc;
+ struct scmi_revision_info version;
+ struct scmi_handle handle;
+ struct scmi_xfers_info tx_minfo;
+ struct scmi_xfers_info rx_minfo;
+ struct idr tx_idr;
+ struct idr rx_idr;
+ struct idr protocols;
+ /* Ensure mutual exclusive access to protocols instance array */
+ u8 *protocols_imp;
+ struct idr active_protocols;
+ struct list_head node;
+ int users;
+};
+
+#define handle_to_scmi_info(h) container_of(h, struct scmi_info, handle)
+
+static const int scmi_linux_errmap[] = {
+ /* better than switch case as long as return value is continuous */
+ 0, /* SCMI_SUCCESS */
+ -EOPNOTSUPP, /* SCMI_ERR_SUPPORT */
+ -EINVAL, /* SCMI_ERR_PARAM */
+ -EACCES, /* SCMI_ERR_ACCESS */
+ -ENOENT, /* SCMI_ERR_ENTRY */
+ -ERANGE, /* SCMI_ERR_RANGE */
+ -EBUSY, /* SCMI_ERR_BUSY */
+ -ECOMM, /* SCMI_ERR_COMMS */
+ -EIO, /* SCMI_ERR_GENERIC */
+ -EREMOTEIO, /* SCMI_ERR_HARDWARE */
+ -EPROTO, /* SCMI_ERR_PROTOCOL */
+};
+
+static inline int scmi_to_linux_errno(int errno)
+{
+ if (errno < SCMI_SUCCESS && errno > SCMI_ERR_MAX)
+ return scmi_linux_errmap[-errno];
+ return -EIO;
+}
+
+/**
+ * scmi_dump_header_dbg() - Helper to dump a message header.
+ *
+ * @dev: Device pointer corresponding to the SCMI entity
+ * @hdr: pointer to header.
+ */
+static inline void scmi_dump_header_dbg(struct device_d *dev,
+ struct scmi_msg_hdr *hdr)
+{
+ dev_dbg(dev, "Message ID: %x Sequence ID: %x Protocol: %x\n",
+ hdr->id, hdr->seq, hdr->protocol_id);
+}
+
+/**
+ * scmi_xfer_get() - Allocate one message
+ *
+ * @handle: Pointer to SCMI entity handle
+ * @minfo: Pointer to Tx/Rx Message management info based on channel type
+ *
+ * Helper function which is used by various message functions that are
+ * exposed to clients of this driver for allocating a message traffic event.
+ *
+ * This function can sleep depending on pending requests already in the system
+ * for the SCMI entity.
+ *
+ * Return: 0 if all went fine, else corresponding error.
+ */
+static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle,
+ struct scmi_xfers_info *minfo)
+{
+ u16 xfer_id;
+ struct scmi_xfer *xfer;
+ unsigned long bit_pos;
+ struct scmi_info *info = handle_to_scmi_info(handle);
+
+ bit_pos = find_first_zero_bit(minfo->xfer_alloc_table,
+ info->desc->max_msg);
+ if (bit_pos == info->desc->max_msg)
+ return ERR_PTR(-ENOMEM);
+ set_bit(bit_pos, minfo->xfer_alloc_table);
+
+ xfer_id = bit_pos;
+
+ xfer = &minfo->xfer_block[xfer_id];
+ xfer->hdr.seq = xfer_id;
+ xfer->done = false;
+ xfer->transfer_id = ++transfer_last_id;
+
+ return xfer;
+}
+
+/**
+ * __scmi_xfer_put() - Release a message
+ *
+ * @minfo: Pointer to Tx/Rx Message management info based on channel type
+ * @xfer: message that was reserved by scmi_xfer_get
+ */
+static void
+__scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer)
+{
+ clear_bit(xfer->hdr.seq, minfo->xfer_alloc_table);
+}
+
+static void scmi_handle_response(struct scmi_chan_info *cinfo,
+ u16 xfer_id, u8 msg_type)
+{
+ struct scmi_xfer *xfer;
+ struct device_d *dev = cinfo->dev;
+ struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
+ struct scmi_xfers_info *minfo = &info->tx_minfo;
+
+ /* Are we even expecting this? */
+ if (!test_bit(xfer_id, minfo->xfer_alloc_table)) {
+ dev_err(dev, "message for %d is not expected!\n", xfer_id);
+ info->desc->ops->clear_channel(cinfo);
+ return;
+ }
+
+ xfer = &minfo->xfer_block[xfer_id];
+ /*
+ * Even if a response was indeed expected on this slot at this point,
+ * a buggy platform could wrongly reply feeding us an unexpected
+ * delayed response we're not prepared to handle: bail-out safely
+ * blaming firmware.
+ */
+ if (unlikely(msg_type == MSG_TYPE_DELAYED_RESP && !xfer->async_done)) {
+ dev_err(dev,
+ "Delayed Response for %d not expected! Buggy F/W ?\n",
+ xfer_id);
+ info->desc->ops->clear_channel(cinfo);
+ /* It was unexpected, so nobody will clear the xfer if not us */
+ __scmi_xfer_put(minfo, xfer);
+ return;
+ }
+
+ scmi_dump_header_dbg(dev, &xfer->hdr);
+
+ info->desc->ops->fetch_response(cinfo, xfer);
+
+ if (msg_type == MSG_TYPE_DELAYED_RESP) {
+ info->desc->ops->clear_channel(cinfo);
+ *xfer->async_done = true;
+ } else {
+ xfer->done = true;
+ }
+}
+
+/**
+ * scmi_rx_callback() - callback for receiving messages
+ *
+ * @cinfo: SCMI channel info
+ * @msg_hdr: Message header
+ *
+ * Processes one received message to appropriate transfer information and
+ * signals completion of the transfer.
+ *
+ * NOTE: This function will be invoked in IRQ context, hence should be
+ * as optimal as possible.
+ */
+void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr)
+{
+ u16 xfer_id = MSG_XTRACT_TOKEN(msg_hdr);
+ u8 msg_type = MSG_XTRACT_TYPE(msg_hdr);
+
+ switch (msg_type) {
+ case MSG_TYPE_COMMAND:
+ case MSG_TYPE_DELAYED_RESP:
+ scmi_handle_response(cinfo, xfer_id, msg_type);
+ break;
+ default:
+ WARN_ONCE(1, "received unknown msg_type:%d\n", msg_type);
+ break;
+ }
+}
+
+/**
+ * xfer_put() - Release a transmit message
+ *
+ * @ph: Pointer to SCMI protocol handle
+ * @xfer: message that was reserved by scmi_xfer_get
+ */
+static void xfer_put(const struct scmi_protocol_handle *ph,
+ struct scmi_xfer *xfer)
+{
+ const struct scmi_protocol_instance *pi = ph_to_pi(ph);
+ struct scmi_info *info = handle_to_scmi_info(pi->handle);
+
+ __scmi_xfer_put(&info->tx_minfo, xfer);
+}
+
+/**
+ * do_xfer() - Do one transfer
+ *
+ * @ph: Pointer to SCMI protocol handle
+ * @xfer: Transfer to initiate and wait for response
+ *
+ * Return: -ETIMEDOUT in case of no response, if transmit error,
+ * return corresponding error, else if all goes well,
+ * return 0.
+ */
+static int do_xfer(const struct scmi_protocol_handle *ph,
+ struct scmi_xfer *xfer)
+{
+ int ret;
+ const struct scmi_protocol_instance *pi = ph_to_pi(ph);
+ struct scmi_info *info = handle_to_scmi_info(pi->handle);
+ struct device_d *dev = info->dev;
+ struct scmi_chan_info *cinfo;
+ u64 start;
+
+ /*
+ * Re-instate protocol id here from protocol handle so that cannot be
+ * overridden by mistake (or malice) by the protocol code mangling with
+ * the scmi_xfer structure.
+ */
+ xfer->hdr.protocol_id = pi->proto->id;
+
+ cinfo = idr_find(&info->tx_idr, xfer->hdr.protocol_id);
+ if (unlikely(!cinfo))
+ return -EINVAL;
+
+ ret = info->desc->ops->send_message(cinfo, xfer);
+ if (ret < 0) {
+ dev_dbg(dev, "Failed to send message %d\n", ret);
+ return ret;
+ }
+
+ /* And we wait for the response. */
+ start = get_time_ns();
+ while (!xfer->done) {
+ if (is_timeout(start, info->desc->max_rx_timeout_ms * (u64)NSEC_PER_MSEC)) {
+ dev_err(dev, "timed out in resp(caller: %pS)\n", (void *)_RET_IP_);
+ ret = -ETIMEDOUT;
+ break;
+ }
+ }
+
+ if (!ret && xfer->hdr.status)
+ ret = scmi_to_linux_errno(xfer->hdr.status);
+
+ if (info->desc->ops->mark_txdone)
+ info->desc->ops->mark_txdone(cinfo, ret);
+
+ return ret;
+}
+
+static void reset_rx_to_maxsz(const struct scmi_protocol_handle *ph,
+ struct scmi_xfer *xfer)
+{
+ const struct scmi_protocol_instance *pi = ph_to_pi(ph);
+ struct scmi_info *info = handle_to_scmi_info(pi->handle);
+
+ xfer->rx.len = info->desc->max_msg_size;
+}
+
+#define SCMI_MAX_RESPONSE_TIMEOUT_NS (2 * NSEC_PER_SEC)
+
+/**
+ * do_xfer_with_response() - Do one transfer and wait until the delayed
+ * response is received
+ *
+ * @ph: Pointer to SCMI protocol handle
+ * @xfer: Transfer to initiate and wait for response
+ *
+ * Return: -ETIMEDOUT in case of no delayed response, if transmit error,
+ * return corresponding error, else if all goes well, return 0.
+ */
+static int do_xfer_with_response(const struct scmi_protocol_handle *ph,
+ struct scmi_xfer *xfer)
+{
+ int ret;
+ const struct scmi_protocol_instance *pi = ph_to_pi(ph);
+ bool async_response = false;
+ u64 start;
+
+ xfer->hdr.protocol_id = pi->proto->id;
+
+ xfer->async_done = &async_response;
+
+ ret = do_xfer(ph, xfer);
+ if (ret)
+ goto out;
+
+ start = get_time_ns();
+ while (!*xfer->async_done) {
+ if (is_timeout(start, SCMI_MAX_RESPONSE_TIMEOUT_NS)) {
+ ret = -ETIMEDOUT;
+ break;
+ }
+ }
+
+out:
+ xfer->async_done = NULL;
+ return ret;
+}
+
+/**
+ * xfer_get_init() - Allocate and initialise one message for transmit
+ *
+ * @ph: Pointer to SCMI protocol handle
+ * @msg_id: Message identifier
+ * @tx_size: transmit message size
+ * @rx_size: receive message size
+ * @p: pointer to the allocated and initialised message
+ *
+ * This function allocates the message using @scmi_xfer_get and
+ * initialise the header.
+ *
+ * Return: 0 if all went fine with @p pointing to message, else
+ * corresponding error.
+ */
+static int xfer_get_init(const struct scmi_protocol_handle *ph,
+ u8 msg_id, size_t tx_size, size_t rx_size,
+ struct scmi_xfer **p)
+{
+ int ret;
+ struct scmi_xfer *xfer;
+ const struct scmi_protocol_instance *pi = ph_to_pi(ph);
+ struct scmi_info *info = handle_to_scmi_info(pi->handle);
+ struct scmi_xfers_info *minfo = &info->tx_minfo;
+ struct device_d *dev = info->dev;
+
+ /* Ensure we have sane transfer sizes */
+ if (rx_size > info->desc->max_msg_size ||
+ tx_size > info->desc->max_msg_size)
+ return -ERANGE;
+
+ xfer = scmi_xfer_get(pi->handle, minfo);
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ dev_err(dev, "failed to get free message slot(%d)\n", ret);
+ return ret;
+ }
+
+ xfer->tx.len = tx_size;
+ xfer->rx.len = rx_size ? : info->desc->max_msg_size;
+ xfer->hdr.id = msg_id;
+ xfer->hdr.protocol_id = pi->proto->id;
+ xfer->hdr.poll_completion = false;
+
+ *p = xfer;
+
+ return 0;
+}
+
+/**
+ * version_get() - command to get the revision of the SCMI entity
+ *
+ * @ph: Pointer to SCMI protocol handle
+ * @version: Holds returned version of protocol.
+ *
+ * Updates the SCMI information in the internal data structure.
+ *
+ * Return: 0 if all went fine, else return appropriate error.
+ */
+static int version_get(const struct scmi_protocol_handle *ph, u32 *version)
+{
+ int ret;
+ __le32 *rev_info;
+ struct scmi_xfer *t;
+
+ ret = xfer_get_init(ph, PROTOCOL_VERSION, 0, sizeof(*version), &t);
+ if (ret)
+ return ret;
+
+ ret = do_xfer(ph, t);
+ if (!ret) {
+ rev_info = t->rx.buf;
+ *version = le32_to_cpu(*rev_info);
+ }
+
+ xfer_put(ph, t);
+ return ret;
+}
+
+/**
+ * scmi_set_protocol_priv - Set protocol specific data at init time
+ *
+ * @ph: A reference to the protocol handle.
+ * @priv: The private data to set.
+ *
+ * Return: 0 on Success
+ */
+static int scmi_set_protocol_priv(const struct scmi_protocol_handle *ph,
+ void *priv)
+{
+ struct scmi_protocol_instance *pi = ph_to_pi(ph);
+
+ pi->priv = priv;
+
+ return 0;
+}
+
+/**
+ * scmi_get_protocol_priv - Set protocol specific data at init time
+ *
+ * @ph: A reference to the protocol handle.
+ *
+ * Return: Protocol private data if any was set.
+ */
+static void *scmi_get_protocol_priv(const struct scmi_protocol_handle *ph)
+{
+ const struct scmi_protocol_instance *pi = ph_to_pi(ph);
+
+ return pi->priv;
+}
+
+static const struct scmi_xfer_ops xfer_ops = {
+ .version_get = version_get,
+ .xfer_get_init = xfer_get_init,
+ .reset_rx_to_maxsz = reset_rx_to_maxsz,
+ .do_xfer = do_xfer,
+ .do_xfer_with_response = do_xfer_with_response,
+ .xfer_put = xfer_put,
+};
+
+/**
+ * scmi_revision_area_get - Retrieve version memory area.
+ *
+ * @ph: A reference to the protocol handle.
+ *
+ * A helper to grab the version memory area reference during SCMI Base protocol
+ * initialization.
+ *
+ * Return: A reference to the version memory area associated to the SCMI
+ * instance underlying this protocol handle.
+ */
+struct scmi_revision_info *
+scmi_revision_area_get(const struct scmi_protocol_handle *ph)
+{
+ const struct scmi_protocol_instance *pi = ph_to_pi(ph);
+
+ return pi->handle->version;
+}
+
+/**
+ * scmi_alloc_init_protocol_instance - Allocate and initialize a protocol
+ * instance descriptor.
+ * @info: The reference to the related SCMI instance.
+ * @proto: The protocol descriptor.
+ *
+ * Allocate a new protocol instance descriptor, using the provided @proto
+ * description, against the specified SCMI instance @info, and initialize it;
+ *
+ * Return: A reference to a freshly allocated and initialized protocol instance
+ * or ERR_PTR on failure. On failure the @proto reference is at first
+ */
+static struct scmi_protocol_instance *
+scmi_alloc_init_protocol_instance(struct scmi_info *info,
+ const struct scmi_protocol *proto)
+{
+ int ret = -ENOMEM;
+ struct scmi_protocol_instance *pi;
+ const struct scmi_handle *handle = &info->handle;
+
+ pi = kzalloc(sizeof(*pi), GFP_KERNEL);
+ if (!pi)
+ goto clean;
+
+ pi->proto = proto;
+ pi->handle = handle;
+ pi->ph.dev = handle->dev;
+ pi->ph.xops = &xfer_ops;
+ pi->ph.set_priv = scmi_set_protocol_priv;
+ pi->ph.get_priv = scmi_get_protocol_priv;
+ pi->users++;
+ /* proto->init is assured NON NULL by scmi_protocol_register */
+ ret = pi->proto->instance_init(&pi->ph);
+ if (ret)
+ goto clean;
+
+ ret = idr_alloc_one(&info->protocols, pi, proto->id);
+ if (ret != proto->id)
+ goto clean;
+
+ dev_dbg(handle->dev, "Initialized protocol: 0x%X\n", pi->proto->id);
+
+ return pi;
+
+clean:
+ return ERR_PTR(ret);
+}
+
+/**
+ * scmi_get_protocol_instance - Protocol initialization helper.
+ * @handle: A reference to the SCMI platform instance.
+ * @protocol_id: The protocol being requested.
+ *
+ * In case the required protocol has never been requested before for this
+ * instance, allocate and initialize all the needed structures.
+ *
+ * Return: A reference to an initialized protocol instance or error on failure:
+ * in particular returns -EPROBE_DEFER when the desired protocol could
+ * NOT be found.
+ */
+static struct scmi_protocol_instance * __must_check
+scmi_get_protocol_instance(const struct scmi_handle *handle, u8 protocol_id)
+{
+ struct scmi_protocol_instance *pi;
+ struct scmi_info *info = handle_to_scmi_info(handle);
+
+ pi = idr_find(&info->protocols, protocol_id);
+
+ if (pi) {
+ pi->users++;
+ } else {
+ const struct scmi_protocol *proto;
+
+ /* Fails if protocol not registered on bus */
+ proto = scmi_protocol_get(protocol_id);
+ if (proto)
+ pi = scmi_alloc_init_protocol_instance(info, proto);
+ else
+ pi = ERR_PTR(-EPROBE_DEFER);
+ }
+
+ return pi;
+}
+
+/**
+ * scmi_protocol_acquire - Protocol acquire
+ * @handle: A reference to the SCMI platform instance.
+ * @protocol_id: The protocol being requested.
+ *
+ * Register a new user for the requested protocol on the specified SCMI
+ * platform instance, possibly triggering its initialization on first user.
+ *
+ * Return: 0 if protocol was acquired successfully.
+ */
+int scmi_protocol_acquire(const struct scmi_handle *handle, u8 protocol_id)
+{
+ return PTR_ERR_OR_ZERO(scmi_get_protocol_instance(handle, protocol_id));
+}
+
+void scmi_setup_protocol_implemented(const struct scmi_protocol_handle *ph,
+ u8 *prot_imp)
+{
+ const struct scmi_protocol_instance *pi = ph_to_pi(ph);
+ struct scmi_info *info = handle_to_scmi_info(pi->handle);
+
+ info->protocols_imp = prot_imp;
+}
+
+static bool
+scmi_is_protocol_implemented(const struct scmi_handle *handle, u8 prot_id)
+{
+ int i;
+ struct scmi_info *info = handle_to_scmi_info(handle);
+
+ if (!info->protocols_imp)
+ return false;
+
+ for (i = 0; i < MAX_PROTOCOLS_IMP; i++)
+ if (info->protocols_imp[i] == prot_id)
+ return true;
+ return false;
+}
+
+/**
+ * scmi_dev_protocol_get - get protocol operations and handle
+ * @protocol_id: The protocol being requested.
+ * @ph: A pointer reference used to pass back the associated protocol handle.
+ *
+ * Get hold of a protocol accounting for its usage, eventually triggering its
+ * initialization, and returning the protocol specific operations and related
+ * protocol handle which will be used as first argument in most of the
+ * protocols operations methods.
+ * Being a devres based managed method, protocol hold will be automatically
+ * released, and possibly de-initialized on last user, once the SCMI driver
+ * owning the scmi_device is unbound from it.
+ *
+ * Return: A reference to the requested protocol operations or error.
+ * Must be checked for errors by caller.
+ */
+static const void __must_check *
+scmi_dev_protocol_get(struct scmi_device *sdev, u8 protocol_id,
+ struct scmi_protocol_handle **ph)
+{
+ struct scmi_protocol_instance *pi;
+ struct scmi_handle *handle = sdev->handle;
+
+ if (!ph)
+ return ERR_PTR(-EINVAL);
+
+ pi = scmi_get_protocol_instance(handle, protocol_id);
+ if (IS_ERR(pi))
+ return pi;
+
+ *ph = &pi->ph;
+
+ return pi->proto->ops;
+}
+
+static inline
+struct scmi_handle *scmi_handle_get_from_info_unlocked(struct scmi_info *info)
+{
+ info->users++;
+ return &info->handle;
+}
+
+/**
+ * scmi_handle_get() - Get the SCMI handle for a device
+ *
+ * @dev: pointer to device for which we want SCMI handle
+ *
+ * NOTE: The function does not track individual clients of the framework
+ * and is expected to be maintained by caller of SCMI protocol library.
+ * scmi_handle_put must be balanced with successful scmi_handle_get
+ *
+ * Return: pointer to handle if successful, NULL on error
+ */
+struct scmi_handle *scmi_handle_get(struct device_d *dev)
+{
+ struct list_head *p;
+ struct scmi_info *info;
+ struct scmi_handle *handle = NULL;
+
+ list_for_each(p, &scmi_list) {
+ info = list_entry(p, struct scmi_info, node);
+ if (dev->parent == info->dev) {
+ handle = scmi_handle_get_from_info_unlocked(info);
+ break;
+ }
+ }
+
+ return handle;
+}
+
+/**
+ * scmi_handle_put() - Release the handle acquired by scmi_handle_get
+ *
+ * @handle: handle acquired by scmi_handle_get
+ *
+ * NOTE: The function does not track individual clients of the framework
+ * and is expected to be maintained by caller of SCMI protocol library.
+ * scmi_handle_put must be balanced with successful scmi_handle_get
+ *
+ * Return: 0 is successfully released
+ * if null was passed, it returns -EINVAL;
+ */
+int scmi_handle_put(const struct scmi_handle *handle)
+{
+ struct scmi_info *info;
+
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_scmi_info(handle);
+ if (!WARN_ON(!info->users))
+ info->users--;
+
+ return 0;
+}
+
+static int __scmi_xfer_info_init(struct scmi_info *sinfo,
+ struct scmi_xfers_info *info)
+{
+ int i;
+ struct scmi_xfer *xfer;
+ struct device_d *dev = sinfo->dev;
+ const struct scmi_desc *desc = sinfo->desc;
+
+ /* Pre-allocated messages, no more than what hdr.seq can support */
+ if (WARN_ON(desc->max_msg >= MSG_TOKEN_MAX)) {
+ dev_err(dev, "Maximum message of %d exceeds supported %ld\n",
+ desc->max_msg, MSG_TOKEN_MAX);
+ return -EINVAL;
+ }
+
+ info->xfer_block = kcalloc(desc->max_msg, sizeof(*info->xfer_block), GFP_KERNEL);
+ if (!info->xfer_block)
+ return -ENOMEM;
+
+ info->xfer_alloc_table = kcalloc(BITS_TO_LONGS(desc->max_msg),
+ sizeof(long), GFP_KERNEL);
+ if (!info->xfer_alloc_table)
+ return -ENOMEM;
+
+ /* Pre-initialize the buffer pointer to pre-allocated buffers */
+ for (i = 0, xfer = info->xfer_block; i < desc->max_msg; i++, xfer++) {
+ xfer->rx.buf = kcalloc(sizeof(u8), desc->max_msg_size,
+ GFP_KERNEL);
+ if (!xfer->rx.buf)
+ return -ENOMEM;
+
+ xfer->tx.buf = xfer->rx.buf;
+ xfer->done = false;
+ }
+
+ return 0;
+}
+
+static int scmi_xfer_info_init(struct scmi_info *sinfo)
+{
+ int ret = __scmi_xfer_info_init(sinfo, &sinfo->tx_minfo);
+
+ if (!ret && idr_find(&sinfo->rx_idr, SCMI_PROTOCOL_BASE))
+ ret = __scmi_xfer_info_init(sinfo, &sinfo->rx_minfo);
+
+ return ret;
+}
+
+static int scmi_chan_setup(struct scmi_info *info, struct device_d *dev,
+ int prot_id, bool tx)
+{
+ int ret, idx;
+ struct scmi_chan_info *cinfo;
+ struct idr *idr;
+
+ /* Transmit channel is first entry i.e. index 0 */
+ idx = tx ? 0 : 1;
+ idr = tx ? &info->tx_idr : &info->rx_idr;
+
+ /* check if already allocated, used for multiple device per protocol */
+ cinfo = idr_find(idr, prot_id);
+ if (cinfo)
+ return 0;
+
+ if (!info->desc->ops->chan_available(dev, idx)) {
+ cinfo = idr_find(idr, SCMI_PROTOCOL_BASE);
+ if (unlikely(!cinfo)) /* Possible only if platform has no Rx */
+ return -EINVAL;
+ goto idr_alloc;
+ }
+
+ cinfo = kzalloc(sizeof(*cinfo), GFP_KERNEL);
+ if (!cinfo)
+ return -ENOMEM;
+
+ cinfo->dev = dev;
+
+ ret = info->desc->ops->chan_setup(cinfo, info->dev, tx);
+ if (ret)
+ return ret;
+
+idr_alloc:
+ ret = idr_alloc_one(idr, cinfo, prot_id);
+ if (ret != prot_id) {
+ dev_err(dev, "unable to allocate SCMI idr slot err %d\n", ret);
+ return ret;
+ }
+
+ cinfo->handle = &info->handle;
+ return 0;
+}
+
+static inline int
+scmi_txrx_setup(struct scmi_info *info, struct device_d *dev, int prot_id)
+{
+ int ret = scmi_chan_setup(info, dev, prot_id, true);
+
+ if (!ret) /* Rx is optional, hence no error check */
+ scmi_chan_setup(info, dev, prot_id, false);
+
+ return ret;
+}
+
+/**
+ * scmi_get_protocol_device - Helper to get/create an SCMI device.
+ *
+ * @np: A device node representing a valid active protocols for the referred
+ * SCMI instance.
+ * @info: The referred SCMI instance for which we are getting/creating this
+ * device.
+ * @prot_id: The protocol ID.
+ * @name: The device name.
+ *
+ * Referring to the specific SCMI instance identified by @info, this helper
+ * takes care to return a properly initialized device matching the requested
+ * @proto_id and @name: if device was still not existent it is created as a
+ * child of the specified SCMI instance @info and its transport properly
+ * initialized as usual.
+ */
+static inline struct scmi_device *
+scmi_get_protocol_device(struct device_node *np, struct scmi_info *info,
+ int prot_id, const char *name)
+{
+ struct scmi_device *sdev;
+
+ /* Already created for this parent SCMI instance ? */
+ sdev = scmi_child_dev_find(info->dev, prot_id, name);
+ if (sdev)
+ return sdev;
+
+ pr_debug("Creating SCMI device (%s) for protocol %x\n", name, prot_id);
+
+ sdev = scmi_device_alloc(np, info->dev, prot_id, name);
+ if (!sdev) {
+ dev_err(info->dev, "failed to create %d protocol device\n",
+ prot_id);
+ return NULL;
+ }
+
+ if (scmi_txrx_setup(info, &sdev->dev, prot_id)) {
+ dev_err(&sdev->dev, "failed to setup transport\n");
+ scmi_device_destroy(sdev);
+ return NULL;
+ }
+
+ return sdev;
+}
+
+static inline void
+scmi_create_protocol_device(struct device_node *np, struct scmi_info *info,
+ int prot_id, const char *name)
+{
+ struct scmi_device *sdev;
+
+ sdev = scmi_get_protocol_device(np, info, prot_id, name);
+ if (!sdev)
+ return;
+
+ /* setup handle now as the transport is ready */
+ scmi_set_handle(sdev);
+
+ /* Register if not done yet */
+ if (sdev->dev.id == DEVICE_ID_DYNAMIC)
+ register_device(&sdev->dev);
+}
+
+/**
+ * scmi_create_protocol_devices - Create devices for all pending requests for
+ * this SCMI instance.
+ *
+ * @np: The device node describing the protocol
+ * @info: The SCMI instance descriptor
+ * @prot_id: The protocol ID
+ *
+ * All devices previously requested for this instance (if any) are found and
+ * created by scanning the proper @&scmi_requested_devices entry.
+ */
+static void scmi_create_protocol_devices(struct device_node *np,
+ struct scmi_info *info, int prot_id)
+{
+ struct list_head *phead;
+
+ phead = idr_find(&scmi_requested_devices, prot_id);
+ if (phead) {
+ struct scmi_requested_dev *rdev;
+
+ list_for_each_entry(rdev, phead, node)
+ scmi_create_protocol_device(np, info, prot_id,
+ rdev->id_table->name);
+ }
+}
+
+/**
+ * scmi_protocol_device_request - Helper to request a device
+ *
+ * @id_table: A protocol/name pair descriptor for the device to be created.
+ *
+ * This helper let an SCMI driver request specific devices identified by the
+ * @id_table to be created for each active SCMI instance.
+ *
+ * The requested device name MUST NOT be already existent for any protocol;
+ * at first the freshly requested @id_table is annotated in the IDR table
+ * @scmi_requested_devices, then a matching device is created for each already
+ * active SCMI instance. (if any)
+ *
+ * This way the requested device is created straight-away for all the already
+ * initialized(probed) SCMI instances (handles) and it remains also annotated
+ * as pending creation if the requesting SCMI driver was loaded before some
+ * SCMI instance and related transports were available: when such late instance
+ * is probed, its probe will take care to scan the list of pending requested
+ * devices and create those on its own (see @scmi_create_protocol_devices and
+ * its enclosing loop)
+ *
+ * Return: 0 on Success
+ */
+int scmi_protocol_device_request(const struct scmi_device_id *id_table)
+{
+ int ret = 0;
+ struct list_head *phead = NULL;
+ struct scmi_requested_dev *rdev;
+ struct scmi_info *info;
+ struct idr *idr;
+
+ pr_debug("Requesting SCMI device (%s) for protocol 0x%x\n",
+ id_table->name, id_table->protocol_id);
+
+ /*
+ * Search for the matching protocol rdev list and then search
+ * of any existent equally named device...fails if any duplicate found.
+ */
+ __idr_for_each_entry(&scmi_requested_devices, idr) {
+ struct list_head *head = idr->ptr;
+ if (!phead) {
+ /* A list found registered in the IDR is never empty */
+ rdev = list_first_entry(head, struct scmi_requested_dev,
+ node);
+ if (rdev->id_table->protocol_id ==
+ id_table->protocol_id)
+ phead = head;
+ }
+ list_for_each_entry(rdev, head, node) {
+ if (!strcmp(rdev->id_table->name, id_table->name)) {
+ pr_err("Ignoring duplicate request [%d] %s\n",
+ rdev->id_table->protocol_id,
+ rdev->id_table->name);
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+ }
+
+ /*
+ * No duplicate found for requested id_table, so let's create a new
+ * requested device entry for this new valid request.
+ */
+ rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
+ if (!rdev) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ rdev->id_table = id_table;
+
+ /*
+ * Append the new requested device table descriptor to the head of the
+ * related protocol list, eventually creating such head if not already
+ * there.
+ */
+ if (!phead) {
+ phead = kzalloc(sizeof(*phead), GFP_KERNEL);
+ if (!phead) {
+ kfree(rdev);
+ ret = -ENOMEM;
+ goto out;
+ }
+ INIT_LIST_HEAD(phead);
+
+ ret = idr_alloc_one(&scmi_requested_devices, (void *)phead,
+ id_table->protocol_id);
+ if (ret != id_table->protocol_id) {
+ pr_err("Failed to save SCMI device - ret:%d\n", ret);
+ kfree(rdev);
+ kfree(phead);
+ ret = -EINVAL;
+ goto out;
+ }
+ ret = 0;
+ }
+ list_add(&rdev->node, phead);
+
+ /*
+ * Now effectively create and initialize the requested device for every
+ * already initialized SCMI instance which has registered the requested
+ * protocol as a valid active one: i.e. defined in DT and supported by
+ * current platform FW.
+ */
+ list_for_each_entry(info, &scmi_list, node) {
+ struct device_node *child;
+
+ child = idr_find(&info->active_protocols,
+ id_table->protocol_id);
+ if (child) {
+ struct scmi_device *sdev;
+
+ sdev = scmi_get_protocol_device(child, info,
+ id_table->protocol_id,
+ id_table->name);
+ /* Set handle if not already set: device existed */
+ if (sdev && !sdev->handle)
+ sdev->handle =
+ scmi_handle_get_from_info_unlocked(info);
+ } else {
+ dev_err(info->dev,
+ "Failed. SCMI protocol %d not active.\n",
+ id_table->protocol_id);
+ }
+ }
+
+out:
+ return ret;
+}
+
+/**
+ * scmi_protocol_device_unrequest - Helper to unrequest a device
+ *
+ * @id_table: A protocol/name pair descriptor for the device to be unrequested.
+ *
+ * An helper to let an SCMI driver release its request about devices; note that
+ * devices are created and initialized once the first SCMI driver request them
+ * but they destroyed only on SCMI core unloading/unbinding.
+ *
+ * The current SCMI transport layer uses such devices as internal references and
+ * as such they could be shared as same transport between multiple drivers so
+ * that cannot be safely destroyed till the whole SCMI stack is removed.
+ * (unless adding further burden of refcounting.)
+ */
+void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table)
+{
+ struct list_head *phead;
+
+ pr_debug("Unrequesting SCMI device (%s) for protocol %x\n",
+ id_table->name, id_table->protocol_id);
+
+ phead = idr_find(&scmi_requested_devices, id_table->protocol_id);
+ if (phead) {
+ struct scmi_requested_dev *victim, *tmp;
+
+ list_for_each_entry_safe(victim, tmp, phead, node) {
+ if (!strcmp(victim->id_table->name, id_table->name)) {
+ list_del(&victim->node);
+ kfree(victim);
+ break;
+ }
+ }
+
+ if (list_empty(phead)) {
+ idr_remove(&scmi_requested_devices,
+ id_table->protocol_id);
+ kfree(phead);
+ }
+ }
+}
+
+static void version_info(struct device_d *dev)
+{
+ struct scmi_info *info = dev->priv;
+
+ printf("SCMI information:\n"
+ " version: %u.%u\n"
+ " firmware version: 0x%x\n"
+ " vendor: %s (sub: %s)\n",
+ info->version.minor_ver,
+ info->version.major_ver,
+ info->version.impl_ver,
+ info->version.vendor_id,
+ info->version.sub_vendor_id);
+}
+
+static int scmi_probe(struct device_d *dev)
+{
+ int ret;
+ struct scmi_handle *handle;
+ const struct scmi_desc *desc;
+ struct scmi_info *info;
+ struct device_node *child, *np = dev->device_node;
+
+ desc = of_device_get_match_data(dev);
+ if (!desc)
+ return -EINVAL;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->dev = dev;
+ info->desc = desc;
+ INIT_LIST_HEAD(&info->node);
+ idr_init(&info->protocols);
+ idr_init(&info->active_protocols);
+
+ dev->priv = info;
+ idr_init(&info->tx_idr);
+ idr_init(&info->rx_idr);
+
+ handle = &info->handle;
+ handle->dev = info->dev;
+ handle->version = &info->version;
+ handle->protocol_get = scmi_dev_protocol_get;
+
+ ret = scmi_txrx_setup(info, dev, SCMI_PROTOCOL_BASE);
+ if (ret)
+ return ret;
+
+ ret = scmi_xfer_info_init(info);
+ if (ret)
+ return ret;
+
+ /*
+ * Trigger SCMI Base protocol initialization.
+ * It's mandatory and won't be ever released/deinit until the
+ * SCMI stack is shutdown/unloaded as a whole.
+ */
+ ret = scmi_protocol_acquire(handle, SCMI_PROTOCOL_BASE);
+ if (ret) {
+ dev_err(dev, "unable to communicate with SCMI\n");
+ return ret;
+ }
+
+ list_add_tail(&info->node, &scmi_list);
+
+ for_each_available_child_of_node(np, child) {
+ u32 prot_id;
+
+ if (of_property_read_u32(child, "reg", &prot_id))
+ continue;
+
+ if (!FIELD_FIT(MSG_PROTOCOL_ID_MASK, prot_id))
+ dev_err(dev, "Out of range protocol %d\n", prot_id);
+
+ if (!scmi_is_protocol_implemented(handle, prot_id)) {
+ dev_err(dev, "SCMI protocol %d not implemented\n",
+ prot_id);
+ continue;
+ }
+
+ /*
+ * Save this valid DT protocol descriptor amongst
+ * @active_protocols for this SCMI instance/
+ */
+ ret = idr_alloc_one(&info->active_protocols, child, prot_id);
+ if (ret != prot_id) {
+ dev_err(dev, "SCMI protocol %d already activated. Skip\n",
+ prot_id);
+ continue;
+ }
+
+ scmi_create_protocol_devices(child, info, prot_id);
+ }
+
+ dev->info = version_info;
+
+ return 0;
+}
+
+void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id)
+{
+ idr_remove(idr, id);
+}
+
+/* Each compatible listed below must have descriptor associated with it */
+static const struct of_device_id scmi_of_match[] = {
+#ifdef CONFIG_ARM_SMCCC
+ { .compatible = "arm,scmi-smc", .data = &scmi_smc_desc},
+#endif
+ { /* Sentinel */ },
+};
+
+static struct driver_d scmi_driver = {
+ .name = "arm-scmi",
+ .of_compatible = scmi_of_match,
+ .probe = scmi_probe,
+};
+
+static int __init scmi_bus_driver_init(void)
+{
+ scmi_bus_init();
+
+ scmi_base_register();
+
+ return 0;
+}
+pure_initcall(scmi_bus_driver_init);
+
+static int __init scmi_platform_driver_init(void)
+{
+ return platform_driver_register(&scmi_driver);
+}
+core_initcall(scmi_platform_driver_init);
+
+MODULE_ALIAS("platform: arm-scmi");
+MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
+MODULE_DESCRIPTION("ARM SCMI protocol driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/firmware/arm_scmi/shmem.c b/drivers/firmware/arm_scmi/shmem.c
new file mode 100644
index 000000000000..2dde2b6e09eb
--- /dev/null
+++ b/drivers/firmware/arm_scmi/shmem.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * For transport using shared mem structure.
+ *
+ * Copyright (C) 2019 ARM Ltd.
+ */
+
+#include <common.h>
+#include <io.h>
+#include <linux/types.h>
+#include <linux/processor.h>
+
+#include "common.h"
+
+/*
+ * SCMI specification requires all parameters, message headers, return
+ * arguments or any protocol data to be expressed in little endian
+ * format only.
+ */
+struct scmi_shared_mem {
+ __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[];
+};
+
+void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem,
+ struct scmi_xfer *xfer)
+{
+ /*
+ * Ideally channel must be free by now unless OS timeout last
+ * request and platform continued to process the same, wait
+ * until it releases the shared memory, otherwise we may endup
+ * overwriting its response with new message payload or vice-versa
+ */
+ spin_until_cond(ioread32(&shmem->channel_status) &
+ SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE);
+ /* Mark channel busy + clear error */
+ iowrite32(0x0, &shmem->channel_status);
+ iowrite32(xfer->hdr.poll_completion ? 0 : SCMI_SHMEM_FLAG_INTR_ENABLED,
+ &shmem->flags);
+ iowrite32(sizeof(shmem->msg_header) + xfer->tx.len, &shmem->length);
+ iowrite32(pack_scmi_header(&xfer->hdr), &shmem->msg_header);
+ if (xfer->tx.buf)
+ memcpy_toio(shmem->msg_payload, xfer->tx.buf, xfer->tx.len);
+}
+
+u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem)
+{
+ return ioread32(&shmem->msg_header);
+}
+
+void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem,
+ struct scmi_xfer *xfer)
+{
+ xfer->hdr.status = ioread32(shmem->msg_payload);
+ /* Skip the length of header and status in shmem area i.e 8 bytes */
+ xfer->rx.len = min_t(size_t, xfer->rx.len,
+ ioread32(&shmem->length) - 8);
+
+ /* Take a copy to the rx buffer.. */
+ memcpy_fromio(xfer->rx.buf, shmem->msg_payload + 4, xfer->rx.len);
+}
+
+void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem)
+{
+ iowrite32(SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE, &shmem->channel_status);
+}
+
+bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem,
+ struct scmi_xfer *xfer)
+{
+ u16 xfer_id;
+
+ xfer_id = MSG_XTRACT_TOKEN(ioread32(&shmem->msg_header));
+
+ if (xfer->hdr.seq != xfer_id)
+ return false;
+
+ return ioread32(&shmem->channel_status) &
+ (SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR |
+ SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE);
+}
diff --git a/drivers/firmware/arm_scmi/smc.c b/drivers/firmware/arm_scmi/smc.c
new file mode 100644
index 000000000000..67f19a7b4308
--- /dev/null
+++ b/drivers/firmware/arm_scmi/smc.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface (SCMI) Message SMC/HVC
+ * Transport driver
+ *
+ * Copyright 2020 NXP
+ */
+
+#include <common.h>
+#include <linux/arm-smccc.h>
+#include <driver.h>
+#include <linux/err.h>
+#include <of.h>
+#include <of_address.h>
+
+#include "common.h"
+
+/**
+ * struct scmi_smc - Structure representing a SCMI smc transport
+ *
+ * @cinfo: SCMI channel info
+ * @shmem: Transmit/Receive shared memory area
+ * @func_id: smc/hvc call function id
+ */
+
+struct scmi_smc {
+ struct scmi_chan_info *cinfo;
+ struct scmi_shared_mem __iomem *shmem;
+ u32 func_id;
+};
+
+static bool smc_chan_available(struct device_d *dev, int idx)
+{
+ return of_parse_phandle(dev->device_node, "shmem", 0) != NULL;
+}
+
+static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device_d *dev,
+ bool tx)
+{
+ struct device_d *cdev = cinfo->dev;
+ struct scmi_smc *scmi_info;
+ resource_size_t size;
+ struct resource res;
+ struct device_node *np;
+ u32 func_id;
+ int ret;
+
+ if (!tx)
+ return -ENODEV;
+
+ scmi_info = kzalloc(sizeof(*scmi_info), GFP_KERNEL);
+ if (!scmi_info)
+ return -ENOMEM;
+
+ np = of_parse_phandle(cdev->device_node, "shmem", 0);
+ ret = of_address_to_resource(np, 0, &res);
+ if (ret) {
+ dev_err(cdev, "failed to get SCMI Tx shared memory\n");
+ return ret;
+ }
+
+ size = resource_size(&res);
+ scmi_info->shmem = IOMEM(res.start);
+
+ ret = of_property_read_u32(dev->device_node, "arm,smc-id", &func_id);
+ if (ret < 0)
+ return ret;
+
+ scmi_info->func_id = func_id;
+ scmi_info->cinfo = cinfo;
+ cinfo->transport_info = scmi_info;
+
+ return 0;
+}
+
+static int smc_chan_free(int id, void *p, void *data)
+{
+ struct scmi_chan_info *cinfo = p;
+ struct scmi_smc *scmi_info = cinfo->transport_info;
+
+ cinfo->transport_info = NULL;
+ scmi_info->cinfo = NULL;
+
+ scmi_free_channel(cinfo, data, id);
+
+ return 0;
+}
+
+static int smc_send_message(struct scmi_chan_info *cinfo,
+ struct scmi_xfer *xfer)
+{
+ struct scmi_smc *scmi_info = cinfo->transport_info;
+ struct arm_smccc_res res;
+
+ shmem_tx_prepare(scmi_info->shmem, xfer);
+
+ arm_smccc_1_1_invoke(scmi_info->func_id, 0, 0, 0, 0, 0, 0, 0, &res);
+
+ scmi_rx_callback(scmi_info->cinfo, shmem_read_header(scmi_info->shmem));
+
+ /* Only SMCCC_RET_NOT_SUPPORTED is valid error code */
+ if (res.a0)
+ return -EOPNOTSUPP;
+ return 0;
+}
+
+static void smc_fetch_response(struct scmi_chan_info *cinfo,
+ struct scmi_xfer *xfer)
+{
+ struct scmi_smc *scmi_info = cinfo->transport_info;
+
+ shmem_fetch_response(scmi_info->shmem, xfer);
+}
+
+static bool
+smc_poll_done(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer)
+{
+ struct scmi_smc *scmi_info = cinfo->transport_info;
+
+ return shmem_poll_done(scmi_info->shmem, xfer);
+}
+
+static const struct scmi_transport_ops scmi_smc_ops = {
+ .chan_available = smc_chan_available,
+ .chan_setup = smc_chan_setup,
+ .chan_free = smc_chan_free,
+ .send_message = smc_send_message,
+ .fetch_response = smc_fetch_response,
+ .poll_done = smc_poll_done,
+};
+
+const struct scmi_desc scmi_smc_desc = {
+ .ops = &scmi_smc_ops,
+ .max_rx_timeout_ms = 30,
+ .max_msg = 20,
+ .max_msg_size = 128,
+};
diff --git a/include/driver.h b/include/driver.h
index a787f985a010..e59f0a164d42 100644
--- a/include/driver.h
+++ b/include/driver.h
@@ -595,4 +595,7 @@ const void *device_get_match_data(struct device_d *dev);
int device_match_of_modalias(struct device_d *dev, struct driver_d *drv);
+struct device_d *device_find_child(struct device_d *parent, void *data,
+ int (*match)(struct device_d *dev, void *data));
+
#endif /* DRIVER_H */
diff --git a/include/linux/idr.h b/include/linux/idr.h
new file mode 100644
index 000000000000..8a0f452d766d
--- /dev/null
+++ b/include/linux/idr.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * include/linux/idr.h
+ *
+ * 2002-10-18 written by Jim Houston jim.houston@ccur.com
+ * Copyright (C) 2002 by Concurrent Computer Corporation
+ *
+ * Small id to pointer translation service avoiding fixed sized
+ * tables.
+ */
+
+#ifndef __IDR_H__
+#define __IDR_H__
+
+#include <errno.h>
+#include <linux/list.h>
+
+struct idr {
+ int id;
+ void *ptr;
+ struct list_head list;
+};
+
+#define DEFINE_IDR(name) \
+ struct idr name = { .list = LIST_HEAD_INIT((name).list) }
+
+#define __idr_for_each_entry(head, idr) \
+ list_for_each_entry((idr), &(head)->list, list)
+
+static inline struct idr *__idr_find(struct idr *head, int id)
+{
+ struct idr *idr;
+
+ __idr_for_each_entry(head, idr) {
+ if (idr->id == id)
+ return idr;
+ }
+
+ return NULL;
+}
+
+static inline void *idr_find(struct idr *head, int id)
+{
+ struct idr *idr = __idr_find(head, id);
+
+ return idr ? idr->ptr : NULL;
+}
+
+static inline int idr_alloc_one(struct idr *head, void *ptr, int start)
+{
+ struct idr *idr;
+
+ if (__idr_find(head, start))
+ return -EBUSY;
+
+ idr = malloc(sizeof(*idr));
+
+ idr->id = start;
+ idr->ptr = ptr;
+
+ list_add(&idr->list, &head->list);
+
+ return start;
+}
+
+static inline void idr_init(struct idr *idr)
+{
+ INIT_LIST_HEAD(&idr->list);
+}
+
+static inline void idr_remove(struct idr *head, int id)
+{
+ struct idr *idr = __idr_find(head, id);
+
+ list_del(&idr->list);
+ free(idr);
+}
+
+#endif /* __IDR_H__ */
diff --git a/include/linux/processor.h b/include/linux/processor.h
new file mode 100644
index 000000000000..94a458c3d197
--- /dev/null
+++ b/include/linux/processor.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Misc low level processor primitives */
+#ifndef _LINUX_PROCESSOR_H
+#define _LINUX_PROCESSOR_H
+
+#include <linux/barebox-wrapper.h>
+
+/*
+ * spin_until_cond can be used to wait for a condition to become true. It
+ * may be expected that the first iteration will true in the common case
+ * (no spinning), so that callers should not require a first "likely" test
+ * for the uncontended case before using this primitive.
+ *
+ * Usage and implementation guidelines are the same as for the spin_begin
+ * primitives, above.
+ */
+#ifndef spin_until_cond
+#define spin_until_cond(cond) \
+do { \
+ if (unlikely(!(cond))) { \
+ do { \
+ cpu_relax(); \
+ } while (!(cond)); \
+ } \
+} while (0)
+
+#endif
+
+#endif /* _LINUX_PROCESSOR_H */
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
new file mode 100644
index 000000000000..5b6de7bb87c1
--- /dev/null
+++ b/include/linux/scmi_protocol.h
@@ -0,0 +1,654 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * SCMI Message Protocol driver header
+ *
+ * Copyright (C) 2018-2021 ARM Ltd.
+ */
+
+#ifndef _LINUX_SCMI_PROTOCOL_H
+#define _LINUX_SCMI_PROTOCOL_H
+
+#include <linux/bitfield.h>
+#include <driver.h>
+#include <linux/types.h>
+
+#define SCMI_MAX_STR_SIZE 16
+#define SCMI_MAX_NUM_RATES 16
+
+/**
+ * struct scmi_revision_info - version information structure
+ *
+ * @major_ver: Major ABI version. Change here implies risk of backward
+ * compatibility break.
+ * @minor_ver: Minor ABI version. Change here implies new feature addition,
+ * or compatible change in ABI.
+ * @num_protocols: Number of protocols that are implemented, excluding the
+ * base protocol.
+ * @num_agents: Number of agents in the system.
+ * @impl_ver: A vendor-specific implementation version.
+ * @vendor_id: A vendor identifier(Null terminated ASCII string)
+ * @sub_vendor_id: A sub-vendor identifier(Null terminated ASCII string)
+ */
+struct scmi_revision_info {
+ u16 major_ver;
+ u16 minor_ver;
+ u8 num_protocols;
+ u8 num_agents;
+ u32 impl_ver;
+ char vendor_id[SCMI_MAX_STR_SIZE];
+ char sub_vendor_id[SCMI_MAX_STR_SIZE];
+};
+
+struct scmi_clock_info {
+ char name[SCMI_MAX_STR_SIZE];
+ bool rate_discrete;
+ union {
+ struct {
+ int num_rates;
+ u64 rates[SCMI_MAX_NUM_RATES];
+ } list;
+ struct {
+ u64 min_rate;
+ u64 max_rate;
+ u64 step_size;
+ } range;
+ };
+};
+
+struct scmi_handle;
+struct scmi_device;
+struct scmi_protocol_handle;
+
+/**
+ * struct scmi_clk_proto_ops - represents the various operations provided
+ * by SCMI Clock Protocol
+ *
+ * @count_get: get the count of clocks provided by SCMI
+ * @info_get: get the information of the specified clock
+ * @rate_get: request the current clock rate of a clock
+ * @rate_set: set the clock rate of a clock
+ * @enable: enables the specified clock
+ * @disable: disables the specified clock
+ */
+struct scmi_clk_proto_ops {
+ int (*count_get)(const struct scmi_protocol_handle *ph);
+
+ const struct scmi_clock_info *(*info_get)
+ (const struct scmi_protocol_handle *ph, u32 clk_id);
+ int (*rate_get)(const struct scmi_protocol_handle *ph, u32 clk_id,
+ u64 *rate);
+ int (*rate_set)(const struct scmi_protocol_handle *ph, u32 clk_id,
+ u64 rate);
+ int (*enable)(const struct scmi_protocol_handle *ph, u32 clk_id);
+ int (*disable)(const struct scmi_protocol_handle *ph, u32 clk_id);
+};
+
+/**
+ * struct scmi_perf_proto_ops - represents the various operations provided
+ * by SCMI Performance Protocol
+ *
+ * @limits_set: sets limits on the performance level of a domain
+ * @limits_get: gets limits on the performance level of a domain
+ * @level_set: sets the performance level of a domain
+ * @level_get: gets the performance level of a domain
+ * @device_domain_id: gets the scmi domain id for a given device_d
+ * @transition_latency_get: gets the DVFS transition latency for a given device_d
+ * @device_opps_add: adds all the OPPs for a given device_d
+ * @freq_set: sets the frequency for a given device_d using sustained frequency
+ * to sustained performance level mapping
+ * @freq_get: gets the frequency for a given device_d using sustained frequency
+ * to sustained performance level mapping
+ * @est_power_get: gets the estimated power cost for a given performance domain
+ * at a given frequency
+ */
+struct scmi_perf_proto_ops {
+ int (*limits_set)(const struct scmi_protocol_handle *ph, u32 domain,
+ u32 max_perf, u32 min_perf);
+ int (*limits_get)(const struct scmi_protocol_handle *ph, u32 domain,
+ u32 *max_perf, u32 *min_perf);
+ int (*level_set)(const struct scmi_protocol_handle *ph, u32 domain,
+ u32 level, bool poll);
+ int (*level_get)(const struct scmi_protocol_handle *ph, u32 domain,
+ u32 *level, bool poll);
+ int (*device_domain_id)(struct device_d *dev);
+ int (*transition_latency_get)(const struct scmi_protocol_handle *ph,
+ struct device_d *dev);
+ int (*device_opps_add)(const struct scmi_protocol_handle *ph,
+ struct device_d *dev);
+ int (*freq_set)(const struct scmi_protocol_handle *ph, u32 domain,
+ unsigned long rate, bool poll);
+ int (*freq_get)(const struct scmi_protocol_handle *ph, u32 domain,
+ unsigned long *rate, bool poll);
+ int (*est_power_get)(const struct scmi_protocol_handle *ph, u32 domain,
+ unsigned long *rate, unsigned long *power);
+ bool (*fast_switch_possible)(const struct scmi_protocol_handle *ph,
+ struct device_d *dev);
+ bool (*power_scale_mw_get)(const struct scmi_protocol_handle *ph);
+};
+
+/**
+ * struct scmi_power_proto_ops - represents the various operations provided
+ * by SCMI Power Protocol
+ *
+ * @num_domains_get: get the count of power domains provided by SCMI
+ * @name_get: gets the name of a power domain
+ * @state_set: sets the power state of a power domain
+ * @state_get: gets the power state of a power domain
+ */
+struct scmi_power_proto_ops {
+ int (*num_domains_get)(const struct scmi_protocol_handle *ph);
+ char *(*name_get)(const struct scmi_protocol_handle *ph, u32 domain);
+#define SCMI_POWER_STATE_TYPE_SHIFT 30
+#define SCMI_POWER_STATE_ID_MASK (BIT(28) - 1)
+#define SCMI_POWER_STATE_PARAM(type, id) \
+ ((((type) & BIT(0)) << SCMI_POWER_STATE_TYPE_SHIFT) | \
+ ((id) & SCMI_POWER_STATE_ID_MASK))
+#define SCMI_POWER_STATE_GENERIC_ON SCMI_POWER_STATE_PARAM(0, 0)
+#define SCMI_POWER_STATE_GENERIC_OFF SCMI_POWER_STATE_PARAM(1, 0)
+ int (*state_set)(const struct scmi_protocol_handle *ph, u32 domain,
+ u32 state);
+ int (*state_get)(const struct scmi_protocol_handle *ph, u32 domain,
+ u32 *state);
+};
+
+/**
+ * scmi_sensor_reading - represent a timestamped read
+ *
+ * Used by @reading_get_timestamped method.
+ *
+ * @value: The signed value sensor read.
+ * @timestamp: An unsigned timestamp for the sensor read, as provided by
+ * SCMI platform. Set to zero when not available.
+ */
+struct scmi_sensor_reading {
+ long long value;
+ unsigned long long timestamp;
+};
+
+/**
+ * scmi_range_attrs - specifies a sensor or axis values' range
+ * @min_range: The minimum value which can be represented by the sensor/axis.
+ * @max_range: The maximum value which can be represented by the sensor/axis.
+ */
+struct scmi_range_attrs {
+ long long min_range;
+ long long max_range;
+};
+
+/**
+ * scmi_sensor_axis_info - describes one sensor axes
+ * @id: The axes ID.
+ * @type: Axes type. Chosen amongst one of @enum scmi_sensor_class.
+ * @scale: Power-of-10 multiplier applied to the axis unit.
+ * @name: NULL-terminated string representing axes name as advertised by
+ * SCMI platform.
+ * @extended_attrs: Flag to indicate the presence of additional extended
+ * attributes for this axes.
+ * @resolution: Extended attribute representing the resolution of the axes.
+ * Set to 0 if not reported by this axes.
+ * @exponent: Extended attribute representing the power-of-10 multiplier that
+ * is applied to the resolution field. Set to 0 if not reported by
+ * this axes.
+ * @attrs: Extended attributes representing minimum and maximum values
+ * measurable by this axes. Set to 0 if not reported by this sensor.
+ */
+struct scmi_sensor_axis_info {
+ unsigned int id;
+ unsigned int type;
+ int scale;
+ char name[SCMI_MAX_STR_SIZE];
+ bool extended_attrs;
+ unsigned int resolution;
+ int exponent;
+ struct scmi_range_attrs attrs;
+};
+
+/**
+ * scmi_sensor_intervals_info - describes number and type of available update
+ * intervals
+ * @segmented: Flag for segmented intervals' representation. When True there
+ * will be exactly 3 intervals in @desc, with each entry
+ * representing a member of a segment in this order:
+ * {lowest update interval, highest update interval, step size}
+ * @count: Number of intervals described in @desc.
+ * @desc: Array of @count interval descriptor bitmask represented as detailed in
+ * the SCMI specification: it can be accessed using the accompanying
+ * macros.
+ * @prealloc_pool: A minimal preallocated pool of desc entries used to avoid
+ * lesser-than-64-bytes dynamic allocation for small @count
+ * values.
+ */
+struct scmi_sensor_intervals_info {
+ bool segmented;
+ unsigned int count;
+#define SCMI_SENS_INTVL_SEGMENT_LOW 0
+#define SCMI_SENS_INTVL_SEGMENT_HIGH 1
+#define SCMI_SENS_INTVL_SEGMENT_STEP 2
+ unsigned int *desc;
+#define SCMI_SENS_INTVL_GET_SECS(x) FIELD_GET(GENMASK(20, 5), (x))
+#define SCMI_SENS_INTVL_GET_EXP(x) \
+ ({ \
+ int __signed_exp = FIELD_GET(GENMASK(4, 0), (x)); \
+ \
+ if (__signed_exp & BIT(4)) \
+ __signed_exp |= GENMASK(31, 5); \
+ __signed_exp; \
+ })
+#define SCMI_MAX_PREALLOC_POOL 16
+ unsigned int prealloc_pool[SCMI_MAX_PREALLOC_POOL];
+};
+
+/**
+ * struct scmi_sensor_info - represents information related to one of the
+ * available sensors.
+ * @id: Sensor ID.
+ * @type: Sensor type. Chosen amongst one of @enum scmi_sensor_class.
+ * @scale: Power-of-10 multiplier applied to the sensor unit.
+ * @num_trip_points: Number of maximum configurable trip points.
+ * @async: Flag for asynchronous read support.
+ * @update: Flag for continuouos update notification support.
+ * @timestamped: Flag for timestamped read support.
+ * @tstamp_scale: Power-of-10 multiplier applied to the sensor timestamps to
+ * represent it in seconds.
+ * @num_axis: Number of supported axis if any. Reported as 0 for scalar sensors.
+ * @axis: Pointer to an array of @num_axis descriptors.
+ * @intervals: Descriptor of available update intervals.
+ * @sensor_config: A bitmask reporting the current sensor configuration as
+ * detailed in the SCMI specification: it can accessed and
+ * modified through the accompanying macros.
+ * @name: NULL-terminated string representing sensor name as advertised by
+ * SCMI platform.
+ * @extended_scalar_attrs: Flag to indicate the presence of additional extended
+ * attributes for this sensor.
+ * @sensor_power: Extended attribute representing the average power
+ * consumed by the sensor in microwatts (uW) when it is active.
+ * Reported here only for scalar sensors.
+ * Set to 0 if not reported by this sensor.
+ * @resolution: Extended attribute representing the resolution of the sensor.
+ * Reported here only for scalar sensors.
+ * Set to 0 if not reported by this sensor.
+ * @exponent: Extended attribute representing the power-of-10 multiplier that is
+ * applied to the resolution field.
+ * Reported here only for scalar sensors.
+ * Set to 0 if not reported by this sensor.
+ * @scalar_attrs: Extended attributes representing minimum and maximum
+ * measurable values by this sensor.
+ * Reported here only for scalar sensors.
+ * Set to 0 if not reported by this sensor.
+ */
+struct scmi_sensor_info {
+ unsigned int id;
+ unsigned int type;
+ int scale;
+ unsigned int num_trip_points;
+ bool async;
+ bool update;
+ bool timestamped;
+ int tstamp_scale;
+ unsigned int num_axis;
+ struct scmi_sensor_axis_info *axis;
+ struct scmi_sensor_intervals_info intervals;
+ unsigned int sensor_config;
+#define SCMI_SENS_CFG_UPDATE_SECS_MASK GENMASK(31, 16)
+#define SCMI_SENS_CFG_GET_UPDATE_SECS(x) \
+ FIELD_GET(SCMI_SENS_CFG_UPDATE_SECS_MASK, (x))
+
+#define SCMI_SENS_CFG_UPDATE_EXP_MASK GENMASK(15, 11)
+#define SCMI_SENS_CFG_GET_UPDATE_EXP(x) \
+ ({ \
+ int __signed_exp = \
+ FIELD_GET(SCMI_SENS_CFG_UPDATE_EXP_MASK, (x)); \
+ \
+ if (__signed_exp & BIT(4)) \
+ __signed_exp |= GENMASK(31, 5); \
+ __signed_exp; \
+ })
+
+#define SCMI_SENS_CFG_ROUND_MASK GENMASK(10, 9)
+#define SCMI_SENS_CFG_ROUND_AUTO 2
+#define SCMI_SENS_CFG_ROUND_UP 1
+#define SCMI_SENS_CFG_ROUND_DOWN 0
+
+#define SCMI_SENS_CFG_TSTAMP_ENABLED_MASK BIT(1)
+#define SCMI_SENS_CFG_TSTAMP_ENABLE 1
+#define SCMI_SENS_CFG_TSTAMP_DISABLE 0
+#define SCMI_SENS_CFG_IS_TSTAMP_ENABLED(x) \
+ FIELD_GET(SCMI_SENS_CFG_TSTAMP_ENABLED_MASK, (x))
+
+#define SCMI_SENS_CFG_SENSOR_ENABLED_MASK BIT(0)
+#define SCMI_SENS_CFG_SENSOR_ENABLE 1
+#define SCMI_SENS_CFG_SENSOR_DISABLE 0
+ char name[SCMI_MAX_STR_SIZE];
+#define SCMI_SENS_CFG_IS_ENABLED(x) FIELD_GET(BIT(0), (x))
+ bool extended_scalar_attrs;
+ unsigned int sensor_power;
+ unsigned int resolution;
+ int exponent;
+ struct scmi_range_attrs scalar_attrs;
+};
+
+/*
+ * Partial list from Distributed Management Task Force (DMTF) specification:
+ * DSP0249 (Platform Level Data Model specification)
+ */
+enum scmi_sensor_class {
+ NONE = 0x0,
+ UNSPEC = 0x1,
+ TEMPERATURE_C = 0x2,
+ TEMPERATURE_F = 0x3,
+ TEMPERATURE_K = 0x4,
+ VOLTAGE = 0x5,
+ CURRENT = 0x6,
+ POWER = 0x7,
+ ENERGY = 0x8,
+ CHARGE = 0x9,
+ VOLTAMPERE = 0xA,
+ NITS = 0xB,
+ LUMENS = 0xC,
+ LUX = 0xD,
+ CANDELAS = 0xE,
+ KPA = 0xF,
+ PSI = 0x10,
+ NEWTON = 0x11,
+ CFM = 0x12,
+ RPM = 0x13,
+ HERTZ = 0x14,
+ SECS = 0x15,
+ MINS = 0x16,
+ HOURS = 0x17,
+ DAYS = 0x18,
+ WEEKS = 0x19,
+ MILS = 0x1A,
+ INCHES = 0x1B,
+ FEET = 0x1C,
+ CUBIC_INCHES = 0x1D,
+ CUBIC_FEET = 0x1E,
+ METERS = 0x1F,
+ CUBIC_CM = 0x20,
+ CUBIC_METERS = 0x21,
+ LITERS = 0x22,
+ FLUID_OUNCES = 0x23,
+ RADIANS = 0x24,
+ STERADIANS = 0x25,
+ REVOLUTIONS = 0x26,
+ CYCLES = 0x27,
+ GRAVITIES = 0x28,
+ OUNCES = 0x29,
+ POUNDS = 0x2A,
+ FOOT_POUNDS = 0x2B,
+ OUNCE_INCHES = 0x2C,
+ GAUSS = 0x2D,
+ GILBERTS = 0x2E,
+ HENRIES = 0x2F,
+ FARADS = 0x30,
+ OHMS = 0x31,
+ SIEMENS = 0x32,
+ MOLES = 0x33,
+ BECQUERELS = 0x34,
+ PPM = 0x35,
+ DECIBELS = 0x36,
+ DBA = 0x37,
+ DBC = 0x38,
+ GRAYS = 0x39,
+ SIEVERTS = 0x3A,
+ COLOR_TEMP_K = 0x3B,
+ BITS = 0x3C,
+ BYTES = 0x3D,
+ WORDS = 0x3E,
+ DWORDS = 0x3F,
+ QWORDS = 0x40,
+ PERCENTAGE = 0x41,
+ PASCALS = 0x42,
+ COUNTS = 0x43,
+ GRAMS = 0x44,
+ NEWTON_METERS = 0x45,
+ HITS = 0x46,
+ MISSES = 0x47,
+ RETRIES = 0x48,
+ OVERRUNS = 0x49,
+ UNDERRUNS = 0x4A,
+ COLLISIONS = 0x4B,
+ PACKETS = 0x4C,
+ MESSAGES = 0x4D,
+ CHARS = 0x4E,
+ ERRORS = 0x4F,
+ CORRECTED_ERRS = 0x50,
+ UNCORRECTABLE_ERRS = 0x51,
+ SQ_MILS = 0x52,
+ SQ_INCHES = 0x53,
+ SQ_FEET = 0x54,
+ SQ_CM = 0x55,
+ SQ_METERS = 0x56,
+ RADIANS_SEC = 0x57,
+ BPM = 0x58,
+ METERS_SEC_SQUARED = 0x59,
+ METERS_SEC = 0x5A,
+ CUBIC_METERS_SEC = 0x5B,
+ MM_MERCURY = 0x5C,
+ RADIANS_SEC_SQUARED = 0x5D,
+ OEM_UNIT = 0xFF
+};
+
+/**
+ * struct scmi_sensor_proto_ops - represents the various operations provided
+ * by SCMI Sensor Protocol
+ *
+ * @count_get: get the count of sensors provided by SCMI
+ * @info_get: get the information of the specified sensor
+ * @trip_point_config: selects and configures a trip-point of interest
+ * @reading_get: gets the current value of the sensor
+ * @reading_get_timestamped: gets the current value and timestamp, when
+ * available, of the sensor. (as of v3.0 spec)
+ * Supports multi-axis sensors for sensors which
+ * supports it and if the @reading array size of
+ * @count entry equals the sensor num_axis
+ * @config_get: Get sensor current configuration
+ * @config_set: Set sensor current configuration
+ */
+struct scmi_sensor_proto_ops {
+ int (*count_get)(const struct scmi_protocol_handle *ph);
+ const struct scmi_sensor_info *(*info_get)
+ (const struct scmi_protocol_handle *ph, u32 sensor_id);
+ int (*trip_point_config)(const struct scmi_protocol_handle *ph,
+ u32 sensor_id, u8 trip_id, u64 trip_value);
+ int (*reading_get)(const struct scmi_protocol_handle *ph, u32 sensor_id,
+ u64 *value);
+ int (*reading_get_timestamped)(const struct scmi_protocol_handle *ph,
+ u32 sensor_id, u8 count,
+ struct scmi_sensor_reading *readings);
+ int (*config_get)(const struct scmi_protocol_handle *ph,
+ u32 sensor_id, u32 *sensor_config);
+ int (*config_set)(const struct scmi_protocol_handle *ph,
+ u32 sensor_id, u32 sensor_config);
+};
+
+/**
+ * struct scmi_reset_proto_ops - represents the various operations provided
+ * by SCMI Reset Protocol
+ *
+ * @num_domains_get: get the count of reset domains provided by SCMI
+ * @name_get: gets the name of a reset domain
+ * @latency_get: gets the reset latency for the specified reset domain
+ * @reset: resets the specified reset domain
+ * @assert: explicitly assert reset signal of the specified reset domain
+ * @deassert: explicitly deassert reset signal of the specified reset domain
+ */
+struct scmi_reset_proto_ops {
+ int (*num_domains_get)(const struct scmi_protocol_handle *ph);
+ char *(*name_get)(const struct scmi_protocol_handle *ph, u32 domain);
+ int (*latency_get)(const struct scmi_protocol_handle *ph, u32 domain);
+ int (*reset)(const struct scmi_protocol_handle *ph, u32 domain);
+ int (*assert)(const struct scmi_protocol_handle *ph, u32 domain);
+ int (*deassert)(const struct scmi_protocol_handle *ph, u32 domain);
+};
+
+/**
+ * struct scmi_voltage_info - describe one available SCMI Voltage Domain
+ *
+ * @id: the domain ID as advertised by the platform
+ * @segmented: defines the layout of the entries of array @levels_uv.
+ * - when True the entries are to be interpreted as triplets,
+ * each defining a segment representing a range of equally
+ * space voltages: <lowest_volts>, <highest_volt>, <step_uV>
+ * - when False the entries simply represent a single discrete
+ * supported voltage level
+ * @negative_volts_allowed: True if any of the entries of @levels_uv represent
+ * a negative voltage.
+ * @attributes: represents Voltage Domain advertised attributes
+ * @name: name assigned to the Voltage Domain by platform
+ * @num_levels: number of total entries in @levels_uv.
+ * @levels_uv: array of entries describing the available voltage levels for
+ * this domain.
+ */
+struct scmi_voltage_info {
+ unsigned int id;
+ bool segmented;
+ bool negative_volts_allowed;
+ unsigned int attributes;
+ char name[SCMI_MAX_STR_SIZE];
+ unsigned int num_levels;
+#define SCMI_VOLTAGE_SEGMENT_LOW 0
+#define SCMI_VOLTAGE_SEGMENT_HIGH 1
+#define SCMI_VOLTAGE_SEGMENT_STEP 2
+ int *levels_uv;
+};
+
+/**
+ * struct scmi_voltage_proto_ops - represents the various operations provided
+ * by SCMI Voltage Protocol
+ *
+ * @num_domains_get: get the count of voltage domains provided by SCMI
+ * @info_get: get the information of the specified domain
+ * @config_set: set the config for the specified domain
+ * @config_get: get the config of the specified domain
+ * @level_set: set the voltage level for the specified domain
+ * @level_get: get the voltage level of the specified domain
+ */
+struct scmi_voltage_proto_ops {
+ int (*num_domains_get)(const struct scmi_protocol_handle *ph);
+ const struct scmi_voltage_info __must_check *(*info_get)
+ (const struct scmi_protocol_handle *ph, u32 domain_id);
+ int (*config_set)(const struct scmi_protocol_handle *ph, u32 domain_id,
+ u32 config);
+#define SCMI_VOLTAGE_ARCH_STATE_OFF 0x0
+#define SCMI_VOLTAGE_ARCH_STATE_ON 0x7
+ int (*config_get)(const struct scmi_protocol_handle *ph, u32 domain_id,
+ u32 *config);
+ int (*level_set)(const struct scmi_protocol_handle *ph, u32 domain_id,
+ u32 flags, s32 volt_uV);
+ int (*level_get)(const struct scmi_protocol_handle *ph, u32 domain_id,
+ s32 *volt_uV);
+};
+
+/**
+ * struct scmi_handle - Handle returned to ARM SCMI clients for usage.
+ *
+ * @dev: pointer to the SCMI device_d
+ * @version: pointer to the structure containing SCMI version information
+ * @protocol_get: method to acquire a protocol and get specific
+ * operations and a dedicated protocol handler
+ */
+struct scmi_handle {
+ struct device_d *dev;
+ struct scmi_revision_info *version;
+
+ const void __must_check *
+ (*protocol_get)(struct scmi_device *sdev, u8 proto,
+ struct scmi_protocol_handle **ph);
+};
+
+enum scmi_std_protocol {
+ SCMI_PROTOCOL_BASE = 0x10,
+ SCMI_PROTOCOL_POWER = 0x11,
+ SCMI_PROTOCOL_SYSTEM = 0x12,
+ SCMI_PROTOCOL_PERF = 0x13,
+ SCMI_PROTOCOL_CLOCK = 0x14,
+ SCMI_PROTOCOL_SENSOR = 0x15,
+ SCMI_PROTOCOL_RESET = 0x16,
+ SCMI_PROTOCOL_VOLTAGE = 0x17,
+};
+
+enum scmi_system_events {
+ SCMI_SYSTEM_SHUTDOWN,
+ SCMI_SYSTEM_COLDRESET,
+ SCMI_SYSTEM_WARMRESET,
+ SCMI_SYSTEM_POWERUP,
+ SCMI_SYSTEM_SUSPEND,
+ SCMI_SYSTEM_MAX
+};
+
+struct scmi_device {
+ u8 protocol_id;
+ const char *name;
+ struct device_d dev;
+ struct scmi_handle *handle;
+};
+
+#define to_scmi_dev(d) container_of(d, struct scmi_device, dev)
+
+struct scmi_device *
+scmi_device_alloc(struct device_node *np, struct device_d *parent, int protocol,
+ const char *name);
+void scmi_device_destroy(struct scmi_device *scmi_dev);
+
+struct scmi_device_id {
+ u8 protocol_id;
+ const char *name;
+};
+
+struct scmi_driver {
+ const char *name;
+ int (*probe)(struct scmi_device *sdev);
+ void (*remove)(struct scmi_device *sdev);
+ const struct scmi_device_id *id_table;
+
+ struct driver_d driver;
+};
+
+#define to_scmi_driver(d) container_of(d, struct scmi_driver, driver)
+
+#if IS_REACHABLE(CONFIG_ARM_SCMI_PROTOCOL)
+int scmi_driver_register(struct scmi_driver *driver);
+#else
+static inline int
+scmi_driver_register(struct scmi_driver *driver)
+{
+ return -EINVAL;
+}
+
+#endif /* CONFIG_ARM_SCMI_PROTOCOL */
+
+#define scmi_register(driver) \
+ scmi_driver_register(driver)
+
+/**
+ * coredevice_scmi_driver() - Helper macro for registering a scmi driver
+ * @__scmi_driver: scmi_driver structure
+ *
+ * Helper macro for scmi drivers to set up proper module init / exit
+ * functions. Replaces module_init() and module_exit() and keeps people from
+ * printing pointless things to the kernel log when their driver is loaded.
+ */
+#define coredevice_scmi_driver(__scmi_driver) \
+ register_driver_macro(coredevice,scmi,__scmi_driver)
+
+#define core_scmi_driver(__scmi_driver) \
+ register_driver_macro(core,scmi,__scmi_driver)
+
+/**
+ * module_scmi_protocol() - Helper macro for registering a scmi protocol
+ * @__scmi_protocol: scmi_protocol structure
+ *
+ * Helper macro for scmi drivers to set up proper module init / exit
+ * functions. Replaces module_init() and module_exit() and keeps people from
+ * printing pointless things to the kernel log when their driver is loaded.
+ */
+#define module_scmi_protocol(__scmi_protocol) \
+ module_driver(__scmi_protocol, \
+ scmi_protocol_register, scmi_protocol_unregister)
+
+struct scmi_protocol;
+int scmi_protocol_register(const struct scmi_protocol *proto);
+void scmi_protocol_unregister(const struct scmi_protocol *proto);
+
+#endif /* _LINUX_SCMI_PROTOCOL_H */
diff --git a/include/linux/slab.h b/include/linux/slab.h
index 348770a3a28c..eb14c58e340f 100644
--- a/include/linux/slab.h
+++ b/include/linux/slab.h
@@ -103,4 +103,7 @@ static inline void *kcalloc(size_t n, size_t size, gfp_t flags)
return calloc(n, size);
}
+#define kstrdup_const(str, flags) strdup(str)
+#define kfree_const(ptr) kfree((void *)ptr)
+
#endif /* _LINUX_SLAB_H */
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 13/24] reset: add SCMI support
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
` (11 preceding siblings ...)
2022-02-20 12:47 ` [PATCH 12/24] firmware: import Linux v5.13 SCMI support Ahmad Fatoum
@ 2022-02-20 12:47 ` Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 14/24] clk: add SCMI clock driver Ahmad Fatoum
` (11 subsequent siblings)
24 siblings, 0 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium, Ahmad Fatoum
Import the Linux v5.13 state of the SCMI reset protocol driver.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
drivers/firmware/arm_scmi/Makefile | 2 +-
drivers/firmware/arm_scmi/common.h | 1 +
drivers/firmware/arm_scmi/driver.c | 2 +
drivers/firmware/arm_scmi/reset.c | 229 +++++++++++++++++++++++++++++
drivers/reset/Kconfig | 11 ++
drivers/reset/Makefile | 1 +
drivers/reset/reset-scmi.c | 130 ++++++++++++++++
7 files changed, 375 insertions(+), 1 deletion(-)
create mode 100644 drivers/firmware/arm_scmi/reset.c
create mode 100644 drivers/reset/reset-scmi.c
diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index 8bb25484b9b2..510c0372a096 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -3,7 +3,7 @@ scmi-bus-y = bus.o
scmi-driver-y = driver.o
scmi-transport-y = shmem.o
scmi-transport-$(CONFIG_ARM_SMCCC) += smc.o
-scmi-protocols-y = base.o
+scmi-protocols-y = base.o reset.o
scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \
$(scmi-transport-y)
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index 9e48c0e7747d..f1f6cdae5af2 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -232,6 +232,7 @@ void __exit scmi_bus_exit(void);
#define DECLARE_SCMI_REGISTER(func) \
int __init scmi_##func##_register(void);
DECLARE_SCMI_REGISTER(base);
+DECLARE_SCMI_REGISTER(reset);
#define DEFINE_SCMI_PROTOCOL_REGISTER(name, proto) \
static const struct scmi_protocol *__this_proto = &(proto); \
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index c9819e7c2aa3..d862e4de8afb 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -1259,6 +1259,8 @@ static int __init scmi_bus_driver_init(void)
scmi_base_register();
+ scmi_reset_register();
+
return 0;
}
pure_initcall(scmi_bus_driver_init);
diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c
new file mode 100644
index 000000000000..94baab99e1e4
--- /dev/null
+++ b/drivers/firmware/arm_scmi/reset.c
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface (SCMI) Reset Protocol
+ *
+ * Copyright (C) 2019-2021 ARM Ltd.
+ */
+
+#define pr_fmt(fmt) "SCMI RESET - " fmt
+
+#include <common.h>
+#include <linux/scmi_protocol.h>
+
+#include "common.h"
+
+enum scmi_reset_protocol_cmd {
+ RESET_DOMAIN_ATTRIBUTES = 0x3,
+ RESET = 0x4,
+ RESET_NOTIFY = 0x5,
+};
+
+#define NUM_RESET_DOMAIN_MASK 0xffff
+
+struct scmi_msg_resp_reset_domain_attributes {
+ __le32 attributes;
+#define SUPPORTS_ASYNC_RESET(x) ((x) & BIT(31))
+#define SUPPORTS_NOTIFY_RESET(x) ((x) & BIT(30))
+ __le32 latency;
+ u8 name[SCMI_MAX_STR_SIZE];
+};
+
+struct scmi_msg_reset_domain_reset {
+ __le32 domain_id;
+ __le32 flags;
+#define AUTONOMOUS_RESET BIT(0)
+#define EXPLICIT_RESET_ASSERT BIT(1)
+#define ASYNCHRONOUS_RESET BIT(2)
+ __le32 reset_state;
+#define ARCH_COLD_RESET 0
+};
+
+struct reset_dom_info {
+ bool async_reset;
+ u32 latency_us;
+ char name[SCMI_MAX_STR_SIZE];
+};
+
+struct scmi_reset_info {
+ u32 version;
+ int num_domains;
+ struct reset_dom_info *dom_info;
+};
+
+static int scmi_reset_attributes_get(const struct scmi_protocol_handle *ph,
+ struct scmi_reset_info *pi)
+{
+ int ret;
+ struct scmi_xfer *t;
+ u32 attr;
+
+ ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES,
+ 0, sizeof(attr), &t);
+ if (ret)
+ return ret;
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret) {
+ attr = get_unaligned_le32(t->rx.buf);
+ pi->num_domains = attr & NUM_RESET_DOMAIN_MASK;
+ }
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
+static int
+scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,
+ u32 domain, struct reset_dom_info *dom_info)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_resp_reset_domain_attributes *attr;
+
+ ret = ph->xops->xfer_get_init(ph, RESET_DOMAIN_ATTRIBUTES,
+ sizeof(domain), sizeof(*attr), &t);
+ if (ret)
+ return ret;
+
+ put_unaligned_le32(domain, t->tx.buf);
+ attr = t->rx.buf;
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret) {
+ u32 attributes = le32_to_cpu(attr->attributes);
+
+ dom_info->async_reset = SUPPORTS_ASYNC_RESET(attributes);
+ dom_info->latency_us = le32_to_cpu(attr->latency);
+ if (dom_info->latency_us == U32_MAX)
+ dom_info->latency_us = 0;
+ strlcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
+ }
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
+static int scmi_reset_num_domains_get(const struct scmi_protocol_handle *ph)
+{
+ struct scmi_reset_info *pi = ph->get_priv(ph);
+
+ return pi->num_domains;
+}
+
+static char *scmi_reset_name_get(const struct scmi_protocol_handle *ph,
+ u32 domain)
+{
+ struct scmi_reset_info *pi = ph->get_priv(ph);
+
+ struct reset_dom_info *dom = pi->dom_info + domain;
+
+ return dom->name;
+}
+
+static int scmi_reset_latency_get(const struct scmi_protocol_handle *ph,
+ u32 domain)
+{
+ struct scmi_reset_info *pi = ph->get_priv(ph);
+ struct reset_dom_info *dom = pi->dom_info + domain;
+
+ return dom->latency_us;
+}
+
+static int scmi_domain_reset(const struct scmi_protocol_handle *ph, u32 domain,
+ u32 flags, u32 state)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_reset_domain_reset *dom;
+ struct scmi_reset_info *pi = ph->get_priv(ph);
+ struct reset_dom_info *rdom = pi->dom_info + domain;
+
+ if (rdom->async_reset)
+ flags |= ASYNCHRONOUS_RESET;
+
+ ret = ph->xops->xfer_get_init(ph, RESET, sizeof(*dom), 0, &t);
+ if (ret)
+ return ret;
+
+ dom = t->tx.buf;
+ dom->domain_id = cpu_to_le32(domain);
+ dom->flags = cpu_to_le32(flags);
+ dom->reset_state = cpu_to_le32(state);
+
+ if (rdom->async_reset)
+ ret = ph->xops->do_xfer_with_response(ph, t);
+ else
+ ret = ph->xops->do_xfer(ph, t);
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
+static int scmi_reset_domain_reset(const struct scmi_protocol_handle *ph,
+ u32 domain)
+{
+ return scmi_domain_reset(ph, domain, AUTONOMOUS_RESET,
+ ARCH_COLD_RESET);
+}
+
+static int
+scmi_reset_domain_assert(const struct scmi_protocol_handle *ph, u32 domain)
+{
+ return scmi_domain_reset(ph, domain, EXPLICIT_RESET_ASSERT,
+ ARCH_COLD_RESET);
+}
+
+static int
+scmi_reset_domain_deassert(const struct scmi_protocol_handle *ph, u32 domain)
+{
+ return scmi_domain_reset(ph, domain, 0, ARCH_COLD_RESET);
+}
+
+static const struct scmi_reset_proto_ops reset_proto_ops = {
+ .num_domains_get = scmi_reset_num_domains_get,
+ .name_get = scmi_reset_name_get,
+ .latency_get = scmi_reset_latency_get,
+ .reset = scmi_reset_domain_reset,
+ .assert = scmi_reset_domain_assert,
+ .deassert = scmi_reset_domain_deassert,
+};
+
+static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph)
+{
+ int domain;
+ u32 version;
+ struct scmi_reset_info *pinfo;
+
+ ph->xops->version_get(ph, &version);
+
+ dev_dbg(ph->dev, "Reset Version %d.%d\n",
+ PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+
+ pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
+ if (!pinfo)
+ return -ENOMEM;
+
+ scmi_reset_attributes_get(ph, pinfo);
+
+ pinfo->dom_info = kcalloc(pinfo->num_domains,
+ sizeof(*pinfo->dom_info), GFP_KERNEL);
+ if (!pinfo->dom_info)
+ return -ENOMEM;
+
+ for (domain = 0; domain < pinfo->num_domains; domain++) {
+ struct reset_dom_info *dom = pinfo->dom_info + domain;
+
+ scmi_reset_domain_attributes_get(ph, domain, dom);
+ }
+
+ pinfo->version = version;
+ return ph->set_priv(ph, pinfo);
+}
+
+static const struct scmi_protocol scmi_reset = {
+ .id = SCMI_PROTOCOL_RESET,
+ .instance_init = &scmi_reset_protocol_init,
+ .ops = &reset_proto_ops,
+};
+
+DEFINE_SCMI_PROTOCOL_REGISTER(reset, scmi_reset)
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index 21a6e1a50d8f..913b309eac3a 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -45,4 +45,15 @@ config RESET_STARFIVE
help
This enables the reset controller driver for the StarFive JH7100.
+config RESET_SCMI
+ tristate "Reset driver controlled via ARM SCMI interface"
+ depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
+ default ARM_SCMI_PROTOCOL
+ help
+ This driver provides support for reset signal/domains that are
+ controlled by firmware that implements the SCMI interface.
+
+ This driver uses SCMI Message Protocol to interact with the
+ firmware controlling all the reset signals.
+
endif
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 6d0cd51f86ce..b1668433d76a 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o
obj-$(CONFIG_RESET_IMX7) += reset-imx7.o
obj-$(CONFIG_RESET_STARFIVE) += reset-starfive-vic.o
+obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
diff --git a/drivers/reset/reset-scmi.c b/drivers/reset/reset-scmi.c
new file mode 100644
index 000000000000..c33bbc5c8afe
--- /dev/null
+++ b/drivers/reset/reset-scmi.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ARM System Control and Management Interface (ARM SCMI) reset driver
+ *
+ * Copyright (C) 2019-2021 ARM Ltd.
+ */
+
+#include <common.h>
+#include <of.h>
+#include <driver.h>
+#include <linux/reset-controller.h>
+#include <linux/scmi_protocol.h>
+
+static const struct scmi_reset_proto_ops *reset_ops;
+
+/**
+ * struct scmi_reset_data - reset controller information structure
+ * @rcdev: reset controller entity
+ * @ph: ARM SCMI protocol handle used for communication with system controller
+ */
+struct scmi_reset_data {
+ struct reset_controller_dev rcdev;
+ const struct scmi_protocol_handle *ph;
+};
+
+#define to_scmi_reset_data(p) container_of((p), struct scmi_reset_data, rcdev)
+#define to_scmi_handle(p) (to_scmi_reset_data(p)->ph)
+
+/**
+ * scmi_reset_assert() - assert device reset
+ * @rcdev: reset controller entity
+ * @id: ID of the reset to be asserted
+ *
+ * This function implements the reset driver op to assert a device's reset
+ * using the ARM SCMI protocol.
+ *
+ * Return: 0 for successful request, else a corresponding error value
+ */
+static int
+scmi_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ const struct scmi_protocol_handle *ph = to_scmi_handle(rcdev);
+
+ return reset_ops->assert(ph, id);
+}
+
+/**
+ * scmi_reset_deassert() - deassert device reset
+ * @rcdev: reset controller entity
+ * @id: ID of the reset to be deasserted
+ *
+ * This function implements the reset driver op to deassert a device's reset
+ * using the ARM SCMI protocol.
+ *
+ * Return: 0 for successful request, else a corresponding error value
+ */
+static int
+scmi_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ const struct scmi_protocol_handle *ph = to_scmi_handle(rcdev);
+
+ return reset_ops->deassert(ph, id);
+}
+
+/**
+ * scmi_reset_reset() - reset the device
+ * @rcdev: reset controller entity
+ * @id: ID of the reset signal to be reset(assert + deassert)
+ *
+ * This function implements the reset driver op to trigger a device's
+ * reset signal using the ARM SCMI protocol.
+ *
+ * Return: 0 for successful request, else a corresponding error value
+ */
+static int
+scmi_reset_reset(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ const struct scmi_protocol_handle *ph = to_scmi_handle(rcdev);
+
+ return reset_ops->reset(ph, id);
+}
+
+static const struct reset_control_ops scmi_reset_ops = {
+ .assert = scmi_reset_assert,
+ .deassert = scmi_reset_deassert,
+ .reset = scmi_reset_reset,
+};
+
+static int scmi_reset_probe(struct scmi_device *sdev)
+{
+ struct scmi_reset_data *data;
+ struct device_d *dev = &sdev->dev;
+ struct device_node *np = dev->device_node;
+ const struct scmi_handle *handle = sdev->handle;
+ struct scmi_protocol_handle *ph;
+
+ if (!handle)
+ return -ENODEV;
+
+ reset_ops = handle->protocol_get(sdev, SCMI_PROTOCOL_RESET, &ph);
+ if (IS_ERR(reset_ops))
+ return PTR_ERR(reset_ops);
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->rcdev.ops = &scmi_reset_ops;
+ data->rcdev.of_node = np;
+ data->rcdev.nr_resets = reset_ops->num_domains_get(ph);
+ data->ph = ph;
+
+ return reset_controller_register(&data->rcdev);
+}
+
+static const struct scmi_device_id scmi_id_table[] = {
+ { SCMI_PROTOCOL_RESET, "reset" },
+ { },
+};
+
+static struct scmi_driver scmi_reset_driver = {
+ .name = "scmi-reset",
+ .probe = scmi_reset_probe,
+ .id_table = scmi_id_table,
+};
+core_scmi_driver(scmi_reset_driver);
+
+MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
+MODULE_DESCRIPTION("ARM SCMI reset controller driver");
+MODULE_LICENSE("GPL v2");
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 14/24] clk: add SCMI clock driver
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
` (12 preceding siblings ...)
2022-02-20 12:47 ` [PATCH 13/24] reset: add " Ahmad Fatoum
@ 2022-02-20 12:47 ` Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 15/24] regulator: add SCMI regulator driver Ahmad Fatoum
` (10 subsequent siblings)
24 siblings, 0 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium, Ahmad Fatoum
Import the Linux v5.13 state of the SCMI clock protocol driver.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
drivers/clk/Kconfig | 11 +
drivers/clk/Makefile | 1 +
drivers/clk/clk-scmi.c | 192 +++++++++++++++
drivers/firmware/arm_scmi/Makefile | 2 +-
drivers/firmware/arm_scmi/clock.c | 374 +++++++++++++++++++++++++++++
drivers/firmware/arm_scmi/common.h | 1 +
drivers/firmware/arm_scmi/driver.c | 1 +
7 files changed, 581 insertions(+), 1 deletion(-)
create mode 100644 drivers/clk/clk-scmi.c
create mode 100644 drivers/firmware/arm_scmi/clock.c
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index e01ffe10f249..5b5acf4e0656 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -20,10 +20,21 @@ config CLK_SOCFPGA
select COMMON_CLK_OF_PROVIDER
default y if ARCH_SOCFPGA && OFDEVICE
+
config COMMON_CLK_STM32F
bool "STM32F4 and STM32F7 clock driver" if COMPILE_TEST
depends on COMMON_CLK && ARCH_STM32
help
Support for stm32f4 and stm32f7 SoC families clocks
+config COMMON_CLK_SCMI
+ tristate "Clock driver controlled via SCMI interface"
+ depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
+ help
+ This driver provides support for clocks that are controlled
+ by firmware that implements the SCMI interface.
+
+ This driver uses SCMI Message Protocol to interact with the
+ firmware providing all the clock controls.
+
source "drivers/clk/sifive/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index c60b38ff6002..ee503c1edb5f 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -26,3 +26,4 @@ obj-$(CONFIG_CLK_SIFIVE) += sifive/
obj-$(CONFIG_SOC_STARFIVE) += starfive/
obj-$(CONFIG_COMMON_CLK_STM32F) += clk-stm32f4.o
obj-$(CONFIG_MACH_RPI_COMMON) += clk-rpi.o
+obj-$(CONFIG_COMMON_CLK_SCMI) += clk-scmi.o
diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c
new file mode 100644
index 000000000000..9170dba393c4
--- /dev/null
+++ b/drivers/clk/clk-scmi.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Power Interface (SCMI) Protocol based clock driver
+ *
+ * Copyright (C) 2018-2021 ARM Ltd.
+ */
+
+#include <common.h>
+#include <linux/clk.h>
+#include <driver.h>
+#include <linux/err.h>
+#include <of.h>
+#include <linux/scmi_protocol.h>
+#include <linux/overflow.h>
+#include <linux/math64.h>
+
+static const struct scmi_clk_proto_ops *scmi_proto_clk_ops;
+
+struct scmi_clk {
+ u32 id;
+ struct clk_hw hw;
+ const struct scmi_clock_info *info;
+ const struct scmi_protocol_handle *ph;
+};
+
+#define to_scmi_clk(clk) container_of(clk, struct scmi_clk, hw)
+
+static unsigned long scmi_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ int ret;
+ u64 rate;
+ struct scmi_clk *clk = to_scmi_clk(hw);
+
+ ret = scmi_proto_clk_ops->rate_get(clk->ph, clk->id, &rate);
+ if (ret)
+ return 0;
+ return rate;
+}
+
+static long scmi_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ u64 fmin, fmax, ftmp;
+ struct scmi_clk *clk = to_scmi_clk(hw);
+
+ /*
+ * We can't figure out what rate it will be, so just return the
+ * rate back to the caller. scmi_clk_recalc_rate() will be called
+ * after the rate is set and we'll know what rate the clock is
+ * running at then.
+ */
+ if (clk->info->rate_discrete)
+ return rate;
+
+ fmin = clk->info->range.min_rate;
+ fmax = clk->info->range.max_rate;
+ if (rate <= fmin)
+ return fmin;
+ else if (rate >= fmax)
+ return fmax;
+
+ ftmp = rate - fmin;
+ ftmp += clk->info->range.step_size - 1; /* to round up */
+ do_div(ftmp, clk->info->range.step_size);
+
+ return ftmp * clk->info->range.step_size + fmin;
+}
+
+static int scmi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct scmi_clk *clk = to_scmi_clk(hw);
+
+ return scmi_proto_clk_ops->rate_set(clk->ph, clk->id, rate);
+}
+
+static int scmi_clk_enable(struct clk_hw *hw)
+{
+ struct scmi_clk *clk = to_scmi_clk(hw);
+
+ return scmi_proto_clk_ops->enable(clk->ph, clk->id);
+}
+
+static void scmi_clk_disable(struct clk_hw *hw)
+{
+ struct scmi_clk *clk = to_scmi_clk(hw);
+
+ scmi_proto_clk_ops->disable(clk->ph, clk->id);
+}
+
+static const struct clk_ops scmi_clk_ops = {
+ .recalc_rate = scmi_clk_recalc_rate,
+ .round_rate = scmi_clk_round_rate,
+ .set_rate = scmi_clk_set_rate,
+ /*
+ * Unlike Linux, we can provide enable/disable callback as everything
+ * runs in atomic context.
+ */
+ .enable = scmi_clk_enable,
+ .disable = scmi_clk_disable,
+};
+
+static int scmi_clk_ops_init(struct device_d *dev, struct scmi_clk *sclk)
+{
+ struct clk_init_data init = {
+ .flags = CLK_GET_RATE_NOCACHE,
+ .num_parents = 0,
+ .ops = &scmi_clk_ops,
+ .name = sclk->info->name,
+ };
+
+ sclk->hw.init = &init;
+ return clk_hw_register(dev, &sclk->hw);
+}
+
+static int scmi_clocks_probe(struct scmi_device *sdev)
+{
+ int idx, count, err;
+ struct clk **clks;
+ struct clk_onecell_data *clk_data;
+ struct device_d *dev = &sdev->dev;
+ struct device_node *np = dev->device_node;
+ const struct scmi_handle *handle = sdev->handle;
+ struct scmi_protocol_handle *ph;
+
+ if (!handle)
+ return -ENODEV;
+
+ scmi_proto_clk_ops =
+ handle->protocol_get(sdev, SCMI_PROTOCOL_CLOCK, &ph);
+ if (IS_ERR(scmi_proto_clk_ops))
+ return PTR_ERR(scmi_proto_clk_ops);
+
+ count = scmi_proto_clk_ops->count_get(ph);
+ if (count < 0) {
+ dev_err(dev, "%pOFn: invalid clock output count\n", np);
+ return -EINVAL;
+ }
+
+ clk_data = kzalloc(sizeof (*clk_data), GFP_KERNEL);
+ if (!clk_data)
+ return -ENOMEM;
+
+ clk_data->clk_num = count;
+ clks = clk_data->clks = calloc(clk_data->clk_num, sizeof(struct clk *));
+
+ for (idx = 0; idx < count; idx++) {
+ struct scmi_clk *sclk;
+
+ sclk = kzalloc(sizeof(*sclk), GFP_KERNEL);
+ if (!sclk)
+ return -ENOMEM;
+
+ sclk->info = scmi_proto_clk_ops->info_get(ph, idx);
+ if (!sclk->info) {
+ dev_dbg(dev, "invalid clock info for idx %d\n", idx);
+ continue;
+ }
+
+ sclk->id = idx;
+ sclk->ph = ph;
+
+ err = scmi_clk_ops_init(dev, sclk);
+ if (err) {
+ dev_err(dev, "failed to register clock %d\n", idx);
+ kfree(sclk);
+ clks[idx] = NULL;
+ } else {
+ dev_dbg(dev, "Registered clock:%s\n", sclk->info->name);
+ clks[idx] = &sclk->hw.clk;
+ }
+ }
+
+ return of_clk_add_provider(dev->device_node, of_clk_src_onecell_get, clk_data);
+}
+
+static const struct scmi_device_id scmi_id_table[] = {
+ { SCMI_PROTOCOL_CLOCK, "clocks" },
+ { },
+};
+
+static struct scmi_driver scmi_clocks_driver = {
+ .name = "scmi-clocks",
+ .probe = scmi_clocks_probe,
+ .id_table = scmi_id_table,
+};
+core_scmi_driver(scmi_clocks_driver);
+
+MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
+MODULE_DESCRIPTION("ARM SCMI clock driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index 510c0372a096..968a7eb7771e 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -3,7 +3,7 @@ scmi-bus-y = bus.o
scmi-driver-y = driver.o
scmi-transport-y = shmem.o
scmi-transport-$(CONFIG_ARM_SMCCC) += smc.o
-scmi-protocols-y = base.o reset.o
+scmi-protocols-y = base.o reset.o clock.o
scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \
$(scmi-transport-y)
diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
new file mode 100644
index 000000000000..8f9017206c1c
--- /dev/null
+++ b/drivers/firmware/arm_scmi/clock.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface (SCMI) Clock Protocol
+ *
+ * Copyright (C) 2018-2021 ARM Ltd.
+ */
+
+#include <common.h>
+#include <qsort.h>
+
+#include "common.h"
+
+enum scmi_clock_protocol_cmd {
+ CLOCK_ATTRIBUTES = 0x3,
+ CLOCK_DESCRIBE_RATES = 0x4,
+ CLOCK_RATE_SET = 0x5,
+ CLOCK_RATE_GET = 0x6,
+ CLOCK_CONFIG_SET = 0x7,
+};
+
+struct scmi_msg_resp_clock_protocol_attributes {
+ __le16 num_clocks;
+ u8 max_async_req;
+ u8 reserved;
+};
+
+struct scmi_msg_resp_clock_attributes {
+ __le32 attributes;
+#define CLOCK_ENABLE BIT(0)
+ u8 name[SCMI_MAX_STR_SIZE];
+};
+
+struct scmi_clock_set_config {
+ __le32 id;
+ __le32 attributes;
+};
+
+struct scmi_msg_clock_describe_rates {
+ __le32 id;
+ __le32 rate_index;
+};
+
+struct scmi_msg_resp_clock_describe_rates {
+ __le32 num_rates_flags;
+#define NUM_RETURNED(x) ((x) & 0xfff)
+#define RATE_DISCRETE(x) !((x) & BIT(12))
+#define NUM_REMAINING(x) ((x) >> 16)
+ struct {
+ __le32 value_low;
+ __le32 value_high;
+ } rate[0];
+#define RATE_TO_U64(X) \
+({ \
+ typeof(X) x = (X); \
+ le32_to_cpu((x).value_low) | (u64)le32_to_cpu((x).value_high) << 32; \
+})
+};
+
+struct scmi_clock_set_rate {
+ __le32 flags;
+#define CLOCK_SET_ASYNC BIT(0)
+#define CLOCK_SET_IGNORE_RESP BIT(1)
+#define CLOCK_SET_ROUND_UP BIT(2)
+#define CLOCK_SET_ROUND_AUTO BIT(3)
+ __le32 id;
+ __le32 value_low;
+ __le32 value_high;
+};
+
+struct clock_info {
+ u32 version;
+ int num_clocks;
+ int max_async_req;
+ unsigned cur_async_req;
+ struct scmi_clock_info *clk;
+};
+
+static int
+scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph,
+ struct clock_info *ci)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_resp_clock_protocol_attributes *attr;
+
+ ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES,
+ 0, sizeof(*attr), &t);
+ if (ret)
+ return ret;
+
+ attr = t->rx.buf;
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret) {
+ ci->num_clocks = le16_to_cpu(attr->num_clocks);
+ ci->max_async_req = attr->max_async_req;
+ }
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
+static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
+ u32 clk_id, struct scmi_clock_info *clk)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_resp_clock_attributes *attr;
+
+ ret = ph->xops->xfer_get_init(ph, CLOCK_ATTRIBUTES,
+ sizeof(clk_id), sizeof(*attr), &t);
+ if (ret)
+ return ret;
+
+ put_unaligned_le32(clk_id, t->tx.buf);
+ attr = t->rx.buf;
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret)
+ strlcpy(clk->name, attr->name, SCMI_MAX_STR_SIZE);
+ else
+ clk->name[0] = '\0';
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
+static int rate_cmp_func(const void *_r1, const void *_r2)
+{
+ const u64 *r1 = _r1, *r2 = _r2;
+
+ if (*r1 < *r2)
+ return -1;
+ else if (*r1 == *r2)
+ return 0;
+ else
+ return 1;
+}
+
+static int
+scmi_clock_describe_rates_get(const struct scmi_protocol_handle *ph, u32 clk_id,
+ struct scmi_clock_info *clk)
+{
+ u64 *rate = NULL;
+ int ret, cnt;
+ bool rate_discrete = false;
+ u32 tot_rate_cnt = 0, rates_flag;
+ u16 num_returned, num_remaining;
+ struct scmi_xfer *t;
+ struct scmi_msg_clock_describe_rates *clk_desc;
+ struct scmi_msg_resp_clock_describe_rates *rlist;
+
+ ret = ph->xops->xfer_get_init(ph, CLOCK_DESCRIBE_RATES,
+ sizeof(*clk_desc), 0, &t);
+ if (ret)
+ return ret;
+
+ clk_desc = t->tx.buf;
+ rlist = t->rx.buf;
+
+ do {
+ clk_desc->id = cpu_to_le32(clk_id);
+ /* Set the number of rates to be skipped/already read */
+ clk_desc->rate_index = cpu_to_le32(tot_rate_cnt);
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (ret)
+ goto err;
+
+ rates_flag = le32_to_cpu(rlist->num_rates_flags);
+ num_remaining = NUM_REMAINING(rates_flag);
+ rate_discrete = RATE_DISCRETE(rates_flag);
+ num_returned = NUM_RETURNED(rates_flag);
+
+ if (tot_rate_cnt + num_returned > SCMI_MAX_NUM_RATES) {
+ dev_err(ph->dev, "No. of rates > MAX_NUM_RATES");
+ break;
+ }
+
+ if (!rate_discrete) {
+ clk->range.min_rate = RATE_TO_U64(rlist->rate[0]);
+ clk->range.max_rate = RATE_TO_U64(rlist->rate[1]);
+ clk->range.step_size = RATE_TO_U64(rlist->rate[2]);
+ dev_dbg(ph->dev, "Min %llu Max %llu Step %llu Hz\n",
+ clk->range.min_rate, clk->range.max_rate,
+ clk->range.step_size);
+ break;
+ }
+
+ rate = &clk->list.rates[tot_rate_cnt];
+ for (cnt = 0; cnt < num_returned; cnt++, rate++) {
+ *rate = RATE_TO_U64(rlist->rate[cnt]);
+ dev_dbg(ph->dev, "Rate %llu Hz\n", *rate);
+ }
+
+ tot_rate_cnt += num_returned;
+
+ ph->xops->reset_rx_to_maxsz(ph, t);
+ /*
+ * check for both returned and remaining to avoid infinite
+ * loop due to buggy firmware
+ */
+ } while (num_returned && num_remaining);
+
+ if (rate_discrete && rate) {
+ clk->list.num_rates = tot_rate_cnt;
+ qsort(rate, tot_rate_cnt, sizeof(*rate), rate_cmp_func);
+ }
+
+ clk->rate_discrete = rate_discrete;
+
+err:
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
+static int
+scmi_clock_rate_get(const struct scmi_protocol_handle *ph,
+ u32 clk_id, u64 *value)
+{
+ int ret;
+ struct scmi_xfer *t;
+
+ ret = ph->xops->xfer_get_init(ph, CLOCK_RATE_GET,
+ sizeof(__le32), sizeof(u64), &t);
+ if (ret)
+ return ret;
+
+ put_unaligned_le32(clk_id, t->tx.buf);
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret)
+ *value = get_unaligned_le64(t->rx.buf);
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
+static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph,
+ u32 clk_id, u64 rate)
+{
+ int ret;
+ u32 flags = 0;
+ struct scmi_xfer *t;
+ struct scmi_clock_set_rate *cfg;
+ struct clock_info *ci = ph->get_priv(ph);
+
+ ret = ph->xops->xfer_get_init(ph, CLOCK_RATE_SET, sizeof(*cfg), 0, &t);
+ if (ret)
+ return ret;
+
+ if (ci->max_async_req &&
+ ci->cur_async_req++ < ci->max_async_req)
+ flags |= CLOCK_SET_ASYNC;
+
+ cfg = t->tx.buf;
+ cfg->flags = cpu_to_le32(flags);
+ cfg->id = cpu_to_le32(clk_id);
+ cfg->value_low = cpu_to_le32(rate & 0xffffffff);
+ cfg->value_high = cpu_to_le32(rate >> 32);
+
+ if (flags & CLOCK_SET_ASYNC)
+ ret = ph->xops->do_xfer_with_response(ph, t);
+ else
+ ret = ph->xops->do_xfer(ph, t);
+
+ if (ci->max_async_req)
+ ci->cur_async_req--;
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
+static int
+scmi_clock_config_set(const struct scmi_protocol_handle *ph, u32 clk_id,
+ u32 config)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_clock_set_config *cfg;
+
+ ret = ph->xops->xfer_get_init(ph, CLOCK_CONFIG_SET,
+ sizeof(*cfg), 0, &t);
+ if (ret)
+ return ret;
+
+ cfg = t->tx.buf;
+ cfg->id = cpu_to_le32(clk_id);
+ cfg->attributes = cpu_to_le32(config);
+
+ ret = ph->xops->do_xfer(ph, t);
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
+static int scmi_clock_enable(const struct scmi_protocol_handle *ph, u32 clk_id)
+{
+ return scmi_clock_config_set(ph, clk_id, CLOCK_ENABLE);
+}
+
+static int scmi_clock_disable(const struct scmi_protocol_handle *ph, u32 clk_id)
+{
+ return scmi_clock_config_set(ph, clk_id, 0);
+}
+
+static int scmi_clock_count_get(const struct scmi_protocol_handle *ph)
+{
+ struct clock_info *ci = ph->get_priv(ph);
+
+ return ci->num_clocks;
+}
+
+static const struct scmi_clock_info *
+scmi_clock_info_get(const struct scmi_protocol_handle *ph, u32 clk_id)
+{
+ struct clock_info *ci = ph->get_priv(ph);
+ struct scmi_clock_info *clk = ci->clk + clk_id;
+
+ if (!clk->name[0])
+ return NULL;
+
+ return clk;
+}
+
+static const struct scmi_clk_proto_ops clk_proto_ops = {
+ .count_get = scmi_clock_count_get,
+ .info_get = scmi_clock_info_get,
+ .rate_get = scmi_clock_rate_get,
+ .rate_set = scmi_clock_rate_set,
+ .enable = scmi_clock_enable,
+ .disable = scmi_clock_disable,
+};
+
+static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph)
+{
+ u32 version;
+ int clkid, ret;
+ struct clock_info *cinfo;
+
+ ph->xops->version_get(ph, &version);
+
+ dev_dbg(ph->dev, "Clock Version %d.%d\n",
+ PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+
+ cinfo = kzalloc(sizeof(*cinfo), GFP_KERNEL);
+ if (!cinfo)
+ return -ENOMEM;
+
+ scmi_clock_protocol_attributes_get(ph, cinfo);
+
+ cinfo->clk = kcalloc(cinfo->num_clocks, sizeof(*cinfo->clk), GFP_KERNEL);
+ if (!cinfo->clk)
+ return -ENOMEM;
+
+ for (clkid = 0; clkid < cinfo->num_clocks; clkid++) {
+ struct scmi_clock_info *clk = cinfo->clk + clkid;
+
+ ret = scmi_clock_attributes_get(ph, clkid, clk);
+ if (!ret)
+ scmi_clock_describe_rates_get(ph, clkid, clk);
+ }
+
+ cinfo->version = version;
+ return ph->set_priv(ph, cinfo);
+}
+
+static const struct scmi_protocol scmi_clock = {
+ .id = SCMI_PROTOCOL_CLOCK,
+ .instance_init = &scmi_clock_protocol_init,
+ .ops = &clk_proto_ops,
+};
+
+DEFINE_SCMI_PROTOCOL_REGISTER(clock, scmi_clock)
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index f1f6cdae5af2..7ac9fd1bc961 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -233,6 +233,7 @@ void __exit scmi_bus_exit(void);
int __init scmi_##func##_register(void);
DECLARE_SCMI_REGISTER(base);
DECLARE_SCMI_REGISTER(reset);
+DECLARE_SCMI_REGISTER(clock);
#define DEFINE_SCMI_PROTOCOL_REGISTER(name, proto) \
static const struct scmi_protocol *__this_proto = &(proto); \
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index d862e4de8afb..ca71e1a279ac 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -1260,6 +1260,7 @@ static int __init scmi_bus_driver_init(void)
scmi_base_register();
scmi_reset_register();
+ scmi_clock_register();
return 0;
}
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 15/24] regulator: add SCMI regulator driver
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
` (13 preceding siblings ...)
2022-02-20 12:47 ` [PATCH 14/24] clk: add SCMI clock driver Ahmad Fatoum
@ 2022-02-20 12:47 ` Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 16/24] clk: accept const arguments in clk_to_clk_hw/clk_hw_to_clk Ahmad Fatoum
` (9 subsequent siblings)
24 siblings, 0 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium, Ahmad Fatoum
Import the Linux v5.13 state of the SCMI clock regulator driver.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
drivers/firmware/arm_scmi/Makefile | 2 +-
drivers/firmware/arm_scmi/common.h | 1 +
drivers/firmware/arm_scmi/driver.c | 1 +
drivers/firmware/arm_scmi/voltage.c | 379 +++++++++++++++++++++++++++
drivers/regulator/Kconfig | 9 +
drivers/regulator/Makefile | 1 +
drivers/regulator/scmi-regulator.c | 391 ++++++++++++++++++++++++++++
7 files changed, 783 insertions(+), 1 deletion(-)
create mode 100644 drivers/firmware/arm_scmi/voltage.c
create mode 100644 drivers/regulator/scmi-regulator.c
diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index 968a7eb7771e..4b21e6609a1e 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -3,7 +3,7 @@ scmi-bus-y = bus.o
scmi-driver-y = driver.o
scmi-transport-y = shmem.o
scmi-transport-$(CONFIG_ARM_SMCCC) += smc.o
-scmi-protocols-y = base.o reset.o clock.o
+scmi-protocols-y = base.o reset.o clock.o voltage.o
scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \
$(scmi-transport-y)
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index 7ac9fd1bc961..5004a71dc910 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -234,6 +234,7 @@ void __exit scmi_bus_exit(void);
DECLARE_SCMI_REGISTER(base);
DECLARE_SCMI_REGISTER(reset);
DECLARE_SCMI_REGISTER(clock);
+DECLARE_SCMI_REGISTER(voltage);
#define DEFINE_SCMI_PROTOCOL_REGISTER(name, proto) \
static const struct scmi_protocol *__this_proto = &(proto); \
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index ca71e1a279ac..ef3d76b3f439 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -1261,6 +1261,7 @@ static int __init scmi_bus_driver_init(void)
scmi_reset_register();
scmi_clock_register();
+ scmi_voltage_register();
return 0;
}
diff --git a/drivers/firmware/arm_scmi/voltage.c b/drivers/firmware/arm_scmi/voltage.c
new file mode 100644
index 000000000000..a3d78db28f26
--- /dev/null
+++ b/drivers/firmware/arm_scmi/voltage.c
@@ -0,0 +1,379 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface (SCMI) Voltage Protocol
+ *
+ * Copyright (C) 2020-2021 ARM Ltd.
+ */
+
+#include <common.h>
+#include <linux/scmi_protocol.h>
+
+#include "common.h"
+
+#define VOLTAGE_DOMS_NUM_MASK GENMASK(15, 0)
+#define REMAINING_LEVELS_MASK GENMASK(31, 16)
+#define RETURNED_LEVELS_MASK GENMASK(11, 0)
+
+enum scmi_voltage_protocol_cmd {
+ VOLTAGE_DOMAIN_ATTRIBUTES = 0x3,
+ VOLTAGE_DESCRIBE_LEVELS = 0x4,
+ VOLTAGE_CONFIG_SET = 0x5,
+ VOLTAGE_CONFIG_GET = 0x6,
+ VOLTAGE_LEVEL_SET = 0x7,
+ VOLTAGE_LEVEL_GET = 0x8,
+};
+
+#define NUM_VOLTAGE_DOMAINS(x) ((u16)(FIELD_GET(VOLTAGE_DOMS_NUM_MASK, (x))))
+
+struct scmi_msg_resp_domain_attributes {
+ __le32 attr;
+ u8 name[SCMI_MAX_STR_SIZE];
+};
+
+struct scmi_msg_cmd_describe_levels {
+ __le32 domain_id;
+ __le32 level_index;
+};
+
+struct scmi_msg_resp_describe_levels {
+ __le32 flags;
+#define NUM_REMAINING_LEVELS(f) ((u16)(FIELD_GET(REMAINING_LEVELS_MASK, (f))))
+#define NUM_RETURNED_LEVELS(f) ((u16)(FIELD_GET(RETURNED_LEVELS_MASK, (f))))
+#define SUPPORTS_SEGMENTED_LEVELS(f) ((f) & BIT(12))
+ __le32 voltage[];
+};
+
+struct scmi_msg_cmd_config_set {
+ __le32 domain_id;
+ __le32 config;
+};
+
+struct scmi_msg_cmd_level_set {
+ __le32 domain_id;
+ __le32 flags;
+ __le32 voltage_level;
+};
+
+struct voltage_info {
+ unsigned int version;
+ unsigned int num_domains;
+ struct scmi_voltage_info *domains;
+};
+
+static int scmi_protocol_attributes_get(const struct scmi_protocol_handle *ph,
+ struct voltage_info *vinfo)
+{
+ int ret;
+ struct scmi_xfer *t;
+
+ ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0,
+ sizeof(__le32), &t);
+ if (ret)
+ return ret;
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret)
+ vinfo->num_domains =
+ NUM_VOLTAGE_DOMAINS(get_unaligned_le32(t->rx.buf));
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
+static int scmi_init_voltage_levels(struct device_d *dev,
+ struct scmi_voltage_info *v,
+ u32 num_returned, u32 num_remaining,
+ bool segmented)
+{
+ u32 num_levels;
+
+ num_levels = num_returned + num_remaining;
+ /*
+ * segmented levels entries are represented by a single triplet
+ * returned all in one go.
+ */
+ if (!num_levels ||
+ (segmented && (num_remaining || num_returned != 3))) {
+ dev_err(dev,
+ "Invalid level descriptor(%d/%d/%d) for voltage dom %d\n",
+ num_levels, num_returned, num_remaining, v->id);
+ return -EINVAL;
+ }
+
+ v->levels_uv = kcalloc(num_levels, sizeof(u32), GFP_KERNEL);
+ if (!v->levels_uv)
+ return -ENOMEM;
+
+ v->num_levels = num_levels;
+ v->segmented = segmented;
+
+ return 0;
+}
+
+static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph,
+ struct voltage_info *vinfo)
+{
+ int ret, dom;
+ struct scmi_xfer *td, *tl;
+ struct device_d *dev = ph->dev;
+ struct scmi_msg_resp_domain_attributes *resp_dom;
+ struct scmi_msg_resp_describe_levels *resp_levels;
+
+ ret = ph->xops->xfer_get_init(ph, VOLTAGE_DOMAIN_ATTRIBUTES,
+ sizeof(__le32), sizeof(*resp_dom), &td);
+ if (ret)
+ return ret;
+ resp_dom = td->rx.buf;
+
+ ret = ph->xops->xfer_get_init(ph, VOLTAGE_DESCRIBE_LEVELS,
+ sizeof(__le64), 0, &tl);
+ if (ret)
+ goto outd;
+ resp_levels = tl->rx.buf;
+
+ for (dom = 0; dom < vinfo->num_domains; dom++) {
+ u32 desc_index = 0;
+ u16 num_returned = 0, num_remaining = 0;
+ struct scmi_msg_cmd_describe_levels *cmd;
+ struct scmi_voltage_info *v;
+
+ /* Retrieve domain attributes at first ... */
+ put_unaligned_le32(dom, td->tx.buf);
+ ret = ph->xops->do_xfer(ph, td);
+ /* Skip domain on comms error */
+ if (ret)
+ continue;
+
+ v = vinfo->domains + dom;
+ v->id = dom;
+ v->attributes = le32_to_cpu(resp_dom->attr);
+ strlcpy(v->name, resp_dom->name, SCMI_MAX_STR_SIZE);
+
+ cmd = tl->tx.buf;
+ /* ...then retrieve domain levels descriptions */
+ do {
+ u32 flags;
+ int cnt;
+
+ cmd->domain_id = cpu_to_le32(v->id);
+ cmd->level_index = desc_index;
+ ret = ph->xops->do_xfer(ph, tl);
+ if (ret)
+ break;
+
+ flags = le32_to_cpu(resp_levels->flags);
+ num_returned = NUM_RETURNED_LEVELS(flags);
+ num_remaining = NUM_REMAINING_LEVELS(flags);
+
+ /* Allocate space for num_levels if not already done */
+ if (!v->num_levels) {
+ ret = scmi_init_voltage_levels(dev, v,
+ num_returned,
+ num_remaining,
+ SUPPORTS_SEGMENTED_LEVELS(flags));
+ if (ret)
+ break;
+ }
+
+ if (desc_index + num_returned > v->num_levels) {
+ dev_err(ph->dev,
+ "No. of voltage levels can't exceed %d\n",
+ v->num_levels);
+ ret = -EINVAL;
+ break;
+ }
+
+ for (cnt = 0; cnt < num_returned; cnt++) {
+ s32 val;
+
+ val =
+ (s32)le32_to_cpu(resp_levels->voltage[cnt]);
+ v->levels_uv[desc_index + cnt] = val;
+ if (val < 0)
+ v->negative_volts_allowed = true;
+ }
+
+ desc_index += num_returned;
+
+ ph->xops->reset_rx_to_maxsz(ph, tl);
+ /* check both to avoid infinite loop due to buggy fw */
+ } while (num_returned && num_remaining);
+
+ if (ret) {
+ v->num_levels = 0;
+ kfree(v->levels_uv);
+ }
+
+ ph->xops->reset_rx_to_maxsz(ph, td);
+ }
+
+ ph->xops->xfer_put(ph, tl);
+outd:
+ ph->xops->xfer_put(ph, td);
+
+ return ret;
+}
+
+static int __scmi_voltage_get_u32(const struct scmi_protocol_handle *ph,
+ u8 cmd_id, u32 domain_id, u32 *value)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct voltage_info *vinfo = ph->get_priv(ph);
+
+ if (domain_id >= vinfo->num_domains)
+ return -EINVAL;
+
+ ret = ph->xops->xfer_get_init(ph, cmd_id, sizeof(__le32), 0, &t);
+ if (ret)
+ return ret;
+
+ put_unaligned_le32(domain_id, t->tx.buf);
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret)
+ *value = get_unaligned_le32(t->rx.buf);
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
+static int scmi_voltage_config_set(const struct scmi_protocol_handle *ph,
+ u32 domain_id, u32 config)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct voltage_info *vinfo = ph->get_priv(ph);
+ struct scmi_msg_cmd_config_set *cmd;
+
+ if (domain_id >= vinfo->num_domains)
+ return -EINVAL;
+
+ ret = ph->xops->xfer_get_init(ph, VOLTAGE_CONFIG_SET,
+ sizeof(*cmd), 0, &t);
+ if (ret)
+ return ret;
+
+ cmd = t->tx.buf;
+ cmd->domain_id = cpu_to_le32(domain_id);
+ cmd->config = cpu_to_le32(config & GENMASK(3, 0));
+
+ ret = ph->xops->do_xfer(ph, t);
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
+static int scmi_voltage_config_get(const struct scmi_protocol_handle *ph,
+ u32 domain_id, u32 *config)
+{
+ return __scmi_voltage_get_u32(ph, VOLTAGE_CONFIG_GET,
+ domain_id, config);
+}
+
+static int scmi_voltage_level_set(const struct scmi_protocol_handle *ph,
+ u32 domain_id, u32 flags, s32 volt_uV)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct voltage_info *vinfo = ph->get_priv(ph);
+ struct scmi_msg_cmd_level_set *cmd;
+
+ if (domain_id >= vinfo->num_domains)
+ return -EINVAL;
+
+ ret = ph->xops->xfer_get_init(ph, VOLTAGE_LEVEL_SET,
+ sizeof(*cmd), 0, &t);
+ if (ret)
+ return ret;
+
+ cmd = t->tx.buf;
+ cmd->domain_id = cpu_to_le32(domain_id);
+ cmd->flags = cpu_to_le32(flags);
+ cmd->voltage_level = cpu_to_le32(volt_uV);
+
+ ret = ph->xops->do_xfer(ph, t);
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
+static int scmi_voltage_level_get(const struct scmi_protocol_handle *ph,
+ u32 domain_id, s32 *volt_uV)
+{
+ return __scmi_voltage_get_u32(ph, VOLTAGE_LEVEL_GET,
+ domain_id, (u32 *)volt_uV);
+}
+
+static const struct scmi_voltage_info * __must_check
+scmi_voltage_info_get(const struct scmi_protocol_handle *ph, u32 domain_id)
+{
+ struct voltage_info *vinfo = ph->get_priv(ph);
+
+ if (domain_id >= vinfo->num_domains ||
+ !vinfo->domains[domain_id].num_levels)
+ return NULL;
+
+ return vinfo->domains + domain_id;
+}
+
+static int scmi_voltage_domains_num_get(const struct scmi_protocol_handle *ph)
+{
+ struct voltage_info *vinfo = ph->get_priv(ph);
+
+ return vinfo->num_domains;
+}
+
+static struct scmi_voltage_proto_ops voltage_proto_ops = {
+ .num_domains_get = scmi_voltage_domains_num_get,
+ .info_get = scmi_voltage_info_get,
+ .config_set = scmi_voltage_config_set,
+ .config_get = scmi_voltage_config_get,
+ .level_set = scmi_voltage_level_set,
+ .level_get = scmi_voltage_level_get,
+};
+
+static int scmi_voltage_protocol_init(const struct scmi_protocol_handle *ph)
+{
+ int ret;
+ u32 version;
+ struct voltage_info *vinfo;
+
+ ret = ph->xops->version_get(ph, &version);
+ if (ret)
+ return ret;
+
+ dev_dbg(ph->dev, "Voltage Version %d.%d\n",
+ PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+
+ vinfo = kzalloc(sizeof(*vinfo), GFP_KERNEL);
+ if (!vinfo)
+ return -ENOMEM;
+ vinfo->version = version;
+
+ ret = scmi_protocol_attributes_get(ph, vinfo);
+ if (ret)
+ return ret;
+
+ if (vinfo->num_domains) {
+ vinfo->domains = kcalloc(vinfo->num_domains,
+ sizeof(*vinfo->domains),
+ GFP_KERNEL);
+ if (!vinfo->domains)
+ return -ENOMEM;
+ ret = scmi_voltage_descriptors_get(ph, vinfo);
+ if (ret)
+ return ret;
+ } else {
+ dev_warn(ph->dev, "No Voltage domains found.\n");
+ }
+
+ return ph->set_priv(ph, vinfo);
+}
+
+static const struct scmi_protocol scmi_voltage = {
+ .id = SCMI_PROTOCOL_VOLTAGE,
+ .instance_init = &scmi_voltage_protocol_init,
+ .ops = &voltage_proto_ops,
+};
+
+DEFINE_SCMI_PROTOCOL_REGISTER(voltage, scmi_voltage)
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index c468e459153b..38a47b7bc2ed 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -55,4 +55,13 @@ config REGULATOR_ANATOP
regulators. It is recommended that this option be
enabled on i.MX6 platform.
+config REGULATOR_ARM_SCMI
+ tristate "SCMI based regulator driver"
+ depends on ARM_SCMI_PROTOCOL && OFDEVICE
+ help
+ This adds the regulator driver support for ARM platforms using SCMI
+ protocol for device voltage management.
+ This driver uses SCMI Message Protocol driver to interact with the
+ firmware providing the device Voltage functionality.
+
endif
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 7b5d527cb1ba..5eaa657ad162 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_REGULATOR_STPMIC1) += stpmic1_regulator.o
obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o
obj-$(CONFIG_REGULATOR_STM32_PWR) += stm32-pwr.o
obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o
+obj-$(CONFIG_REGULATOR_ARM_SCMI) += scmi-regulator.o
diff --git a/drivers/regulator/scmi-regulator.c b/drivers/regulator/scmi-regulator.c
new file mode 100644
index 000000000000..3e6fd3ec6084
--- /dev/null
+++ b/drivers/regulator/scmi-regulator.c
@@ -0,0 +1,391 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// System Control and Management Interface (SCMI) based regulator driver
+//
+// Copyright (C) 2020-2021 ARM Ltd.
+//
+// Implements a regulator driver on top of the SCMI Voltage Protocol.
+//
+// The ARM SCMI Protocol aims in general to hide as much as possible all the
+// underlying operational details while providing an abstracted interface for
+// its users to operate upon: as a consequence the resulting operational
+// capabilities and configurability of this regulator device are much more
+// limited than the ones usually available on a standard physical regulator.
+//
+// The supported SCMI regulator ops are restricted to the bare minimum:
+//
+// - 'status_ops': enable/disable/is_enabled
+// - 'voltage_ops': get_voltage_sel/set_voltage_sel
+// list_voltage/map_voltage
+//
+// Each SCMI regulator instance is associated, through the means of a proper DT
+// entry description, to a specific SCMI Voltage Domain.
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <common.h>
+#include <of.h>
+#include <regulator.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/scmi_protocol.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+static const struct scmi_voltage_proto_ops *voltage_ops;
+
+struct scmi_reg_desc {
+ struct regulator_desc desc;
+ const char *name;
+ const char *supply_name;
+};
+
+struct scmi_regulator {
+ u32 id;
+ struct scmi_device *sdev;
+ struct scmi_protocol_handle *ph;
+ struct regulator_dev rdev;
+ struct device_node *device_node;
+ struct scmi_reg_desc sdesc;
+};
+
+struct scmi_regulator_info {
+ int num_doms;
+ struct scmi_regulator **sregv;
+};
+
+static inline struct scmi_regulator *to_scmi_regulator(struct regulator_dev *rdev)
+{
+ return container_of(rdev, struct scmi_regulator, rdev);
+}
+
+static int scmi_reg_enable(struct regulator_dev *rdev)
+{
+ struct scmi_regulator *sreg = to_scmi_regulator(rdev);
+
+ return voltage_ops->config_set(sreg->ph, sreg->id,
+ SCMI_VOLTAGE_ARCH_STATE_ON);
+}
+
+static int scmi_reg_disable(struct regulator_dev *rdev)
+{
+ struct scmi_regulator *sreg = to_scmi_regulator(rdev);
+
+ return voltage_ops->config_set(sreg->ph, sreg->id,
+ SCMI_VOLTAGE_ARCH_STATE_OFF);
+}
+
+static int scmi_reg_is_enabled(struct regulator_dev *rdev)
+{
+ int ret;
+ u32 config;
+ struct scmi_regulator *sreg = to_scmi_regulator(rdev);
+ struct scmi_reg_desc *sdesc = &sreg->sdesc;
+
+ ret = voltage_ops->config_get(sreg->ph, sreg->id, &config);
+ if (ret) {
+ dev_err(&sreg->sdev->dev,
+ "Error %d reading regulator %s status.\n",
+ ret, sdesc->name);
+ return ret;
+ }
+
+ return config & SCMI_VOLTAGE_ARCH_STATE_ON;
+}
+
+static int scmi_reg_get_voltage_sel(struct regulator_dev *rdev)
+{
+ int ret;
+ s32 volt_uV;
+ struct scmi_regulator *sreg = to_scmi_regulator(rdev);
+
+ ret = voltage_ops->level_get(sreg->ph, sreg->id, &volt_uV);
+ if (ret)
+ return ret;
+
+ return sreg->sdesc.desc.ops->map_voltage(rdev, volt_uV, volt_uV);
+}
+
+static int scmi_reg_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ s32 volt_uV;
+ struct scmi_regulator *sreg = to_scmi_regulator(rdev);
+
+ volt_uV = sreg->sdesc.desc.ops->list_voltage(rdev, selector);
+ if (volt_uV <= 0)
+ return -EINVAL;
+
+ return voltage_ops->level_set(sreg->ph, sreg->id, 0x0, volt_uV);
+}
+
+static const struct regulator_ops scmi_reg_fixed_ops = {
+ .enable = scmi_reg_enable,
+ .disable = scmi_reg_disable,
+ .is_enabled = scmi_reg_is_enabled,
+};
+
+static const struct regulator_ops scmi_reg_linear_ops = {
+ .enable = scmi_reg_enable,
+ .disable = scmi_reg_disable,
+ .is_enabled = scmi_reg_is_enabled,
+ .get_voltage_sel = scmi_reg_get_voltage_sel,
+ .set_voltage_sel = scmi_reg_set_voltage_sel,
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+};
+
+static const struct regulator_ops scmi_reg_discrete_ops = {
+ .enable = scmi_reg_enable,
+ .disable = scmi_reg_disable,
+ .is_enabled = scmi_reg_is_enabled,
+ .get_voltage_sel = scmi_reg_get_voltage_sel,
+ .set_voltage_sel = scmi_reg_set_voltage_sel,
+ .list_voltage = regulator_list_voltage_table,
+ .map_voltage = regulator_map_voltage_iterate,
+};
+
+static int
+scmi_config_linear_regulator_mappings(struct scmi_regulator *sreg,
+ const struct scmi_voltage_info *vinfo)
+{
+ struct regulator_desc *desc = &sreg->sdesc.desc;
+ s32 delta_uV;
+
+ /*
+ * Note that SCMI voltage domains describable by linear ranges
+ * (segments) {low, high, step} are guaranteed to come in one single
+ * triplet by the SCMI Voltage Domain protocol support itself.
+ */
+
+ delta_uV = (vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_HIGH] -
+ vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]);
+
+ /* Rule out buggy negative-intervals answers from fw */
+ if (delta_uV < 0) {
+ dev_err(&sreg->sdev->dev,
+ "Invalid volt-range %d-%duV for domain %d\n",
+ vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW],
+ vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_HIGH],
+ sreg->id);
+ return -EINVAL;
+ }
+
+ if (!delta_uV) {
+ /* Just one fixed voltage exposed by SCMI */
+ desc->fixed_uV =
+ vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW];
+ desc->n_voltages = 1;
+ desc->ops = &scmi_reg_fixed_ops;
+ } else {
+ /* One simple linear mapping. */
+ desc->min_uV =
+ vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW];
+ desc->uV_step =
+ vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_STEP];
+ desc->linear_min_sel = 0;
+ desc->n_voltages = (delta_uV / desc->uV_step) + 1;
+ desc->ops = &scmi_reg_linear_ops;
+ }
+
+ return 0;
+}
+
+static int
+scmi_config_discrete_regulator_mappings(struct scmi_regulator *sreg,
+ const struct scmi_voltage_info *vinfo)
+{
+ struct regulator_desc *desc = &sreg->sdesc.desc;
+
+ /* Discrete non linear levels are mapped to volt_table */
+ desc->n_voltages = vinfo->num_levels;
+
+ if (desc->n_voltages > 1) {
+ desc->volt_table = (const unsigned int *)vinfo->levels_uv;
+ desc->ops = &scmi_reg_discrete_ops;
+ } else {
+ desc->fixed_uV = vinfo->levels_uv[0];
+ desc->ops = &scmi_reg_fixed_ops;
+ }
+
+ return 0;
+}
+
+static int scmi_regulator_common_init(struct scmi_regulator *sreg)
+{
+ int ret;
+ struct device_d *dev = &sreg->sdev->dev;
+ const struct scmi_voltage_info *vinfo;
+ struct scmi_reg_desc *sdesc = &sreg->sdesc;
+
+ vinfo = voltage_ops->info_get(sreg->ph, sreg->id);
+ if (!vinfo) {
+ dev_warn(dev, "Failure to get voltage domain %d\n",
+ sreg->id);
+ return -ENODEV;
+ }
+
+ /*
+ * Regulator framework does not fully support negative voltages
+ * so we discard any voltage domain reported as supporting negative
+ * voltages: as a consequence each levels_uv entry is guaranteed to
+ * be non-negative from here on.
+ */
+ if (vinfo->negative_volts_allowed) {
+ dev_warn(dev, "Negative voltages NOT supported...skip %s\n",
+ sreg->device_node->full_name);
+ return -EOPNOTSUPP;
+ }
+
+ sdesc->name = basprintf("%s", vinfo->name);
+ if (!sdesc->name)
+ return -ENOMEM;
+
+ if (vinfo->segmented)
+ ret = scmi_config_linear_regulator_mappings(sreg, vinfo);
+ else
+ ret = scmi_config_discrete_regulator_mappings(sreg, vinfo);
+
+ return ret;
+}
+
+static int process_scmi_regulator_of_node(struct scmi_device *sdev,
+ struct scmi_protocol_handle *ph,
+ struct device_node *np,
+ struct scmi_regulator_info *rinfo)
+{
+ u32 dom, ret;
+
+ ret = of_property_read_u32(np, "reg", &dom);
+ if (ret)
+ return ret;
+
+ if (dom >= rinfo->num_doms)
+ return -ENODEV;
+
+ if (rinfo->sregv[dom]) {
+ dev_err(&sdev->dev,
+ "SCMI Voltage Domain %d already in use. Skipping: %s\n",
+ dom, np->full_name);
+ return -EINVAL;
+ }
+
+ rinfo->sregv[dom] = kzalloc(sizeof(struct scmi_regulator), GFP_KERNEL);
+ if (!rinfo->sregv[dom])
+ return -ENOMEM;
+
+ rinfo->sregv[dom]->id = dom;
+ rinfo->sregv[dom]->sdev = sdev;
+ rinfo->sregv[dom]->ph = ph;
+
+ /* get hold of good nodes */
+ rinfo->sregv[dom]->device_node = np;
+
+ dev_dbg(&sdev->dev,
+ "Found SCMI Regulator entry -- OF node [%d] -> %s\n",
+ dom, np->full_name);
+
+ return 0;
+}
+
+static int scmi_regulator_probe(struct scmi_device *sdev)
+{
+ int d, ret, num_doms;
+ struct device_node *np, *child;
+ const struct scmi_handle *handle = sdev->handle;
+ struct scmi_regulator_info *rinfo;
+ struct scmi_protocol_handle *ph;
+ struct scmi_reg_desc *sdesc;
+
+ if (!handle)
+ return -ENODEV;
+
+ voltage_ops = handle->protocol_get(sdev, SCMI_PROTOCOL_VOLTAGE, &ph);
+ if (IS_ERR(voltage_ops))
+ return PTR_ERR(voltage_ops);
+
+ num_doms = voltage_ops->num_domains_get(ph);
+ if (num_doms <= 0) {
+ if (!num_doms) {
+ dev_err(&sdev->dev,
+ "number of voltage domains invalid\n");
+ num_doms = -EINVAL;
+ } else {
+ dev_err(&sdev->dev,
+ "failed to get voltage domains - err:%d\n",
+ num_doms);
+ }
+
+ return num_doms;
+ }
+
+ rinfo = kzalloc(sizeof(*rinfo), GFP_KERNEL);
+ if (!rinfo)
+ return -ENOMEM;
+
+ /* Allocate pointers array for all possible domains */
+ rinfo->sregv = kcalloc(num_doms, sizeof(void *), GFP_KERNEL);
+ if (!rinfo->sregv)
+ return -ENOMEM;
+
+ rinfo->num_doms = num_doms;
+
+ /*
+ * Start collecting into rinfo->sregv possibly good SCMI Regulators as
+ * described by a well-formed DT entry and associated with an existing
+ * plausible SCMI Voltage Domain number, all belonging to this SCMI
+ * platform instance node (handle->dev->of_node).
+ */
+ np = of_find_node_by_name(handle->dev->device_node, "regulators");
+ for_each_child_of_node(np, child) {
+ ret = process_scmi_regulator_of_node(sdev, ph, child, rinfo);
+ /* abort on any mem issue */
+ if (ret == -ENOMEM)
+ return ret;
+ }
+
+ /*
+ * Register a regulator for each valid regulator-DT-entry that we
+ * can successfully reach via SCMI and has a valid associated voltage
+ * domain.
+ */
+ for (d = 0; d < num_doms; d++) {
+ struct scmi_regulator *sreg = rinfo->sregv[d];
+
+ /* Skip empty slots */
+ if (!sreg)
+ continue;
+
+ sdesc = &sreg->sdesc;
+
+ ret = scmi_regulator_common_init(sreg);
+ /* Skip invalid voltage domains */
+ if (ret)
+ continue;
+
+ ret = of_regulator_register(&sreg->rdev, np);
+ if (ret)
+ continue;
+
+ dev_info(&sdev->dev,
+ "Regulator %s registered for domain [%d]\n",
+ sreg->sdesc.name, sreg->id);
+ }
+
+ return 0;
+}
+
+static const struct scmi_device_id scmi_regulator_id_table[] = {
+ { SCMI_PROTOCOL_VOLTAGE, "regulator" },
+ { },
+};
+
+static struct scmi_driver scmi_drv = {
+ .name = "scmi-regulator",
+ .probe = scmi_regulator_probe,
+ .id_table = scmi_regulator_id_table,
+};
+core_scmi_driver(scmi_drv);
+
+MODULE_AUTHOR("Cristian Marussi <cristian.marussi@arm.com>");
+MODULE_DESCRIPTION("ARM SCMI regulator driver");
+MODULE_LICENSE("GPL v2");
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 16/24] clk: accept const arguments in clk_to_clk_hw/clk_hw_to_clk
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
` (14 preceding siblings ...)
2022-02-20 12:47 ` [PATCH 15/24] regulator: add SCMI regulator driver Ahmad Fatoum
@ 2022-02-20 12:47 ` Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 17/24] serial: stm32: bail if clock_get_rate returns zero Ahmad Fatoum
` (8 subsequent siblings)
24 siblings, 0 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium, Ahmad Fatoum
Driver code may want to pass const pointers into these
functions. Change the implementation to support this.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
include/linux/clk.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/include/linux/clk.h b/include/linux/clk.h
index 9bee20465247..9396e01003e1 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -431,14 +431,14 @@ struct clk_hw {
const struct clk_init_data *init;
};
-static inline struct clk *clk_hw_to_clk(struct clk_hw *hw)
+static inline struct clk *clk_hw_to_clk(const struct clk_hw *hw)
{
- return IS_ERR(hw) ? ERR_CAST(hw) : &hw->clk;
+ return IS_ERR(hw) ? ERR_CAST(hw) : (struct clk *)&hw->clk;
}
-static inline struct clk_hw *clk_to_clk_hw(struct clk *clk)
+static inline struct clk_hw *clk_to_clk_hw(const struct clk *clk)
{
- return IS_ERR(clk) ? ERR_CAST(clk) : container_of(clk, struct clk_hw, clk);
+ return IS_ERR(clk) ? ERR_CAST(clk) : (struct clk_hw *)container_of(clk, struct clk_hw, clk);
}
struct clk_div_table {
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 17/24] serial: stm32: bail if clock_get_rate returns zero
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
` (15 preceding siblings ...)
2022-02-20 12:47 ` [PATCH 16/24] clk: accept const arguments in clk_to_clk_hw/clk_hw_to_clk Ahmad Fatoum
@ 2022-02-20 12:47 ` Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 18/24] clk: implement of_clk_hw_{onecell,simple}_get Ahmad Fatoum
` (7 subsequent siblings)
24 siblings, 0 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium, Ahmad Fatoum
While this shouldn't happen in a well-behaving system, with SCMI barebox
may become dependent on the firmware to provide a non-zero rate here.
Sanitize the value to avoid a division -by-zero.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
drivers/serial/serial_stm32.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/serial/serial_stm32.c b/drivers/serial/serial_stm32.c
index 271897f1743c..f1d4c6de9074 100644
--- a/drivers/serial/serial_stm32.c
+++ b/drivers/serial/serial_stm32.c
@@ -41,6 +41,8 @@ static int stm32_serial_setbaudrate(struct console_device *cdev, int baudrate)
unsigned long clock_rate;
clock_rate = clk_get_rate(stm32->clk);
+ if (!clock_rate)
+ return -EINVAL;
int_div = DIV_ROUND_CLOSEST(clock_rate, baudrate);
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 18/24] clk: implement of_clk_hw_{onecell,simple}_get
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
` (16 preceding siblings ...)
2022-02-20 12:47 ` [PATCH 17/24] serial: stm32: bail if clock_get_rate returns zero Ahmad Fatoum
@ 2022-02-20 12:47 ` Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 19/24] clk: implement clk_hw_reparent Ahmad Fatoum
` (6 subsequent siblings)
24 siblings, 0 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium, Ahmad Fatoum
These functions are easily implemented and make porting code easier.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
drivers/clk/clk.c | 22 ++++++++++++++++++++++
include/linux/clk.h | 17 +++++++++++++++++
2 files changed, 39 insertions(+)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 227ab75ed65d..8e317b4b05d0 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -578,6 +578,12 @@ struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec,
}
EXPORT_SYMBOL_GPL(of_clk_src_simple_get);
+struct clk_hw *of_clk_hw_simple_get(struct of_phandle_args *clkspec, void *data)
+{
+ return data;
+}
+EXPORT_SYMBOL_GPL(of_clk_hw_simple_get);
+
struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data)
{
struct clk_onecell_data *clk_data = data;
@@ -592,6 +598,22 @@ struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data)
}
EXPORT_SYMBOL_GPL(of_clk_src_onecell_get);
+struct clk_hw *
+of_clk_hw_onecell_get(struct of_phandle_args *clkspec, void *data)
+{
+ struct clk_hw_onecell_data *hw_data = data;
+ unsigned int idx = clkspec->args[0];
+
+ if (idx >= hw_data->num) {
+ pr_err("%s: invalid index %u\n", __func__, idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return hw_data->hws[idx];
+}
+EXPORT_SYMBOL_GPL(of_clk_hw_onecell_get);
+
+
static int __of_clk_add_provider(struct device_node *np,
struct clk *(*clk_src_get)(struct of_phandle_args *clkspec,
void *data),
diff --git a/include/linux/clk.h b/include/linux/clk.h
index 9396e01003e1..c0e998e54ae6 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -778,6 +778,11 @@ struct clk_onecell_data {
unsigned int clk_num;
};
+struct clk_hw_onecell_data {
+ unsigned int num;
+ struct clk_hw *hws[];
+};
+
#if defined(CONFIG_COMMON_CLK_OF_PROVIDER)
#define CLK_OF_DECLARE(name, compat, fn) \
@@ -791,6 +796,8 @@ typedef int (*of_clk_init_cb_t)(struct device_node *);
struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data);
struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec, void *data);
+struct clk_hw *of_clk_hw_onecell_get(struct of_phandle_args *clkspec, void *data);
+struct clk_hw *of_clk_hw_simple_get(struct of_phandle_args *clkspec, void *data);
struct clk *of_clk_get(struct device_node *np, int index);
struct clk *of_clk_get_by_name(struct device_node *np, const char *name);
@@ -826,11 +833,21 @@ static inline struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec
{
return ERR_PTR(-ENOENT);
}
+static inline struct clk_hw *of_clk_hw_onecell_get(struct of_phandle_args *clkspec,
+ void *data)
+{
+ return ERR_PTR(-ENOENT);
+}
static inline struct clk *
of_clk_src_simple_get(struct of_phandle_args *clkspec, void *data)
{
return ERR_PTR(-ENOENT);
}
+static inline struct clk *
+of_clk_hw_simple_get(struct of_phandle_args *clkspec, void *data)
+{
+ return ERR_PTR(-ENOENT);
+}
static inline struct clk *of_clk_get(struct device_node *np, int index)
{
return ERR_PTR(-ENOENT);
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 19/24] clk: implement clk_hw_reparent
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
` (17 preceding siblings ...)
2022-02-20 12:47 ` [PATCH 18/24] clk: implement of_clk_hw_{onecell,simple}_get Ahmad Fatoum
@ 2022-02-20 12:47 ` Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 20/24] reset: add support for reset_control_status Ahmad Fatoum
` (5 subsequent siblings)
24 siblings, 0 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium, Ahmad Fatoum
Make porting Linux clock driver code using clk_hw_reparent easier.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
include/linux/clk.h | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/include/linux/clk.h b/include/linux/clk.h
index c0e998e54ae6..1a9c0aa69c43 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -241,6 +241,14 @@ int clk_hw_set_rate(struct clk_hw *hw, unsigned long rate);
int clk_set_parent(struct clk *clk, struct clk *parent);
int clk_hw_set_parent(struct clk_hw *hw, struct clk_hw *hwp);
+static inline void clk_hw_reparent(struct clk_hw *hw, struct clk_hw *new_parent)
+{
+ if (!hw)
+ return;
+
+ clk_hw_set_parent(hw, new_parent);
+}
+
/**
* clk_get_parent - get the parent clock source for this clock
* @clk: clock source
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 20/24] reset: add support for reset_control_status
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
` (18 preceding siblings ...)
2022-02-20 12:47 ` [PATCH 19/24] clk: implement clk_hw_reparent Ahmad Fatoum
@ 2022-02-20 12:47 ` Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 21/24] clk: stm32mp1: sync with Linux v5.17-rc1 Ahmad Fatoum
` (4 subsequent siblings)
24 siblings, 0 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium, Ahmad Fatoum
Driver code may want to query status of a reset line. Add an optional
callback for drivers to provide this.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
drivers/reset/core.c | 21 +++++++++++++++++++++
drivers/reset/reset-simple.c | 3 ++-
include/linux/reset-controller.h | 2 ++
include/linux/reset.h | 6 ++++++
4 files changed, 31 insertions(+), 1 deletion(-)
diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index 17deb3fa8fc3..4355c3415eb0 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -81,6 +81,27 @@ void reset_controller_unregister(struct reset_controller_dev *rcdev)
}
EXPORT_SYMBOL_GPL(reset_controller_unregister);
+/**
+ * reset_control_status - returns a negative errno if not supported, a
+ * positive value if the reset line is asserted, or zero if the reset
+ * line is not asserted or if the desc is NULL (optional reset).
+ * @rstc: reset controller
+ */
+int reset_control_status(struct reset_control *rstc)
+{
+ if (!rstc)
+ return 0;
+
+ if (WARN_ON(IS_ERR(rstc)))
+ return -EINVAL;
+
+ if (rstc->rcdev->ops->status)
+ return rstc->rcdev->ops->status(rstc->rcdev, rstc->id);
+
+ return -ENOTSUPP;
+}
+EXPORT_SYMBOL_GPL(reset_control_status);
+
/**
* reset_control_reset - reset the controlled device
* @rstc: reset controller
diff --git a/drivers/reset/reset-simple.c b/drivers/reset/reset-simple.c
index 082956d94dae..9db00f64f438 100644
--- a/drivers/reset/reset-simple.c
+++ b/drivers/reset/reset-simple.c
@@ -75,7 +75,7 @@ static int reset_simple_reset(struct reset_controller_dev *rcdev,
return reset_simple_deassert(rcdev, id);
}
-static int __maybe_unused reset_simple_status(struct reset_controller_dev *rcdev,
+static int reset_simple_status(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct reset_simple_data *data = to_reset_simple_data(rcdev);
@@ -93,6 +93,7 @@ const struct reset_control_ops reset_simple_ops = {
.assert = reset_simple_assert,
.deassert = reset_simple_deassert,
.reset = reset_simple_reset,
+ .status = reset_simple_status,
};
EXPORT_SYMBOL_GPL(reset_simple_ops);
diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h
index 8ace8cd41794..a9047e947e93 100644
--- a/include/linux/reset-controller.h
+++ b/include/linux/reset-controller.h
@@ -14,11 +14,13 @@ struct reset_controller_dev;
* things to reset the device
* @assert: manually assert the reset line, if supported
* @deassert: manually deassert the reset line, if supported
+ * @status: return the status of the reset line, if supported
*/
struct reset_control_ops {
int (*reset)(struct reset_controller_dev *rcdev, unsigned long id);
int (*assert)(struct reset_controller_dev *rcdev, unsigned long id);
int (*deassert)(struct reset_controller_dev *rcdev, unsigned long id);
+ int (*status)(struct reset_controller_dev *rcdev, unsigned long id);
};
struct device_node;
diff --git a/include/linux/reset.h b/include/linux/reset.h
index d25464d4bd60..d0677b1d9f63 100644
--- a/include/linux/reset.h
+++ b/include/linux/reset.h
@@ -8,6 +8,7 @@ struct reset_control;
#ifdef CONFIG_RESET_CONTROLLER
+int reset_control_status(struct reset_control *rstc);
int reset_control_reset(struct reset_control *rstc);
int reset_control_assert(struct reset_control *rstc);
int reset_control_deassert(struct reset_control *rstc);
@@ -25,6 +26,11 @@ int __must_check device_reset_all(struct device_d *dev);
#else
+static inline int reset_control_status(struct reset_control *rstc)
+{
+ return 0;
+}
+
static inline int reset_control_reset(struct reset_control *rstc)
{
return 0;
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 21/24] clk: stm32mp1: sync with Linux v5.17-rc1
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
` (19 preceding siblings ...)
2022-02-20 12:47 ` [PATCH 20/24] reset: add support for reset_control_status Ahmad Fatoum
@ 2022-02-20 12:47 ` Ahmad Fatoum
2022-02-21 10:35 ` [PATCH] fixup! " Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 22/24] regulator: core: fall back to node name if no regulator-name property Ahmad Fatoum
` (3 subsequent siblings)
24 siblings, 1 reply; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium, Ahmad Fatoum
Linux has meanwhile extended the RCC driver to support both reset and
clocks as well as peaceful co-existence with the SCMI driver.
Import these changes into barebox and remove the reset controller
handling from the old RCC reset driver.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
drivers/clk/clk-stm32mp1.c | 886 ++++++++++++++++++++---------
drivers/power/reset/Kconfig | 5 +-
drivers/power/reset/stm32-reboot.c | 60 +-
include/driver.h | 5 +
include/soc/stm32/reboot.h | 16 +
5 files changed, 652 insertions(+), 320 deletions(-)
create mode 100644 include/soc/stm32/reboot.h
diff --git a/drivers/clk/clk-stm32mp1.c b/drivers/clk/clk-stm32mp1.c
index 8f1ba96e6399..3e0c15ee32b9 100644
--- a/drivers/clk/clk-stm32mp1.c
+++ b/drivers/clk/clk-stm32mp1.c
@@ -6,14 +6,22 @@
*/
#include <linux/clk.h>
+#include <clock.h>
#include <linux/err.h>
+#include <linux/overflow.h>
#include <io.h>
#include <of.h>
#include <of_address.h>
-#include <linux/math64.h>
+#include <driver.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <soc/stm32/reboot.h>
#include <dt-bindings/clock/stm32mp1-clks.h>
+static DEFINE_SPINLOCK(rlock);
+
#define RCC_OCENSETR 0x0C
#define RCC_HSICFGR 0x18
#define RCC_RDLSICR 0x144
@@ -116,7 +124,7 @@ static const char * const cpu_src[] = {
};
static const char * const axi_src[] = {
- "ck_hsi", "ck_hse", "pll2_p", "pll3_p"
+ "ck_hsi", "ck_hse", "pll2_p"
};
static const char * const per_src[] = {
@@ -220,19 +228,19 @@ static const char * const usart6_src[] = {
};
static const char * const fdcan_src[] = {
- "ck_hse", "pll3_q", "pll4_q"
+ "ck_hse", "pll3_q", "pll4_q", "pll4_r"
};
static const char * const sai_src[] = {
- "pll4_q", "pll3_q", "i2s_ckin", "ck_per"
+ "pll4_q", "pll3_q", "i2s_ckin", "ck_per", "pll3_r"
};
static const char * const sai2_src[] = {
- "pll4_q", "pll3_q", "i2s_ckin", "ck_per", "spdif_ck_symb"
+ "pll4_q", "pll3_q", "i2s_ckin", "ck_per", "spdif_ck_symb", "pll3_r"
};
static const char * const adc12_src[] = {
- "pll4_q", "ck_per"
+ "pll4_r", "ck_per", "pll3_q"
};
static const char * const dsi_src[] = {
@@ -240,7 +248,7 @@ static const char * const dsi_src[] = {
};
static const char * const rtc_src[] = {
- "off", "ck_lse", "ck_lsi", "ck_hse_rtc"
+ "off", "ck_lse", "ck_lsi", "ck_hse"
};
static const char * const mco1_src[] = {
@@ -264,7 +272,7 @@ static const struct clk_div_table axi_div_table[] = {
static const struct clk_div_table mcu_div_table[] = {
{ 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 },
{ 4, 16 }, { 5, 32 }, { 6, 64 }, { 7, 128 },
- { 8, 512 }, { 9, 512 }, { 10, 512}, { 11, 512 },
+ { 8, 256 }, { 9, 512 }, { 10, 512}, { 11, 512 },
{ 12, 512 }, { 13, 512 }, { 14, 512}, { 15, 512 },
{ 0 },
};
@@ -312,7 +320,10 @@ struct clock_config {
int num_parents;
unsigned long flags;
void *cfg;
- struct clk * (*func)(void __iomem *base, const struct clock_config *cfg);
+ struct clk_hw * (*func)(struct device_d *dev,
+ struct clk_hw_onecell_data *clk_data,
+ void __iomem *base, spinlock_t *lock,
+ const struct clock_config *cfg);
};
#define NO_ID ~0
@@ -368,53 +379,69 @@ struct stm32_composite_cfg {
const struct stm32_mux_cfg *mux;
};
-static struct clk *
-_clk_hw_register_gate(void __iomem *base,
+static struct clk_hw *
+_clk_hw_register_gate(struct device_d *dev,
+ struct clk_hw_onecell_data *clk_data,
+ void __iomem *base, spinlock_t *lock,
const struct clock_config *cfg)
{
struct gate_cfg *gate_cfg = cfg->cfg;
- return clk_gate(cfg->name, cfg->parent_name, gate_cfg->reg_off + base,
- gate_cfg->bit_idx, cfg->flags, gate_cfg->gate_flags);
+ return clk_hw_register_gate(dev,
+ cfg->name,
+ cfg->parent_name,
+ cfg->flags,
+ gate_cfg->reg_off + base,
+ gate_cfg->bit_idx,
+ gate_cfg->gate_flags,
+ lock);
}
-static struct clk *
-_clk_hw_register_fixed_factor(void __iomem *base,
+static struct clk_hw *
+_clk_hw_register_fixed_factor(struct device_d *dev,
+ struct clk_hw_onecell_data *clk_data,
+ void __iomem *base, spinlock_t *lock,
const struct clock_config *cfg)
{
struct fixed_factor_cfg *ff_cfg = cfg->cfg;
- return clk_fixed_factor(cfg->name, cfg->parent_name, ff_cfg->mult,
- ff_cfg->div, cfg->flags);
+ return clk_hw_register_fixed_factor(dev, cfg->name, cfg->parent_name,
+ cfg->flags, ff_cfg->mult,
+ ff_cfg->div);
}
-static struct clk *
-_clk_hw_register_divider_table(void __iomem *base,
+static struct clk_hw *
+_clk_hw_register_divider_table(struct device_d *dev,
+ struct clk_hw_onecell_data *clk_data,
+ void __iomem *base, spinlock_t *lock,
const struct clock_config *cfg)
{
-
struct div_cfg *div_cfg = cfg->cfg;
- if (div_cfg->table)
- return clk_divider_table(cfg->name, cfg->parent_name, cfg->flags,
- div_cfg->reg_off + base, div_cfg->shift,
- div_cfg->width, div_cfg->table,
- div_cfg->div_flags);
- else
- return clk_divider(cfg->name, cfg->parent_name, cfg->flags,
- div_cfg->reg_off + base, div_cfg->shift,
- div_cfg->width, div_cfg->div_flags);
+ return clk_hw_register_divider_table(dev,
+ cfg->name,
+ cfg->parent_name,
+ cfg->flags,
+ div_cfg->reg_off + base,
+ div_cfg->shift,
+ div_cfg->width,
+ div_cfg->div_flags,
+ div_cfg->table,
+ lock);
}
-static struct clk *
-_clk_hw_register_mux(void __iomem *base,
+static struct clk_hw *
+_clk_hw_register_mux(struct device_d *dev,
+ struct clk_hw_onecell_data *clk_data,
+ void __iomem *base, spinlock_t *lock,
const struct clock_config *cfg)
{
struct mux_cfg *mux_cfg = cfg->cfg;
- return clk_mux(cfg->name,cfg->flags, mux_cfg->reg_off + base, mux_cfg->shift,
- mux_cfg->width, cfg->parent_names, cfg->num_parents,
- mux_cfg->mux_flags);
+ return clk_hw_register_mux(dev, cfg->name, cfg->parent_names,
+ cfg->num_parents, cfg->flags,
+ mux_cfg->reg_off + base, mux_cfg->shift,
+ mux_cfg->width, mux_cfg->mux_flags, lock);
}
/* MP1 Gate clock with set & clear registers */
@@ -430,9 +457,12 @@ static int mp1_gate_clk_enable(struct clk_hw *hw)
static void mp1_gate_clk_disable(struct clk_hw *hw)
{
struct clk_gate *gate = to_clk_gate(hw);
+ unsigned long flags = 0;
if (clk_gate_ops.is_enabled(hw)) {
- writel(BIT(gate->shift), gate->reg + RCC_CLR);
+ spin_lock_irqsave(gate->lock, flags);
+ writel_relaxed(BIT(gate->shift), gate->reg + RCC_CLR);
+ spin_unlock_irqrestore(gate->lock, flags);
}
}
@@ -442,8 +472,9 @@ static const struct clk_ops mp1_gate_clk_ops = {
.is_enabled = clk_gate_is_enabled,
};
-static struct clk_hw *_get_stm32_mux(void __iomem *base,
- const struct stm32_mux_cfg *cfg)
+static struct clk_hw *_get_stm32_mux(struct device_d *dev, void __iomem *base,
+ const struct stm32_mux_cfg *cfg,
+ spinlock_t *lock)
{
struct stm32_clk_mmux *mmux;
struct clk_mux *mux;
@@ -457,10 +488,13 @@ static struct clk_hw *_get_stm32_mux(void __iomem *base,
mmux->mux.reg = cfg->mux->reg_off + base;
mmux->mux.shift = cfg->mux->shift;
mmux->mux.width = cfg->mux->width;
+ mmux->mux.flags = cfg->mux->mux_flags;
+ mmux->mux.table = cfg->mux->table;
+ mmux->mux.lock = lock;
mmux->mmux = cfg->mmux;
mux_hw = &mmux->mux.hw;
cfg->mmux->hws[cfg->mmux->nbr_clk++] = mux_hw;
- mux = &mmux->mux;
+
} else {
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
if (!mux)
@@ -469,23 +503,23 @@ static struct clk_hw *_get_stm32_mux(void __iomem *base,
mux->reg = cfg->mux->reg_off + base;
mux->shift = cfg->mux->shift;
mux->width = cfg->mux->width;
+ mux->flags = cfg->mux->mux_flags;
+ mux->table = cfg->mux->table;
+ mux->lock = lock;
mux_hw = &mux->hw;
}
- if (cfg->ops)
- mux->hw.clk.ops = cfg->ops;
- else
- mux->hw.clk.ops = &clk_mux_ops;
-
return mux_hw;
}
-static struct clk_hw *_get_stm32_div(void __iomem *base,
- const struct stm32_div_cfg *cfg)
+static struct clk_hw *_get_stm32_div(struct device_d *dev, void __iomem *base,
+ const struct stm32_div_cfg *cfg,
+ spinlock_t *lock)
{
struct clk_divider *div;
div = kzalloc(sizeof(*div), GFP_KERNEL);
+
if (!div)
return ERR_PTR(-ENOMEM);
@@ -494,21 +528,18 @@ static struct clk_hw *_get_stm32_div(void __iomem *base,
div->width = cfg->div->width;
div->flags = cfg->div->div_flags;
div->table = cfg->div->table;
-
- if (cfg->ops)
- div->hw.clk.ops = cfg->ops;
- else
- div->hw.clk.ops = &clk_divider_ops;
+ div->lock = lock;
return &div->hw;
}
-static struct clk_gate *
-_get_stm32_gate(void __iomem *base,
- const struct stm32_gate_cfg *cfg)
+static struct clk_hw *_get_stm32_gate(struct device_d *dev, void __iomem *base,
+ const struct stm32_gate_cfg *cfg,
+ spinlock_t *lock)
{
struct stm32_clk_mgate *mgate;
struct clk_gate *gate;
+ struct clk_hw *gate_hw;
if (cfg->mgate) {
mgate = kzalloc(sizeof(*mgate), GFP_KERNEL);
@@ -518,11 +549,12 @@ _get_stm32_gate(void __iomem *base,
mgate->gate.reg = cfg->gate->reg_off + base;
mgate->gate.shift = cfg->gate->bit_idx;
mgate->gate.flags = cfg->gate->gate_flags;
+ mgate->gate.lock = lock;
mgate->mask = BIT(cfg->mgate->nbr_clk++);
mgate->mgate = cfg->mgate;
- gate = &mgate->gate;
+ gate_hw = &mgate->gate.hw;
} else {
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
@@ -532,71 +564,103 @@ _get_stm32_gate(void __iomem *base,
gate->reg = cfg->gate->reg_off + base;
gate->shift = cfg->gate->bit_idx;
gate->flags = cfg->gate->gate_flags;
+ gate->lock = lock;
+
+ gate_hw = &gate->hw;
}
-
- if (cfg->ops)
- gate->hw.clk.ops = cfg->ops;
- else
- gate->hw.clk.ops = &clk_gate_ops;
- return gate;
+ return gate_hw;
}
-static struct clk *
-clk_stm32_register_gate_ops(const char *name,
+static struct clk_hw *
+clk_stm32_register_gate_ops(struct device_d *dev,
+ const char *name,
const char *parent_name,
unsigned long flags,
void __iomem *base,
- const struct stm32_gate_cfg *cfg)
+ const struct stm32_gate_cfg *cfg,
+ spinlock_t *lock)
{
- struct clk *clk;
- struct clk_gate *gate;
+ struct clk_init_data init = { NULL };
+ struct clk_hw *hw;
int ret;
- gate = _get_stm32_gate(base, cfg);
- if (IS_ERR(gate))
+ init.name = name;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = flags;
+
+ init.ops = &clk_gate_ops;
+
+ if (cfg->ops)
+ init.ops = cfg->ops;
+
+ hw = _get_stm32_gate(dev, base, cfg, lock);
+ if (IS_ERR(hw))
return ERR_PTR(-ENOMEM);
- gate->parent = parent_name;
- clk = &gate->hw.clk;
- clk->name = name;
- clk->parent_names = &gate->parent;
- clk->num_parents = 1;
- clk->flags = flags;
+ hw->init = &init;
- ret = bclk_register(clk);
+ ret = clk_hw_register(dev, hw);
if (ret)
- clk = ERR_PTR(ret);
+ hw = ERR_PTR(ret);
- return clk;
+ return hw;
}
-static struct clk *
-clk_stm32_register_composite(const char *name, const char * const *parent_names,
+static struct clk_hw *
+clk_stm32_register_composite(struct device_d *dev,
+ const char *name, const char * const *parent_names,
int num_parents, void __iomem *base,
const struct stm32_composite_cfg *cfg,
- unsigned long flags)
+ unsigned long flags, spinlock_t *lock)
{
+ const struct clk_ops *mux_ops, *div_ops, *gate_ops;
struct clk_hw *mux_hw, *div_hw, *gate_hw;
- struct clk_gate *gate;
mux_hw = NULL;
div_hw = NULL;
gate_hw = NULL;
+ mux_ops = NULL;
+ div_ops = NULL;
+ gate_ops = NULL;
+
+ if (cfg->mux) {
+ mux_hw = _get_stm32_mux(dev, base, cfg->mux, lock);
+
+ if (!IS_ERR(mux_hw)) {
+ mux_ops = &clk_mux_ops;
+
+ if (cfg->mux->ops)
+ mux_ops = cfg->mux->ops;
+ }
+ }
+
+ if (cfg->div) {
+ div_hw = _get_stm32_div(dev, base, cfg->div, lock);
- if (cfg->mux)
- mux_hw = _get_stm32_mux(base, cfg->mux);
+ if (!IS_ERR(div_hw)) {
+ div_ops = &clk_divider_ops;
- if (cfg->div)
- div_hw = _get_stm32_div(base, cfg->div);
+ if (cfg->div->ops)
+ div_ops = cfg->div->ops;
+ }
+ }
if (cfg->gate) {
- gate = _get_stm32_gate(base, cfg->gate);
- gate_hw = &gate->hw;
+ gate_hw = _get_stm32_gate(dev, base, cfg->gate, lock);
+
+ if (!IS_ERR(gate_hw)) {
+ gate_ops = &clk_gate_ops;
+
+ if (cfg->gate->ops)
+ gate_ops = cfg->gate->ops;
+ }
}
- return clk_register_composite(name, parent_names, num_parents,
- &mux_hw->clk, &div_hw->clk, &gate_hw->clk, flags);
+ return clk_hw_register_composite(dev, name, parent_names, num_parents,
+ mux_hw, mux_ops, div_hw, div_ops,
+ gate_hw, gate_ops, flags);
}
#define to_clk_mgate(_gate) container_of(_gate, struct stm32_clk_mgate, gate)
@@ -652,7 +716,8 @@ static int clk_mmux_set_parent(struct clk_hw *hw, u8 index)
hwp = clk_hw_get_parent(hw);
for (n = 0; n < clk_mmux->mmux->nbr_clk; n++)
- clk_hw_set_parent(clk_mmux->mmux->hws[n], hw);
+ if (clk_mmux->mmux->hws[n] != hw)
+ clk_hw_reparent(clk_mmux->mmux->hws[n], hwp);
return 0;
}
@@ -660,16 +725,19 @@ static int clk_mmux_set_parent(struct clk_hw *hw, u8 index)
static const struct clk_ops clk_mmux_ops = {
.get_parent = clk_mmux_get_parent,
.set_parent = clk_mmux_set_parent,
+ .round_rate = clk_mux_round_rate,
};
/* STM32 PLL */
struct stm32_pll_obj {
+ /* lock pll enable/disable registers */
+ spinlock_t *lock;
void __iomem *reg;
- const char *parent;
- struct clk clk;
+ struct clk_hw hw;
+ struct clk_mux mux;
};
-#define to_pll(clk) container_of(clk, struct stm32_pll_obj, clk)
+#define to_pll(_hw) container_of(_hw, struct stm32_pll_obj, hw)
#define PLL_ON BIT(0)
#define PLL_RDY BIT(1)
@@ -681,30 +749,34 @@ struct stm32_pll_obj {
#define FRAC_MASK 0x1FFF
#define FRAC_SHIFT 3
#define FRACLE BIT(16)
+#define PLL_MUX_SHIFT 0
+#define PLL_MUX_WIDTH 2
-static int __pll_is_enabled(struct clk *clk)
+static int __pll_is_enabled(struct clk_hw *hw)
{
- struct stm32_pll_obj *clk_elem = to_pll(clk);
+ struct stm32_pll_obj *clk_elem = to_pll(hw);
- return readl(clk_elem->reg) & PLL_ON;
+ return readl_relaxed(clk_elem->reg) & PLL_ON;
}
#define TIMEOUT 5
static int pll_enable(struct clk_hw *hw)
{
- struct clk *clk = clk_hw_to_clk(hw);
- struct stm32_pll_obj *clk_elem = to_pll(clk);
+ struct stm32_pll_obj *clk_elem = to_pll(hw);
u32 reg;
+ unsigned long flags = 0;
unsigned int timeout = TIMEOUT;
int bit_status = 0;
- if (__pll_is_enabled(clk))
+ spin_lock_irqsave(clk_elem->lock, flags);
+
+ if (__pll_is_enabled(hw))
goto unlock;
- reg = readl(clk_elem->reg);
+ reg = readl_relaxed(clk_elem->reg);
reg |= PLL_ON;
- writel(reg, clk_elem->reg);
+ writel_relaxed(reg, clk_elem->reg);
/* We can't use readl_poll_timeout() because we can be blocked if
* someone enables this clock before clocksource changes.
@@ -712,7 +784,7 @@ static int pll_enable(struct clk_hw *hw)
* interruptions and enable op does not allow to be interrupted.
*/
do {
- bit_status = !(readl(clk_elem->reg) & PLL_RDY);
+ bit_status = !(readl_relaxed(clk_elem->reg) & PLL_RDY);
if (bit_status)
udelay(120);
@@ -720,26 +792,32 @@ static int pll_enable(struct clk_hw *hw)
} while (bit_status && --timeout);
unlock:
+ spin_unlock_irqrestore(clk_elem->lock, flags);
+
return bit_status;
}
static void pll_disable(struct clk_hw *hw)
{
- struct clk *clk = clk_hw_to_clk(hw);
- struct stm32_pll_obj *clk_elem = to_pll(clk);
+ struct stm32_pll_obj *clk_elem = to_pll(hw);
u32 reg;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(clk_elem->lock, flags);
- reg = readl(clk_elem->reg);
+ reg = readl_relaxed(clk_elem->reg);
reg &= ~PLL_ON;
- writel(reg, clk_elem->reg);
+ writel_relaxed(reg, clk_elem->reg);
+
+ spin_unlock_irqrestore(clk_elem->lock, flags);
}
-static u32 pll_frac_val(struct clk *clk)
+static u32 pll_frac_val(struct clk_hw *hw)
{
- struct stm32_pll_obj *clk_elem = to_pll(clk);
+ struct stm32_pll_obj *clk_elem = to_pll(hw);
u32 reg, frac = 0;
- reg = readl(clk_elem->reg + FRAC_OFFSET);
+ reg = readl_relaxed(clk_elem->reg + FRAC_OFFSET);
if (reg & FRACLE)
frac = (reg >> FRAC_SHIFT) & FRAC_MASK;
@@ -749,13 +827,12 @@ static u32 pll_frac_val(struct clk *clk)
static unsigned long pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
- struct clk *clk = clk_hw_to_clk(hw);
- struct stm32_pll_obj *clk_elem = to_pll(clk);
+ struct stm32_pll_obj *clk_elem = to_pll(hw);
u32 reg;
u32 frac, divm, divn;
u64 rate, rate_frac = 0;
- reg = readl(clk_elem->reg + 4);
+ reg = readl_relaxed(clk_elem->reg + 4);
divm = ((reg >> DIVM_SHIFT) & DIVM_MASK) + 1;
divn = ((reg >> DIVN_SHIFT) & DIVN_MASK) + 1;
@@ -763,7 +840,7 @@ static unsigned long pll_recalc_rate(struct clk_hw *hw,
do_div(rate, divm);
- frac = pll_frac_val(clk);
+ frac = pll_frac_val(hw);
if (frac) {
rate_frac = (u64)parent_rate * (u64)frac;
do_div(rate_frac, (divm * 8192));
@@ -772,79 +849,89 @@ static unsigned long pll_recalc_rate(struct clk_hw *hw,
return rate + rate_frac;
}
-static int pll_is_enabled(struct clk_hw *hw)
+static int pll_get_parent(struct clk_hw *hw)
{
- struct clk *clk = clk_hw_to_clk(hw);
- int ret;
+ struct stm32_pll_obj *clk_elem = to_pll(hw);
+ struct clk_hw *mux_hw = &clk_elem->mux.hw;
- ret = __pll_is_enabled(clk);
+ mux_hw->clk = hw->clk;
- return ret;
+ return clk_mux_ops.get_parent(mux_hw);
}
static const struct clk_ops pll_ops = {
.enable = pll_enable,
.disable = pll_disable,
.recalc_rate = pll_recalc_rate,
- .is_enabled = pll_is_enabled,
+ .is_enabled = __pll_is_enabled,
+ .get_parent = pll_get_parent,
};
-static struct clk *clk_register_pll(const char *name,
- const char *parent_name,
- void __iomem *reg,
- unsigned long flags)
+static struct clk_hw *clk_register_pll(struct device_d *dev, const char *name,
+ const char * const *parent_names,
+ int num_parents,
+ void __iomem *reg,
+ void __iomem *mux_reg,
+ unsigned long flags,
+ spinlock_t *lock)
{
struct stm32_pll_obj *element;
- struct clk *clk;
+ struct clk_init_data init;
+ struct clk_hw *hw;
int err;
element = kzalloc(sizeof(*element), GFP_KERNEL);
if (!element)
return ERR_PTR(-ENOMEM);
- element->parent = parent_name;
-
- clk = &element->clk;
+ init.name = name;
+ init.ops = &pll_ops;
+ init.flags = flags;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
- clk->name = name;
- clk->ops = &pll_ops;
- clk->flags = flags;
- clk->parent_names = &element->parent;
- clk->num_parents = 1;
+ element->mux.lock = lock;
+ element->mux.reg = mux_reg;
+ element->mux.shift = PLL_MUX_SHIFT;
+ element->mux.width = PLL_MUX_WIDTH;
+ element->mux.flags = CLK_MUX_READ_ONLY;
+ element->mux.reg = mux_reg;
+ element->hw.init = &init;
element->reg = reg;
+ element->lock = lock;
- err = bclk_register(clk);
+ hw = &element->hw;
+ err = clk_hw_register(dev, hw);
- if (err) {
- kfree(element);
+ if (err)
return ERR_PTR(err);
- }
- return clk;
+ return hw;
}
/* Kernel Timer */
struct timer_cker {
+ /* lock the kernel output divider register */
+ spinlock_t *lock;
void __iomem *apbdiv;
void __iomem *timpre;
- const char *parent;
- struct clk clk;
+ struct clk_hw hw;
};
-#define to_timer_cker(_hw) container_of(_hw, struct timer_cker, clk)
+#define to_timer_cker(_hw) container_of(_hw, struct timer_cker, hw)
#define APB_DIV_MASK 0x07
#define TIM_PRE_MASK 0x01
-static unsigned long __bestmult(struct clk *clk, unsigned long rate,
+static unsigned long __bestmult(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
- struct timer_cker *tim_ker = to_timer_cker(clk);
+ struct timer_cker *tim_ker = to_timer_cker(hw);
u32 prescaler;
unsigned int mult = 0;
- prescaler = readl(tim_ker->apbdiv) & APB_DIV_MASK;
+ prescaler = readl_relaxed(tim_ker->apbdiv) & APB_DIV_MASK;
if (prescaler < 2)
return 1;
@@ -859,8 +946,7 @@ static unsigned long __bestmult(struct clk *clk, unsigned long rate,
static long timer_ker_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
- struct clk *clk = clk_hw_to_clk(hw);
- unsigned long factor = __bestmult(clk, rate, *parent_rate);
+ unsigned long factor = __bestmult(hw, rate, *parent_rate);
return *parent_rate * factor;
}
@@ -868,23 +954,26 @@ static long timer_ker_round_rate(struct clk_hw *hw, unsigned long rate,
static int timer_ker_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
- struct clk *clk = clk_hw_to_clk(hw);
- struct timer_cker *tim_ker = to_timer_cker(clk);
- unsigned long factor = __bestmult(clk, rate, parent_rate);
+ struct timer_cker *tim_ker = to_timer_cker(hw);
+ unsigned long flags = 0;
+ unsigned long factor = __bestmult(hw, rate, parent_rate);
int ret = 0;
+ spin_lock_irqsave(tim_ker->lock, flags);
+
switch (factor) {
case 1:
break;
case 2:
- writel(0, tim_ker->timpre);
+ writel_relaxed(0, tim_ker->timpre);
break;
case 4:
- writel(1, tim_ker->timpre);
+ writel_relaxed(1, tim_ker->timpre);
break;
default:
ret = -EINVAL;
}
+ spin_unlock_irqrestore(tim_ker->lock, flags);
return ret;
}
@@ -892,14 +981,13 @@ static int timer_ker_set_rate(struct clk_hw *hw, unsigned long rate,
static unsigned long timer_ker_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
- struct clk *clk = clk_hw_to_clk(hw);
- struct timer_cker *tim_ker = to_timer_cker(clk);
+ struct timer_cker *tim_ker = to_timer_cker(hw);
u32 prescaler, timpre;
u32 mul;
- prescaler = readl(tim_ker->apbdiv) & APB_DIV_MASK;
+ prescaler = readl_relaxed(tim_ker->apbdiv) & APB_DIV_MASK;
- timpre = readl(tim_ker->timpre) & TIM_PRE_MASK;
+ timpre = readl_relaxed(tim_ker->timpre) & TIM_PRE_MASK;
if (!prescaler)
return parent_rate;
@@ -916,51 +1004,59 @@ static const struct clk_ops timer_ker_ops = {
};
-static struct clk *clk_register_cktim(const char *name,
+static struct clk_hw *clk_register_cktim(struct device_d *dev, const char *name,
const char *parent_name,
unsigned long flags,
void __iomem *apbdiv,
- void __iomem *timpre)
+ void __iomem *timpre,
+ spinlock_t *lock)
{
struct timer_cker *tim_ker;
- struct clk *clk;
+ struct clk_init_data init;
+ struct clk_hw *hw;
int err;
tim_ker = kzalloc(sizeof(*tim_ker), GFP_KERNEL);
if (!tim_ker)
return ERR_PTR(-ENOMEM);
- clk = &tim_ker->clk;
- tim_ker->parent = parent_name;
- clk->name = name;
- clk->parent_names = &tim_ker->parent;
- clk->num_parents = 1;
- clk->ops = &timer_ker_ops;
- clk->flags = flags;
+ init.name = name;
+ init.ops = &timer_ker_ops;
+ init.flags = flags;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ tim_ker->hw.init = &init;
+ tim_ker->lock = lock;
tim_ker->apbdiv = apbdiv;
tim_ker->timpre = timpre;
- err = bclk_register(clk);
- if (err) {
- kfree(tim_ker);
+ hw = &tim_ker->hw;
+ err = clk_hw_register(dev, hw);
+
+ if (err)
return ERR_PTR(err);
- }
- return clk;
+ return hw;
}
struct stm32_pll_cfg {
u32 offset;
+ u32 muxoff;
};
-static struct clk *_clk_register_pll(void __iomem *base,
- const struct clock_config *cfg)
+static struct clk_hw *_clk_register_pll(struct device_d *dev,
+ struct clk_hw_onecell_data *clk_data,
+ void __iomem *base, spinlock_t *lock,
+ const struct clock_config *cfg)
{
struct stm32_pll_cfg *stm_pll_cfg = cfg->cfg;
- return clk_register_pll(cfg->name, cfg->parent_name,
- base + stm_pll_cfg->offset, cfg->flags);
+ return clk_register_pll(dev, cfg->name, cfg->parent_names,
+ cfg->num_parents,
+ base + stm_pll_cfg->offset,
+ base + stm_pll_cfg->muxoff,
+ cfg->flags, lock);
}
struct stm32_cktim_cfg {
@@ -968,31 +1064,42 @@ struct stm32_cktim_cfg {
u32 offset_timpre;
};
-static struct clk *_clk_register_cktim(void __iomem *base,
- const struct clock_config *cfg)
+static struct clk_hw *_clk_register_cktim(struct device_d *dev,
+ struct clk_hw_onecell_data *clk_data,
+ void __iomem *base, spinlock_t *lock,
+ const struct clock_config *cfg)
{
struct stm32_cktim_cfg *cktim_cfg = cfg->cfg;
- return clk_register_cktim(cfg->name, cfg->parent_name, cfg->flags,
+ return clk_register_cktim(dev, cfg->name, cfg->parent_name, cfg->flags,
cktim_cfg->offset_apbdiv + base,
- cktim_cfg->offset_timpre + base);
+ cktim_cfg->offset_timpre + base, lock);
}
-static struct clk *
-_clk_stm32_register_gate(void __iomem *base,
+static struct clk_hw *
+_clk_stm32_register_gate(struct device_d *dev,
+ struct clk_hw_onecell_data *clk_data,
+ void __iomem *base, spinlock_t *lock,
const struct clock_config *cfg)
{
- return clk_stm32_register_gate_ops(cfg->name, cfg->parent_name,
- cfg->flags, base, cfg->cfg);
+ return clk_stm32_register_gate_ops(dev,
+ cfg->name,
+ cfg->parent_name,
+ cfg->flags,
+ base,
+ cfg->cfg,
+ lock);
}
-static struct clk *
-_clk_stm32_register_composite(void __iomem *base,
+static struct clk_hw *
+_clk_stm32_register_composite(struct device_d *dev,
+ struct clk_hw_onecell_data *clk_data,
+ void __iomem *base, spinlock_t *lock,
const struct clock_config *cfg)
{
- return clk_stm32_register_composite(cfg->name, cfg->parent_names,
+ return clk_stm32_register_composite(dev, cfg->name, cfg->parent_names,
cfg->num_parents, base, cfg->cfg,
- cfg->flags);
+ cfg->flags, lock);
}
#define GATE(_id, _name, _parent, _flags, _offset, _bit_idx, _gate_flags)\
@@ -1059,14 +1166,16 @@ _clk_stm32_register_composite(void __iomem *base,
.func = _clk_hw_register_mux,\
}
-#define PLL(_id, _name, _parent, _flags, _offset)\
+#define PLL(_id, _name, _parents, _flags, _offset_p, _offset_mux)\
{\
.id = _id,\
.name = _name,\
- .parent_name = _parent,\
- .flags = _flags,\
+ .parent_names = _parents,\
+ .num_parents = ARRAY_SIZE(_parents),\
+ .flags = CLK_IGNORE_UNUSED | (_flags),\
.cfg = &(struct stm32_pll_cfg) {\
- .offset = _offset,\
+ .offset = _offset_p,\
+ .muxoff = _offset_mux,\
},\
.func = _clk_register_pll,\
}
@@ -1099,13 +1208,14 @@ _clk_stm32_register_composite(void __iomem *base,
.func = _clk_stm32_register_gate,\
}
-#define _STM32_GATE(_gate_offset, _gate_bit_idx, _gate_flags, _ops)\
+#define _STM32_GATE(_gate_offset, _gate_bit_idx, _gate_flags, _mgate, _ops)\
(&(struct stm32_gate_cfg) {\
&(struct gate_cfg) {\
.reg_off = _gate_offset,\
.bit_idx = _gate_bit_idx,\
.gate_flags = _gate_flags,\
},\
+ .mgate = _mgate,\
.ops = _ops,\
})
@@ -1114,11 +1224,11 @@ _clk_stm32_register_composite(void __iomem *base,
#define _GATE(_gate_offset, _gate_bit_idx, _gate_flags)\
_STM32_GATE(_gate_offset, _gate_bit_idx, _gate_flags,\
- NULL)\
+ NULL, NULL)\
#define _GATE_MP1(_gate_offset, _gate_bit_idx, _gate_flags)\
_STM32_GATE(_gate_offset, _gate_bit_idx, _gate_flags,\
- &mp1_gate_clk_ops)\
+ NULL, &mp1_gate_clk_ops)\
#define _MGATE_MP1(_mgate)\
.gate = &per_gate_cfg[_mgate]
@@ -1191,10 +1301,11 @@ _clk_stm32_register_composite(void __iomem *base,
MGATE_MP1(_id, _name, _parent, _flags, _mgate)
#define KCLK(_id, _name, _parents, _flags, _mgate, _mmux)\
- COMPOSITE(_id, _name, _parents, CLK_OPS_PARENT_ENABLE | _flags,\
- _MGATE_MP1(_mgate),\
- _MMUX(_mmux),\
- _NO_DIV)
+ COMPOSITE(_id, _name, _parents, CLK_OPS_PARENT_ENABLE |\
+ CLK_SET_RATE_NO_REPARENT | _flags,\
+ _MGATE_MP1(_mgate),\
+ _MMUX(_mmux),\
+ _NO_DIV)
enum {
G_SAI1,
@@ -1306,6 +1417,7 @@ enum {
G_CRYP1,
G_HASH1,
G_BKPSRAM,
+ G_DDRPERFM,
G_LAST
};
@@ -1392,6 +1504,7 @@ static struct stm32_gate_cfg per_gate_cfg[G_LAST] = {
K_GATE(G_STGENRO, RCC_APB4ENSETR, 20, 0),
K_MGATE(G_USBPHY, RCC_APB4ENSETR, 16, 0),
K_GATE(G_IWDG2, RCC_APB4ENSETR, 15, 0),
+ K_GATE(G_DDRPERFM, RCC_APB4ENSETR, 8, 0),
K_MGATE(G_DSI, RCC_APB4ENSETR, 4, 0),
K_MGATE(G_LTDC, RCC_APB4ENSETR, 0, 0),
@@ -1533,7 +1646,7 @@ static const struct stm32_mux_cfg ker_mux_cfg[M_LAST] = {
K_MMUX(M_ETHCK, RCC_ETHCKSELR, 0, 2, 0),
K_MMUX(M_I2C46, RCC_I2C46CKSELR, 0, 3, 0),
- /* Kernel simple mux */
+ /* Kernel simple mux */
K_MUX(M_RNG2, RCC_RNG2CKSELR, 0, 2, 0),
K_MUX(M_SDMMC3, RCC_SDMMC3CKSELR, 0, 3, 0),
K_MUX(M_FMC, RCC_FMCCKSELR, 0, 2, 0),
@@ -1559,34 +1672,26 @@ static const struct stm32_mux_cfg ker_mux_cfg[M_LAST] = {
};
static const struct clock_config stm32mp1_clock_cfg[] = {
- /* Oscillator divider */
- DIV(NO_ID, "clk-hsi-div", "clk-hsi", 0, RCC_HSICFGR, 0, 2,
- CLK_DIVIDER_READ_ONLY),
-
/* External / Internal Oscillators */
GATE_MP1(CK_HSE, "ck_hse", "clk-hse", 0, RCC_OCENSETR, 8, 0),
- GATE_MP1(CK_CSI, "ck_csi", "clk-csi", 0, RCC_OCENSETR, 4, 0),
- GATE_MP1(CK_HSI, "ck_hsi", "clk-hsi-div", 0, RCC_OCENSETR, 0, 0),
+ /* ck_csi is used by IO compensation and should be critical */
+ GATE_MP1(CK_CSI, "ck_csi", "clk-csi", CLK_IS_CRITICAL,
+ RCC_OCENSETR, 4, 0),
+ COMPOSITE(CK_HSI, "ck_hsi", PARENT("clk-hsi"), 0,
+ _GATE_MP1(RCC_OCENSETR, 0, 0),
+ _NO_MUX,
+ _DIV(RCC_HSICFGR, 0, 2, CLK_DIVIDER_POWER_OF_TWO |
+ CLK_DIVIDER_READ_ONLY, NULL)),
GATE(CK_LSI, "ck_lsi", "clk-lsi", 0, RCC_RDLSICR, 0, 0),
GATE(CK_LSE, "ck_lse", "clk-lse", 0, RCC_BDCR, 0, 0),
FIXED_FACTOR(CK_HSE_DIV2, "clk-hse-div2", "ck_hse", 0, 1, 2),
- /* ref clock pll */
- MUX(NO_ID, "ref1", ref12_parents, CLK_OPS_PARENT_ENABLE, RCC_RCK12SELR,
- 0, 2, CLK_MUX_READ_ONLY),
-
- MUX(NO_ID, "ref3", ref3_parents, CLK_OPS_PARENT_ENABLE, RCC_RCK3SELR,
- 0, 2, CLK_MUX_READ_ONLY),
-
- MUX(NO_ID, "ref4", ref4_parents, CLK_OPS_PARENT_ENABLE, RCC_RCK4SELR,
- 0, 2, CLK_MUX_READ_ONLY),
-
/* PLLs */
- PLL(PLL1, "pll1", "ref1", CLK_IGNORE_UNUSED, RCC_PLL1CR),
- PLL(PLL2, "pll2", "ref1", CLK_IGNORE_UNUSED, RCC_PLL2CR),
- PLL(PLL3, "pll3", "ref3", CLK_IGNORE_UNUSED, RCC_PLL3CR),
- PLL(PLL4, "pll4", "ref4", CLK_IGNORE_UNUSED, RCC_PLL4CR),
+ PLL(PLL1, "pll1", ref12_parents, 0, RCC_PLL1CR, RCC_RCK12SELR),
+ PLL(PLL2, "pll2", ref12_parents, 0, RCC_PLL2CR, RCC_RCK12SELR),
+ PLL(PLL3, "pll3", ref3_parents, 0, RCC_PLL3CR, RCC_RCK3SELR),
+ PLL(PLL4, "pll4", ref4_parents, 0, RCC_PLL4CR, RCC_RCK4SELR),
/* ODF */
COMPOSITE(PLL1_P, "pll1_p", PARENT("pll1"), 0,
@@ -1801,6 +1906,7 @@ static const struct clock_config stm32mp1_clock_cfg[] = {
PCLK(CRC1, "crc1", "ck_axi", 0, G_CRC1),
PCLK(USBH, "usbh", "ck_axi", 0, G_USBH),
PCLK(ETHSTP, "ethstp", "ck_axi", 0, G_ETHSTP),
+ PCLK(DDRPERFM, "ddrperfm", "pclk4", 0, G_DDRPERFM),
/* Kernel clocks */
KCLK(SDMMC1_K, "sdmmc1_k", sdmmc12_src, 0, G_SDMMC1, M_SDMMC12),
@@ -1857,18 +1963,17 @@ static const struct clock_config stm32mp1_clock_cfg[] = {
MGATE_MP1(GPU_K, "gpu_k", "pll2_q", 0, G_GPU),
MGATE_MP1(DAC12_K, "dac12_k", "ck_lsi", 0, G_DAC12),
- COMPOSITE(ETHPTP_K, "ethptp_k", eth_src, CLK_OPS_PARENT_ENABLE,
+ COMPOSITE(ETHPTP_K, "ethptp_k", eth_src, CLK_OPS_PARENT_ENABLE |
+ CLK_SET_RATE_NO_REPARENT,
_NO_GATE,
_MMUX(M_ETHCK),
_DIV(RCC_ETHCKSELR, 4, 4, 0, NULL)),
/* RTC clock */
- DIV(NO_ID, "ck_hse_rtc", "ck_hse", 0, RCC_RTCDIVR, 0, 7, 0),
-
COMPOSITE(RTC, "ck_rtc", rtc_src, CLK_OPS_PARENT_ENABLE |
- CLK_SET_RATE_PARENT,
- _GATE(RCC_BDCR, 20, 0),
- _MUX(RCC_BDCR, 16, 2, 0),
+ CLK_SET_RATE_PARENT,
+ _NO_GATE,
+ _NO_MUX,
_NO_DIV),
/* MCO clocks */
@@ -1894,16 +1999,76 @@ static const struct clock_config stm32mp1_clock_cfg[] = {
_DIV(RCC_DBGCFGR, 0, 3, 0, ck_trace_div_table)),
};
-struct stm32_clock_match_data {
+static const u32 stm32mp1_clock_secured[] = {
+ CK_HSE,
+ CK_HSI,
+ CK_CSI,
+ CK_LSI,
+ CK_LSE,
+ PLL1,
+ PLL2,
+ PLL1_P,
+ PLL2_P,
+ PLL2_Q,
+ PLL2_R,
+ CK_MPU,
+ CK_AXI,
+ SPI6,
+ I2C4,
+ I2C6,
+ USART1,
+ RTCAPB,
+ TZC1,
+ TZC2,
+ TZPC,
+ IWDG1,
+ BSEC,
+ STGEN,
+ GPIOZ,
+ CRYP1,
+ HASH1,
+ RNG1,
+ BKPSRAM,
+ RNG1_K,
+ STGEN_K,
+ SPI6_K,
+ I2C4_K,
+ I2C6_K,
+ USART1_K,
+ RTC,
+};
+
+static bool stm32_check_security(const struct clock_config *cfg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(stm32mp1_clock_secured); i++)
+ if (cfg->id == stm32mp1_clock_secured[i])
+ return true;
+ return false;
+}
+
+struct stm32_rcc_match_data {
const struct clock_config *cfg;
unsigned int num;
unsigned int maxbinding;
+ u32 clear_offset;
+ bool (*check_security)(const struct clock_config *cfg);
+};
+
+static struct stm32_rcc_match_data stm32mp1_data = {
+ .cfg = stm32mp1_clock_cfg,
+ .num = ARRAY_SIZE(stm32mp1_clock_cfg),
+ .maxbinding = STM32MP1_LAST_CLK,
+ .clear_offset = RCC_CLR,
};
-static struct stm32_clock_match_data stm32mp1_data = {
+static struct stm32_rcc_match_data stm32mp1_data_secure = {
.cfg = stm32mp1_clock_cfg,
.num = ARRAY_SIZE(stm32mp1_clock_cfg),
.maxbinding = STM32MP1_LAST_CLK,
+ .clear_offset = RCC_CLR,
+ .check_security = &stm32_check_security
};
static const struct of_device_id stm32mp1_match_data[] = {
@@ -1911,85 +2076,282 @@ static const struct of_device_id stm32mp1_match_data[] = {
.compatible = "st,stm32mp1-rcc",
.data = &stm32mp1_data,
},
+ {
+ .compatible = "st,stm32mp1-rcc-secure",
+ .data = &stm32mp1_data_secure,
+ },
{ }
};
-static int stm32_register_hw_clk(struct clk_onecell_data *clk_data,
- void __iomem *base,
+static int stm32_register_hw_clk(struct device_d *dev,
+ struct clk_hw_onecell_data *clk_data,
+ void __iomem *base, spinlock_t *lock,
const struct clock_config *cfg)
{
- struct clk *clk = ERR_PTR(-ENOENT);
+ struct clk_hw **hws;
+ struct clk_hw *hw = ERR_PTR(-ENOENT);
+
+ hws = clk_data->hws;
if (cfg->func)
- clk = (*cfg->func)(base, cfg);
+ hw = (*cfg->func)(dev, clk_data, base, lock, cfg);
- if (IS_ERR(clk)) {
+ if (IS_ERR(hw)) {
pr_err("Unable to register %s\n", cfg->name);
- return PTR_ERR(clk);
+ return PTR_ERR(hw);
}
if (cfg->id != NO_ID)
- clk_data->clks[cfg->id] = clk;
+ hws[cfg->id] = hw;
return 0;
}
-static int stm32_rcc_init(struct device_node *np,
- void __iomem *base,
- const struct of_device_id *match_data)
+#define STM32_RESET_ID_MASK GENMASK(15, 0)
+
+struct stm32_reset_data {
+ /* reset lock */
+ spinlock_t lock;
+ struct reset_controller_dev rcdev;
+ void __iomem *membase;
+ u32 clear_offset;
+};
+
+static inline struct stm32_reset_data *
+to_stm32_reset_data(struct reset_controller_dev *rcdev)
{
- struct clk_onecell_data *clk_data;
- struct clk **clks;
- const struct of_device_id *match;
- const struct stm32_clock_match_data *data;
- int err, n, max_binding;
+ return container_of(rcdev, struct stm32_reset_data, rcdev);
+}
- match = of_match_node(match_data, np);
- if (!match) {
- pr_err("%s: match data not found\n", __func__);
- return -ENODEV;
+static int stm32_reset_update(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct stm32_reset_data *data = to_stm32_reset_data(rcdev);
+ int reg_width = sizeof(u32);
+ int bank = id / (reg_width * BITS_PER_BYTE);
+ int offset = id % (reg_width * BITS_PER_BYTE);
+
+ if (data->clear_offset) {
+ void __iomem *addr;
+
+ addr = data->membase + (bank * reg_width);
+ if (!assert)
+ addr += data->clear_offset;
+
+ writel(BIT(offset), addr);
+
+ } else {
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&data->lock, flags);
+
+ reg = readl(data->membase + (bank * reg_width));
+
+ if (assert)
+ reg |= BIT(offset);
+ else
+ reg &= ~BIT(offset);
+
+ writel(reg, data->membase + (bank * reg_width));
+
+ spin_unlock_irqrestore(&data->lock, flags);
}
- data = match->data;
+ return 0;
+}
+
+static int stm32_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return stm32_reset_update(rcdev, id, true);
+}
+
+static int stm32_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return stm32_reset_update(rcdev, id, false);
+}
+
+static int stm32_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct stm32_reset_data *data = to_stm32_reset_data(rcdev);
+ int reg_width = sizeof(u32);
+ int bank = id / (reg_width * BITS_PER_BYTE);
+ int offset = id % (reg_width * BITS_PER_BYTE);
+ u32 reg;
+
+ reg = readl(data->membase + (bank * reg_width));
+
+ return !!(reg & BIT(offset));
+}
+
+static const struct reset_control_ops stm32_reset_ops = {
+ .assert = stm32_reset_assert,
+ .deassert = stm32_reset_deassert,
+ .status = stm32_reset_status,
+};
+
+static int stm32_rcc_reset_init(struct device_d *dev, void __iomem *base,
+ const struct of_device_id *match)
+{
+ const struct stm32_rcc_match_data *data = match->data;
+ struct stm32_reset_data *reset_data = NULL;
+
+ reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL);
+ if (!reset_data)
+ return -ENOMEM;
+
+ reset_data->membase = base;
+ reset_data->rcdev.ops = &stm32_reset_ops;
+ reset_data->rcdev.of_node = dev_of_node(dev);
+ reset_data->rcdev.nr_resets = STM32_RESET_ID_MASK;
+ reset_data->clear_offset = data->clear_offset;
+
+ return reset_controller_register(&reset_data->rcdev);
+}
+
+static int stm32_rcc_clock_init(struct device_d *dev, void __iomem *base,
+ const struct of_device_id *match)
+{
+ const struct stm32_rcc_match_data *data = match->data;
+ struct clk_hw_onecell_data *clk_data;
+ struct clk_hw **hws;
+ int err, n, max_binding;
+
+ max_binding = data->maxbinding;
- max_binding = data->maxbinding;
+ clk_data = kzalloc(struct_size(clk_data, hws, max_binding),
+ GFP_KERNEL);
+ if (!clk_data)
+ return -ENOMEM;
- clk_data = xzalloc(sizeof(*clk_data));
- clk_data->clks = xzalloc(sizeof(void *) * max_binding);
- clk_data->clk_num = max_binding;
+ clk_data->num = max_binding;
- clks = clk_data->clks;
+ hws = clk_data->hws;
for (n = 0; n < max_binding; n++)
- clks[n] = ERR_PTR(-ENOENT);
+ hws[n] = ERR_PTR(-ENOENT);
for (n = 0; n < data->num; n++) {
- err = stm32_register_hw_clk(clk_data, base,
+ if (data->check_security && data->check_security(&data->cfg[n]))
+ continue;
+
+ err = stm32_register_hw_clk(dev, clk_data, base, &rlock,
&data->cfg[n]);
if (err) {
- pr_err("%s: can't register %s\n", __func__,
- data->cfg[n].name);
-
- kfree(clk_data);
+ dev_err(dev, "Can't register clk %s: %d\n",
+ data->cfg[n].name, err);
return err;
}
}
- return of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
+ return of_clk_add_hw_provider(dev_of_node(dev), of_clk_hw_onecell_get, clk_data);
}
-static void stm32mp1_rcc_init(struct device_node *np)
+static int stm32_rcc_init(struct device_d *dev, void __iomem *base,
+ const struct of_device_id *match_data)
+{
+ const struct of_device_id *match;
+ int err;
+
+ match = of_match_node(match_data, dev_of_node(dev));
+ if (!match) {
+ dev_err(dev, "match data not found\n");
+ return -ENODEV;
+ }
+
+ /* RCC Reset Configuration */
+ err = stm32_rcc_reset_init(dev, base, match);
+ if (err) {
+ pr_err("stm32mp1 reset failed to initialize\n");
+ return err;
+ }
+
+ /* RCC Clock Configuration */
+ err = stm32_rcc_clock_init(dev, base, match);
+ if (err) {
+ pr_err("stm32mp1 clock failed to initialize\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int stm32mp1_rcc_init(struct device_d *dev)
{
void __iomem *base;
+ int ret;
- base = of_iomap(np, 0);
+ base = of_iomap(dev_of_node(dev), 0);
if (!base) {
- pr_err("%pOFn: unable to map resource", np);
- return;
+ dev_err(dev, "unable to map resource\n");
+ return -ENOMEM;
}
- stm32_rcc_init(np, base, stm32mp1_match_data);
+ ret = stm32_rcc_init(dev, base, stm32mp1_match_data);
+ if (ret)
+ return ret;
+
+ stm32mp_system_restart_init(base);
+ return 0;
}
-CLK_OF_DECLARE_DRIVER(stm32mp1_rcc, "st,stm32mp1-rcc", stm32mp1_rcc_init);
+static int get_clock_deps(struct device_d *dev)
+{
+ static const char * const clock_deps_name[] = {
+ "hsi", "hse", "csi", "lsi", "lse",
+ };
+ size_t deps_size = sizeof(struct clk *) * ARRAY_SIZE(clock_deps_name);
+ struct clk **clk_deps;
+ int i;
+
+ clk_deps = kzalloc(deps_size, GFP_KERNEL);
+ if (!clk_deps)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(clock_deps_name); i++) {
+ struct clk *clk = of_clk_get_by_name(dev_of_node(dev),
+ clock_deps_name[i]);
+
+ if (IS_ERR(clk)) {
+ if (PTR_ERR(clk) != -EINVAL && PTR_ERR(clk) != -ENOENT)
+ return PTR_ERR(clk);
+ } else {
+ /* Device gets a reference count on the clock */
+ clk_deps[i] = clk_get(dev, __clk_get_name(clk));
+ clk_put(clk);
+ }
+ }
+
+ return 0;
+}
+
+static int stm32mp1_rcc_clocks_probe(struct device_d *dev)
+{
+ int ret = get_clock_deps(dev);
+
+ if (!ret)
+ ret = stm32mp1_rcc_init(dev);
+
+ return ret;
+}
+
+static void stm32mp1_rcc_clocks_remove(struct device_d *dev)
+{
+ struct device_node *child, *np = dev_of_node(dev);
+
+ for_each_available_child_of_node(np, child)
+ of_clk_del_provider(child);
+}
+
+static struct driver_d stm32mp1_rcc_clocks_driver = {
+ .name = "stm32mp1_rcc",
+ .of_compatible = stm32mp1_match_data,
+ .probe = stm32mp1_rcc_clocks_probe,
+ .remove = stm32mp1_rcc_clocks_remove,
+};
+
+core_platform_driver(stm32mp1_rcc_clocks_driver);
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index 40cbc4a3dffd..f931c117f402 100644
--- a/drivers/power/reset/Kconfig
+++ b/drivers/power/reset/Kconfig
@@ -64,7 +64,8 @@ config POWER_RESET_HTIF_POWEROFF
supporting the UC Berkely Host/Target Interface (HTIF).
config RESET_STM32
- bool "STM32 Reset Driver"
+ bool "STM32 restart Driver"
depends on ARCH_STM32MP || COMPILE_TEST
help
- This enables the reset controller driver for STM32MP1.
+ This enables support for restarting and reset source
+ computation on the STM32MP1.
diff --git a/drivers/power/reset/stm32-reboot.c b/drivers/power/reset/stm32-reboot.c
index e625ba27fff6..809531e71396 100644
--- a/drivers/power/reset/stm32-reboot.c
+++ b/drivers/power/reset/stm32-reboot.c
@@ -6,12 +6,11 @@
*/
#include <common.h>
-#include <init.h>
#include <linux/err.h>
-#include <linux/reset-controller.h>
#include <restart.h>
#include <reset_source.h>
#include <asm/io.h>
+#include <soc/stm32/reboot.h>
#define RCC_CL 0x4
@@ -42,15 +41,9 @@ struct stm32_reset_reason {
struct stm32_reset {
void __iomem *base;
- struct reset_controller_dev rcdev;
struct restart_handler restart;
};
-static struct stm32_reset *to_stm32_reset(struct reset_controller_dev *rcdev)
-{
- return container_of(rcdev, struct stm32_reset, rcdev);
-}
-
static u32 stm32_reset_status(struct stm32_reset *priv, unsigned long bank)
{
return readl(priv->base + bank);
@@ -115,64 +108,19 @@ static void stm32_set_reset_reason(struct stm32_reset *priv,
reset_source_to_string(type), reg);
}
-static int stm32_reset_assert(struct reset_controller_dev *rcdev,
- unsigned long id)
-{
- stm32_reset(to_stm32_reset(rcdev), id, true);
- return 0;
-}
-
-static int stm32_reset_deassert(struct reset_controller_dev *rcdev,
- unsigned long id)
-{
- stm32_reset(to_stm32_reset(rcdev), id, false);
- return 0;
-}
-
-static const struct reset_control_ops stm32_reset_ops = {
- .assert = stm32_reset_assert,
- .deassert = stm32_reset_deassert,
-};
-
-static int stm32_reset_probe(struct device_d *dev)
+void stm32mp_system_restart_init(void __iomem *base)
{
struct stm32_reset *priv;
- struct resource *iores;
- int ret;
priv = xzalloc(sizeof(*priv));
- iores = dev_request_mem_resource(dev, 0);
- if (IS_ERR(iores))
- return PTR_ERR(iores);
-
- priv->base = IOMEM(iores->start);
- priv->rcdev.nr_resets = (iores->end - iores->start) * BITS_PER_BYTE;
- priv->rcdev.ops = &stm32_reset_ops;
- priv->rcdev.of_node = dev->device_node;
+ priv->base = base;
priv->restart.name = "stm32-rcc";
priv->restart.restart = stm32mp_rcc_restart_handler;
priv->restart.priority = 200;
- ret = restart_handler_register(&priv->restart);
- if (ret)
- dev_warn(dev, "Cannot register restart handler\n");
+ restart_handler_register(&priv->restart);
stm32_set_reset_reason(priv, stm32mp_reset_reasons);
-
- return reset_controller_register(&priv->rcdev);
}
-
-static const struct of_device_id stm32_rcc_reset_dt_ids[] = {
- { .compatible = "st,stm32mp1-rcc" },
- { /* sentinel */ },
-};
-
-static struct driver_d stm32_rcc_reset_driver = {
- .name = "stm32mp_rcc_reset",
- .probe = stm32_reset_probe,
- .of_compatible = DRV_OF_COMPAT(stm32_rcc_reset_dt_ids),
-};
-
-postcore_platform_driver(stm32_rcc_reset_driver);
diff --git a/include/driver.h b/include/driver.h
index e59f0a164d42..1ee255c9ad0c 100644
--- a/include/driver.h
+++ b/include/driver.h
@@ -598,4 +598,9 @@ int device_match_of_modalias(struct device_d *dev, struct driver_d *drv);
struct device_d *device_find_child(struct device_d *parent, void *data,
int (*match)(struct device_d *dev, void *data));
+static inline struct device_node *dev_of_node(struct device_d *dev)
+{
+ return dev->device_node;
+}
+
#endif /* DRIVER_H */
diff --git a/include/soc/stm32/reboot.h b/include/soc/stm32/reboot.h
new file mode 100644
index 000000000000..d6c731f59f74
--- /dev/null
+++ b/include/soc/stm32/reboot.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __SOC_STM32_REBOOT_H_
+#define __SOC_STM32_REBOOT_H_
+
+#include <linux/compiler.h>
+
+#ifdef CONFIG_RESET_STM32
+void stm32mp_system_restart_init(void __iomem *rcc);
+#else
+static inline void stm32mp_system_restart_init(void __iomem *rcc)
+{
+}
+#endif
+
+#endif
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 22/24] regulator: core: fall back to node name if no regulator-name property
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
` (20 preceding siblings ...)
2022-02-20 12:47 ` [PATCH 21/24] clk: stm32mp1: sync with Linux v5.17-rc1 Ahmad Fatoum
@ 2022-02-20 12:47 ` Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 23/24] ARM: dts: stm32mp: remove regulator-name override in stm32mp151.dtsi Ahmad Fatoum
` (2 subsequent siblings)
24 siblings, 0 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium, Ahmad Fatoum
So far, the regulator was created anyway, but attempting to print the
name by the regulator command just output a "<NULL>" string.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
drivers/regulator/core.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index b2e5f8caa2ca..997b986d5f07 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -166,6 +166,8 @@ int of_regulator_register(struct regulator_dev *rd, struct device_node *node)
rd->always_on = of_property_read_bool(node, "regulator-always-on");
name = of_get_property(node, "regulator-name", NULL);
+ if (!name)
+ name = node->name;
ri = __regulator_register(rd, name);
if (IS_ERR(ri))
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 23/24] ARM: dts: stm32mp: remove regulator-name override in stm32mp151.dtsi
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
` (21 preceding siblings ...)
2022-02-20 12:47 ` [PATCH 22/24] regulator: core: fall back to node name if no regulator-name property Ahmad Fatoum
@ 2022-02-20 12:47 ` Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 24/24] ARM: stm32mp: enable more config options Ahmad Fatoum
2022-02-23 10:57 ` [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Sascha Hauer
24 siblings, 0 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium, Ahmad Fatoum
barebox regulator core will now just take node name in absence of the
property, so no need for this fixup any longer.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
arch/arm/dts/stm32mp151.dtsi | 4 ----
1 file changed, 4 deletions(-)
diff --git a/arch/arm/dts/stm32mp151.dtsi b/arch/arm/dts/stm32mp151.dtsi
index aac2907bc6b5..eac997dfceaa 100644
--- a/arch/arm/dts/stm32mp151.dtsi
+++ b/arch/arm/dts/stm32mp151.dtsi
@@ -51,10 +51,6 @@
barebox,provide-mac-address = <ðernet0 0x39>;
};
-&vrefbuf {
- regulator-name = "vref";
-};
-
&usbphyc {
vdda1v1-supply = <®11>;
vdda1v8-supply = <®18>;
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 24/24] ARM: stm32mp: enable more config options
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
` (22 preceding siblings ...)
2022-02-20 12:47 ` [PATCH 23/24] ARM: dts: stm32mp: remove regulator-name override in stm32mp151.dtsi Ahmad Fatoum
@ 2022-02-20 12:47 ` Ahmad Fatoum
2022-02-23 10:57 ` [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Sascha Hauer
24 siblings, 0 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-20 12:47 UTC (permalink / raw)
To: barebox; +Cc: Xogium, Ahmad Fatoum
We have recently gained SCMI and LTDC support as well as a new SPI
display. Enable them in the defconfig.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
arch/arm/configs/stm32mp_defconfig | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/arch/arm/configs/stm32mp_defconfig b/arch/arm/configs/stm32mp_defconfig
index d682083d400a..2fec3a2d3b9c 100644
--- a/arch/arm/configs/stm32mp_defconfig
+++ b/arch/arm/configs/stm32mp_defconfig
@@ -4,6 +4,7 @@ CONFIG_MACH_LXA_MC1=y
CONFIG_MACH_SEEED_ODYSSEY=y
CONFIG_MACH_STM32MP15X_EV1=y
CONFIG_MACH_PROTONIC_STM32MP1=y
+CONFIG_BOARD_ARM_GENERIC_DT=y
CONFIG_THUMB2_BAREBOX=y
CONFIG_ARM_BOARD_APPEND_ATAG=y
CONFIG_ARM_OPTIMZED_STRING_FUNCTIONS=y
@@ -118,12 +119,15 @@ CONFIG_USB_GADGET_DFU=y
CONFIG_USB_GADGET_SERIAL=y
CONFIG_USB_GADGET_FASTBOOT=y
CONFIG_VIDEO=y
+CONFIG_DRIVER_VIDEO_FB_SSD1307=y
+CONFIG_DRIVER_VIDEO_STM32_LTDC=y
CONFIG_DRIVER_VIDEO_BACKLIGHT=y
CONFIG_DRIVER_VIDEO_SIMPLE_PANEL=y
CONFIG_MCI=y
CONFIG_MCI_STARTUP=y
CONFIG_MCI_MMC_BOOT_PARTITIONS=y
CONFIG_MCI_STM32_SDMMC2=y
+CONFIG_COMMON_CLK_SCMI=y
CONFIG_MFD_STPMIC1=y
CONFIG_MFD_STM32_TIMERS=y
CONFIG_LED=y
@@ -149,12 +153,14 @@ CONFIG_REGULATOR_FIXED=y
CONFIG_REGULATOR_STM32_PWR=y
CONFIG_REGULATOR_STM32_VREFBUF=y
CONFIG_REGULATOR_STPMIC1=y
+CONFIG_REGULATOR_ARM_SCMI=y
CONFIG_REMOTEPROC=y
CONFIG_STM32_REMOTEPROC=y
-CONFIG_RESET_STM32=y
+CONFIG_ARM_SCMI_PROTOCOL=y
CONFIG_GENERIC_PHY=y
CONFIG_PHY_STM32_USBPHYC=y
CONFIG_SYSCON_REBOOT_MODE=y
+CONFIG_RESET_STM32=y
CONFIG_FS_EXT4=y
CONFIG_FS_TFTP=y
CONFIG_FS_NFS=y
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH] fixup! ARM: stm32mp: build extra barebox-stm32mp-generic-bl33.img
2022-02-20 12:47 ` [PATCH 07/24] ARM: stm32mp: build extra barebox-stm32mp-generic-bl33.img Ahmad Fatoum
@ 2022-02-21 10:35 ` Ahmad Fatoum
0 siblings, 0 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-21 10:35 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Otherwise, this image is built even for non-STM32MP machines.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
images/Makefile.stm32mp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/images/Makefile.stm32mp b/images/Makefile.stm32mp
index 862e955dbc59..fa79e09f952f 100644
--- a/images/Makefile.stm32mp
+++ b/images/Makefile.stm32mp
@@ -26,9 +26,9 @@ endef
# --------------------------------------
# For use as --nt-fw (BL33) in FIP images
-pblb-y += start_stm32mp_bl33
+pblb-$(CONFIG_ARCH_STM32MP) += start_stm32mp_bl33
FILE_barebox-stm32mp-generic-bl33.img = start_stm32mp_bl33.pblb
-image-y += barebox-stm32mp-generic-bl33.img
+image-$(CONFIG_ARCH_STM32MP) += barebox-stm32mp-generic-bl33.img
$(call build_stm32mp_image, CONFIG_MACH_STM32MP15XX_DKX, start_stm32mp15xx_dkx, stm32mp15xx-dkx)
$(call build_stm32mp_image, CONFIG_MACH_STM32MP15X_EV1, start_stm32mp15x_ev1, stm32mp15x-ev1)
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH] fixup! clk: stm32mp1: sync with Linux v5.17-rc1
2022-02-20 12:47 ` [PATCH 21/24] clk: stm32mp1: sync with Linux v5.17-rc1 Ahmad Fatoum
@ 2022-02-21 10:35 ` Ahmad Fatoum
0 siblings, 0 replies; 28+ messages in thread
From: Ahmad Fatoum @ 2022-02-21 10:35 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
This allows discarding code at compile-time for drivers that can be
compiled without DT support.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
include/driver.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/driver.h b/include/driver.h
index 1ee255c9ad0c..85cb30f8b0b1 100644
--- a/include/driver.h
+++ b/include/driver.h
@@ -600,7 +600,7 @@ struct device_d *device_find_child(struct device_d *parent, void *data,
static inline struct device_node *dev_of_node(struct device_d *dev)
{
- return dev->device_node;
+ return IS_ENABLED(CONFIG_OFDEVICE) ? dev->device_node : NULL;
}
#endif /* DRIVER_H */
--
2.30.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
` (23 preceding siblings ...)
2022-02-20 12:47 ` [PATCH 24/24] ARM: stm32mp: enable more config options Ahmad Fatoum
@ 2022-02-23 10:57 ` Sascha Hauer
24 siblings, 0 replies; 28+ messages in thread
From: Sascha Hauer @ 2022-02-23 10:57 UTC (permalink / raw)
To: Ahmad Fatoum; +Cc: barebox, Xogium
On Sun, Feb 20, 2022 at 01:47:12PM +0100, Ahmad Fatoum wrote:
> ST calls trusted firmware + SCMI for accessing some secure resources the
> trusted boot chain (as opposed to the basic bootchain). This is
> independent of cryptographically secured boot. So far, barebox generated
> images with a STM32 header prepended, which could be booted after
> upstream TF-A. This extends barebox to generate an extra image suitable
> for inclusion in A TF-A Firmware Image Package and teaches barebox to
> support firmware clocks and resets provided by TF-A over secure monitor
> calls. There are no upstream DTs yet with SCMI and intention seems to be
> to have boot firmware fix up the nodes. The SCMI changes here have been
> tested by taking OpenSTLinux with its downstream TF-A, U-Boot, OP-TEE
> and Linux components and just replacing U-Boot with barebox via fiptool
> update.
>
> Cheers,
> Ahmad Fatoum (24):
> PBL: fdt: factor reg property parsing into helper
> pinctrl: stm32: use gpio-ranges instead of alias
> ARM: stm32mp: simplify with build_stm32mp_image macro
> ARM: stm32mp: change stm32image extension to .stm32
> filetype: detect TF-A Firmware Image Packages (FIP)
> scripts: add tool to adjust bl33 load address in existing FIP
> ARM: stm32mp: build extra barebox-stm32mp-generic-bl33.img
> ARM: stm32mp: ddrctrl: fix wrong register field widths
> reset: stm32: drop stm32mp1_reset_ops indirection
> reset: move stm32 reset code to drivers/power/reset
> ARM: smccc: sync header with upstream
> firmware: import Linux v5.13 SCMI support
> reset: add SCMI support
> clk: add SCMI clock driver
> regulator: add SCMI regulator driver
> clk: accept const arguments in clk_to_clk_hw/clk_hw_to_clk
> serial: stm32: bail if clock_get_rate returns zero
> clk: implement of_clk_hw_{onecell,simple}_get
> clk: implement clk_hw_reparent
> reset: add support for reset_control_status
> clk: stm32mp1: sync with Linux v5.17-rc1
> regulator: core: fall back to node name if no regulator-name property
> ARM: dts: stm32mp: remove regulator-name override in stm32mp151.dtsi
> ARM: stm32mp: enable more config options
Applied, thanks
Sascha
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 28+ messages in thread
end of thread, other threads:[~2022-02-23 10:58 UTC | newest]
Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-20 12:47 [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 01/24] PBL: fdt: factor reg property parsing into helper Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 02/24] pinctrl: stm32: use gpio-ranges instead of alias Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 03/24] ARM: stm32mp: simplify with build_stm32mp_image macro Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 04/24] ARM: stm32mp: change stm32image extension to .stm32 Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 05/24] filetype: detect TF-A Firmware Image Packages (FIP) Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 06/24] scripts: add tool to adjust bl33 load address in existing FIP Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 07/24] ARM: stm32mp: build extra barebox-stm32mp-generic-bl33.img Ahmad Fatoum
2022-02-21 10:35 ` [PATCH] fixup! " Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 08/24] ARM: stm32mp: ddrctrl: fix wrong register field widths Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 09/24] reset: stm32: drop stm32mp1_reset_ops indirection Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 10/24] reset: move stm32 reset code to drivers/power/reset Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 11/24] ARM: smccc: sync header with upstream Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 12/24] firmware: import Linux v5.13 SCMI support Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 13/24] reset: add " Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 14/24] clk: add SCMI clock driver Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 15/24] regulator: add SCMI regulator driver Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 16/24] clk: accept const arguments in clk_to_clk_hw/clk_hw_to_clk Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 17/24] serial: stm32: bail if clock_get_rate returns zero Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 18/24] clk: implement of_clk_hw_{onecell,simple}_get Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 19/24] clk: implement clk_hw_reparent Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 20/24] reset: add support for reset_control_status Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 21/24] clk: stm32mp1: sync with Linux v5.17-rc1 Ahmad Fatoum
2022-02-21 10:35 ` [PATCH] fixup! " Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 22/24] regulator: core: fall back to node name if no regulator-name property Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 23/24] ARM: dts: stm32mp: remove regulator-name override in stm32mp151.dtsi Ahmad Fatoum
2022-02-20 12:47 ` [PATCH 24/24] ARM: stm32mp: enable more config options Ahmad Fatoum
2022-02-23 10:57 ` [PATCH 00/24] ARM: stm32mp: add trusted bootchain (SCMI&FIP) support Sascha Hauer
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox