diff options
Diffstat (limited to 'drivers')
29 files changed, 2335 insertions, 128 deletions
diff --git a/drivers/Makefile b/drivers/Makefile index 3bc6d279d7..efc2a4afb2 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -39,6 +39,8 @@ obj-$(CONFIG_$(SPL_TPL_)TPM) += tpm/ obj-$(CONFIG_$(SPL_)NVME) += nvme/ obj-$(CONFIG_XEN) += xen/ obj-$(CONFIG_$(SPL_)FPGA) += fpga/ +obj-$(CONFIG_$(SPL_)VIDEO) += video/ + obj-y += bus/ ifndef CONFIG_TPL_BUILD @@ -64,7 +66,6 @@ obj-$(CONFIG_SPL_USB_HOST) += usb/host/ obj-$(CONFIG_SPL_SATA) += ata/ scsi/ obj-$(CONFIG_SPL_LEGACY_BLOCK) += block/ obj-$(CONFIG_SPL_THERMAL) += thermal/ -obj-$(CONFIG_SPL_VIDEO) +=video/ endif endif @@ -99,7 +100,6 @@ obj-y += rtc/ obj-y += scsi/ obj-y += sound/ obj-y += spmi/ -obj-y += video/ obj-y += watchdog/ obj-$(CONFIG_QE) += qe/ obj-$(CONFIG_U_QE) += qe/ @@ -115,6 +115,7 @@ obj-y += iommu/ obj-y += smem/ obj-y += thermal/ obj-$(CONFIG_TEE) += tee/ +obj-$(CONFIG_ARM_FFA_TRANSPORT) += firmware/arm-ffa/ obj-y += axi/ obj-y += ufs/ obj-$(CONFIG_W1) += w1/ diff --git a/drivers/block/ide.c b/drivers/block/ide.c index 89201dd4d2..c698f9cbd5 100644 --- a/drivers/block/ide.c +++ b/drivers/block/ide.c @@ -1059,9 +1059,9 @@ static int ide_probe(struct udevice *udev) desc->lba48 = pdesc.lba48; desc->type = pdesc.type; - ret = bootdev_setup_for_dev(udev, "ide_bootdev"); + ret = bootdev_setup_for_sibling_blk(blk, "ide_bootdev"); if (ret) - return log_msg_ret("bootdev", ret); + return log_msg_ret("bd", ret); } return 0; diff --git a/drivers/clk/renesas/clk-rcar-gen3.c b/drivers/clk/renesas/clk-rcar-gen3.c index c8a5512b65..0d274bb986 100644 --- a/drivers/clk/renesas/clk-rcar-gen3.c +++ b/drivers/clk/renesas/clk-rcar-gen3.c @@ -526,6 +526,7 @@ U_BOOT_DRIVER(clk_gen3) = { .ops = &gen3_clk_ops, .probe = gen3_clk_probe, .remove = gen3_clk_remove, + .flags = DM_FLAG_OS_PREPARE | DM_FLAG_VITAL, }; static int gen3_reset_assert(struct reset_ctl *reset_ctl) @@ -565,6 +566,7 @@ U_BOOT_DRIVER(rst_gen3) = { .name = "rst_gen3", .id = UCLASS_RESET, .ops = &rst_gen3_ops, + .flags = DM_FLAG_OS_PREPARE | DM_FLAG_VITAL, }; int gen3_cpg_bind(struct udevice *parent) diff --git a/drivers/clk/rockchip/clk_rk3308.c b/drivers/clk/rockchip/clk_rk3308.c index 64f33587e2..d0a3f65446 100644 --- a/drivers/clk/rockchip/clk_rk3308.c +++ b/drivers/clk/rockchip/clk_rk3308.c @@ -150,7 +150,7 @@ static ulong rk3308_i2c_get_clk(struct clk *clk) } con = readl(&cru->clksel_con[con_id]); - div = con >> CLK_I2C_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK; + div = (con & CLK_I2C_DIV_CON_MASK) >> CLK_I2C_DIV_CON_SHIFT; return DIV_TO_RATE(priv->dpll_hz, div); } @@ -314,7 +314,7 @@ static ulong rk3308_saradc_get_clk(struct clk *clk) u32 div, con; con = readl(&cru->clksel_con[34]); - div = con >> CLK_SARADC_DIV_CON_SHIFT & CLK_SARADC_DIV_CON_MASK; + div = (con & CLK_SARADC_DIV_CON_MASK) >> CLK_SARADC_DIV_CON_SHIFT; return DIV_TO_RATE(OSC_HZ, div); } @@ -342,7 +342,7 @@ static ulong rk3308_tsadc_get_clk(struct clk *clk) u32 div, con; con = readl(&cru->clksel_con[33]); - div = con >> CLK_SARADC_DIV_CON_SHIFT & CLK_SARADC_DIV_CON_MASK; + div = (con & CLK_SARADC_DIV_CON_MASK) >> CLK_SARADC_DIV_CON_SHIFT; return DIV_TO_RATE(OSC_HZ, div); } @@ -385,7 +385,7 @@ static ulong rk3308_spi_get_clk(struct clk *clk) } con = readl(&cru->clksel_con[con_id]); - div = con >> CLK_SPI_DIV_CON_SHIFT & CLK_SPI_DIV_CON_MASK; + div = (con & CLK_SPI_DIV_CON_MASK) >> CLK_SPI_DIV_CON_SHIFT; return DIV_TO_RATE(priv->dpll_hz, div); } @@ -429,7 +429,7 @@ static ulong rk3308_pwm_get_clk(struct clk *clk) u32 div, con; con = readl(&cru->clksel_con[29]); - div = con >> CLK_PWM_DIV_CON_SHIFT & CLK_PWM_DIV_CON_MASK; + div = (con & CLK_PWM_DIV_CON_MASK) >> CLK_PWM_DIV_CON_SHIFT; return DIV_TO_RATE(priv->dpll_hz, div); } @@ -451,6 +451,58 @@ static ulong rk3308_pwm_set_clk(struct clk *clk, uint hz) return rk3308_pwm_get_clk(clk); } +static ulong rk3308_uart_get_clk(struct clk *clk) +{ + struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3308_cru *cru = priv->cru; + u32 div, pll_sel, con, con_id, parent; + + switch (clk->id) { + case SCLK_UART0: + con_id = 10; + break; + case SCLK_UART1: + con_id = 13; + break; + case SCLK_UART2: + con_id = 16; + break; + case SCLK_UART3: + con_id = 19; + break; + case SCLK_UART4: + con_id = 22; + break; + default: + printf("do not support this uart interface\n"); + return -EINVAL; + } + + con = readl(&cru->clksel_con[con_id]); + pll_sel = (con & CLK_UART_PLL_SEL_MASK) >> CLK_UART_PLL_SEL_SHIFT; + div = (con & CLK_UART_DIV_CON_MASK) >> CLK_UART_DIV_CON_SHIFT; + + switch (pll_sel) { + case CLK_UART_PLL_SEL_DPLL: + parent = priv->dpll_hz; + break; + case CLK_UART_PLL_SEL_VPLL0: + parent = priv->vpll0_hz; + break; + case CLK_UART_PLL_SEL_VPLL1: + parent = priv->vpll0_hz; + break; + case CLK_UART_PLL_SEL_24M: + parent = OSC_HZ; + break; + default: + printf("do not support this uart pll sel\n"); + return -EINVAL; + } + + return DIV_TO_RATE(parent, div); +} + static ulong rk3308_vop_get_clk(struct clk *clk) { struct rk3308_clk_priv *priv = dev_get_priv(clk->dev); @@ -813,6 +865,13 @@ static ulong rk3308_clk_get_rate(struct clk *clk) case SCLK_EMMC_SAMPLE: rate = rk3308_mmc_get_clk(clk); break; + case SCLK_UART0: + case SCLK_UART1: + case SCLK_UART2: + case SCLK_UART3: + case SCLK_UART4: + rate = rk3308_uart_get_clk(clk); + break; case SCLK_I2C0: case SCLK_I2C1: case SCLK_I2C2: diff --git a/drivers/clk/rockchip/clk_rk3568.c b/drivers/clk/rockchip/clk_rk3568.c index 0df82f5971..599b7b130e 100644 --- a/drivers/clk/rockchip/clk_rk3568.c +++ b/drivers/clk/rockchip/clk_rk3568.c @@ -702,7 +702,10 @@ static ulong rk3568_cpll_div_set_rate(struct rk3568_clk_priv *priv, } div = DIV_ROUND_UP(priv->cpll_hz, rate); - assert(div - 1 <= 31); + if (clk_id == CPLL_25M) + assert(div - 1 <= 63); + else + assert(div - 1 <= 31); rk_clrsetreg(&cru->clksel_con[con], mask, (div - 1) << shift); return rk3568_cpll_div_get_rate(priv, clk_id); @@ -1142,7 +1145,7 @@ static ulong rk3568_pwm_get_clk(struct rk3568_clk_priv *priv, ulong clk_id) switch (clk_id) { case CLK_PWM1: - sel = (con & CLK_PWM1_SEL_MASK) >> CLK_PWM3_SEL_SHIFT; + sel = (con & CLK_PWM1_SEL_MASK) >> CLK_PWM1_SEL_SHIFT; break; case CLK_PWM2: sel = (con & CLK_PWM2_SEL_MASK) >> CLK_PWM2_SEL_SHIFT; @@ -2186,6 +2189,7 @@ static ulong rk3568_rkvdec_set_clk(struct rk3568_clk_priv *priv, return rk3568_rkvdec_get_clk(priv, clk_id); } +#endif static ulong rk3568_uart_get_rate(struct rk3568_clk_priv *priv, ulong clk_id) { @@ -2321,7 +2325,6 @@ static ulong rk3568_uart_set_rate(struct rk3568_clk_priv *priv, return rk3568_uart_get_rate(priv, clk_id); } -#endif static ulong rk3568_clk_get_rate(struct clk *clk) { @@ -2460,6 +2463,7 @@ static ulong rk3568_clk_get_rate(struct clk *clk) case TCLK_WDT_NS: rate = OSC_HZ; break; +#endif case SCLK_UART1: case SCLK_UART2: case SCLK_UART3: @@ -2471,7 +2475,6 @@ static ulong rk3568_clk_get_rate(struct clk *clk) case SCLK_UART9: rate = rk3568_uart_get_rate(priv, clk->id); break; -#endif case ACLK_SECURE_FLASH: case ACLK_CRYPTO_NS: case HCLK_SECURE_FLASH: @@ -2645,6 +2648,7 @@ static ulong rk3568_clk_set_rate(struct clk *clk, ulong rate) case TCLK_WDT_NS: ret = OSC_HZ; break; +#endif case SCLK_UART1: case SCLK_UART2: case SCLK_UART3: @@ -2656,7 +2660,6 @@ static ulong rk3568_clk_set_rate(struct clk *clk, ulong rate) case SCLK_UART9: ret = rk3568_uart_set_rate(priv, clk->id, rate); break; -#endif case ACLK_SECURE_FLASH: case ACLK_CRYPTO_NS: case HCLK_SECURE_FLASH: @@ -2840,6 +2843,10 @@ static int rk3568_clk_set_parent(struct clk *clk, struct clk *parent) case CLK_RKVDEC_CORE: return rk3568_rkvdec_set_parent(clk, parent); case I2S1_MCLKOUT_TX: + case SCLK_GMAC0_RGMII_SPEED: + case SCLK_GMAC0_RMII_SPEED: + case SCLK_GMAC1_RGMII_SPEED: + case SCLK_GMAC1_RMII_SPEED: break; default: return -ENOENT; diff --git a/drivers/clk/stm32/clk-stm32mp1.c b/drivers/clk/stm32/clk-stm32mp1.c index 4f4524fcb2..6150287694 100644 --- a/drivers/clk/stm32/clk-stm32mp1.c +++ b/drivers/clk/stm32/clk-stm32mp1.c @@ -881,7 +881,8 @@ static int stm32mp1_clk_get_parent(struct stm32mp1_clk_priv *priv, return sel[s].parent[p]; } - log_err("no parents defined for clk id %d\n", (u32)id); + /* clock is DISABLED when the clock src is not in clk_parent[] range */ + log_debug("no parents defined for clk id %d\n", (u32)id); return -EINVAL; } diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index eae1c8ddc9..8789b1ea14 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -45,4 +45,5 @@ config ARM_SMCCC_FEATURES the PSCI driver is always probed and binds dirvers registered to the Arm SMCCC services if any and reported as supported by the SMCCC firmware. +source "drivers/firmware/arm-ffa/Kconfig" source "drivers/firmware/scmi/Kconfig" diff --git a/drivers/firmware/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig new file mode 100644 index 0000000000..d75f8b53fd --- /dev/null +++ b/drivers/firmware/arm-ffa/Kconfig @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: GPL-2.0 + +config ARM_FFA_TRANSPORT + bool "Enable Arm Firmware Framework for Armv8-A driver" + depends on DM && (ARM64 || SANDBOX) + select ARM_SMCCC if !SANDBOX + select ARM_SMCCC_FEATURES if !SANDBOX + imply CMD_ARMFFA + select LIB_UUID + select DEVRES + help + The Firmware Framework for Arm A-profile processors (FF-A) + describes interfaces (ABIs) that standardize communication + between the Secure World and Normal World leveraging TrustZone + technology. + + The FF-A support in U-Boot is based on FF-A specification v1.0 and uses SMC32 + calling convention. + + FF-A specification: + + https://developer.arm.com/documentation/den0077/a/?lang=en + + In U-Boot FF-A design, FF-A is considered as a discoverable bus. + FF-A bus is discovered using ARM_SMCCC_FEATURES mechanism performed + by the PSCI driver. + The Secure World is considered as one entity to communicate with + using the FF-A bus. + FF-A communication is handled by one device and one instance (the bus). + The FF-A support on U-Boot takes care of all the interactions between Normal + world and Secure World. + + Generic FF-A methods are implemented in the Uclass (arm-ffa-uclass.c). + Arm specific methods are implemented in the Arm driver (arm-ffa.c). + + FF-A sandbox is provided to run FF-A under sandbox and allows to test the FF-A Uclass. + Sandbox support includes an emulator for Arm FF-A which emulates the FF-A side of + the Secure World and provides FF-A ABIs inspection methods (ffa-emul-uclass.c). + An FF-A sandbox driver is also provided for FF-A communication with the emulated + Secure World (sandbox_ffa.c). + + For more details about the FF-A support, please refer to doc/arch/arm64.ffa.rst diff --git a/drivers/firmware/arm-ffa/Makefile b/drivers/firmware/arm-ffa/Makefile new file mode 100644 index 0000000000..318123a7f4 --- /dev/null +++ b/drivers/firmware/arm-ffa/Makefile @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright 2022-2023 Arm Limited and/or its affiliates <open-source-office@arm.com> +# +# Authors: +# Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> + +# build the generic FF-A methods +obj-y += arm-ffa-uclass.o +ifeq ($(CONFIG_SANDBOX),y) +# build the FF-A sandbox emulator and driver +obj-y += ffa-emul-uclass.o sandbox_ffa.o +else +# build the Arm64 FF-A driver +obj-y += arm-ffa.o +endif diff --git a/drivers/firmware/arm-ffa/arm-ffa-uclass.c b/drivers/firmware/arm-ffa/arm-ffa-uclass.c new file mode 100644 index 0000000000..8c17b19eaf --- /dev/null +++ b/drivers/firmware/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,1065 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates <open-source-office@arm.com> + * + * Authors: + * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> + */ +#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Error mapping declarations */ + +int ffa_to_std_errmap[MAX_NUMBER_FFA_ERR] = { + [NOT_SUPPORTED] = -EOPNOTSUPP, + [INVALID_PARAMETERS] = -EINVAL, + [NO_MEMORY] = -ENOMEM, + [BUSY] = -EBUSY, + [INTERRUPTED] = -EINTR, + [DENIED] = -EACCES, + [RETRY] = -EAGAIN, + [ABORTED] = -ECANCELED, +}; + +static struct ffa_abi_errmap err_msg_map[FFA_ERRMAP_COUNT] = { + [FFA_ID_TO_ERRMAP_ID(FFA_VERSION)] = { + { + [NOT_SUPPORTED] = + "NOT_SUPPORTED: A Firmware Framework implementation does not exist", + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_ID_GET)] = { + { + [NOT_SUPPORTED] = + "NOT_SUPPORTED: This function is not implemented at this FF-A instance", + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_FEATURES)] = { + { + [NOT_SUPPORTED] = + "NOT_SUPPORTED: FFA_RXTX_MAP is not implemented at this FF-A instance", + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_PARTITION_INFO_GET)] = { + { + [NOT_SUPPORTED] = + "NOT_SUPPORTED: This function is not implemented at this FF-A instance", + [INVALID_PARAMETERS] = + "INVALID_PARAMETERS: Unrecognized UUID", + [NO_MEMORY] = + "NO_MEMORY: Results cannot fit in RX buffer of the caller", + [BUSY] = + "BUSY: RX buffer of the caller is not free", + [DENIED] = + "DENIED: Callee is not in a state to handle this request", + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_RXTX_UNMAP)] = { + { + [NOT_SUPPORTED] = + "NOT_SUPPORTED: FFA_RXTX_UNMAP is not implemented at this FF-A instance", + [INVALID_PARAMETERS] = + "INVALID_PARAMETERS: No buffer pair registered on behalf of the caller", + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_RX_RELEASE)] = { + { + [NOT_SUPPORTED] = + "NOT_SUPPORTED: FFA_RX_RELEASE is not implemented at this FF-A instance", + [DENIED] = + "DENIED: Caller did not have ownership of the RX buffer", + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_RXTX_MAP)] = { + { + [NOT_SUPPORTED] = + "NOT_SUPPORTED: This function is not implemented at this FF-A instance", + [INVALID_PARAMETERS] = + "INVALID_PARAMETERS: Field(s) in input parameters incorrectly encoded", + [NO_MEMORY] = + "NO_MEMORY: Not enough memory", + [DENIED] = + "DENIED: Buffer pair already registered", + }, + }, +}; + +/** + * ffa_to_std_errno() - convert FF-A error code to standard error code + * @ffa_errno: Error code returned by the FF-A ABI + * + * Map the given FF-A error code as specified + * by the spec to a u-boot standard error code. + * + * Return: + * + * The standard error code on success. . Otherwise, failure + */ +static int ffa_to_std_errno(int ffa_errno) +{ + int err_idx = -ffa_errno; + + /* Map the FF-A error code to the standard u-boot error code */ + if (err_idx > 0 && err_idx < MAX_NUMBER_FFA_ERR) + return ffa_to_std_errmap[err_idx]; + return -EINVAL; +} + +/** + * ffa_print_error_log() - print the error log corresponding to the selected FF-A ABI + * @ffa_id: FF-A ABI ID + * @ffa_errno: Error code returned by the FF-A ABI + * + * Map the FF-A error code to the error log relevant to the + * selected FF-A ABI. Then the error log is printed. + * + * Return: + * + * 0 on success. . Otherwise, failure + */ +static int ffa_print_error_log(u32 ffa_id, int ffa_errno) +{ + int err_idx = -ffa_errno, abi_idx = 0; + + /* Map the FF-A error code to the corresponding error log */ + + if (err_idx <= 0 || err_idx >= MAX_NUMBER_FFA_ERR) + return -EINVAL; + + if (ffa_id < FFA_FIRST_ID || ffa_id > FFA_LAST_ID) + return -EINVAL; + + abi_idx = FFA_ID_TO_ERRMAP_ID(ffa_id); + if (abi_idx < 0 || abi_idx >= FFA_ERRMAP_COUNT) + return -EINVAL; + + if (!err_msg_map[abi_idx].err_str[err_idx]) + return -EINVAL; + + log_err("%s\n", err_msg_map[abi_idx].err_str[err_idx]); + + return 0; +} + +/* FF-A ABIs implementation (U-Boot side) */ + +/** + * invoke_ffa_fn() - SMC wrapper + * @args: FF-A ABI arguments to be copied to Xn registers + * @res: FF-A ABI return data to be copied from Xn registers + * + * Calls low level SMC implementation. + * This function should be implemented by the user driver. + */ +void __weak invoke_ffa_fn(ffa_value_t args, ffa_value_t *res) +{ +} + +/** + * ffa_get_version_hdlr() - FFA_VERSION handler function + * @dev: The FF-A bus device + * + * Implement FFA_VERSION FF-A function + * to get from the secure world the FF-A framework version + * FFA_VERSION is used to discover the FF-A framework. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_get_version_hdlr(struct udevice *dev) +{ + u16 major, minor; + ffa_value_t res = {0}; + int ffa_errno; + struct ffa_priv *uc_priv; + + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_VERSION), .a1 = FFA_VERSION_1_0, + }, &res); + + ffa_errno = res.a0; + if (ffa_errno < 0) { + ffa_print_error_log(FFA_VERSION, ffa_errno); + return ffa_to_std_errno(ffa_errno); + } + + major = GET_FFA_MAJOR_VERSION(res.a0); + minor = GET_FFA_MINOR_VERSION(res.a0); + + log_debug("FF-A driver %d.%d\nFF-A framework %d.%d\n", + FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor); + + if (major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION) { + log_debug("FF-A versions are compatible\n"); + + if (dev) { + uc_priv = dev_get_uclass_priv(dev); + if (uc_priv) + uc_priv->fwk_version = res.a0; + } + + return 0; + } + + log_err("versions are incompatible\nExpected: %d.%d , Found: %d.%d\n", + FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor); + + return -EPROTONOSUPPORT; +} + +/** + * ffa_get_endpoint_id() - FFA_ID_GET handler function + * @dev: The FF-A bus device + * + * Implement FFA_ID_GET FF-A function + * to get from the secure world u-boot endpoint ID + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_get_endpoint_id(struct udevice *dev) +{ + ffa_value_t res = {0}; + int ffa_errno; + struct ffa_priv *uc_priv = dev_get_uclass_priv(dev); + + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_ID_GET), + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + uc_priv->id = GET_SELF_ENDPOINT_ID((u32)res.a2); + log_debug("FF-A endpoint ID is %u\n", uc_priv->id); + + return 0; + } + + ffa_errno = res.a2; + + ffa_print_error_log(FFA_ID_GET, ffa_errno); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_set_rxtx_buffers_pages_cnt() - set the minimum number of pages in each of the RX/TX buffers + * @dev: The FF-A bus device + * @prop_field: properties field obtained from FFA_FEATURES ABI + * + * Set the minimum number of pages in each of the RX/TX buffers in uc_priv + * + * Return: + * + * rxtx_min_pages field contains the returned number of pages + * 0 on success. Otherwise, failure + */ +static int ffa_set_rxtx_buffers_pages_cnt(struct udevice *dev, u32 prop_field) +{ + struct ffa_priv *uc_priv = dev_get_uclass_priv(dev); + + switch (prop_field) { + case RXTX_4K: + uc_priv->pair.rxtx_min_pages = 1; + break; + case RXTX_16K: + uc_priv->pair.rxtx_min_pages = 4; + break; + case RXTX_64K: + uc_priv->pair.rxtx_min_pages = 16; + break; + default: + log_err("RX/TX buffer size not supported\n"); + return -EINVAL; + } + + return 0; +} + +/** + * ffa_get_rxtx_map_features_hdlr() - FFA_FEATURES handler function with FFA_RXTX_MAP argument + * @dev: The FF-A bus device + * + * Implement FFA_FEATURES FF-A function to retrieve the FFA_RXTX_MAP features + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_get_rxtx_map_features_hdlr(struct udevice *dev) +{ + ffa_value_t res = {0}; + int ffa_errno; + + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_FEATURES), + .a1 = FFA_SMC_64(FFA_RXTX_MAP), + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) + return ffa_set_rxtx_buffers_pages_cnt(dev, res.a2); + + ffa_errno = res.a2; + ffa_print_error_log(FFA_FEATURES, ffa_errno); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_free_rxtx_buffers() - free the RX/TX buffers + * @dev: The FF-A bus device + * + * Free the RX/TX buffers + */ +static void ffa_free_rxtx_buffers(struct udevice *dev) +{ + struct ffa_priv *uc_priv = dev_get_uclass_priv(dev); + + log_debug("Freeing FF-A RX/TX buffers\n"); + + if (uc_priv->pair.rxbuf) { + free(uc_priv->pair.rxbuf); + uc_priv->pair.rxbuf = NULL; + } + + if (uc_priv->pair.txbuf) { + free(uc_priv->pair.txbuf); + uc_priv->pair.txbuf = NULL; + } +} + +/** + * ffa_alloc_rxtx_buffers() - allocate the RX/TX buffers + * @dev: The FF-A bus device + * + * Used by ffa_map_rxtx_buffers to allocate + * the RX/TX buffers before mapping them. The allocated memory is physically + * contiguous since memalign ends up calling malloc which allocates + * contiguous memory in u-boot. + * The size of the memory allocated is the minimum allowed. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_alloc_rxtx_buffers(struct udevice *dev) +{ + u64 bytes; + struct ffa_priv *uc_priv = dev_get_uclass_priv(dev); + + log_debug("Using %lu 4KB page(s) for FF-A RX/TX buffers size\n", + uc_priv->pair.rxtx_min_pages); + + bytes = uc_priv->pair.rxtx_min_pages * SZ_4K; + + /* + * The alignment of the RX and TX buffers must be equal + * to the larger translation granule size + * Assumption: Memory allocated with memalign is always physically contiguous + */ + + uc_priv->pair.rxbuf = memalign(bytes, bytes); + if (!uc_priv->pair.rxbuf) { + log_err("failure to allocate RX buffer\n"); + return -ENOBUFS; + } + + log_debug("FF-A RX buffer at virtual address %p\n", uc_priv->pair.rxbuf); + + uc_priv->pair.txbuf = memalign(bytes, bytes); + if (!uc_priv->pair.txbuf) { + free(uc_priv->pair.rxbuf); + uc_priv->pair.rxbuf = NULL; + log_err("failure to allocate the TX buffer\n"); + return -ENOBUFS; + } + + log_debug("FF-A TX buffer at virtual address %p\n", uc_priv->pair.txbuf); + + /* Make sure the buffers are cleared before use */ + memset(uc_priv->pair.rxbuf, 0, bytes); + memset(uc_priv->pair.txbuf, 0, bytes); + + return 0; +} + +/** + * ffa_map_rxtx_buffers_hdlr() - FFA_RXTX_MAP handler function + * @dev: The FF-A bus device + * + * Implement FFA_RXTX_MAP FF-A function to map the RX/TX buffers + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_map_rxtx_buffers_hdlr(struct udevice *dev) +{ + int ret; + ffa_value_t res = {0}; + int ffa_errno; + struct ffa_priv *uc_priv = dev_get_uclass_priv(dev); + + ret = ffa_alloc_rxtx_buffers(dev); + if (ret) + return ret; + + /* + * we need to pass the physical addresses of the RX/TX buffers + * in u-boot physical/virtual mapping is 1:1 + * no need to convert from virtual to physical + */ + + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_64(FFA_RXTX_MAP), + .a1 = map_to_sysmem(uc_priv->pair.txbuf), + .a2 = map_to_sysmem(uc_priv->pair.rxbuf), + .a3 = uc_priv->pair.rxtx_min_pages, + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + log_debug("FF-A RX/TX buffers mapped\n"); + return 0; + } + + ffa_errno = res.a2; + ffa_print_error_log(FFA_RXTX_MAP, ffa_errno); + + ffa_free_rxtx_buffers(dev); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_unmap_rxtx_buffers_hdlr() - FFA_RXTX_UNMAP handler function + * @dev: The FF-A bus device + * + * Implement FFA_RXTX_UNMAP FF-A function to unmap the RX/TX buffers + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_unmap_rxtx_buffers_hdlr(struct udevice *dev) +{ + ffa_value_t res = {0}; + int ffa_errno; + struct ffa_priv *uc_priv; + + log_debug("unmapping FF-A RX/TX buffers\n"); + + uc_priv = dev_get_uclass_priv(dev); + + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_RXTX_UNMAP), + .a1 = PREP_SELF_ENDPOINT_ID(uc_priv->id), + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + ffa_free_rxtx_buffers(dev); + return 0; + } + + ffa_errno = res.a2; + ffa_print_error_log(FFA_RXTX_UNMAP, ffa_errno); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_release_rx_buffer_hdlr() - FFA_RX_RELEASE handler function + * @dev: The FF-A bus device + * + * Invoke FFA_RX_RELEASE FF-A function to release the ownership of the RX buffer + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_release_rx_buffer_hdlr(struct udevice *dev) +{ + ffa_value_t res = {0}; + int ffa_errno; + + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_RX_RELEASE), + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) + return 0; + + ffa_errno = res.a2; + ffa_print_error_log(FFA_RX_RELEASE, ffa_errno); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_uuid_are_identical() - check whether two given UUIDs are identical + * @uuid1: first UUID + * @uuid2: second UUID + * + * Used by ffa_read_partitions_info to search for a UUID in the partitions descriptors table + * + * Return: + * + * 1 when UUIDs match. Otherwise, 0 + */ +static bool ffa_uuid_are_identical(const struct ffa_partition_uuid *uuid1, + const struct ffa_partition_uuid *uuid2) +{ + if (!uuid1 || !uuid2) + return 0; + + return !memcmp(uuid1, uuid2, sizeof(struct ffa_partition_uuid)); +} + +/** + * ffa_read_partitions_info() - read queried partition data + * @dev: The FF-A bus device + * @count: The number of partitions queried + * @part_uuid: Pointer to the partition(s) UUID + * + * Read the partitions information returned by the FFA_PARTITION_INFO_GET and saves it in uc_priv + * + * Return: + * + * uc_priv is updated with the partition(s) information + * 0 is returned on success. Otherwise, failure + */ +static int ffa_read_partitions_info(struct udevice *dev, u32 count, + struct ffa_partition_uuid *part_uuid) +{ + struct ffa_priv *uc_priv = dev_get_uclass_priv(dev); + + if (!count) { + log_err("no partition detected\n"); + return -ENODATA; + } + + log_debug("Reading FF-A partitions data from the RX buffer\n"); + + if (!part_uuid) { + /* Querying information of all partitions */ + u64 buf_bytes; + u64 data_bytes; + u32 desc_idx; + struct ffa_partition_info *parts_info; + + data_bytes = count * sizeof(struct ffa_partition_desc); + + buf_bytes = uc_priv->pair.rxtx_min_pages * SZ_4K; + + if (data_bytes > buf_bytes) { + log_err("partitions data size exceeds the RX buffer size:\n"); + log_err(" sizes in bytes: data %llu , RX buffer %llu\n", + data_bytes, + buf_bytes); + + return -ENOMEM; + } + + uc_priv->partitions.descs = devm_kmalloc(dev, data_bytes, __GFP_ZERO); + if (!uc_priv->partitions.descs) { + log_err("cannot allocate partitions data buffer\n"); + return -ENOMEM; + } + + parts_info = uc_priv->pair.rxbuf; + + for (desc_idx = 0 ; desc_idx < count ; desc_idx++) { + uc_priv->partitions.descs[desc_idx].info = + parts_info[desc_idx]; + + log_debug("FF-A partition ID %x : info cached\n", + uc_priv->partitions.descs[desc_idx].info.id); + } + + uc_priv->partitions.count = count; + + log_debug("%d FF-A partition(s) found and cached\n", count); + + } else { + u32 rx_desc_idx, cached_desc_idx; + struct ffa_partition_info *parts_info; + u8 desc_found; + + parts_info = uc_priv->pair.rxbuf; + + /* + * Search for the SP IDs read from the RX buffer + * in the already cached SPs. + * Update the UUID when ID found. + */ + for (rx_desc_idx = 0; rx_desc_idx < count ; rx_desc_idx++) { + desc_found = 0; + + /* Search the current ID in the cached partitions */ + for (cached_desc_idx = 0; + cached_desc_idx < uc_priv->partitions.count; + cached_desc_idx++) { + /* Save the UUID */ + if (uc_priv->partitions.descs[cached_desc_idx].info.id == + parts_info[rx_desc_idx].id) { + uc_priv->partitions.descs[cached_desc_idx].sp_uuid = + *part_uuid; + + desc_found = 1; + break; + } + } + + if (!desc_found) + return -ENODATA; + } + } + + return 0; +} + +/** + * ffa_query_partitions_info() - invoke FFA_PARTITION_INFO_GET and save partitions data + * @dev: The FF-A bus device + * @part_uuid: Pointer to the partition(s) UUID + * @pcount: Pointer to the number of partitions variable filled when querying + * + * Execute the FFA_PARTITION_INFO_GET to query the partitions data. + * Then, call ffa_read_partitions_info to save the data in uc_priv. + * + * After reading the data the RX buffer is released using ffa_release_rx_buffer + * + * Return: + * + * When part_uuid is NULL, all partitions data are retrieved from secure world + * When part_uuid is non NULL, data for partitions matching the given UUID are + * retrieved and the number of partitions is returned + * 0 is returned on success. Otherwise, failure + */ +static int ffa_query_partitions_info(struct udevice *dev, struct ffa_partition_uuid *part_uuid, + u32 *pcount) +{ + struct ffa_partition_uuid query_uuid = {0}; + ffa_value_t res = {0}; + int ffa_errno; + + /* + * If a UUID is specified. Information for one or more + * partitions in the system is queried. Otherwise, information + * for all installed partitions is queried + */ + + if (part_uuid) { + if (!pcount) + return -EINVAL; + + query_uuid = *part_uuid; + } else if (pcount) { + return -EINVAL; + } + + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_PARTITION_INFO_GET), + .a1 = query_uuid.a1, + .a2 = query_uuid.a2, + .a3 = query_uuid.a3, + .a4 = query_uuid.a4, + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + int ret; + + /* + * res.a2 contains the count of partition information descriptors + * populated in the RX buffer + */ + if (res.a2) { + ret = ffa_read_partitions_info(dev, (u32)res.a2, part_uuid); + if (ret) { + log_err("failed reading SP(s) data , err (%d)\n", ret); + ffa_release_rx_buffer_hdlr(dev); + return -EINVAL; + } + } + + /* Return the SP count (when querying using a UUID) */ + if (pcount) + *pcount = (u32)res.a2; + + /* + * After calling FFA_PARTITION_INFO_GET the buffer ownership + * is assigned to the consumer (u-boot). So, we need to give + * the ownership back to the SPM or hypervisor + */ + ret = ffa_release_rx_buffer_hdlr(dev); + + return ret; + } + + ffa_errno = res.a2; + ffa_print_error_log(FFA_PARTITION_INFO_GET, ffa_errno); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_get_partitions_info_hdlr() - FFA_PARTITION_INFO_GET handler function + * @uuid_str: pointer to the UUID string + * @sp_count: address of the variable containing the number of partitions matching the UUID + * The variable is set by the driver + * @sp_descs: address of the descriptors of the partitions matching the UUID + * The address is set by the driver + * + * Return the number of partitions and their descriptors matching the UUID + * + * Query the secure partition data from uc_priv. + * If not found, invoke FFA_PARTITION_INFO_GET FF-A function to query the partition information + * from secure world. + * + * A client of the FF-A driver should know the UUID of the service it wants to + * access. It should use the UUID to request the FF-A driver to provide the + * partition(s) information of the service. The FF-A driver uses + * PARTITION_INFO_GET to obtain this information. This is implemented through + * ffa_get_partitions_info_hdlr() function. + * If the partition(s) matching the UUID found, the partition(s) information and the + * number are returned. + * If no partition matching the UUID is found in the cached area, a new FFA_PARTITION_INFO_GET + * call is issued. + * If not done yet, the UUID is updated in the cached area. + * This assumes that partitions data does not change in the secure world. + * Otherwise u-boot will have an outdated partition data. The benefit of caching + * the information in the FF-A driver is to accommodate discovery after + * ExitBootServices(). + * + * Return: + * + * @sp_count: the number of partitions + * @sp_descs: address of the partitions descriptors + * + * On success 0 is returned. Otherwise, failure + */ +int ffa_get_partitions_info_hdlr(struct udevice *dev, const char *uuid_str, + u32 *sp_count, struct ffa_partition_desc **sp_descs) +{ + u32 i; + struct ffa_partition_uuid part_uuid = {0}; + struct ffa_priv *uc_priv; + struct ffa_partition_desc *rx_descs; + + uc_priv = dev_get_uclass_priv(dev); + + if (!uc_priv->partitions.count || !uc_priv->partitions.descs) { + log_err("no partition installed\n"); + return -EINVAL; + } + + if (!uuid_str) { + log_err("no UUID provided\n"); + return -EINVAL; + } + + if (!sp_count) { + log_err("no count argument provided\n"); + return -EINVAL; + } + + if (!sp_descs) { + log_err("no info argument provided\n"); + return -EINVAL; + } + + if (uuid_str_to_le_bin(uuid_str, (unsigned char *)&part_uuid)) { + log_err("invalid UUID\n"); + return -EINVAL; + } + + log_debug("Searching FF-A partitions using the provided UUID\n"); + + *sp_count = 0; + *sp_descs = uc_priv->pair.rxbuf; + rx_descs = *sp_descs; + + /* Search in the cached partitions */ + for (i = 0; i < uc_priv->partitions.count; i++) + if (ffa_uuid_are_identical(&uc_priv->partitions.descs[i].sp_uuid, + &part_uuid)) { + log_debug("FF-A partition ID %x matches the provided UUID\n", + uc_priv->partitions.descs[i].info.id); + + (*sp_count)++; + *rx_descs++ = uc_priv->partitions.descs[i]; + } + + if (!(*sp_count)) { + int ret; + + log_debug("No FF-A partition found. Querying framework ...\n"); + + ret = ffa_query_partitions_info(dev, &part_uuid, sp_count); + + if (!ret) { + log_debug("Number of FF-A partition(s) matching the UUID: %d\n", *sp_count); + + if (*sp_count) + ret = ffa_get_partitions_info_hdlr(dev, uuid_str, sp_count, + sp_descs); + else + ret = -ENODATA; + } + + return ret; + } + + return 0; +} + +/** + * ffa_cache_partitions_info() - Query and saves all secure partitions data + * @dev: The FF-A bus device + * + * Invoke FFA_PARTITION_INFO_GET FF-A function to query from secure world + * all partitions information. + * + * The FFA_PARTITION_INFO_GET call is issued with nil UUID as an argument. + * All installed partitions information are returned. We cache them in uc_priv + * and we keep the UUID field empty (in FF-A 1.0 UUID is not provided by the partition descriptor) + * + * Called at the device probing level. + * ffa_cache_partitions_info uses ffa_query_partitions_info to get the data + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_cache_partitions_info(struct udevice *dev) +{ + return ffa_query_partitions_info(dev, NULL, NULL); +} + +/** + * ffa_msg_send_direct_req_hdlr() - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function + * @dev: The FF-A bus device + * @dst_part_id: destination partition ID + * @msg: pointer to the message data preallocated by the client (in/out) + * @is_smc64: select 64-bit or 32-bit FF-A ABI + * + * Implement FFA_MSG_SEND_DIRECT_{REQ,RESP} + * FF-A functions. + * + * FFA_MSG_SEND_DIRECT_REQ is used to send the data to the secure partition. + * The response from the secure partition is handled by reading the + * FFA_MSG_SEND_DIRECT_RESP arguments. + * + * The maximum size of the data that can be exchanged is 40 bytes which is + * sizeof(struct ffa_send_direct_data) as defined by the FF-A specification 1.0 + * in the section relevant to FFA_MSG_SEND_DIRECT_{REQ,RESP} + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_msg_send_direct_req_hdlr(struct udevice *dev, u16 dst_part_id, + struct ffa_send_direct_data *msg, bool is_smc64) +{ + ffa_value_t res = {0}; + int ffa_errno; + u64 req_mode, resp_mode; + struct ffa_priv *uc_priv; + + uc_priv = dev_get_uclass_priv(dev); + + /* No partition installed */ + if (!uc_priv->partitions.count || !uc_priv->partitions.descs) + return -ENODEV; + + if (is_smc64) { + req_mode = FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ); + resp_mode = FFA_SMC_64(FFA_MSG_SEND_DIRECT_RESP); + } else { + req_mode = FFA_SMC_32(FFA_MSG_SEND_DIRECT_REQ); + resp_mode = FFA_SMC_32(FFA_MSG_SEND_DIRECT_RESP); + } + + invoke_ffa_fn((ffa_value_t){ + .a0 = req_mode, + .a1 = PREP_SELF_ENDPOINT_ID(uc_priv->id) | + PREP_PART_ENDPOINT_ID(dst_part_id), + .a2 = 0, + .a3 = msg->data0, + .a4 = msg->data1, + .a5 = msg->data2, + .a6 = msg->data3, + .a7 = msg->data4, + }, &res); + + while (res.a0 == FFA_SMC_32(FFA_INTERRUPT)) + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_RUN), + .a1 = res.a1, + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + /* Message sent with no response */ + return 0; + } + + if (res.a0 == resp_mode) { + /* Message sent with response extract the return data */ + msg->data0 = res.a3; + msg->data1 = res.a4; + msg->data2 = res.a5; + msg->data3 = res.a6; + msg->data4 = res.a7; + + return 0; + } + + ffa_errno = res.a2; + return ffa_to_std_errno(ffa_errno); +} + +/* FF-A driver operations (used by clients for communicating with FF-A)*/ + +/** + * ffa_partition_info_get() - FFA_PARTITION_INFO_GET driver operation + * @uuid_str: pointer to the UUID string + * @sp_count: address of the variable containing the number of partitions matching the UUID + * The variable is set by the driver + * @sp_descs: address of the descriptors of the partitions matching the UUID + * The address is set by the driver + * + * Driver operation for FFA_PARTITION_INFO_GET. + * Please see ffa_get_partitions_info_hdlr() description for more details. + * + * Return: + * + * @sp_count: the number of partitions + * @sp_descs: address of the partitions descriptors + * + * On success 0 is returned. Otherwise, failure + */ +int ffa_partition_info_get(struct udevice *dev, const char *uuid_str, + u32 *sp_count, struct ffa_partition_desc **sp_descs) +{ + struct ffa_bus_ops *ops = ffa_get_ops(dev); + + if (!ops->partition_info_get) + return -ENOSYS; + + return ops->partition_info_get(dev, uuid_str, sp_count, sp_descs); +} + +/** + * ffa_sync_send_receive() - FFA_MSG_SEND_DIRECT_{REQ,RESP} driver operation + * @dev: The FF-A bus device + * @dst_part_id: destination partition ID + * @msg: pointer to the message data preallocated by the client (in/out) + * @is_smc64: select 64-bit or 32-bit FF-A ABI + * + * Driver operation for FFA_MSG_SEND_DIRECT_{REQ,RESP}. + * Please see ffa_msg_send_direct_req_hdlr() description for more details. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_sync_send_receive(struct udevice *dev, u16 dst_part_id, + struct ffa_send_direct_data *msg, bool is_smc64) +{ + struct ffa_bus_ops *ops = ffa_get_ops(dev); + + if (!ops->sync_send_receive) + return -ENOSYS; + + return ops->sync_send_receive(dev, dst_part_id, msg, is_smc64); +} + +/** + * ffa_rxtx_unmap() - FFA_RXTX_UNMAP driver operation + * @dev: The FF-A bus device + * + * Driver operation for FFA_RXTX_UNMAP. + * Please see ffa_unmap_rxtx_buffers_hdlr() description for more details. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_rxtx_unmap(struct udevice *dev) +{ + struct ffa_bus_ops *ops = ffa_get_ops(dev); + + if (!ops->rxtx_unmap) + return -ENOSYS; + + return ops->rxtx_unmap(dev); +} + +/** + * ffa_do_probe() - probing FF-A framework + * @dev: the FF-A bus device (arm_ffa) + * + * Probing is triggered on demand by clients searching for the uclass. + * At probe level the following actions are done: + * - saving the FF-A framework version in uc_priv + * - querying from secure world the u-boot endpoint ID + * - querying from secure world the supported features of FFA_RXTX_MAP + * - mapping the RX/TX buffers + * - querying from secure world all the partitions information + * + * All data queried from secure world is saved in uc_priv. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_do_probe(struct udevice *dev) +{ + int ret; + + ret = ffa_get_version_hdlr(dev); + if (ret) + return ret; + + ret = ffa_get_endpoint_id(dev); + if (ret) + return ret; + + ret = ffa_get_rxtx_map_features_hdlr(dev); + if (ret) + return ret; + + ret = ffa_map_rxtx_buffers_hdlr(dev); + if (ret) + return ret; + + ret = ffa_cache_partitions_info(dev); + if (ret) { + ffa_unmap_rxtx_buffers_hdlr(dev); + return ret; + } + + return 0; +} + +UCLASS_DRIVER(ffa) = { + .name = "ffa", + .id = UCLASS_FFA, + .pre_probe = ffa_do_probe, + .pre_remove = ffa_unmap_rxtx_buffers_hdlr, + .per_device_auto = sizeof(struct ffa_priv) +}; diff --git a/drivers/firmware/arm-ffa/arm-ffa.c b/drivers/firmware/arm-ffa/arm-ffa.c new file mode 100644 index 0000000000..ee0bf9a55b --- /dev/null +++ b/drivers/firmware/arm-ffa/arm-ffa.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates <open-source-office@arm.com> + * + * Authors: + * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> + */ + +#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <linux/errno.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * invoke_ffa_fn() - SMC wrapper + * @args: FF-A ABI arguments to be copied to Xn registers + * @res: FF-A ABI return data to be copied from Xn registers + * + * Calls low level SMC assembly function + */ +void invoke_ffa_fn(ffa_value_t args, ffa_value_t *res) +{ + arm_smccc_1_2_smc(&args, res); +} + +/** + * arm_ffa_discover() - perform FF-A discovery + * @dev: The Arm FF-A bus device (arm_ffa) + * Try to discover the FF-A framework. Discovery is performed by + * querying the FF-A framework version from secure world using the FFA_VERSION ABI. + * Return: + * + * true on success. Otherwise, false. + */ +static bool arm_ffa_discover(struct udevice *dev) +{ + int ret; + + log_debug("Arm FF-A framework discovery\n"); + + ret = ffa_get_version_hdlr(dev); + if (ret) + return false; + + return true; +} + +/** + * arm_ffa_is_supported() - FF-A bus discovery callback + * @invoke_fn: legacy SMC invoke function (not used) + * + * Perform FF-A discovery by calling arm_ffa_discover(). + * Discovery is performed by querying the FF-A framework version from + * secure world using the FFA_VERSION ABI. + * + * The FF-A driver is registered as an SMCCC feature driver. So, features discovery + * callbacks are called by the PSCI driver (PSCI device is the SMCCC features + * root device). + * + * The FF-A driver supports the SMCCCv1.2 extended input/output registers. + * So, the legacy SMC invocation is not used. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static bool arm_ffa_is_supported(void (*invoke_fn)(ulong a0, ulong a1, + ulong a2, ulong a3, + ulong a4, ulong a5, + ulong a6, ulong a7, + struct arm_smccc_res *res)) +{ + return arm_ffa_discover(NULL); +} + +/* Arm FF-A driver operations */ + +static const struct ffa_bus_ops ffa_ops = { + .partition_info_get = ffa_get_partitions_info_hdlr, + .sync_send_receive = ffa_msg_send_direct_req_hdlr, + .rxtx_unmap = ffa_unmap_rxtx_buffers_hdlr, +}; + +/* Registering the FF-A driver as an SMCCC feature driver */ + +ARM_SMCCC_FEATURE_DRIVER(arm_ffa) = { + .driver_name = FFA_DRV_NAME, + .is_supported = arm_ffa_is_supported, +}; + +/* Declaring the FF-A driver under UCLASS_FFA */ + +U_BOOT_DRIVER(arm_ffa) = { + .name = FFA_DRV_NAME, + .id = UCLASS_FFA, + .flags = DM_REMOVE_OS_PREPARE, + .ops = &ffa_ops, +}; diff --git a/drivers/firmware/arm-ffa/ffa-emul-uclass.c b/drivers/firmware/arm-ffa/ffa-emul-uclass.c new file mode 100644 index 0000000000..4bf9f6041f --- /dev/null +++ b/drivers/firmware/arm-ffa/ffa-emul-uclass.c @@ -0,0 +1,720 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates <open-source-office@arm.com> + * + * Authors: + * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> + */ +#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <string.h> +#include <asm/global_data.h> +#include <asm/sandbox_arm_ffa.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* The partitions (SPs) table */ +static struct ffa_partition_desc sandbox_partitions[SANDBOX_PARTITIONS_CNT] = { + { + .info = { .id = SANDBOX_SP1_ID, .exec_ctxt = 0x5687, .properties = 0x89325621 }, + .sp_uuid = { + .a1 = SANDBOX_SERVICE1_UUID_A1, + .a2 = SANDBOX_SERVICE1_UUID_A2, + .a3 = SANDBOX_SERVICE1_UUID_A3, + .a4 = SANDBOX_SERVICE1_UUID_A4, + } + }, + { + .info = { .id = SANDBOX_SP3_ID, .exec_ctxt = 0x7687, .properties = 0x23325621 }, + .sp_uuid = { + .a1 = SANDBOX_SERVICE2_UUID_A1, + .a2 = SANDBOX_SERVICE2_UUID_A2, + .a3 = SANDBOX_SERVICE2_UUID_A3, + .a4 = SANDBOX_SERVICE2_UUID_A4, + } + }, + { + .info = { .id = SANDBOX_SP2_ID, .exec_ctxt = 0x9587, .properties = 0x45325621 }, + .sp_uuid = { + .a1 = SANDBOX_SERVICE1_UUID_A1, + .a2 = SANDBOX_SERVICE1_UUID_A2, + .a3 = SANDBOX_SERVICE1_UUID_A3, + .a4 = SANDBOX_SERVICE1_UUID_A4, + } + }, + { + .info = { .id = SANDBOX_SP4_ID, .exec_ctxt = 0x1487, .properties = 0x70325621 }, + .sp_uuid = { + .a1 = SANDBOX_SERVICE2_UUID_A1, + .a2 = SANDBOX_SERVICE2_UUID_A2, + .a3 = SANDBOX_SERVICE2_UUID_A3, + .a4 = SANDBOX_SERVICE2_UUID_A4, + } + } + +}; + +/* The emulator functions */ + +/** + * sandbox_ffa_version() - Emulated FFA_VERSION handler function + * @emul: The sandbox FF-A emulator device + * @pargs: The SMC call input arguments a0-a7 + * @res: The SMC return data + * + * Emulate FFA_VERSION FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ + +static int sandbox_ffa_version(struct udevice *emul, ffa_value_t *pargs, ffa_value_t *res) +{ + struct sandbox_ffa_emul *priv = dev_get_priv(emul); + + priv->fwk_version = FFA_VERSION_1_0; + res->a0 = priv->fwk_version; + + /* x1-x7 MBZ */ + memset(FFA_X1X7_MBZ_REG_START, 0, FFA_X1X7_MBZ_CNT * sizeof(ulong)); + + return 0; +} + +/** + * sandbox_ffa_id_get() - Emulated FFA_ID_GET handler function + * @emul: The sandbox FF-A emulator device + * @pargs: The SMC call input arguments a0-a7 + * @res: The SMC return data + * + * Emulate FFA_ID_GET FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_id_get(struct udevice *emul, ffa_value_t *pargs, ffa_value_t *res) +{ + struct sandbox_ffa_emul *priv = dev_get_priv(emul); + + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a1 = 0; + + priv->id = NS_PHYS_ENDPOINT_ID; + res->a2 = priv->id; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong)); + + return 0; +} + +/** + * sandbox_ffa_features() - Emulated FFA_FEATURES handler function + * @pargs: The SMC call input arguments a0-a7 + * @res: The SMC return data + * + * Emulate FFA_FEATURES FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_features(ffa_value_t *pargs, ffa_value_t *res) +{ + res->a1 = 0; + + if (pargs->a1 == FFA_SMC_64(FFA_RXTX_MAP)) { + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = RXTX_BUFFERS_MIN_SIZE; + res->a3 = 0; + /* x4-x7 MBZ */ + memset(FFA_X4X7_MBZ_REG_START, 0, FFA_X4X7_MBZ_CNT * sizeof(ulong)); + return 0; + } + + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a2 = -NOT_SUPPORTED; + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong)); + log_err("FF-A interface %lx not implemented\n", pargs->a1); + + return ffa_to_std_errmap[NOT_SUPPORTED]; +} + +/** + * sandbox_ffa_partition_info_get() - Emulated FFA_PARTITION_INFO_GET handler + * @emul: The sandbox FF-A emulator device + * @pargs: The SMC call input arguments a0-a7 + * @res: The SMC return data + * + * Emulate FFA_PARTITION_INFO_GET FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_partition_info_get(struct udevice *emul, ffa_value_t *pargs, + ffa_value_t *res) +{ + struct ffa_partition_info *rxbuf_desc_info = NULL; + u32 descs_cnt; + u32 descs_size_bytes; + int ret; + struct sandbox_ffa_emul *priv = dev_get_priv(emul); + + res->a0 = FFA_SMC_32(FFA_ERROR); + + if (!priv->pair.rxbuf) { + res->a2 = -DENIED; + ret = ffa_to_std_errmap[DENIED]; + goto cleanup; + } + + if (priv->pair_info.rxbuf_owned) { + res->a2 = -BUSY; + ret = ffa_to_std_errmap[BUSY]; + goto cleanup; + } + + if (!priv->partitions.descs) { + priv->partitions.descs = sandbox_partitions; + priv->partitions.count = SANDBOX_PARTITIONS_CNT; + } + + descs_size_bytes = SANDBOX_PARTITIONS_CNT * + sizeof(struct ffa_partition_desc); + + /* Abort if the RX buffer size is smaller than the descs buffer size */ + if ((priv->pair_info.rxtx_buf_size * SZ_4K) < descs_size_bytes) { + res->a2 = -NO_MEMORY; + ret = ffa_to_std_errmap[NO_MEMORY]; + goto cleanup; + } + + rxbuf_desc_info = priv->pair.rxbuf; + + /* No UUID specified. Return the information of all partitions */ + if (!pargs->a1 && !pargs->a2 && !pargs->a3 && !pargs->a4) { + for (descs_cnt = 0; descs_cnt < SANDBOX_PARTITIONS_CNT; descs_cnt++) + *(rxbuf_desc_info++) = priv->partitions.descs[descs_cnt].info; + + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = SANDBOX_PARTITIONS_CNT; + /* Transfer ownership to the consumer: the non secure world */ + priv->pair_info.rxbuf_owned = 1; + ret = 0; + + goto cleanup; + } + + /* A UUID specified. Return the info of all SPs matching the UUID */ + + for (descs_cnt = 0 ; descs_cnt < SANDBOX_PARTITIONS_CNT ; descs_cnt++) + if (pargs->a1 == priv->partitions.descs[descs_cnt].sp_uuid.a1 && + pargs->a2 == priv->partitions.descs[descs_cnt].sp_uuid.a2 && + pargs->a3 == priv->partitions.descs[descs_cnt].sp_uuid.a3 && + pargs->a4 == priv->partitions.descs[descs_cnt].sp_uuid.a4) { + *(rxbuf_desc_info++) = priv->partitions.descs[descs_cnt].info; + } + + if (rxbuf_desc_info != priv->pair.rxbuf) { + res->a0 = FFA_SMC_32(FFA_SUCCESS); + /* Store the partitions count */ + res->a2 = (ulong) + (rxbuf_desc_info - (struct ffa_partition_info *) + priv->pair.rxbuf); + ret = 0; + + /* Transfer ownership to the consumer: the non secure world */ + priv->pair_info.rxbuf_owned = 1; + } else { + /* Unrecognized UUID */ + res->a2 = -INVALID_PARAMETERS; + ret = ffa_to_std_errmap[INVALID_PARAMETERS]; + } + +cleanup: + + log_err("FFA_PARTITION_INFO_GET (%ld)\n", res->a2); + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong)); + + return ret; +} + +/** + * sandbox_ffa_rxtx_map() - Emulated FFA_RXTX_MAP handler + * @emul: The sandbox FF-A emulator device + * @pargs: The SMC call input arguments a0-a7 + * @res: The SMC return data + * + * Emulate FFA_RXTX_MAP FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_rxtx_map(struct udevice *emul, ffa_value_t *pargs, ffa_value_t *res) +{ + int ret; + struct sandbox_ffa_emul *priv = dev_get_priv(emul); + + res->a0 = FFA_SMC_32(FFA_ERROR); + + if (priv->pair.txbuf && priv->pair.rxbuf) { + res->a2 = -DENIED; + ret = ffa_to_std_errmap[DENIED]; + goto feedback; + } + + if (pargs->a3 >= RXTX_BUFFERS_MIN_PAGES && pargs->a1 && pargs->a2) { + priv->pair.txbuf = map_sysmem(pargs->a1, 0); + priv->pair.rxbuf = map_sysmem(pargs->a2, 0); + priv->pair_info.rxtx_buf_size = pargs->a3; + priv->pair_info.rxbuf_mapped = 1; + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = 0; + ret = 0; + goto feedback; + } + + if (!pargs->a1 || !pargs->a2) { + res->a2 = -INVALID_PARAMETERS; + ret = ffa_to_std_errmap[INVALID_PARAMETERS]; + } else { + res->a2 = -NO_MEMORY; + ret = ffa_to_std_errmap[NO_MEMORY]; + } + + log_err("Error in FFA_RXTX_MAP arguments (%d)\n", + (int)res->a2); + +feedback: + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong)); + + return ret; +} + +/** + * sandbox_ffa_rxtx_unmap() - Emulated FFA_RXTX_UNMAP handler + * @emul: The sandbox FF-A emulator device + * @pargs: The SMC call input arguments a0-a7 + * @res: The SMC return data + * + * Emulate FFA_RXTX_UNMAP FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_rxtx_unmap(struct udevice *emul, ffa_value_t *pargs, ffa_value_t *res) +{ + int ret; + struct sandbox_ffa_emul *priv = dev_get_priv(emul); + + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a2 = -INVALID_PARAMETERS; + ret = ffa_to_std_errmap[INVALID_PARAMETERS]; + + if (GET_NS_PHYS_ENDPOINT_ID(pargs->a1) != priv->id) + goto feedback; + + if (priv->pair.txbuf && priv->pair.rxbuf) { + priv->pair.txbuf = 0; + priv->pair.rxbuf = 0; + priv->pair_info.rxtx_buf_size = 0; + priv->pair_info.rxbuf_mapped = 0; + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = 0; + ret = 0; + goto feedback; + } + + log_err("No buffer pair registered on behalf of the caller\n"); + +feedback: + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong)); + + return ret; +} + +/** + * sandbox_ffa_rx_release() - Emulated FFA_RX_RELEASE handler + * @emul: The sandbox FF-A emulator device + * @pargs: The SMC call input arguments a0-a7 + * @res: The SMC return data + * + * Emulate FFA_RX_RELEASE FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_rx_release(struct udevice *emul, ffa_value_t *pargs, ffa_value_t *res) +{ + int ret; + struct sandbox_ffa_emul *priv = dev_get_priv(emul); + + if (!priv->pair_info.rxbuf_owned) { + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a2 = -DENIED; + ret = ffa_to_std_errmap[DENIED]; + } else { + priv->pair_info.rxbuf_owned = 0; + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = 0; + ret = 0; + } + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong)); + + return ret; +} + +/** + * sandbox_ffa_sp_valid() - Check SP validity + * @emul: The sandbox FF-A emulator device + * @part_id: partition ID to check + * + * Search the input ID in the descriptors table. + * + * Return: + * + * 1 on success (Partition found). Otherwise, failure + */ +static int sandbox_ffa_sp_valid(struct udevice *emul, u16 part_id) +{ + u32 descs_cnt; + struct sandbox_ffa_emul *priv = dev_get_priv(emul); + + for (descs_cnt = 0 ; descs_cnt < SANDBOX_PARTITIONS_CNT ; descs_cnt++) + if (priv->partitions.descs[descs_cnt].info.id == part_id) + return 1; + + return 0; +} + +/** + * sandbox_ffa_msg_send_direct_req() - Emulated FFA_MSG_SEND_DIRECT_{REQ,RESP} handler + * @emul: The sandbox FF-A emulator device + * @pargs: The SMC call input arguments a0-a7 + * @res: The SMC return data + * + * Emulate FFA_MSG_SEND_DIRECT_{REQ,RESP} FF-A ABIs. + * Only SMC 64-bit is supported in Sandbox. + * + * Emulating interrupts is not supported. So, FFA_RUN and FFA_INTERRUPT are not + * supported. In case of success FFA_MSG_SEND_DIRECT_RESP is returned with + * default pattern data (0xff). + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_msg_send_direct_req(struct udevice *emul, + ffa_value_t *pargs, ffa_value_t *res) +{ + u16 part_id; + struct sandbox_ffa_emul *priv = dev_get_priv(emul); + + part_id = GET_DST_SP_ID(pargs->a1); + + if (GET_NS_PHYS_ENDPOINT_ID(pargs->a1) != priv->id || + !sandbox_ffa_sp_valid(emul, part_id) || pargs->a2) { + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a1 = 0; + res->a2 = -INVALID_PARAMETERS; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong)); + + return ffa_to_std_errmap[INVALID_PARAMETERS]; + } + + res->a0 = FFA_SMC_64(FFA_MSG_SEND_DIRECT_RESP); + + res->a1 = PREP_SRC_SP_ID(part_id) | + PREP_NS_PHYS_ENDPOINT_ID(priv->id); + + res->a2 = 0; + + /* Return 0xff bytes as a response */ + res->a3 = -1UL; + res->a4 = -1UL; + res->a5 = -1UL; + res->a6 = -1UL; + res->a7 = -1UL; + + return 0; +} + +/** + * sandbox_ffa_get_rxbuf_flags() - Read the mapping/ownership flags + * @emul: The sandbox FF-A emulator device + * @queried_func_id: The FF-A function to be queried + * @func_data: Pointer to the FF-A function arguments container structure + * + * Query the status flags of the following emulated + * ABIs: FFA_RXTX_MAP, FFA_RXTX_UNMAP, FFA_RX_RELEASE. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_get_rxbuf_flags(struct udevice *emul, u32 queried_func_id, + struct ffa_sandbox_data *func_data) +{ + struct sandbox_ffa_emul *priv = dev_get_priv(emul); + + if (!func_data) + return -EINVAL; + + if (!func_data->data0 || func_data->data0_size != sizeof(u8)) + return -EINVAL; + + switch (queried_func_id) { + case FFA_RXTX_MAP: + case FFA_RXTX_UNMAP: + *((u8 *)func_data->data0) = priv->pair_info.rxbuf_mapped; + return 0; + case FFA_RX_RELEASE: + *((u8 *)func_data->data0) = priv->pair_info.rxbuf_owned; + return 0; + default: + log_err("The querried FF-A interface flag (%d) undefined\n", + queried_func_id); + return -EINVAL; + } +} + +/** + * sandbox_ffa_get_fwk_version() - Return the FFA framework version + * @emul: The sandbox FF-A emulator device + * @func_data: Pointer to the FF-A function arguments container structure + * + * Return the FFA framework version read from the FF-A emulator data. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_get_fwk_version(struct udevice *emul, struct ffa_sandbox_data *func_data) +{ + struct sandbox_ffa_emul *priv = dev_get_priv(emul); + + if (!func_data) + return -EINVAL; + + if (!func_data->data0 || + func_data->data0_size != sizeof(priv->fwk_version)) + return -EINVAL; + + *((u32 *)func_data->data0) = priv->fwk_version; + + return 0; +} + +/** + * sandbox_ffa_get_parts() - Return the address of partitions data + * @emul: The sandbox FF-A emulator device + * @func_data: Pointer to the FF-A function arguments container structure + * + * Return the address of partitions data read from the FF-A emulator data. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_get_parts(struct udevice *emul, struct ffa_sandbox_data *func_data) +{ + struct sandbox_ffa_emul *priv = dev_get_priv(emul); + + if (!func_data) + return -EINVAL; + + if (!func_data->data0 || + func_data->data0_size != sizeof(struct ffa_partitions *)) + return -EINVAL; + + *((struct ffa_partitions **)func_data->data0) = &priv->partitions; + + return 0; +} + +/** + * sandbox_query_ffa_emul_state() - Inspect the FF-A ABIs + * @queried_func_id: The FF-A function to be queried + * @func_data: Pointer to the FF-A function arguments container structure + * + * Query the status of FF-A ABI specified in the input argument. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int sandbox_query_ffa_emul_state(u32 queried_func_id, + struct ffa_sandbox_data *func_data) +{ + struct udevice *emul; + int ret; + + ret = uclass_first_device_err(UCLASS_FFA_EMUL, &emul); + if (ret) { + log_err("Cannot find FF-A emulator during querying state\n"); + return ret; + } + + switch (queried_func_id) { + case FFA_RXTX_MAP: + case FFA_RXTX_UNMAP: + case FFA_RX_RELEASE: + return sandbox_ffa_get_rxbuf_flags(emul, queried_func_id, func_data); + case FFA_VERSION: + return sandbox_ffa_get_fwk_version(emul, func_data); + case FFA_PARTITION_INFO_GET: + return sandbox_ffa_get_parts(emul, func_data); + default: + log_err("Undefined FF-A interface (%d)\n", + queried_func_id); + return -EINVAL; + } +} + +/** + * sandbox_arm_ffa_smccc_smc() - FF-A SMC call emulation + * @args: the SMC call arguments + * @res: the SMC call returned data + * + * Emulate the FF-A ABIs SMC call. + * The emulated FF-A ABI is identified and invoked. + * FF-A emulation is based on the FF-A specification 1.0 + * + * Return: + * + * 0 on success. Otherwise, failure. + * FF-A protocol error codes are returned using the registers arguments as + * described by the specification + */ +void sandbox_arm_ffa_smccc_smc(ffa_value_t *args, ffa_value_t *res) +{ + int ret = 0; + struct udevice *emul; + + ret = uclass_first_device_err(UCLASS_FFA_EMUL, &emul); + if (ret) { + log_err("Cannot find FF-A emulator during SMC emulation\n"); + return; + } + + switch (args->a0) { + case FFA_SMC_32(FFA_VERSION): + ret = sandbox_ffa_version(emul, args, res); + break; + case FFA_SMC_32(FFA_PARTITION_INFO_GET): + ret = sandbox_ffa_partition_info_get(emul, args, res); + break; + case FFA_SMC_32(FFA_RXTX_UNMAP): + ret = sandbox_ffa_rxtx_unmap(emul, args, res); + break; + case FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ): + ret = sandbox_ffa_msg_send_direct_req(emul, args, res); + break; + case FFA_SMC_32(FFA_ID_GET): + ret = sandbox_ffa_id_get(emul, args, res); + break; + case FFA_SMC_32(FFA_FEATURES): + ret = sandbox_ffa_features(args, res); + break; + case FFA_SMC_64(FFA_RXTX_MAP): + ret = sandbox_ffa_rxtx_map(emul, args, res); + break; + case FFA_SMC_32(FFA_RX_RELEASE): + ret = sandbox_ffa_rx_release(emul, args, res); + break; + default: + log_err("Undefined FF-A interface (%lx)\n", + args->a0); + } + + if (ret != 0) + log_err("FF-A ABI internal failure (%d)\n", ret); +} + +/** + * invoke_ffa_fn() - SMC wrapper + * @args: FF-A ABI arguments to be copied to Xn registers + * @res: FF-A ABI return data to be copied from Xn registers + * + * Calls the emulated SMC call. + */ +void invoke_ffa_fn(ffa_value_t args, ffa_value_t *res) +{ + sandbox_arm_ffa_smccc_smc(&args, res); +} + +/** + * ffa_emul_find() - Find the FF-A emulator + * @dev: the sandbox FF-A device (sandbox-arm-ffa) + * @emulp: the FF-A emulator device (sandbox-ffa-emul) + * + * Search for the FF-A emulator and returns its device pointer. + * + * Return: + * 0 on success. Otherwise, failure + */ +int ffa_emul_find(struct udevice *dev, struct udevice **emulp) +{ + int ret; + + ret = uclass_first_device_err(UCLASS_FFA_EMUL, emulp); + if (ret) { + log_err("Cannot find FF-A emulator\n"); + return ret; + } + + log_debug("FF-A emulator ready to use\n"); + + return 0; +} + +UCLASS_DRIVER(ffa_emul) = { + .name = "ffa_emul", + .id = UCLASS_FFA_EMUL, + .post_bind = dm_scan_fdt_dev, +}; + +static const struct udevice_id sandbox_ffa_emul_ids[] = { + { .compatible = "sandbox,arm-ffa-emul" }, + { } +}; + +/* Declaring the sandbox FF-A emulator under UCLASS_FFA_EMUL */ +U_BOOT_DRIVER(sandbox_ffa_emul) = { + .name = "sandbox_ffa_emul", + .id = UCLASS_FFA_EMUL, + .of_match = sandbox_ffa_emul_ids, + .priv_auto = sizeof(struct sandbox_ffa_emul), +}; diff --git a/drivers/firmware/arm-ffa/sandbox_ffa.c b/drivers/firmware/arm-ffa/sandbox_ffa.c new file mode 100644 index 0000000000..11142429c0 --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox_ffa.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates <open-source-office@arm.com> + * + * Authors: + * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> + */ +#include <common.h> +#include <arm_ffa.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/device-internal.h> +#include <linux/errno.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * sandbox_ffa_discover() - perform sandbox FF-A discovery + * @dev: The sandbox FF-A bus device + * Try to discover the FF-A framework. Discovery is performed by + * querying the FF-A framework version from secure world using the FFA_VERSION ABI. + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_discover(struct udevice *dev) +{ + int ret; + struct udevice *emul; + + log_debug("Emulated FF-A framework discovery\n"); + + ret = ffa_emul_find(dev, &emul); + if (ret) { + log_err("Cannot find FF-A emulator\n"); + return ret; + } + + ret = ffa_get_version_hdlr(dev); + if (ret) + return ret; + + return 0; +} + +/** + * sandbox_ffa_probe() - The sandbox FF-A driver probe function + * @dev: the sandbox-arm-ffa device + * Save the emulator device in uc_priv. + * Return: + * + * 0 on success. + */ +static int sandbox_ffa_probe(struct udevice *dev) +{ + int ret; + struct ffa_priv *uc_priv = dev_get_uclass_priv(dev); + + ret = uclass_first_device_err(UCLASS_FFA_EMUL, &uc_priv->emul); + if (ret) { + log_err("Cannot find FF-A emulator\n"); + return ret; + } + + return 0; +} + +/** + * sandbox_ffa_bind() - The sandbox FF-A driver bind function + * @dev: the sandbox-arm-ffa device + * Try to discover the emulated FF-A bus. + * Return: + * + * 0 on success. + */ +static int sandbox_ffa_bind(struct udevice *dev) +{ + int ret; + + ret = sandbox_ffa_discover(dev); + if (ret) + return ret; + + return 0; +} + +/* Sandbox Arm FF-A emulator operations */ + +static const struct ffa_bus_ops sandbox_ffa_ops = { + .partition_info_get = ffa_get_partitions_info_hdlr, + .sync_send_receive = ffa_msg_send_direct_req_hdlr, + .rxtx_unmap = ffa_unmap_rxtx_buffers_hdlr, +}; + +static const struct udevice_id sandbox_ffa_id[] = { + { "sandbox,arm-ffa", 0 }, + { }, +}; + +/* Declaring the sandbox FF-A driver under UCLASS_FFA */ +U_BOOT_DRIVER(sandbox_arm_ffa) = { + .name = "sandbox_arm_ffa", + .of_match = sandbox_ffa_id, + .id = UCLASS_FFA, + .bind = sandbox_ffa_bind, + .probe = sandbox_ffa_probe, + .ops = &sandbox_ffa_ops, +}; diff --git a/drivers/i2c/mvtwsi.c b/drivers/i2c/mvtwsi.c index 93bbc6916e..14cdb0f663 100644 --- a/drivers/i2c/mvtwsi.c +++ b/drivers/i2c/mvtwsi.c @@ -142,6 +142,8 @@ enum mvtwsi_ctrl_register_fields { * code. */ enum mvstwsi_status_values { + /* Protocol violation on bus; this is a terminal state */ + MVTWSI_BUS_ERROR = 0x00, /* START condition transmitted */ MVTWSI_STATUS_START = 0x08, /* Repeated START condition transmitted */ @@ -526,6 +528,36 @@ static void __twsi_i2c_init(struct mvtwsi_registers *twsi, int speed, } /* + * __twsi_i2c_reinit() - Reset and reinitialize the I2C controller. + * + * This function should be called to get the MVTWSI controller out of the + * "bus error" state. It saves and restores the baud and address registers. + * + * @twsi: The MVTWSI register structure to use. + * @tick: The duration of a clock cycle at the current I2C speed. + */ +static void __twsi_i2c_reinit(struct mvtwsi_registers *twsi, uint tick) +{ + uint baud; + uint slaveadd; + + /* Save baud, address registers */ + baud = readl(&twsi->baudrate); + slaveadd = readl(&twsi->slave_address); + + /* Reset controller */ + twsi_reset(twsi); + + /* Restore baud, address registers */ + writel(baud, &twsi->baudrate); + writel(slaveadd, &twsi->slave_address); + writel(0, &twsi->xtnd_slave_addr); + + /* Assert STOP, but don't care for the result */ + (void) twsi_stop(twsi, tick); +} + +/* * i2c_begin() - Start a I2C transaction. * * Begin a I2C transaction with a given expected start status and chip address. @@ -621,6 +653,11 @@ static int __twsi_i2c_read(struct mvtwsi_registers *twsi, uchar chip, int stop_status; int expected_start = MVTWSI_STATUS_START; + /* Check for (and clear) a bus error from a previous failed transaction + * or another master on the same bus */ + if (readl(&twsi->status) == MVTWSI_BUS_ERROR) + __twsi_i2c_reinit(twsi, tick); + if (alen > 0) { /* Begin i2c write to send the address bytes */ status = i2c_begin(twsi, expected_start, (chip << 1), tick); @@ -668,6 +705,11 @@ static int __twsi_i2c_write(struct mvtwsi_registers *twsi, uchar chip, { int status, stop_status; + /* Check for (and clear) a bus error from a previous failed transaction + * or another master on the same bus */ + if (readl(&twsi->status) == MVTWSI_BUS_ERROR) + __twsi_i2c_reinit(twsi, tick); + /* Begin i2c write to send first the address bytes, then the * data bytes */ status = i2c_begin(twsi, MVTWSI_STATUS_START, (chip << 1), tick); diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c index 01d9b0201f..0e157672ea 100644 --- a/drivers/mmc/mmc-uclass.c +++ b/drivers/mmc/mmc-uclass.c @@ -421,7 +421,7 @@ int mmc_bind(struct udevice *dev, struct mmc *mmc, const struct mmc_config *cfg) mmc->cfg = cfg; mmc->priv = dev; - ret = bootdev_setup_sibling_blk(bdev, "mmc_bootdev"); + ret = bootdev_setup_for_sibling_blk(bdev, "mmc_bootdev"); if (ret) return log_msg_ret("bootdev", ret); diff --git a/drivers/nvme/nvme.c b/drivers/nvme/nvme.c index a7add66ab4..20dc910d8a 100644 --- a/drivers/nvme/nvme.c +++ b/drivers/nvme/nvme.c @@ -910,7 +910,7 @@ int nvme_init(struct udevice *udev) if (ret) goto free_id; - ret = bootdev_setup_sibling_blk(ns_udev, "nvme_bootdev"); + ret = bootdev_setup_for_sibling_blk(ns_udev, "nvme_bootdev"); if (ret) return log_msg_ret("bootdev", ret); diff --git a/drivers/pci/pcie_plda_common.c b/drivers/pci/pcie_plda_common.c index 005b92616a..cd74bb4711 100644 --- a/drivers/pci/pcie_plda_common.c +++ b/drivers/pci/pcie_plda_common.c @@ -36,8 +36,8 @@ static int plda_pcie_conf_address(const struct udevice *udev, pci_dev_t bdf, uint offset, void **paddr) { struct pcie_plda *priv = dev_get_priv(udev); - int where = PCIE_ECAM_OFFSET(PCI_BUS(bdf), PCI_DEV(bdf), - PCI_FUNC(bdf), offset); + int where = PCIE_ECAM_OFFSET(PCI_BUS(bdf) - dev_seq(udev), + PCI_DEV(bdf), PCI_FUNC(bdf), offset); if (!plda_pcie_addr_valid(priv, bdf)) return -ENODEV; @@ -71,6 +71,7 @@ int plda_pcie_config_write(struct udevice *udev, pci_dev_t bdf, (offset == PCI_PRIMARY_BUS && size != PCI_SIZE_8))) { priv->sec_busno = ((offset == PCI_PRIMARY_BUS) ? (value >> 8) : value) & 0xff; + priv->sec_busno += dev_seq(udev); debug("Secondary bus number was changed to %d\n", priv->sec_busno); } diff --git a/drivers/pci/pcie_rockchip.c b/drivers/pci/pcie_rockchip.c index 72b41398f2..624841e9d8 100644 --- a/drivers/pci/pcie_rockchip.c +++ b/drivers/pci/pcie_rockchip.c @@ -12,23 +12,15 @@ */ #include <common.h> -#include <clk.h> #include <dm.h> -#include <asm/global_data.h> #include <dm/device_compat.h> #include <generic-phy.h> #include <pci.h> -#include <power-domain.h> #include <power/regulator.h> #include <reset.h> -#include <syscon.h> -#include <asm/io.h> #include <asm-generic/gpio.h> -#include <asm/arch-rockchip/clock.h> #include <linux/iopoll.h> -DECLARE_GLOBAL_DATA_PTR; - #define HIWORD_UPDATE(mask, val) (((mask) << 16) | (val)) #define HIWORD_UPDATE_BIT(val) HIWORD_UPDATE(val, val) @@ -383,41 +375,38 @@ static int rockchip_pcie_set_vpcie(struct udevice *dev) struct rockchip_pcie *priv = dev_get_priv(dev); int ret; - if (priv->vpcie3v3) { - ret = regulator_set_enable(priv->vpcie3v3, true); - if (ret) { - dev_err(dev, "failed to enable vpcie3v3 (ret=%d)\n", - ret); - return ret; - } + ret = regulator_set_enable_if_allowed(priv->vpcie12v, true); + if (ret && ret != -ENOSYS) { + dev_err(dev, "failed to enable vpcie12v (ret=%d)\n", ret); + return ret; } - if (priv->vpcie1v8) { - ret = regulator_set_enable(priv->vpcie1v8, true); - if (ret) { - dev_err(dev, "failed to enable vpcie1v8 (ret=%d)\n", - ret); - goto err_disable_3v3; - } + ret = regulator_set_enable_if_allowed(priv->vpcie3v3, true); + if (ret && ret != -ENOSYS) { + dev_err(dev, "failed to enable vpcie3v3 (ret=%d)\n", ret); + goto err_disable_12v; } - if (priv->vpcie0v9) { - ret = regulator_set_enable(priv->vpcie0v9, true); - if (ret) { - dev_err(dev, "failed to enable vpcie0v9 (ret=%d)\n", - ret); - goto err_disable_1v8; - } + ret = regulator_set_enable_if_allowed(priv->vpcie1v8, true); + if (ret && ret != -ENOSYS) { + dev_err(dev, "failed to enable vpcie1v8 (ret=%d)\n", ret); + goto err_disable_3v3; + } + + ret = regulator_set_enable_if_allowed(priv->vpcie0v9, true); + if (ret && ret != -ENOSYS) { + dev_err(dev, "failed to enable vpcie0v9 (ret=%d)\n", ret); + goto err_disable_1v8; } return 0; err_disable_1v8: - if (priv->vpcie1v8) - regulator_set_enable(priv->vpcie1v8, false); + regulator_set_enable_if_allowed(priv->vpcie1v8, false); err_disable_3v3: - if (priv->vpcie3v3) - regulator_set_enable(priv->vpcie3v3, false); + regulator_set_enable_if_allowed(priv->vpcie3v3, false); +err_disable_12v: + regulator_set_enable_if_allowed(priv->vpcie12v, false); return ret; } @@ -427,19 +416,12 @@ static int rockchip_pcie_parse_dt(struct udevice *dev) int ret; priv->axi_base = dev_read_addr_name(dev, "axi-base"); - if (!priv->axi_base) - return -ENODEV; + if (priv->axi_base == FDT_ADDR_T_NONE) + return -EINVAL; priv->apb_base = dev_read_addr_name(dev, "apb-base"); - if (!priv->axi_base) - return -ENODEV; - - ret = gpio_request_by_name(dev, "ep-gpios", 0, - &priv->ep_gpio, GPIOD_IS_OUT); - if (ret) { - dev_err(dev, "failed to find ep-gpios property\n"); - return ret; - } + if (priv->apb_base == FDT_ADDR_T_NONE) + return -EINVAL; ret = reset_get_by_name(dev, "core", &priv->core_rst); if (ret) { @@ -483,6 +465,13 @@ static int rockchip_pcie_parse_dt(struct udevice *dev) return ret; } + ret = device_get_supply_regulator(dev, "vpcie12v-supply", + &priv->vpcie12v); + if (ret && ret != -ENOENT) { + dev_err(dev, "failed to get vpcie12v supply (ret=%d)\n", ret); + return ret; + } + ret = device_get_supply_regulator(dev, "vpcie3v3-supply", &priv->vpcie3v3); if (ret && ret != -ENOENT) { @@ -510,6 +499,13 @@ static int rockchip_pcie_parse_dt(struct udevice *dev) return ret; } + ret = gpio_request_by_name(dev, "ep-gpios", 0, + &priv->ep_gpio, GPIOD_IS_OUT); + if (ret) { + dev_err(dev, "failed to find ep-gpios property\n"); + return ret; + } + return 0; } @@ -529,16 +525,26 @@ static int rockchip_pcie_probe(struct udevice *dev) ret = rockchip_pcie_set_vpcie(dev); if (ret) - return ret; + goto err_gpio_free; ret = rockchip_pcie_init_port(dev); if (ret) - return ret; + goto err_disable_vpcie; dev_info(dev, "PCIE-%d: Link up (Bus%d)\n", dev_seq(dev), hose->first_busno); return 0; + +err_disable_vpcie: + regulator_set_enable_if_allowed(priv->vpcie0v9, false); + regulator_set_enable_if_allowed(priv->vpcie1v8, false); + regulator_set_enable_if_allowed(priv->vpcie3v3, false); + regulator_set_enable_if_allowed(priv->vpcie12v, false); +err_gpio_free: + if (dm_gpio_is_valid(&priv->ep_gpio)) + dm_gpio_free(dev, &priv->ep_gpio); + return ret; } static const struct dm_pci_ops rockchip_pcie_ops = { @@ -552,10 +558,10 @@ static const struct udevice_id rockchip_pcie_ids[] = { }; U_BOOT_DRIVER(rockchip_pcie) = { - .name = "rockchip_pcie", - .id = UCLASS_PCI, - .of_match = rockchip_pcie_ids, - .ops = &rockchip_pcie_ops, - .probe = rockchip_pcie_probe, + .name = "rockchip_pcie", + .id = UCLASS_PCI, + .of_match = rockchip_pcie_ids, + .ops = &rockchip_pcie_ops, + .probe = rockchip_pcie_probe, .priv_auto = sizeof(struct rockchip_pcie), }; diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3568.c b/drivers/pinctrl/rockchip/pinctrl-rk3568.c index 314edb5a60..1d43919826 100644 --- a/drivers/pinctrl/rockchip/pinctrl-rk3568.c +++ b/drivers/pinctrl/rockchip/pinctrl-rk3568.c @@ -113,11 +113,9 @@ static int rk3568_set_mux(struct rockchip_pin_bank *bank, int pin, int mux) struct rockchip_pinctrl_priv *priv = bank->priv; int iomux_num = (pin / 8); struct regmap *regmap; - int reg, ret, mask; + int reg, mask; u8 bit; - u32 data; - - debug("setting mux of GPIO%d-%d to %d\n", bank->bank_num, pin, mux); + u32 data, rmask; if (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU) regmap = priv->regmap_pmu; @@ -131,10 +129,10 @@ static int rk3568_set_mux(struct rockchip_pin_bank *bank, int pin, int mux) mask = 0xf; data = (mask << (bit + 16)); + rmask = data | (data >> 16); data |= (mux & mask) << bit; - ret = regmap_write(regmap, reg, data); - return ret; + return regmap_update_bits(regmap, reg, rmask, data); } #define RK3568_PULL_PMU_OFFSET 0x20 @@ -225,7 +223,7 @@ static int rk3568_set_pull(struct rockchip_pin_bank *bank, struct regmap *regmap; int reg, ret; u8 bit, type; - u32 data; + u32 data, rmask; if (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT) return -ENOTSUPP; @@ -249,52 +247,59 @@ static int rk3568_set_pull(struct rockchip_pin_bank *bank, /* enable the write to the equivalent lower bits */ data = ((1 << ROCKCHIP_PULL_BITS_PER_PIN) - 1) << (bit + 16); - + rmask = data | (data >> 16); data |= (ret << bit); - ret = regmap_write(regmap, reg, data); - return ret; + return regmap_update_bits(regmap, reg, rmask, data); } +#define GRF_GPIO1C5_DS 0x0840 +#define GRF_GPIO2A2_DS 0x0844 +#define GRF_GPIO2B0_DS 0x0848 +#define GRF_GPIO3A0_DS 0x084c +#define GRF_GPIO3A6_DS 0x0850 +#define GRF_GPIO4A0_DS 0x0854 + static int rk3568_set_drive(struct rockchip_pin_bank *bank, int pin_num, int strength) { struct regmap *regmap; - int reg; - u32 data; + int reg, ret; + u32 data, rmask; u8 bit; int drv = (1 << (strength + 1)) - 1; - int ret = 0; rk3568_calc_drv_reg_and_bit(bank, pin_num, ®map, ®, &bit); /* enable the write to the equivalent lower bits */ data = ((1 << RK3568_DRV_BITS_PER_PIN) - 1) << (bit + 16); + rmask = data | (data >> 16); data |= (drv << bit); - ret = regmap_write(regmap, reg, data); + ret = regmap_update_bits(regmap, reg, rmask, data); if (ret) return ret; if (bank->bank_num == 1 && pin_num == 21) - reg = 0x0840; + reg = GRF_GPIO1C5_DS; else if (bank->bank_num == 2 && pin_num == 2) - reg = 0x0844; + reg = GRF_GPIO2A2_DS; else if (bank->bank_num == 2 && pin_num == 8) - reg = 0x0848; + reg = GRF_GPIO2B0_DS; else if (bank->bank_num == 3 && pin_num == 0) - reg = 0x084c; + reg = GRF_GPIO3A0_DS; else if (bank->bank_num == 3 && pin_num == 6) - reg = 0x0850; + reg = GRF_GPIO3A6_DS; else if (bank->bank_num == 4 && pin_num == 0) - reg = 0x0854; + reg = GRF_GPIO4A0_DS; else return 0; data = ((1 << RK3568_DRV_BITS_PER_PIN) - 1) << 16; - data |= drv; + rmask = data | (data >> 16); + data |= drv >> 6; - return regmap_write(regmap, reg, data); + return regmap_update_bits(regmap, reg, rmask, data); } static int rk3568_set_schmitt(struct rockchip_pin_bank *bank, @@ -302,16 +307,17 @@ static int rk3568_set_schmitt(struct rockchip_pin_bank *bank, { struct regmap *regmap; int reg; - u32 data; + u32 data, rmask; u8 bit; rk3568_calc_schmitt_reg_and_bit(bank, pin_num, ®map, ®, &bit); /* enable the write to the equivalent lower bits */ data = ((1 << RK3568_SCHMITT_BITS_PER_PIN) - 1) << (bit + 16); - data |= (enable << bit); + rmask = data | (data >> 16); + data |= ((enable ? 0x2 : 0x1) << bit); - return regmap_write(regmap, reg, data); + return regmap_update_bits(regmap, reg, rmask, data); } static struct rockchip_pin_bank rk3568_pin_banks[] = { diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 77c75240d5..6498f998ad 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -608,7 +608,7 @@ static int do_scsi_scan_one(struct udevice *dev, int id, int lun, bool verbose) /* TODO: undo create */ return log_msg_ret("pro", ret); - ret = bootdev_setup_sibling_blk(bdev, "scsi_bootdev"); + ret = bootdev_setup_for_sibling_blk(bdev, "scsi_bootdev"); if (ret) return log_msg_ret("bd", ret); diff --git a/drivers/serial/serial_stm32.c b/drivers/serial/serial_stm32.c index 0085113f67..23d476fba2 100644 --- a/drivers/serial/serial_stm32.c +++ b/drivers/serial/serial_stm32.c @@ -22,6 +22,14 @@ #include "serial_stm32.h" #include <dm/device_compat.h> +/* + * At 115200 bits/s + * 1 bit = 1 / 115200 = 8,68 us + * 8 bits = 69,444 us + * 10 bits are needed for worst case (8 bits + 1 start + 1 stop) = 86.806 us + */ +#define ONE_BYTE_B115200_US 87 + static void _stm32_serial_setbrg(fdt_addr_t base, struct stm32_uart_info *uart_info, u32 clock_rate, @@ -209,12 +217,10 @@ static int stm32_serial_probe(struct udevice *dev) * before uart initialization, wait for TC bit (Transmission Complete) * in case there is still chars from previous bootstage to transmit */ - ret = read_poll_timeout(readl, isr, isr & USART_ISR_TC, 10, 150, - plat->base + ISR_OFFSET(stm32f4)); - if (ret) { - clk_disable(&clk); - return ret; - } + ret = read_poll_timeout(readl, isr, isr & USART_ISR_TC, 50, + 16 * ONE_BYTE_B115200_US, plat->base + ISR_OFFSET(stm32f4)); + if (ret) + dev_dbg(dev, "FIFO not empty, some character can be lost (%d)\n", ret); ret = reset_get_by_index(dev, 0, &reset); if (!ret) { diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c index 02c0138a20..7a03435ba7 100644 --- a/drivers/usb/host/usb-uclass.c +++ b/drivers/usb/host/usb-uclass.c @@ -346,7 +346,7 @@ int usb_init(void) if (controllers_initialized == 0) printf("No working controllers found\n"); - return usb_started ? 0 : -1; + return usb_started ? 0 : -ENOENT; } int usb_setup_ehci_gadget(struct ehci_ctrl **ctlrp) diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index fe43fbd700..09f2cb1a73 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1011,6 +1011,16 @@ config SPL_VIDEO if SPL_VIDEO source "drivers/video/tidss/Kconfig" +config SPL_VIDEO_HANDOFF + bool "Pass the video frame-buffer through to U-Boot proper" + depends on SPL_BLOBLIST + default y if !X86 + help + Enable this to set up video-handoff information in SPL which can be + picked up in U-Boot proper. This includes the frame buffer and + various other pieces of information. With this enabled, SPL can set + up video and avoid re-initing it later. + config SPL_VIDEO_LOGO bool "Show the U-Boot logo on the display at SPL" default y if !SPL_SPLASH_SCREEN @@ -1105,7 +1115,7 @@ config SPL_VIDEO_REMOVE bool "Remove video driver after SPL stage" help if this option is enabled video driver will be removed at the end of - SPL stage, beforeloading the next stage. + SPL stage, before loading the next stage. if SPL_SPLASH_SCREEN diff --git a/drivers/video/bcm2835.c b/drivers/video/bcm2835.c index c2962932c9..14942526f1 100644 --- a/drivers/video/bcm2835.c +++ b/drivers/video/bcm2835.c @@ -54,6 +54,9 @@ static const struct udevice_id bcm2835_video_ids[] = { { .compatible = "brcm,bcm2835-hdmi" }, { .compatible = "brcm,bcm2711-hdmi0" }, { .compatible = "brcm,bcm2708-fb" }, +#if !IS_ENABLED(CONFIG_VIDEO_DT_SIMPLEFB) + { .compatible = "simple-framebuffer" }, +#endif { } }; diff --git a/drivers/video/pwm_backlight.c b/drivers/video/pwm_backlight.c index 46c16a8f44..aa0e292866 100644 --- a/drivers/video/pwm_backlight.c +++ b/drivers/video/pwm_backlight.c @@ -14,6 +14,7 @@ #include <pwm.h> #include <asm/gpio.h> #include <linux/delay.h> +#include <linux/math64.h> #include <power/regulator.h> /** @@ -59,12 +60,14 @@ struct pwm_backlight_priv { static int set_pwm(struct pwm_backlight_priv *priv) { + u64 width; uint duty_cycle; int ret; if (priv->period_ns) { - duty_cycle = (u64)priv->period_ns * (priv->cur_level - priv->min_level) / - (priv->max_level - priv->min_level); + width = priv->period_ns * (priv->cur_level - priv->min_level); + duty_cycle = div_u64(width, + (priv->max_level - priv->min_level)); ret = pwm_set_config(priv->pwm, priv->channel, priv->period_ns, duty_cycle); } else { diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c index 05f9304780..b5b3b66259 100644 --- a/drivers/video/vidconsole-uclass.c +++ b/drivers/video/vidconsole-uclass.c @@ -603,7 +603,7 @@ int vidconsole_measure(struct udevice *dev, const char *name, uint size, struct vidconsole_ops *ops = vidconsole_get_ops(dev); int ret; - if (ops->select_font) { + if (ops->measure) { ret = ops->measure(dev, name, size, text, bbox); if (ret != -ENOSYS) return ret; diff --git a/drivers/video/video-uclass.c b/drivers/video/video-uclass.c index 8f268fc406..f743ed74c8 100644 --- a/drivers/video/video-uclass.c +++ b/drivers/video/video-uclass.c @@ -141,7 +141,7 @@ int video_reserve(ulong *addrp) debug("Video frame buffers from %lx to %lx\n", gd->video_bottom, gd->video_top); - if (spl_phase() == PHASE_SPL && CONFIG_IS_ENABLED(BLOBLIST)) { + if (spl_phase() == PHASE_SPL && CONFIG_IS_ENABLED(VIDEO_HANDOFF)) { struct video_handoff *ho; ho = bloblist_add(BLOBLISTT_U_BOOT_VIDEO, sizeof(*ho), 0); diff --git a/drivers/virtio/virtio-uclass.c b/drivers/virtio/virtio-uclass.c index f2b3ef1d8b..c542016273 100644 --- a/drivers/virtio/virtio-uclass.c +++ b/drivers/virtio/virtio-uclass.c @@ -248,7 +248,7 @@ static int virtio_uclass_post_probe(struct udevice *udev) device_set_name_alloced(vdev); if (uc_priv->device == VIRTIO_ID_BLOCK && !IS_ENABLED(CONFIG_SANDBOX)) { - ret = bootdev_setup_sibling_blk(vdev, "virtio_bootdev"); + ret = bootdev_setup_for_sibling_blk(vdev, "virtio_bootdev"); if (ret) return log_msg_ret("bootdev", ret); } diff --git a/drivers/watchdog/ftwdt010_wdt.c b/drivers/watchdog/ftwdt010_wdt.c index a6b33b1720..1f5f301b12 100644 --- a/drivers/watchdog/ftwdt010_wdt.c +++ b/drivers/watchdog/ftwdt010_wdt.c @@ -25,8 +25,27 @@ struct ftwdt010_wdt_priv { struct ftwdt010_wdt __iomem *regs; }; +static int ftwdt010_wdt_reset(struct udevice *dev) +{ + struct ftwdt010_wdt_priv *priv = dev_get_priv(dev); + struct ftwdt010_wdt *wd = priv->regs; + + debug("Reset WDT..\n"); + + /* clear control register */ + writel(0, &wd->wdcr); + + /* Write Magic number */ + writel(FTWDT010_WDRESTART_MAGIC, &wd->wdrestart); + + /* Enable WDT */ + writel(FTWDT010_WDCR_RST | FTWDT010_WDCR_ENABLE, &wd->wdcr); + + return 0; +} + /* - * Set the watchdog time interval. + * Set the watchdog time interval and start the timer. * Counter is 32 bit. */ static int ftwdt010_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags) @@ -52,24 +71,7 @@ static int ftwdt010_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags) writel(reg, &wd->wdload); - return 0; -} - -static int ftwdt010_wdt_reset(struct udevice *dev) -{ - struct ftwdt010_wdt_priv *priv = dev_get_priv(dev); - struct ftwdt010_wdt *wd = priv->regs; - - /* clear control register */ - writel(0, &wd->wdcr); - - /* Write Magic number */ - writel(FTWDT010_WDRESTART_MAGIC, &wd->wdrestart); - - /* Enable WDT */ - writel((FTWDT010_WDCR_RST | FTWDT010_WDCR_ENABLE), &wd->wdcr); - - return 0; + return ftwdt010_wdt_reset(dev); } static int ftwdt010_wdt_stop(struct udevice *dev) |