
At the moment, only smc API is a set of functions in arch/arm/mach-meson/sm.c. This approach is hard to configure and also doesn't contain any generic API for calling smc.
This patch add Meson SM driver with generic API (struct meson_sm_ops):
- sm_call() - sm_call_write() - sm_call_read()
A typical driver usage example is shown here:
1. uclass_get_device_by_driver(UCLASS_FIRMWARE, "secure-monitor", &dev); 2. handle = meson_sm_get_handle(dev); 3. handle->ops.sm_call(dev, cmd, ...);
Signed-off-by: Alexey Romanov avromanov@sberdevices.ru --- arch/arm/mach-meson/Kconfig | 1 + drivers/firmware/Kconfig | 10 ++ drivers/firmware/Makefile | 1 + drivers/firmware/meson/Kconfig | 6 + drivers/firmware/meson/Makefile | 3 + drivers/firmware/meson/meson_sm.c | 217 ++++++++++++++++++++++++++++++ include/meson/sm_handle.h | 38 ++++++ 7 files changed, 276 insertions(+) create mode 100644 drivers/firmware/meson/Kconfig create mode 100644 drivers/firmware/meson/Makefile create mode 100644 drivers/firmware/meson/meson_sm.c create mode 100644 include/meson/sm_handle.h
diff --git a/arch/arm/mach-meson/Kconfig b/arch/arm/mach-meson/Kconfig index 669ca09a00a..b8746d27f63 100644 --- a/arch/arm/mach-meson/Kconfig +++ b/arch/arm/mach-meson/Kconfig @@ -11,6 +11,7 @@ config MESON64_COMMON select PWRSEQ select MMC_PWRSEQ select BOARD_LATE_INIT + select MESON_FIRMWARE imply CMD_DM
config MESON_GX diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index eae1c8ddc9f..17b70fdea6d 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -37,6 +37,15 @@ config ZYNQMP_FIRMWARE Say yes to enable ZynqMP firmware interface driver. If in doubt, say N.
+config MESON_FIRMWARE + bool "Meson Firmware interface" + select FIRMWARE + help + This option enables Meson firmware interface, + which is used by different drivers to communicate + with the firmware for various platform management + services. + config ARM_SMCCC_FEATURES bool "Arm SMCCC features discovery" depends on ARM_PSCI_FW @@ -45,4 +54,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/meson/Kconfig" source "drivers/firmware/scmi/Kconfig" diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 7ce83d72bd3..a6300be27ad 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o obj-$(CONFIG_SANDBOX) += firmware-sandbox.o obj-$(CONFIG_ZYNQMP_FIRMWARE) += firmware-zynqmp.o obj-$(CONFIG_SCMI_FIRMWARE) += scmi/ +obj-$(CONFIG_MESON_FIRMWARE) += meson/ diff --git a/drivers/firmware/meson/Kconfig b/drivers/firmware/meson/Kconfig new file mode 100644 index 00000000000..0fd4f3251e1 --- /dev/null +++ b/drivers/firmware/meson/Kconfig @@ -0,0 +1,6 @@ +config MESON_SM + bool "Amlogic Secure Monitor driver" + depends on ARCH_MESON + default y + help + Say y here to enable the Amlogic secure monitor driver. diff --git a/drivers/firmware/meson/Makefile b/drivers/firmware/meson/Makefile new file mode 100644 index 00000000000..b5d26f150b0 --- /dev/null +++ b/drivers/firmware/meson/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_MESON_SM) += meson_sm.o diff --git a/drivers/firmware/meson/meson_sm.c b/drivers/firmware/meson/meson_sm.c new file mode 100644 index 00000000000..28eacb89810 --- /dev/null +++ b/drivers/firmware/meson/meson_sm.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023 SberDevices, Inc. + * + * Author: Alexey Romanov avromanov@sberdevices.ru + */ + +#include <dm.h> +#include <common.h> +#include <stdlib.h> +#include <regmap.h> +#include <syscon.h> +#include <asm/ptrace.h> +#include <asm/system.h> +#include <linux/err.h> +#include <linux/sizes.h> +#include <linux/bitfield.h> +#include <meson/sm_handle.h> + +struct meson_sm_cmd { + u32 smc_id; +}; + +#define SET_CMD(index, id) \ + [index] = { \ + .smc_id = id, \ + } + +struct meson_sm_data { + u32 cmd_get_shmem_in; + u32 cmd_get_shmem_out; + unsigned int shmem_size; + struct meson_sm_cmd cmd[]; +}; + +struct meson_sm_priv { + void *sm_shmem_in; + void *sm_shmem_out; + struct meson_sm_handle handle; + const struct meson_sm_data *data; +}; + +static unsigned long __meson_sm_call(u32 cmd, u32 arg0, u32 arg1, u32 arg2, + u32 arg3, u32 arg4) +{ + struct pt_regs r; + + r.regs[0] = cmd; + r.regs[1] = arg0; + r.regs[2] = arg1; + r.regs[3] = arg2; + r.regs[4] = arg3; + r.regs[5] = arg4; + + smc_call(&r); + + return r.regs[0]; +}; + +static u32 meson_sm_get_cmd(const struct meson_sm_data *data, + u32 cmd_index) +{ + struct meson_sm_cmd cmd; + + if (cmd_index >= MESON_SMC_CMD_COUNT) + return 0; + + cmd = data->cmd[cmd_index]; + return cmd.smc_id; +} + +static int meson_sm_call(struct udevice *dev, u32 cmd_index, u32 *retval, + u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) +{ + struct meson_sm_priv *priv = dev_get_priv(dev); + u32 cmd, ret; + + cmd = meson_sm_get_cmd(priv->data, cmd_index); + if (!cmd) + return -ENOENT; + + ret = __meson_sm_call(cmd, arg0, arg1, arg2, arg3, arg4); + if (retval) + *retval = ret; + + return 0; +} + +static int meson_sm_call_read(struct udevice *dev, void *buffer, size_t size, + u32 cmd_index, u32 offset, u32 cnt) +{ + struct meson_sm_priv *priv = dev_get_priv(dev); + u32 nbytes; + int ret; + + if (!buffer || size > priv->data->shmem_size) + return -EINVAL; + + ret = meson_sm_call(dev, cmd_index, &nbytes, offset, cnt, 0, 0, 0); + if (ret) + return ret; + + if (nbytes > size) + return -ENOBUFS; + + /* In some cases (for example GET_CHIP_ID command), + * SMC doesn't return the number of bytes read, even + * though the bytes were actually read into sm_shmem_out. + * So this check is needed. + */ + ret = nbytes; + if (!nbytes) + nbytes = size; + + memcpy(buffer, priv->sm_shmem_out, nbytes); + + return ret; +} + +static int meson_sm_call_write(struct udevice *dev, void *buffer, size_t size, + u32 cmd_index, u32 offset, u32 cnt) +{ + struct meson_sm_priv *priv = dev_get_priv(dev); + u32 nbytes; + int ret; + + if (!buffer || size > priv->data->shmem_size) + return -EINVAL; + + memcpy(priv->sm_shmem_in, buffer, size); + + ret = meson_sm_call(dev, cmd_index, &nbytes, offset, cnt, 0, 0, 0); + if (ret) + return ret; + + if (!nbytes) + return -EIO; + + return nbytes; +} + +struct meson_sm_handle *meson_sm_get_handle(struct udevice *dev) +{ + struct meson_sm_priv *priv; + struct meson_sm_handle *handle; + + priv = dev_get_priv(dev); + if (!priv) + return ERR_PTR(-EINVAL); + + handle = &priv->handle; + + return handle; +} + +static const struct meson_sm_ops sm_ops = { + .sm_call = meson_sm_call, + .sm_call_read = meson_sm_call_read, + .sm_call_write = meson_sm_call_write, +}; + +static int meson_sm_probe(struct udevice *dev) +{ + struct meson_sm_priv *priv = dev_get_priv(dev); + + priv->handle.ops = sm_ops; + priv->data = (struct meson_sm_data *)dev_get_driver_data(dev); + if (!priv->data) + return -EINVAL; + + priv->sm_shmem_in = + (void *)__meson_sm_call(priv->data->cmd_get_shmem_in, 0, 0, 0, 0, 0); + + if (!priv->sm_shmem_in) + return -ENOMEM; + + priv->sm_shmem_out = + (void *)__meson_sm_call(priv->data->cmd_get_shmem_out, 0, 0, 0, 0, 0); + + if (!priv->sm_shmem_out) + return -ENOMEM; + + pr_debug("meson sm driver probed\n" + "shmem_in addr: 0x%p, shmem_out addr: 0x%p\n", + priv->sm_shmem_in, + priv->sm_shmem_out); + + return 0; +} + +static const struct meson_sm_data meson_sm_gxbb_data = { + .cmd_get_shmem_in = 0x82000020, + .cmd_get_shmem_out = 0x82000021, + .shmem_size = SZ_4K, + .cmd = { + SET_CMD(MESON_SMC_CMD_EFUSE_READ, 0x82000030), + SET_CMD(MESON_SMC_CMD_EFUSE_WRITE, 0x82000031), + SET_CMD(MESON_SMC_CMD_CHIP_ID_GET, 0x82000044), + SET_CMD(MESON_SMC_CMD_PWRDM_SET, 0x82000093), + }, +}; + +static const struct udevice_id meson_sm_ids[] = { + { + .compatible = "amlogic,meson-gxbb-sm", + .data = (ulong)&meson_sm_gxbb_data, + }, + { } +}; + +U_BOOT_DRIVER(meson_sm) = { + .name = "meson_sm", + .id = UCLASS_FIRMWARE, + .of_match = meson_sm_ids, + .probe = meson_sm_probe, + .priv_auto = sizeof(struct meson_sm_priv), +}; diff --git a/include/meson/sm_handle.h b/include/meson/sm_handle.h new file mode 100644 index 00000000000..a3c69b77bd6 --- /dev/null +++ b/include/meson/sm_handle.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2023 SberDevices, Inc. + * + * Author: Alexey Romanov avromanov@sberdevices.ru + */ + +#ifndef _MESON_SM_H_ +#define _MESON_SM_H_ + +#include <dm/device.h> + +enum meson_smc_cmd { + MESON_SMC_CMD_EFUSE_READ, + MESON_SMC_CMD_EFUSE_WRITE, + MESON_SMC_CMD_CHIP_ID_GET, + MESON_SMC_CMD_PWRDM_SET, + MESON_SMC_CMD_COUNT, +}; + +struct meson_sm_ops { + int (*sm_call)(struct udevice *dev, u32 cmd, u32 *ret, + u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4); + + int (*sm_call_read)(struct udevice *dev, void *buffer, + size_t size, u32 cmd, u32 offset, u32 cnt); + + int (*sm_call_write)(struct udevice *dev, void *buffer, + size_t size, u32 cmd, u32 offset, u32 cnt); +}; + +struct meson_sm_handle { + struct meson_sm_ops ops; +}; + +struct meson_sm_handle *meson_sm_get_handle(struct udevice *dev); + +#endif /* _MESON_SM_H_ */