summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/bus/Kconfig2
-rw-r--r--drivers/bus/fsl-mc/fsl-mc-bus.c89
-rw-r--r--drivers/bus/omap-ocp2scp.c13
-rw-r--r--drivers/bus/qcom-ebi2.c7
-rw-r--r--drivers/char/hw_random/optee-rng.c26
-rw-r--r--drivers/char/tpm/tpm_ftpm_tee.c35
-rw-r--r--drivers/clk/qcom/common.c2
-rw-r--r--drivers/cpuidle/cpuidle-zynq.c2
-rw-r--r--drivers/firmware/arm_ffa/driver.c48
-rw-r--r--drivers/firmware/arm_scmi/base.c11
-rw-r--r--drivers/firmware/arm_scmi/clock.c24
-rw-r--r--drivers/firmware/arm_scmi/driver.c98
-rw-r--r--drivers/firmware/arm_scmi/perf.c59
-rw-r--r--drivers/firmware/arm_scmi/pinctrl.c120
-rw-r--r--drivers/firmware/arm_scmi/power.c18
-rw-r--r--drivers/firmware/arm_scmi/powercap.c21
-rw-r--r--drivers/firmware/arm_scmi/protocols.h9
-rw-r--r--drivers/firmware/arm_scmi/reset.c68
-rw-r--r--drivers/firmware/arm_scmi/sensors.c22
-rw-r--r--drivers/firmware/arm_scmi/shmem.c5
-rw-r--r--drivers/firmware/arm_scmi/system.c14
-rw-r--r--drivers/firmware/arm_scmi/transports/optee.c32
-rw-r--r--drivers/firmware/arm_scmi/vendors/imx/imx-sm-bbm.c10
-rw-r--r--drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c9
-rw-r--r--drivers/firmware/arm_scmi/vendors/imx/imx-sm-lmm.c9
-rw-r--r--drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c93
-rw-r--r--drivers/firmware/arm_scmi/voltage.c13
-rw-r--r--drivers/firmware/broadcom/tee_bnxt_fw.c30
-rw-r--r--drivers/firmware/efi/stmm/tee_stmm_efi.c25
-rw-r--r--drivers/firmware/imx/sm-misc.c37
-rw-r--r--drivers/firmware/qcom/qcom_scm.c507
-rw-r--r--drivers/firmware/qcom/qcom_scm.h2
-rw-r--r--drivers/firmware/ti_sci.h29
-rw-r--r--drivers/hwspinlock/omap_hwspinlock.c4
-rw-r--r--drivers/irqchip/irq-ls-extirq.c47
-rw-r--r--drivers/irqchip/irq-renesas-rza1.c43
-rw-r--r--drivers/mailbox/mtk-cmdq-mailbox.c74
-rw-r--r--drivers/memory/mtk-smi.c38
-rw-r--r--drivers/of/irq.c70
-rw-r--r--drivers/of/unittest-data/tests-interrupts.dtsi9
-rw-r--r--drivers/of/unittest.c116
-rw-r--r--drivers/remoteproc/qcom_q6v5_pas.c165
-rw-r--r--drivers/reset/Kconfig14
-rw-r--r--drivers/reset/Makefile2
-rw-r--r--drivers/reset/core.c7
-rw-r--r--drivers/reset/reset-gpio.c9
-rw-r--r--drivers/reset/reset-imx8mp-audiomix.c169
-rw-r--r--drivers/reset/reset-rzg2l-usbphy-ctrl.c110
-rw-r--r--drivers/reset/spacemit/Kconfig36
-rw-r--r--drivers/reset/spacemit/Makefile5
-rw-r--r--drivers/reset/spacemit/reset-spacemit-common.c77
-rw-r--r--drivers/reset/spacemit/reset-spacemit-common.h42
-rw-r--r--drivers/reset/spacemit/reset-spacemit-k1.c (renamed from drivers/reset/reset-spacemit.c)109
-rw-r--r--drivers/reset/spacemit/reset-spacemit-k3.c233
-rw-r--r--drivers/rtc/rtc-optee.c27
-rw-r--r--drivers/soc/amlogic/meson-gx-socinfo.c1
-rw-r--r--drivers/soc/apple/rtkit.c16
-rw-r--r--drivers/soc/dove/pmu.c9
-rw-r--r--drivers/soc/fsl/qe/Makefile2
-rw-r--r--drivers/soc/fsl/qe/qe_ports_ic.c141
-rw-r--r--drivers/soc/fsl/qe/qmc.c13
-rw-r--r--drivers/soc/imx/soc-imx8m.c6
-rw-r--r--drivers/soc/imx/soc-imx9.c46
-rw-r--r--drivers/soc/mediatek/mtk-cmdq-helper.c77
-rw-r--r--drivers/soc/mediatek/mtk-dvfsrc.c364
-rw-r--r--drivers/soc/mediatek/mtk-socinfo.c1
-rw-r--r--drivers/soc/mediatek/mtk-svs.c5
-rw-r--r--drivers/soc/qcom/cmd-db.c7
-rw-r--r--drivers/soc/qcom/llcc-qcom.c207
-rw-r--r--drivers/soc/qcom/mdt_loader.c51
-rw-r--r--drivers/soc/qcom/pmic_glink_altmode.c188
-rw-r--r--drivers/soc/qcom/qmi_encdec.c137
-rw-r--r--drivers/soc/qcom/smem.c4
-rw-r--r--drivers/soc/renesas/Kconfig5
-rw-r--r--drivers/soc/renesas/Makefile1
-rw-r--r--drivers/soc/renesas/rzn1_irqmux.c127
-rw-r--r--drivers/soc/rockchip/grf.c57
-rw-r--r--drivers/soc/samsung/exynos-chipid.c133
-rw-r--r--drivers/soc/tegra/pmc.c425
-rw-r--r--drivers/soc/ti/Kconfig2
-rw-r--r--drivers/soc/ti/k3-socinfo.c2
-rw-r--r--drivers/soc/ti/knav_dma.c21
-rw-r--r--drivers/soc/ti/knav_qmss_queue.c25
-rw-r--r--drivers/soc/ti/pruss.c6
-rw-r--r--drivers/soc/xilinx/zynqmp_power.c6
-rw-r--r--drivers/tee/amdtee/call.c8
-rw-r--r--drivers/tee/optee/core.c23
-rw-r--r--drivers/tee/optee/ffa_abi.c54
-rw-r--r--drivers/tee/optee/optee_private.h19
-rw-r--r--drivers/tee/optee/rpc.c6
-rw-r--r--drivers/tee/optee/smc_abi.c15
-rw-r--r--drivers/tee/qcomtee/call.c17
-rw-r--r--drivers/tee/qcomtee/mem_obj.c4
-rw-r--r--drivers/tee/qcomtee/user_obj.c8
-rw-r--r--drivers/tee/tee_core.c135
95 files changed, 4019 insertions, 1250 deletions
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index fe7600283e70..2a1b46f07080 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -141,7 +141,7 @@ config OMAP_INTERCONNECT
config OMAP_OCP2SCP
tristate "OMAP OCP2SCP DRIVER"
- depends on ARCH_OMAP2PLUS
+ depends on ARCH_OMAP2PLUS || COMPILE_TEST
help
Driver to enable ocp2scp module which transforms ocp interface
protocol to scp protocol. In OMAP4, USB PHY is connected via
diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c
index 25845c04e562..007223549887 100644
--- a/drivers/bus/fsl-mc/fsl-mc-bus.c
+++ b/drivers/bus/fsl-mc/fsl-mc-bus.c
@@ -137,6 +137,35 @@ static int fsl_mc_bus_uevent(const struct device *dev, struct kobj_uevent_env *e
return 0;
}
+static int fsl_mc_probe(struct device *dev)
+{
+ struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+
+ if (mc_drv->probe)
+ return mc_drv->probe(mc_dev);
+
+ return 0;
+}
+
+static void fsl_mc_remove(struct device *dev)
+{
+ struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+
+ if (mc_drv->remove)
+ mc_drv->remove(mc_dev);
+}
+
+static void fsl_mc_shutdown(struct device *dev)
+{
+ struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+
+ if (dev->driver && mc_drv->shutdown)
+ mc_drv->shutdown(mc_dev);
+}
+
static int fsl_mc_dma_configure(struct device *dev)
{
const struct device_driver *drv = READ_ONCE(dev->driver);
@@ -202,8 +231,12 @@ static ssize_t driver_override_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+ ssize_t len;
- return sysfs_emit(buf, "%s\n", mc_dev->driver_override);
+ device_lock(dev);
+ len = sysfs_emit(buf, "%s\n", mc_dev->driver_override);
+ device_unlock(dev);
+ return len;
}
static DEVICE_ATTR_RW(driver_override);
@@ -314,6 +347,9 @@ const struct bus_type fsl_mc_bus_type = {
.name = "fsl-mc",
.match = fsl_mc_bus_match,
.uevent = fsl_mc_bus_uevent,
+ .probe = fsl_mc_probe,
+ .remove = fsl_mc_remove,
+ .shutdown = fsl_mc_shutdown,
.dma_configure = fsl_mc_dma_configure,
.dma_cleanup = fsl_mc_dma_cleanup,
.dev_groups = fsl_mc_dev_groups,
@@ -434,42 +470,6 @@ static const struct device_type *fsl_mc_get_device_type(const char *type)
return NULL;
}
-static int fsl_mc_driver_probe(struct device *dev)
-{
- struct fsl_mc_driver *mc_drv;
- struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
- int error;
-
- mc_drv = to_fsl_mc_driver(dev->driver);
-
- error = mc_drv->probe(mc_dev);
- if (error < 0) {
- if (error != -EPROBE_DEFER)
- dev_err(dev, "%s failed: %d\n", __func__, error);
- return error;
- }
-
- return 0;
-}
-
-static int fsl_mc_driver_remove(struct device *dev)
-{
- struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
- struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
-
- mc_drv->remove(mc_dev);
-
- return 0;
-}
-
-static void fsl_mc_driver_shutdown(struct device *dev)
-{
- struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
- struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
-
- mc_drv->shutdown(mc_dev);
-}
-
/*
* __fsl_mc_driver_register - registers a child device driver with the
* MC bus
@@ -486,15 +486,6 @@ int __fsl_mc_driver_register(struct fsl_mc_driver *mc_driver,
mc_driver->driver.owner = owner;
mc_driver->driver.bus = &fsl_mc_bus_type;
- if (mc_driver->probe)
- mc_driver->driver.probe = fsl_mc_driver_probe;
-
- if (mc_driver->remove)
- mc_driver->driver.remove = fsl_mc_driver_remove;
-
- if (mc_driver->shutdown)
- mc_driver->driver.shutdown = fsl_mc_driver_shutdown;
-
error = driver_register(&mc_driver->driver);
if (error < 0) {
pr_err("driver_register() failed for %s: %d\n",
@@ -905,11 +896,7 @@ int fsl_mc_device_add(struct fsl_mc_obj_desc *obj_desc,
return 0;
error_cleanup_dev:
- kfree(mc_dev->regions);
- if (mc_bus)
- kfree(mc_bus);
- else
- kfree(mc_dev);
+ put_device(&mc_dev->dev);
return error;
}
diff --git a/drivers/bus/omap-ocp2scp.c b/drivers/bus/omap-ocp2scp.c
index e4dfda7b3b10..eee5ad191ea9 100644
--- a/drivers/bus/omap-ocp2scp.c
+++ b/drivers/bus/omap-ocp2scp.c
@@ -17,15 +17,6 @@
#define OCP2SCP_TIMING 0x18
#define SYNC2_MASK 0xf
-static int ocp2scp_remove_devices(struct device *dev, void *c)
-{
- struct platform_device *pdev = to_platform_device(dev);
-
- platform_device_unregister(pdev);
-
- return 0;
-}
-
static int omap_ocp2scp_probe(struct platform_device *pdev)
{
int ret;
@@ -79,7 +70,7 @@ err1:
pm_runtime_disable(&pdev->dev);
err0:
- device_for_each_child(&pdev->dev, NULL, ocp2scp_remove_devices);
+ of_platform_depopulate(&pdev->dev);
return ret;
}
@@ -87,7 +78,7 @@ err0:
static void omap_ocp2scp_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
- device_for_each_child(&pdev->dev, NULL, ocp2scp_remove_devices);
+ of_platform_depopulate(&pdev->dev);
}
#ifdef CONFIG_OF
diff --git a/drivers/bus/qcom-ebi2.c b/drivers/bus/qcom-ebi2.c
index c1fef1b4bd89..be8166565e7c 100644
--- a/drivers/bus/qcom-ebi2.c
+++ b/drivers/bus/qcom-ebi2.c
@@ -292,7 +292,6 @@ static void qcom_ebi2_setup_chipselect(struct device_node *np,
static int qcom_ebi2_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
- struct device_node *child;
struct device *dev = &pdev->dev;
struct resource *res;
void __iomem *ebi2_base;
@@ -348,15 +347,13 @@ static int qcom_ebi2_probe(struct platform_device *pdev)
writel(val, ebi2_base);
/* Walk over the child nodes and see what chipselects we use */
- for_each_available_child_of_node(np, child) {
+ for_each_available_child_of_node_scoped(np, child) {
u32 csindex;
/* Figure out the chipselect */
ret = of_property_read_u32(child, "reg", &csindex);
- if (ret) {
- of_node_put(child);
+ if (ret)
return ret;
- }
if (csindex > 5) {
dev_err(dev,
diff --git a/drivers/char/hw_random/optee-rng.c b/drivers/char/hw_random/optee-rng.c
index 1cb741a6d112..72af9f4aa810 100644
--- a/drivers/char/hw_random/optee-rng.c
+++ b/drivers/char/hw_random/optee-rng.c
@@ -208,9 +208,9 @@ static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
return (ver->impl_id == TEE_IMPL_ID_OPTEE);
}
-static int optee_rng_probe(struct device *dev)
+static int optee_rng_probe(struct tee_client_device *rng_device)
{
- struct tee_client_device *rng_device = to_tee_client_device(dev);
+ struct device *dev = &rng_device->dev;
int ret = 0, err = -ENODEV;
struct tee_ioctl_open_session_arg sess_arg;
@@ -258,12 +258,10 @@ out_ctx:
return err;
}
-static int optee_rng_remove(struct device *dev)
+static void optee_rng_remove(struct tee_client_device *tee_dev)
{
tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
tee_client_close_context(pvt_data.ctx);
-
- return 0;
}
static const struct tee_client_device_id optee_rng_id_table[] = {
@@ -275,27 +273,15 @@ static const struct tee_client_device_id optee_rng_id_table[] = {
MODULE_DEVICE_TABLE(tee, optee_rng_id_table);
static struct tee_client_driver optee_rng_driver = {
+ .probe = optee_rng_probe,
+ .remove = optee_rng_remove,
.id_table = optee_rng_id_table,
.driver = {
.name = DRIVER_NAME,
- .bus = &tee_bus_type,
- .probe = optee_rng_probe,
- .remove = optee_rng_remove,
},
};
-static int __init optee_rng_mod_init(void)
-{
- return driver_register(&optee_rng_driver.driver);
-}
-
-static void __exit optee_rng_mod_exit(void)
-{
- driver_unregister(&optee_rng_driver.driver);
-}
-
-module_init(optee_rng_mod_init);
-module_exit(optee_rng_mod_exit);
+module_tee_client_driver(optee_rng_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Sumit Garg <sumit.garg@linaro.org>");
diff --git a/drivers/char/tpm/tpm_ftpm_tee.c b/drivers/char/tpm/tpm_ftpm_tee.c
index 4e63c30aeaf1..b82490439633 100644
--- a/drivers/char/tpm/tpm_ftpm_tee.c
+++ b/drivers/char/tpm/tpm_ftpm_tee.c
@@ -163,13 +163,13 @@ static int ftpm_tee_match(struct tee_ioctl_version_data *ver, const void *data)
}
/**
- * ftpm_tee_probe() - initialize the fTPM
+ * ftpm_tee_probe_generic() - initialize the fTPM
* @dev: the device description.
*
* Return:
* On success, 0. On failure, -errno.
*/
-static int ftpm_tee_probe(struct device *dev)
+static int ftpm_tee_probe_generic(struct device *dev)
{
int rc;
struct tpm_chip *chip;
@@ -251,21 +251,28 @@ out_tee_session:
return rc;
}
+static int ftpm_tee_probe(struct tee_client_device *tcdev)
+{
+ struct device *dev = &tcdev->dev;
+
+ return ftpm_tee_probe_generic(dev);
+}
+
static int ftpm_plat_tee_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- return ftpm_tee_probe(dev);
+ return ftpm_tee_probe_generic(dev);
}
/**
- * ftpm_tee_remove() - remove the TPM device
+ * ftpm_tee_remove_generic() - remove the TPM device
* @dev: the device description.
*
* Return:
* 0 always.
*/
-static int ftpm_tee_remove(struct device *dev)
+static void ftpm_tee_remove_generic(struct device *dev)
{
struct ftpm_tee_private *pvt_data = dev_get_drvdata(dev);
@@ -285,15 +292,20 @@ static int ftpm_tee_remove(struct device *dev)
tee_client_close_context(pvt_data->ctx);
/* memory allocated with devm_kzalloc() is freed automatically */
+}
- return 0;
+static void ftpm_tee_remove(struct tee_client_device *tcdev)
+{
+ struct device *dev = &tcdev->dev;
+
+ ftpm_tee_remove_generic(dev);
}
static void ftpm_plat_tee_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- ftpm_tee_remove(dev);
+ ftpm_tee_remove_generic(dev);
}
/**
@@ -335,12 +347,11 @@ static const struct tee_client_device_id optee_ftpm_id_table[] = {
MODULE_DEVICE_TABLE(tee, optee_ftpm_id_table);
static struct tee_client_driver ftpm_tee_driver = {
+ .probe = ftpm_tee_probe,
+ .remove = ftpm_tee_remove,
.id_table = optee_ftpm_id_table,
.driver = {
.name = "optee-ftpm",
- .bus = &tee_bus_type,
- .probe = ftpm_tee_probe,
- .remove = ftpm_tee_remove,
},
};
@@ -352,7 +363,7 @@ static int __init ftpm_mod_init(void)
if (rc)
return rc;
- rc = driver_register(&ftpm_tee_driver.driver);
+ rc = tee_client_driver_register(&ftpm_tee_driver);
if (rc) {
platform_driver_unregister(&ftpm_tee_plat_driver);
return rc;
@@ -364,7 +375,7 @@ static int __init ftpm_mod_init(void)
static void __exit ftpm_mod_exit(void)
{
platform_driver_unregister(&ftpm_tee_plat_driver);
- driver_unregister(&ftpm_tee_driver.driver);
+ tee_client_driver_unregister(&ftpm_tee_driver);
}
module_init(ftpm_mod_init);
diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
index 121591886774..eec369d2173b 100644
--- a/drivers/clk/qcom/common.c
+++ b/drivers/clk/qcom/common.c
@@ -454,7 +454,7 @@ int qcom_cc_probe_by_index(struct platform_device *pdev, int index,
base = devm_platform_ioremap_resource(pdev, index);
if (IS_ERR(base))
- return -ENOMEM;
+ return PTR_ERR(base);
regmap = devm_regmap_init_mmio(&pdev->dev, base, desc->config);
if (IS_ERR(regmap))
diff --git a/drivers/cpuidle/cpuidle-zynq.c b/drivers/cpuidle/cpuidle-zynq.c
index a79610e723b3..89448ae4845c 100644
--- a/drivers/cpuidle/cpuidle-zynq.c
+++ b/drivers/cpuidle/cpuidle-zynq.c
@@ -11,7 +11,7 @@
* #1 wait-for-interrupt
* #2 wait-for-interrupt and RAM self refresh
*
- * Maintainer: Michal Simek <michal.simek@xilinx.com>
+ * Maintainer: Michal Simek <michal.simek@amd.com>
*/
#include <linux/init.h>
diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c
index c72ee4756585..8144f6a9f0e9 100644
--- a/drivers/firmware/arm_ffa/driver.c
+++ b/drivers/firmware/arm_ffa/driver.c
@@ -246,6 +246,11 @@ static int ffa_features(u32 func_feat_id, u32 input_props,
}
#define PARTITION_INFO_GET_RETURN_COUNT_ONLY BIT(0)
+#define FFA_SUPPORTS_GET_COUNT_ONLY(version) ((version) > FFA_VERSION_1_0)
+#define FFA_PART_INFO_HAS_SIZE_IN_RESP(version) ((version) > FFA_VERSION_1_0)
+#define FFA_PART_INFO_HAS_UUID_IN_RESP(version) ((version) > FFA_VERSION_1_0)
+#define FFA_PART_INFO_HAS_EXEC_STATE_IN_RESP(version) \
+ ((version) > FFA_VERSION_1_0)
/* buffer must be sizeof(struct ffa_partition_info) * num_partitions */
static int
@@ -255,7 +260,7 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
int idx, count, flags = 0, sz, buf_sz;
ffa_value_t partition_info;
- if (drv_info->version > FFA_VERSION_1_0 &&
+ if (FFA_SUPPORTS_GET_COUNT_ONLY(drv_info->version) &&
(!buffer || !num_partitions)) /* Just get the count for now */
flags = PARTITION_INFO_GET_RETURN_COUNT_ONLY;
@@ -273,12 +278,11 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
count = partition_info.a2;
- if (drv_info->version > FFA_VERSION_1_0) {
+ if (FFA_PART_INFO_HAS_SIZE_IN_RESP(drv_info->version)) {
buf_sz = sz = partition_info.a3;
if (sz > sizeof(*buffer))
buf_sz = sizeof(*buffer);
} else {
- /* FFA_VERSION_1_0 lacks size in the response */
buf_sz = sz = 8;
}
@@ -981,10 +985,27 @@ static void __do_sched_recv_cb(u16 part_id, u16 vcpu, bool is_per_vcpu)
}
}
+/*
+ * Map logical ID index to the u16 index within the packed ID list.
+ *
+ * For native responses (FF-A width == kernel word size), IDs are
+ * tightly packed: idx -> idx.
+ *
+ * For 32-bit responses on a 64-bit kernel, each 64-bit register
+ * contributes 4 x u16 values but only the lower 2 are defined; the
+ * upper 2 are garbage. This mapping skips those upper halves:
+ * 0,1,2,3,4,5,... -> 0,1,4,5,8,9,...
+ */
+static int list_idx_to_u16_idx(int idx, bool is_native_resp)
+{
+ return is_native_resp ? idx : idx + 2 * (idx >> 1);
+}
+
static void ffa_notification_info_get(void)
{
- int idx, list, max_ids, lists_cnt, ids_processed, ids_count[MAX_IDS_64];
- bool is_64b_resp;
+ int ids_processed, ids_count[MAX_IDS_64];
+ int idx, list, max_ids, lists_cnt;
+ bool is_64b_resp, is_native_resp;
ffa_value_t ret;
u64 id_list;
@@ -1001,6 +1022,7 @@ static void ffa_notification_info_get(void)
}
is_64b_resp = (ret.a0 == FFA_FN64_SUCCESS);
+ is_native_resp = (ret.a0 == FFA_FN_NATIVE(SUCCESS));
ids_processed = 0;
lists_cnt = FIELD_GET(NOTIFICATION_INFO_GET_ID_COUNT, ret.a2);
@@ -1017,12 +1039,16 @@ static void ffa_notification_info_get(void)
/* Process IDs */
for (list = 0; list < lists_cnt; list++) {
+ int u16_idx;
u16 vcpu_id, part_id, *packed_id_list = (u16 *)&ret.a3;
if (ids_processed >= max_ids - 1)
break;
- part_id = packed_id_list[ids_processed++];
+ u16_idx = list_idx_to_u16_idx(ids_processed,
+ is_native_resp);
+ part_id = packed_id_list[u16_idx];
+ ids_processed++;
if (ids_count[list] == 1) { /* Global Notification */
__do_sched_recv_cb(part_id, 0, false);
@@ -1034,7 +1060,10 @@ static void ffa_notification_info_get(void)
if (ids_processed >= max_ids - 1)
break;
- vcpu_id = packed_id_list[ids_processed++];
+ u16_idx = list_idx_to_u16_idx(ids_processed,
+ is_native_resp);
+ vcpu_id = packed_id_list[u16_idx];
+ ids_processed++;
__do_sched_recv_cb(part_id, vcpu_id, true);
}
@@ -1706,7 +1735,7 @@ static int ffa_setup_partitions(void)
struct ffa_device *ffa_dev;
struct ffa_partition_info *pbuf, *tpbuf;
- if (drv_info->version == FFA_VERSION_1_0) {
+ if (!FFA_PART_INFO_HAS_UUID_IN_RESP(drv_info->version)) {
ret = bus_register_notifier(&ffa_bus_type, &ffa_bus_nb);
if (ret)
pr_err("Failed to register FF-A bus notifiers\n");
@@ -1733,7 +1762,7 @@ static int ffa_setup_partitions(void)
continue;
}
- if (drv_info->version > FFA_VERSION_1_0 &&
+ if (FFA_PART_INFO_HAS_EXEC_STATE_IN_RESP(drv_info->version) &&
!(tpbuf->properties & FFA_PARTITION_AARCH64_EXEC))
ffa_mode_32bit_set(ffa_dev);
@@ -2068,6 +2097,7 @@ static int __init ffa_init(void)
pr_err("failed to setup partitions\n");
ffa_notifications_cleanup();
+ ffa_rxtx_unmap(drv_info->vm_id);
free_pages:
if (drv_info->tx_buffer)
free_pages_exact(drv_info->tx_buffer, rxtx_bufsz);
diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
index 86b376c50a13..22267bbd0f4d 100644
--- a/drivers/firmware/arm_scmi/base.c
+++ b/drivers/firmware/arm_scmi/base.c
@@ -375,18 +375,13 @@ static int scmi_base_protocol_init(const struct scmi_protocol_handle *ph)
{
int id, ret;
u8 *prot_imp;
- u32 version;
char name[SCMI_SHORT_NAME_MAX_SIZE];
struct device *dev = ph->dev;
struct scmi_revision_info *rev = scmi_revision_area_get(ph);
- ret = ph->xops->version_get(ph, &version);
- if (ret)
- return ret;
-
- rev->major_ver = PROTOCOL_REV_MAJOR(version);
- rev->minor_ver = PROTOCOL_REV_MINOR(version);
- ph->set_priv(ph, rev, version);
+ rev->major_ver = PROTOCOL_REV_MAJOR(ph->version);
+ rev->minor_ver = PROTOCOL_REV_MINOR(ph->version);
+ ph->set_priv(ph, rev);
ret = scmi_base_attributes_get(ph);
if (ret)
diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
index afa7981efe82..ab36871650a1 100644
--- a/drivers/firmware/arm_scmi/clock.c
+++ b/drivers/firmware/arm_scmi/clock.c
@@ -157,7 +157,6 @@ struct scmi_clock_rate_notify_payld {
};
struct clock_info {
- u32 version;
int num_clocks;
int max_async_req;
bool notify_rate_changed_cmd;
@@ -346,8 +345,7 @@ scmi_clock_get_permissions(const struct scmi_protocol_handle *ph, u32 clk_id,
}
static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
- u32 clk_id, struct clock_info *cinfo,
- u32 version)
+ u32 clk_id, struct clock_info *cinfo)
{
int ret;
u32 attributes;
@@ -370,7 +368,7 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
attributes = le32_to_cpu(attr->attributes);
strscpy(clk->name, attr->name, SCMI_SHORT_NAME_MAX_SIZE);
/* clock_enable_latency field is present only since SCMI v3.1 */
- if (PROTOCOL_REV_MAJOR(version) >= 0x2)
+ if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2)
latency = le32_to_cpu(attr->clock_enable_latency);
clk->enable_latency = latency ? : U32_MAX;
}
@@ -381,7 +379,7 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
* If supported overwrite short name with the extended one;
* on error just carry on and use already provided short name.
*/
- if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x2) {
+ if (!ret && PROTOCOL_REV_MAJOR(ph->version) >= 0x2) {
if (SUPPORTS_EXTENDED_NAMES(attributes))
ph->hops->extended_name_get(ph, CLOCK_NAME_GET, clk_id,
NULL, clk->name,
@@ -393,7 +391,7 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
if (cinfo->notify_rate_change_requested_cmd &&
SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(attributes))
clk->rate_change_requested_notifications = true;
- if (PROTOCOL_REV_MAJOR(version) >= 0x3) {
+ if (PROTOCOL_REV_MAJOR(ph->version) >= 0x3) {
if (SUPPORTS_PARENT_CLOCK(attributes))
scmi_clock_possible_parents(ph, clk_id, clk);
if (SUPPORTS_GET_PERMISSIONS(attributes))
@@ -1068,16 +1066,11 @@ static const struct scmi_protocol_events clk_protocol_events = {
static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph)
{
- u32 version;
int clkid, ret;
struct clock_info *cinfo;
- ret = ph->xops->version_get(ph, &version);
- if (ret)
- return ret;
-
dev_dbg(ph->dev, "Clock Version %d.%d\n",
- PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+ PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
cinfo = devm_kzalloc(ph->dev, sizeof(*cinfo), GFP_KERNEL);
if (!cinfo)
@@ -1095,12 +1088,12 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph)
for (clkid = 0; clkid < cinfo->num_clocks; clkid++) {
struct scmi_clock_info *clk = cinfo->clk + clkid;
- ret = scmi_clock_attributes_get(ph, clkid, cinfo, version);
+ ret = scmi_clock_attributes_get(ph, clkid, cinfo);
if (!ret)
scmi_clock_describe_rates_get(ph, clkid, clk);
}
- if (PROTOCOL_REV_MAJOR(version) >= 0x3) {
+ if (PROTOCOL_REV_MAJOR(ph->version) >= 0x3) {
cinfo->clock_config_set = scmi_clock_config_set_v2;
cinfo->clock_config_get = scmi_clock_config_get_v2;
} else {
@@ -1108,8 +1101,7 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph)
cinfo->clock_config_get = scmi_clock_config_get;
}
- cinfo->version = version;
- return ph->set_priv(ph, cinfo, version);
+ return ph->set_priv(ph, cinfo);
}
static const struct scmi_protocol scmi_clock = {
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 5caa9191a8d1..3e76a3204ba4 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -1627,17 +1627,15 @@ static int version_get(const struct scmi_protocol_handle *ph, u32 *version)
*
* @ph: A reference to the protocol handle.
* @priv: The private data to set.
- * @version: The detected protocol version for the core to register.
*
* Return: 0 on Success
*/
static int scmi_set_protocol_priv(const struct scmi_protocol_handle *ph,
- void *priv, u32 version)
+ void *priv)
{
struct scmi_protocol_instance *pi = ph_to_pi(ph);
pi->priv = priv;
- pi->version = version;
return 0;
}
@@ -1657,7 +1655,6 @@ static void *scmi_get_protocol_priv(const struct scmi_protocol_handle *ph)
}
static const struct scmi_xfer_ops xfer_ops = {
- .version_get = version_get,
.xfer_get_init = xfer_get_init,
.reset_rx_to_maxsz = reset_rx_to_maxsz,
.do_xfer = do_xfer,
@@ -2113,6 +2110,76 @@ static int scmi_protocol_version_negotiate(struct scmi_protocol_handle *ph)
}
/**
+ * scmi_protocol_version_initialize - Initialize protocol version
+ * @dev: A device reference.
+ * @pi: A reference to the protocol instance being initialized
+ *
+ * At first retrieve the newest protocol version supported by the platform for
+ * this specific protoocol.
+ *
+ * Negotiation is attempted only when the platform advertised a protocol
+ * version newer than the most recent version known to this agent, since
+ * backward compatibility is NOT assured in general between versions.
+ *
+ * Failing to negotiate a fallback version or to query supported version at
+ * all will result in an attempt to use the newest version known to this agent
+ * even though compatibility is NOT assured.
+ *
+ * Versions are defined as:
+ *
+ * pi->version: the version supported by the platform as returned by the query.
+ * pi->proto->supported_version: the newest version supported by this agent
+ * for this protocol.
+ * pi->negotiated_version: The version successfully negotiated with the platform.
+ * ph->version: The final version effectively chosen for this session.
+ */
+static void scmi_protocol_version_initialize(struct device *dev,
+ struct scmi_protocol_instance *pi)
+{
+ struct scmi_protocol_handle *ph = &pi->ph;
+ int ret;
+
+ /*
+ * Query and store platform supported protocol version: this is usually
+ * the newest version the platfom can support.
+ */
+ ret = version_get(ph, &pi->version);
+ if (ret) {
+ dev_warn(dev,
+ "Failed to query supported version for protocol 0x%X.\n",
+ pi->proto->id);
+ goto best_effort;
+ }
+
+ /* Need to negotiate at all ? */
+ if (pi->version <= pi->proto->supported_version) {
+ ph->version = pi->version;
+ return;
+ }
+
+ /* Attempt negotiation */
+ ret = scmi_protocol_version_negotiate(ph);
+ if (!ret) {
+ ph->version = pi->negotiated_version;
+ dev_info(dev,
+ "Protocol 0x%X successfully negotiated version 0x%X\n",
+ pi->proto->id, ph->version);
+ return;
+ }
+
+ dev_warn(dev,
+ "Detected UNSUPPORTED higher version 0x%X for protocol 0x%X.\n",
+ pi->version, pi->proto->id);
+
+best_effort:
+ /* Fallback to use newest version known to this agent */
+ ph->version = pi->proto->supported_version;
+ dev_warn(dev,
+ "Trying version 0x%X. Backward compatibility is NOT assured.\n",
+ ph->version);
+}
+
+/**
* scmi_alloc_init_protocol_instance - Allocate and initialize a protocol
* instance descriptor.
* @info: The reference to the related SCMI instance.
@@ -2157,6 +2224,13 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
pi->ph.set_priv = scmi_set_protocol_priv;
pi->ph.get_priv = scmi_get_protocol_priv;
refcount_set(&pi->users, 1);
+
+ /*
+ * Initialize effectively used protocol version performing any
+ * possibly needed negotiations.
+ */
+ scmi_protocol_version_initialize(handle->dev, pi);
+
/* proto->init is assured NON NULL by scmi_protocol_register */
ret = pi->proto->instance_init(&pi->ph);
if (ret)
@@ -2184,22 +2258,6 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
devres_close_group(handle->dev, pi->gid);
dev_dbg(handle->dev, "Initialized protocol: 0x%X\n", pi->proto->id);
- if (pi->version > proto->supported_version) {
- ret = scmi_protocol_version_negotiate(&pi->ph);
- if (!ret) {
- dev_info(handle->dev,
- "Protocol 0x%X successfully negotiated version 0x%X\n",
- proto->id, pi->negotiated_version);
- } else {
- dev_warn(handle->dev,
- "Detected UNSUPPORTED higher version 0x%X for protocol 0x%X.\n",
- pi->version, pi->proto->id);
- dev_warn(handle->dev,
- "Trying version 0x%X. Backward compatibility is NOT assured.\n",
- pi->proto->supported_version);
- }
- }
-
return pi;
clean:
diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c
index 683fd9b85c5c..4583d02bee1c 100644
--- a/drivers/firmware/arm_scmi/perf.c
+++ b/drivers/firmware/arm_scmi/perf.c
@@ -27,7 +27,7 @@
/* Updated only after ALL the mandatory features for that version are merged */
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x40000
-#define MAX_OPPS 32
+#define MAX_OPPS 64
enum scmi_performance_protocol_cmd {
PERF_DOMAIN_ATTRIBUTES = 0x3,
@@ -178,7 +178,6 @@ struct perf_dom_info {
})
struct scmi_perf_info {
- u32 version;
u16 num_domains;
enum scmi_power_scale power_scale;
u64 stats_addr;
@@ -215,7 +214,7 @@ static int scmi_perf_attributes_get(const struct scmi_protocol_handle *ph,
if (POWER_SCALE_IN_MILLIWATT(flags))
pi->power_scale = SCMI_POWER_MILLIWATTS;
- if (PROTOCOL_REV_MAJOR(pi->version) >= 0x3)
+ if (PROTOCOL_REV_MAJOR(ph->version) >= 0x3)
if (POWER_SCALE_IN_MICROWATT(flags))
pi->power_scale = SCMI_POWER_MICROWATTS;
@@ -251,8 +250,7 @@ static void scmi_perf_xa_destroy(void *data)
static int
scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
struct perf_dom_info *dom_info,
- bool notify_lim_cmd, bool notify_lvl_cmd,
- u32 version)
+ bool notify_lim_cmd, bool notify_lvl_cmd)
{
int ret;
u32 flags;
@@ -280,7 +278,7 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
dom_info->perf_level_notify =
SUPPORTS_PERF_LEVEL_NOTIFY(flags);
dom_info->perf_fastchannels = SUPPORTS_PERF_FASTCHANNELS(flags);
- if (PROTOCOL_REV_MAJOR(version) >= 0x4)
+ if (PROTOCOL_REV_MAJOR(ph->version) >= 0x4)
dom_info->level_indexing_mode =
SUPPORTS_LEVEL_INDEXING(flags);
dom_info->rate_limit_us = le32_to_cpu(attr->rate_limit_us) &
@@ -323,7 +321,7 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
* If supported overwrite short name with the extended one;
* on error just carry on and use already provided short name.
*/
- if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 &&
+ if (!ret && PROTOCOL_REV_MAJOR(ph->version) >= 0x3 &&
SUPPORTS_EXTENDED_NAMES(flags))
ph->hops->extended_name_get(ph, PERF_DOMAIN_NAME_GET,
dom_info->id, NULL, dom_info->info.name,
@@ -345,19 +343,14 @@ static int opp_cmp_func(const void *opp1, const void *opp2)
return t1->perf - t2->perf;
}
-struct scmi_perf_ipriv {
- u32 version;
- struct perf_dom_info *perf_dom;
-};
-
static void iter_perf_levels_prepare_message(void *message,
unsigned int desc_index,
const void *priv)
{
struct scmi_msg_perf_describe_levels *msg = message;
- const struct scmi_perf_ipriv *p = priv;
+ const struct perf_dom_info *perf_dom = priv;
- msg->domain = cpu_to_le32(p->perf_dom->id);
+ msg->domain = cpu_to_le32(perf_dom->id);
/* Set the number of OPPs to be skipped/already read */
msg->level_index = cpu_to_le32(desc_index);
}
@@ -445,21 +438,21 @@ iter_perf_levels_process_response(const struct scmi_protocol_handle *ph,
{
int ret;
struct scmi_opp *opp;
- struct scmi_perf_ipriv *p = priv;
+ struct perf_dom_info *perf_dom = priv;
- opp = &p->perf_dom->opp[p->perf_dom->opp_count];
- if (PROTOCOL_REV_MAJOR(p->version) <= 0x3)
- ret = process_response_opp(ph->dev, p->perf_dom, opp,
+ opp = &perf_dom->opp[perf_dom->opp_count];
+ if (PROTOCOL_REV_MAJOR(ph->version) <= 0x3)
+ ret = process_response_opp(ph->dev, perf_dom, opp,
st->loop_idx, response);
else
- ret = process_response_opp_v4(ph->dev, p->perf_dom, opp,
+ ret = process_response_opp_v4(ph->dev, perf_dom, opp,
st->loop_idx, response);
/* Skip BAD duplicates received from firmware */
if (ret)
return ret == -EBUSY ? 0 : ret;
- p->perf_dom->opp_count++;
+ perf_dom->opp_count++;
dev_dbg(ph->dev, "Level %d Power %d Latency %dus Ifreq %d Index %d\n",
opp->perf, opp->power, opp->trans_latency_us,
@@ -470,7 +463,7 @@ iter_perf_levels_process_response(const struct scmi_protocol_handle *ph,
static int
scmi_perf_describe_levels_get(const struct scmi_protocol_handle *ph,
- struct perf_dom_info *perf_dom, u32 version)
+ struct perf_dom_info *perf_dom)
{
int ret;
void *iter;
@@ -479,15 +472,11 @@ scmi_perf_describe_levels_get(const struct scmi_protocol_handle *ph,
.update_state = iter_perf_levels_update_state,
.process_response = iter_perf_levels_process_response,
};
- struct scmi_perf_ipriv ppriv = {
- .version = version,
- .perf_dom = perf_dom,
- };
iter = ph->hops->iter_response_init(ph, &ops, MAX_OPPS,
PERF_DESCRIBE_LEVELS,
sizeof(struct scmi_msg_perf_describe_levels),
- &ppriv);
+ perf_dom);
if (IS_ERR(iter))
return PTR_ERR(iter);
@@ -576,7 +565,6 @@ static int __scmi_perf_limits_set(const struct scmi_protocol_handle *ph,
static int scmi_perf_limits_set(const struct scmi_protocol_handle *ph,
u32 domain, u32 max_perf, u32 min_perf)
{
- struct scmi_perf_info *pi = ph->get_priv(ph);
struct perf_dom_info *dom;
dom = scmi_perf_domain_lookup(ph, domain);
@@ -586,7 +574,7 @@ static int scmi_perf_limits_set(const struct scmi_protocol_handle *ph,
if (!dom->set_limits)
return -EOPNOTSUPP;
- if (PROTOCOL_REV_MAJOR(pi->version) >= 0x3 && !max_perf && !min_perf)
+ if (PROTOCOL_REV_MAJOR(ph->version) >= 0x3 && !max_perf && !min_perf)
return -EINVAL;
if (dom->level_indexing_mode) {
@@ -1281,22 +1269,15 @@ static const struct scmi_protocol_events perf_protocol_events = {
static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph)
{
int domain, ret;
- u32 version;
struct scmi_perf_info *pinfo;
- ret = ph->xops->version_get(ph, &version);
- if (ret)
- return ret;
-
dev_dbg(ph->dev, "Performance Version %d.%d\n",
- PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+ PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
if (!pinfo)
return -ENOMEM;
- pinfo->version = version;
-
ret = scmi_perf_attributes_get(ph, pinfo);
if (ret)
return ret;
@@ -1311,8 +1292,8 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph)
dom->id = domain;
scmi_perf_domain_attributes_get(ph, dom, pinfo->notify_lim_cmd,
- pinfo->notify_lvl_cmd, version);
- scmi_perf_describe_levels_get(ph, dom, version);
+ pinfo->notify_lvl_cmd);
+ scmi_perf_describe_levels_get(ph, dom);
if (dom->perf_fastchannels)
scmi_perf_domain_init_fc(ph, dom);
@@ -1322,7 +1303,7 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph)
if (ret)
return ret;
- return ph->set_priv(ph, pinfo, version);
+ return ph->set_priv(ph, pinfo);
}
static const struct scmi_protocol scmi_perf = {
diff --git a/drivers/firmware/arm_scmi/pinctrl.c b/drivers/firmware/arm_scmi/pinctrl.c
index 3855c98caf06..a020e23d7c49 100644
--- a/drivers/firmware/arm_scmi/pinctrl.c
+++ b/drivers/firmware/arm_scmi/pinctrl.c
@@ -117,7 +117,6 @@ struct scmi_pin_info {
};
struct scmi_pinctrl_info {
- u32 version;
int nr_groups;
int nr_functions;
int nr_pins;
@@ -596,11 +595,19 @@ static int scmi_pinctrl_pin_free(const struct scmi_protocol_handle *ph, u32 pin)
}
static int scmi_pinctrl_get_group_info(const struct scmi_protocol_handle *ph,
- u32 selector,
- struct scmi_group_info *group)
+ u32 selector)
{
+ struct scmi_pinctrl_info *pi = ph->get_priv(ph);
+ struct scmi_group_info *group;
int ret;
+ if (selector >= pi->nr_groups)
+ return -EINVAL;
+
+ group = &pi->groups[selector];
+ if (group->present)
+ return 0;
+
ret = scmi_pinctrl_attributes(ph, GROUP_TYPE, selector, group->name,
&group->nr_pins);
if (ret)
@@ -632,21 +639,14 @@ static int scmi_pinctrl_get_group_name(const struct scmi_protocol_handle *ph,
u32 selector, const char **name)
{
struct scmi_pinctrl_info *pi = ph->get_priv(ph);
+ int ret;
if (!name)
return -EINVAL;
- if (selector >= pi->nr_groups || pi->nr_groups == 0)
- return -EINVAL;
-
- if (!pi->groups[selector].present) {
- int ret;
-
- ret = scmi_pinctrl_get_group_info(ph, selector,
- &pi->groups[selector]);
- if (ret)
- return ret;
- }
+ ret = scmi_pinctrl_get_group_info(ph, selector);
+ if (ret)
+ return ret;
*name = pi->groups[selector].name;
@@ -658,21 +658,14 @@ static int scmi_pinctrl_group_pins_get(const struct scmi_protocol_handle *ph,
u32 *nr_pins)
{
struct scmi_pinctrl_info *pi = ph->get_priv(ph);
+ int ret;
if (!pins || !nr_pins)
return -EINVAL;
- if (selector >= pi->nr_groups || pi->nr_groups == 0)
- return -EINVAL;
-
- if (!pi->groups[selector].present) {
- int ret;
-
- ret = scmi_pinctrl_get_group_info(ph, selector,
- &pi->groups[selector]);
- if (ret)
- return ret;
- }
+ ret = scmi_pinctrl_get_group_info(ph, selector);
+ if (ret)
+ return ret;
*pins = pi->groups[selector].group_pins;
*nr_pins = pi->groups[selector].nr_pins;
@@ -681,11 +674,19 @@ static int scmi_pinctrl_group_pins_get(const struct scmi_protocol_handle *ph,
}
static int scmi_pinctrl_get_function_info(const struct scmi_protocol_handle *ph,
- u32 selector,
- struct scmi_function_info *func)
+ u32 selector)
{
+ struct scmi_pinctrl_info *pi = ph->get_priv(ph);
+ struct scmi_function_info *func;
int ret;
+ if (selector >= pi->nr_functions)
+ return -EINVAL;
+
+ func = &pi->functions[selector];
+ if (func->present)
+ return 0;
+
ret = scmi_pinctrl_attributes(ph, FUNCTION_TYPE, selector, func->name,
&func->nr_groups);
if (ret)
@@ -716,21 +717,14 @@ static int scmi_pinctrl_get_function_name(const struct scmi_protocol_handle *ph,
u32 selector, const char **name)
{
struct scmi_pinctrl_info *pi = ph->get_priv(ph);
+ int ret;
if (!name)
return -EINVAL;
- if (selector >= pi->nr_functions || pi->nr_functions == 0)
- return -EINVAL;
-
- if (!pi->functions[selector].present) {
- int ret;
-
- ret = scmi_pinctrl_get_function_info(ph, selector,
- &pi->functions[selector]);
- if (ret)
- return ret;
- }
+ ret = scmi_pinctrl_get_function_info(ph, selector);
+ if (ret)
+ return ret;
*name = pi->functions[selector].name;
return 0;
@@ -742,21 +736,14 @@ scmi_pinctrl_function_groups_get(const struct scmi_protocol_handle *ph,
const u32 **groups)
{
struct scmi_pinctrl_info *pi = ph->get_priv(ph);
+ int ret;
if (!groups || !nr_groups)
return -EINVAL;
- if (selector >= pi->nr_functions || pi->nr_functions == 0)
- return -EINVAL;
-
- if (!pi->functions[selector].present) {
- int ret;
-
- ret = scmi_pinctrl_get_function_info(ph, selector,
- &pi->functions[selector]);
- if (ret)
- return ret;
- }
+ ret = scmi_pinctrl_get_function_info(ph, selector);
+ if (ret)
+ return ret;
*groups = pi->functions[selector].groups;
*nr_groups = pi->functions[selector].nr_groups;
@@ -771,13 +758,19 @@ static int scmi_pinctrl_mux_set(const struct scmi_protocol_handle *ph,
}
static int scmi_pinctrl_get_pin_info(const struct scmi_protocol_handle *ph,
- u32 selector, struct scmi_pin_info *pin)
+ u32 selector)
{
+ struct scmi_pinctrl_info *pi = ph->get_priv(ph);
+ struct scmi_pin_info *pin;
int ret;
- if (!pin)
+ if (selector >= pi->nr_pins)
return -EINVAL;
+ pin = &pi->pins[selector];
+ if (pin->present)
+ return 0;
+
ret = scmi_pinctrl_attributes(ph, PIN_TYPE, selector, pin->name, NULL);
if (ret)
return ret;
@@ -790,20 +783,14 @@ static int scmi_pinctrl_get_pin_name(const struct scmi_protocol_handle *ph,
u32 selector, const char **name)
{
struct scmi_pinctrl_info *pi = ph->get_priv(ph);
+ int ret;
if (!name)
return -EINVAL;
- if (selector >= pi->nr_pins)
- return -EINVAL;
-
- if (!pi->pins[selector].present) {
- int ret;
-
- ret = scmi_pinctrl_get_pin_info(ph, selector, &pi->pins[selector]);
- if (ret)
- return ret;
- }
+ ret = scmi_pinctrl_get_pin_info(ph, selector);
+ if (ret)
+ return ret;
*name = pi->pins[selector].name;
@@ -843,15 +830,10 @@ static const struct scmi_pinctrl_proto_ops pinctrl_proto_ops = {
static int scmi_pinctrl_protocol_init(const struct scmi_protocol_handle *ph)
{
int ret;
- u32 version;
struct scmi_pinctrl_info *pinfo;
- ret = ph->xops->version_get(ph, &version);
- if (ret)
- return ret;
-
dev_dbg(ph->dev, "Pinctrl Version %d.%d\n",
- PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+ PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
if (!pinfo)
@@ -876,9 +858,7 @@ static int scmi_pinctrl_protocol_init(const struct scmi_protocol_handle *ph)
if (!pinfo->functions)
return -ENOMEM;
- pinfo->version = version;
-
- return ph->set_priv(ph, pinfo, version);
+ return ph->set_priv(ph, pinfo);
}
static int scmi_pinctrl_protocol_deinit(const struct scmi_protocol_handle *ph)
diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c
index 59aa16444c64..bb5062ab8280 100644
--- a/drivers/firmware/arm_scmi/power.c
+++ b/drivers/firmware/arm_scmi/power.c
@@ -67,7 +67,6 @@ struct power_dom_info {
};
struct scmi_power_info {
- u32 version;
bool notify_state_change_cmd;
int num_domains;
u64 stats_addr;
@@ -109,7 +108,7 @@ static int scmi_power_attributes_get(const struct scmi_protocol_handle *ph,
static int
scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,
u32 domain, struct power_dom_info *dom_info,
- u32 version, bool notify_state_change_cmd)
+ bool notify_state_change_cmd)
{
int ret;
u32 flags;
@@ -141,7 +140,7 @@ scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,
* If supported overwrite short name with the extended one;
* on error just carry on and use already provided short name.
*/
- if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 &&
+ if (!ret && PROTOCOL_REV_MAJOR(ph->version) >= 0x3 &&
SUPPORTS_EXTENDED_NAMES(flags)) {
ph->hops->extended_name_get(ph, POWER_DOMAIN_NAME_GET,
domain, NULL, dom_info->name,
@@ -323,15 +322,10 @@ static const struct scmi_protocol_events power_protocol_events = {
static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph)
{
int domain, ret;
- u32 version;
struct scmi_power_info *pinfo;
- ret = ph->xops->version_get(ph, &version);
- if (ret)
- return ret;
-
dev_dbg(ph->dev, "Power Version %d.%d\n",
- PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+ PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
if (!pinfo)
@@ -349,13 +343,11 @@ static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph)
for (domain = 0; domain < pinfo->num_domains; domain++) {
struct power_dom_info *dom = pinfo->dom_info + domain;
- scmi_power_domain_attributes_get(ph, domain, dom, version,
+ scmi_power_domain_attributes_get(ph, domain, dom,
pinfo->notify_state_change_cmd);
}
- pinfo->version = version;
-
- return ph->set_priv(ph, pinfo, version);
+ return ph->set_priv(ph, pinfo);
}
static const struct scmi_protocol scmi_power = {
diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_scmi/powercap.c
index 1fa79bba492e..ab9733f4458b 100644
--- a/drivers/firmware/arm_scmi/powercap.c
+++ b/drivers/firmware/arm_scmi/powercap.c
@@ -122,7 +122,6 @@ struct scmi_powercap_state {
};
struct powercap_info {
- u32 version;
int num_domains;
bool notify_cap_cmd;
bool notify_measurements_cmd;
@@ -434,7 +433,7 @@ static int __scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
}
/* Save the last explicitly set non-zero powercap value */
- if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 && !ret && power_cap)
+ if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2 && !ret && power_cap)
pi->states[domain_id].last_pcap = power_cap;
return ret;
@@ -454,7 +453,7 @@ static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
return -EINVAL;
/* Just log the last set request if acting on a disabled domain */
- if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 &&
+ if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2 &&
!pi->states[domain_id].enabled) {
pi->states[domain_id].last_pcap = power_cap;
return 0;
@@ -635,7 +634,7 @@ static int scmi_powercap_cap_enable_set(const struct scmi_protocol_handle *ph,
u32 power_cap;
struct powercap_info *pi = ph->get_priv(ph);
- if (PROTOCOL_REV_MAJOR(pi->version) < 0x2)
+ if (PROTOCOL_REV_MAJOR(ph->version) < 0x2)
return -EINVAL;
if (enable == pi->states[domain_id].enabled)
@@ -676,7 +675,7 @@ static int scmi_powercap_cap_enable_get(const struct scmi_protocol_handle *ph,
struct powercap_info *pi = ph->get_priv(ph);
*enable = true;
- if (PROTOCOL_REV_MAJOR(pi->version) < 0x2)
+ if (PROTOCOL_REV_MAJOR(ph->version) < 0x2)
return 0;
/*
@@ -961,15 +960,10 @@ static int
scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
{
int domain, ret;
- u32 version;
struct powercap_info *pinfo;
- ret = ph->xops->version_get(ph, &version);
- if (ret)
- return ret;
-
dev_dbg(ph->dev, "Powercap Version %d.%d\n",
- PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+ PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
if (!pinfo)
@@ -1006,7 +1000,7 @@ scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
&pinfo->powercaps[domain].fc_info);
/* Grab initial state when disable is supported. */
- if (PROTOCOL_REV_MAJOR(version) >= 0x2) {
+ if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2) {
ret = __scmi_powercap_cap_get(ph,
&pinfo->powercaps[domain],
&pinfo->states[domain].last_pcap);
@@ -1018,8 +1012,7 @@ scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
}
}
- pinfo->version = version;
- return ph->set_priv(ph, pinfo, version);
+ return ph->set_priv(ph, pinfo);
}
static const struct scmi_protocol scmi_powercap = {
diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_scmi/protocols.h
index d62c4469d1fd..4c75970326e6 100644
--- a/drivers/firmware/arm_scmi/protocols.h
+++ b/drivers/firmware/arm_scmi/protocols.h
@@ -159,6 +159,9 @@ struct scmi_proto_helpers_ops;
* struct scmi_protocol_handle - Reference to an initialized protocol instance
*
* @dev: A reference to the associated SCMI instance device (handle->dev).
+ * @version: The protocol version currently effectively in use by this
+ * initialized instance of the protocol as determined at the end of
+ * any possibly needed negotiations performed by the core.
* @xops: A reference to a struct holding refs to the core xfer operations that
* can be used by the protocol implementation to generate SCMI messages.
* @set_priv: A method to set protocol private data for this instance.
@@ -177,10 +180,10 @@ struct scmi_proto_helpers_ops;
*/
struct scmi_protocol_handle {
struct device *dev;
+ unsigned int version;
const struct scmi_xfer_ops *xops;
const struct scmi_proto_helpers_ops *hops;
- int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv,
- u32 version);
+ int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv);
void *(*get_priv)(const struct scmi_protocol_handle *ph);
};
@@ -287,7 +290,6 @@ struct scmi_proto_helpers_ops {
/**
* struct scmi_xfer_ops - References to the core SCMI xfer operations.
- * @version_get: Get this version protocol.
* @xfer_get_init: Initialize one struct xfer if any xfer slot is free.
* @reset_rx_to_maxsz: Reset rx size to max transport size.
* @do_xfer: Do the SCMI transfer.
@@ -300,7 +302,6 @@ struct scmi_proto_helpers_ops {
* another protocol.
*/
struct scmi_xfer_ops {
- int (*version_get)(const struct scmi_protocol_handle *ph, u32 *version);
int (*xfer_get_init)(const struct scmi_protocol_handle *ph, u8 msg_id,
size_t tx_size, size_t rx_size,
struct scmi_xfer **p);
diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c
index 0aa82b96f41b..4bc5c24c2d72 100644
--- a/drivers/firmware/arm_scmi/reset.c
+++ b/drivers/firmware/arm_scmi/reset.c
@@ -65,7 +65,6 @@ struct reset_dom_info {
};
struct scmi_reset_info {
- u32 version;
int num_domains;
bool notify_reset_cmd;
struct reset_dom_info *dom_info;
@@ -98,10 +97,20 @@ static int scmi_reset_attributes_get(const struct scmi_protocol_handle *ph,
return ret;
}
+static struct reset_dom_info *
+scmi_reset_domain_lookup(const struct scmi_protocol_handle *ph, u32 domain)
+{
+ struct scmi_reset_info *pi = ph->get_priv(ph);
+
+ if (domain >= pi->num_domains)
+ return ERR_PTR(-EINVAL);
+
+ return pi->dom_info + domain;
+}
+
static int
scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,
- struct scmi_reset_info *pinfo,
- u32 domain, u32 version)
+ struct scmi_reset_info *pinfo, u32 domain)
{
int ret;
u32 attributes;
@@ -137,7 +146,7 @@ scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,
* If supported overwrite short name with the extended one;
* on error just carry on and use already provided short name.
*/
- if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 &&
+ if (!ret && PROTOCOL_REV_MAJOR(ph->version) >= 0x3 &&
SUPPORTS_EXTENDED_NAMES(attributes))
ph->hops->extended_name_get(ph, RESET_DOMAIN_NAME_GET, domain,
NULL, dom_info->name,
@@ -156,20 +165,25 @@ static int scmi_reset_num_domains_get(const struct scmi_protocol_handle *ph)
static const char *
scmi_reset_name_get(const struct scmi_protocol_handle *ph, u32 domain)
{
- struct scmi_reset_info *pi = ph->get_priv(ph);
+ struct reset_dom_info *dom_info;
- struct reset_dom_info *dom = pi->dom_info + domain;
+ dom_info = scmi_reset_domain_lookup(ph, domain);
+ if (IS_ERR(dom_info))
+ return "unknown";
- return dom->name;
+ return dom_info->name;
}
static int scmi_reset_latency_get(const struct scmi_protocol_handle *ph,
u32 domain)
{
- struct scmi_reset_info *pi = ph->get_priv(ph);
- struct reset_dom_info *dom = pi->dom_info + domain;
+ struct reset_dom_info *dom_info;
- return dom->latency_us;
+ dom_info = scmi_reset_domain_lookup(ph, domain);
+ if (IS_ERR(dom_info))
+ return PTR_ERR(dom_info);
+
+ return dom_info->latency_us;
}
static int scmi_domain_reset(const struct scmi_protocol_handle *ph, u32 domain,
@@ -178,14 +192,13 @@ static int scmi_domain_reset(const struct scmi_protocol_handle *ph, u32 domain,
int ret;
struct scmi_xfer *t;
struct scmi_msg_reset_domain_reset *dom;
- struct scmi_reset_info *pi = ph->get_priv(ph);
- struct reset_dom_info *rdom;
+ struct reset_dom_info *dom_info;
- if (domain >= pi->num_domains)
- return -EINVAL;
+ dom_info = scmi_reset_domain_lookup(ph, domain);
+ if (IS_ERR(dom_info))
+ return PTR_ERR(dom_info);
- rdom = pi->dom_info + domain;
- if (rdom->async_reset && flags & AUTONOMOUS_RESET)
+ if (dom_info->async_reset && flags & AUTONOMOUS_RESET)
flags |= ASYNCHRONOUS_RESET;
ret = ph->xops->xfer_get_init(ph, RESET, sizeof(*dom), 0, &t);
@@ -238,15 +251,16 @@ static const struct scmi_reset_proto_ops reset_proto_ops = {
static bool scmi_reset_notify_supported(const struct scmi_protocol_handle *ph,
u8 evt_id, u32 src_id)
{
- struct reset_dom_info *dom;
- struct scmi_reset_info *pi = ph->get_priv(ph);
+ struct reset_dom_info *dom_info;
- if (evt_id != SCMI_EVENT_RESET_ISSUED || src_id >= pi->num_domains)
+ if (evt_id != SCMI_EVENT_RESET_ISSUED)
return false;
- dom = pi->dom_info + src_id;
+ dom_info = scmi_reset_domain_lookup(ph, src_id);
+ if (IS_ERR(dom_info))
+ return false;
- return dom->reset_notify;
+ return dom_info->reset_notify;
}
static int scmi_reset_notify(const struct scmi_protocol_handle *ph,
@@ -340,15 +354,10 @@ static const struct scmi_protocol_events reset_protocol_events = {
static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph)
{
int domain, ret;
- u32 version;
struct scmi_reset_info *pinfo;
- ret = ph->xops->version_get(ph, &version);
- if (ret)
- return ret;
-
dev_dbg(ph->dev, "Reset Version %d.%d\n",
- PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+ PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
if (!pinfo)
@@ -364,10 +373,9 @@ static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph)
return -ENOMEM;
for (domain = 0; domain < pinfo->num_domains; domain++)
- scmi_reset_domain_attributes_get(ph, pinfo, domain, version);
+ scmi_reset_domain_attributes_get(ph, pinfo, domain);
- pinfo->version = version;
- return ph->set_priv(ph, pinfo, version);
+ return ph->set_priv(ph, pinfo);
}
static const struct scmi_protocol scmi_reset = {
diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c
index 791efd0f82d7..882d55f987d2 100644
--- a/drivers/firmware/arm_scmi/sensors.c
+++ b/drivers/firmware/arm_scmi/sensors.c
@@ -214,7 +214,6 @@ struct scmi_sensor_update_notify_payld {
};
struct sensors_info {
- u32 version;
bool notify_trip_point_cmd;
bool notify_continuos_update_cmd;
int num_sensors;
@@ -524,8 +523,7 @@ scmi_sensor_axis_extended_names_get(const struct scmi_protocol_handle *ph,
}
static int scmi_sensor_axis_description(const struct scmi_protocol_handle *ph,
- struct scmi_sensor_info *s,
- u32 version)
+ struct scmi_sensor_info *s)
{
int ret;
void *iter;
@@ -555,7 +553,7 @@ static int scmi_sensor_axis_description(const struct scmi_protocol_handle *ph,
if (ret)
return ret;
- if (PROTOCOL_REV_MAJOR(version) >= 0x3 &&
+ if (PROTOCOL_REV_MAJOR(ph->version) >= 0x3 &&
apriv.any_axes_support_extended_names)
ret = scmi_sensor_axis_extended_names_get(ph, s);
@@ -621,7 +619,7 @@ iter_sens_descr_process_response(const struct scmi_protocol_handle *ph,
s->type = SENSOR_TYPE(attrh);
/* Use pre-allocated pool wherever possible */
s->intervals.desc = s->intervals.prealloc_pool;
- if (si->version == SCMIv2_SENSOR_PROTOCOL) {
+ if (ph->version == SCMIv2_SENSOR_PROTOCOL) {
s->intervals.segmented = false;
s->intervals.count = 1;
/*
@@ -659,7 +657,7 @@ iter_sens_descr_process_response(const struct scmi_protocol_handle *ph,
* one; on error just carry on and use already provided
* short name.
*/
- if (PROTOCOL_REV_MAJOR(si->version) >= 0x3 &&
+ if (PROTOCOL_REV_MAJOR(ph->version) >= 0x3 &&
SUPPORTS_EXTENDED_NAMES(attrl))
ph->hops->extended_name_get(ph, SENSOR_NAME_GET, s->id,
NULL, s->name, SCMI_MAX_STR_SIZE);
@@ -683,7 +681,7 @@ iter_sens_descr_process_response(const struct scmi_protocol_handle *ph,
}
if (s->num_axis > 0)
- ret = scmi_sensor_axis_description(ph, s, si->version);
+ ret = scmi_sensor_axis_description(ph, s);
st->priv = ((u8 *)sdesc + dsize);
@@ -1148,21 +1146,15 @@ static const struct scmi_protocol_events sensor_protocol_events = {
static int scmi_sensors_protocol_init(const struct scmi_protocol_handle *ph)
{
- u32 version;
int ret;
struct sensors_info *sinfo;
- ret = ph->xops->version_get(ph, &version);
- if (ret)
- return ret;
-
dev_dbg(ph->dev, "Sensor Version %d.%d\n",
- PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+ PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
sinfo = devm_kzalloc(ph->dev, sizeof(*sinfo), GFP_KERNEL);
if (!sinfo)
return -ENOMEM;
- sinfo->version = version;
ret = scmi_sensor_attributes_get(ph, sinfo);
if (ret)
@@ -1176,7 +1168,7 @@ static int scmi_sensors_protocol_init(const struct scmi_protocol_handle *ph)
if (ret)
return ret;
- return ph->set_priv(ph, sinfo, version);
+ return ph->set_priv(ph, sinfo);
}
static const struct scmi_protocol scmi_sensors = {
diff --git a/drivers/firmware/arm_scmi/shmem.c b/drivers/firmware/arm_scmi/shmem.c
index 11c347bff766..dadb37557f8a 100644
--- a/drivers/firmware/arm_scmi/shmem.c
+++ b/drivers/firmware/arm_scmi/shmem.c
@@ -196,7 +196,6 @@ static void __iomem *shmem_setup_iomap(struct scmi_chan_info *cinfo,
struct resource *res,
struct scmi_shmem_io_ops **ops)
{
- struct device_node *shmem __free(device_node);
const char *desc = tx ? "Tx" : "Rx";
int ret, idx = tx ? 0 : 1;
struct device *cdev = cinfo->dev;
@@ -205,7 +204,9 @@ static void __iomem *shmem_setup_iomap(struct scmi_chan_info *cinfo,
void __iomem *addr;
u32 reg_io_width;
- shmem = of_parse_phandle(cdev->of_node, "shmem", idx);
+ struct device_node *shmem __free(device_node) = of_parse_phandle(cdev->of_node,
+ "shmem", idx);
+
if (!shmem)
return IOMEM_ERR_PTR(-ENODEV);
diff --git a/drivers/firmware/arm_scmi/system.c b/drivers/firmware/arm_scmi/system.c
index ec3d355d1772..0f51c36f6a9d 100644
--- a/drivers/firmware/arm_scmi/system.c
+++ b/drivers/firmware/arm_scmi/system.c
@@ -34,7 +34,6 @@ struct scmi_system_power_state_notifier_payld {
};
struct scmi_system_info {
- u32 version;
bool graceful_timeout_supported;
bool power_state_notify_cmd;
};
@@ -141,29 +140,22 @@ static const struct scmi_protocol_events system_protocol_events = {
static int scmi_system_protocol_init(const struct scmi_protocol_handle *ph)
{
- int ret;
- u32 version;
struct scmi_system_info *pinfo;
- ret = ph->xops->version_get(ph, &version);
- if (ret)
- return ret;
-
dev_dbg(ph->dev, "System Power Version %d.%d\n",
- PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+ PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
if (!pinfo)
return -ENOMEM;
- pinfo->version = version;
- if (PROTOCOL_REV_MAJOR(pinfo->version) >= 0x2)
+ if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2)
pinfo->graceful_timeout_supported = true;
if (!ph->hops->protocol_msg_check(ph, SYSTEM_POWER_STATE_NOTIFY, NULL))
pinfo->power_state_notify_cmd = true;
- return ph->set_priv(ph, pinfo, version);
+ return ph->set_priv(ph, pinfo);
}
static const struct scmi_protocol scmi_system = {
diff --git a/drivers/firmware/arm_scmi/transports/optee.c b/drivers/firmware/arm_scmi/transports/optee.c
index dc0f46340153..07ae18d5279d 100644
--- a/drivers/firmware/arm_scmi/transports/optee.c
+++ b/drivers/firmware/arm_scmi/transports/optee.c
@@ -529,8 +529,9 @@ static const struct of_device_id scmi_of_match[] = {
DEFINE_SCMI_TRANSPORT_DRIVER(scmi_optee, scmi_optee_driver, scmi_optee_desc,
scmi_of_match, core);
-static int scmi_optee_service_probe(struct device *dev)
+static int scmi_optee_service_probe(struct tee_client_device *scmi_pta)
{
+ struct device *dev = &scmi_pta->dev;
struct scmi_optee_agent *agent;
struct tee_context *tee_ctx;
int ret;
@@ -578,24 +579,22 @@ err:
return ret;
}
-static int scmi_optee_service_remove(struct device *dev)
+static void scmi_optee_service_remove(struct tee_client_device *scmi_pta)
{
struct scmi_optee_agent *agent = scmi_optee_private;
if (!scmi_optee_private)
- return -EINVAL;
+ return;
platform_driver_unregister(&scmi_optee_driver);
if (!list_empty(&scmi_optee_private->channel_list))
- return -EBUSY;
+ return;
/* Ensure cleared reference is visible before resources are released */
smp_store_mb(scmi_optee_private, NULL);
tee_client_close_context(agent->tee_ctx);
-
- return 0;
}
static const struct tee_client_device_id scmi_optee_service_id[] = {
@@ -609,26 +608,15 @@ static const struct tee_client_device_id scmi_optee_service_id[] = {
MODULE_DEVICE_TABLE(tee, scmi_optee_service_id);
static struct tee_client_driver scmi_optee_service_driver = {
- .id_table = scmi_optee_service_id,
- .driver = {
+ .probe = scmi_optee_service_probe,
+ .remove = scmi_optee_service_remove,
+ .id_table = scmi_optee_service_id,
+ .driver = {
.name = "scmi-optee",
- .bus = &tee_bus_type,
- .probe = scmi_optee_service_probe,
- .remove = scmi_optee_service_remove,
},
};
-static int __init scmi_transport_optee_init(void)
-{
- return driver_register(&scmi_optee_service_driver.driver);
-}
-module_init(scmi_transport_optee_init);
-
-static void __exit scmi_transport_optee_exit(void)
-{
- driver_unregister(&scmi_optee_service_driver.driver);
-}
-module_exit(scmi_transport_optee_exit);
+module_tee_client_driver(scmi_optee_service_driver);
MODULE_AUTHOR("Etienne Carriere <etienne.carriere@foss.st.com>");
MODULE_DESCRIPTION("SCMI OPTEE Transport driver");
diff --git a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-bbm.c b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-bbm.c
index aa176c1a5eef..33f9ebf6092b 100644
--- a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-bbm.c
+++ b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-bbm.c
@@ -48,7 +48,6 @@ enum scmi_imx_bbm_protocol_cmd {
#define SCMI_IMX_BBM_EVENT_RTC_MASK GENMASK(31, 24)
struct scmi_imx_bbm_info {
- u32 version;
int nr_rtc;
int nr_gpr;
};
@@ -345,16 +344,11 @@ static const struct scmi_imx_bbm_proto_ops scmi_imx_bbm_proto_ops = {
static int scmi_imx_bbm_protocol_init(const struct scmi_protocol_handle *ph)
{
- u32 version;
int ret;
struct scmi_imx_bbm_info *binfo;
- ret = ph->xops->version_get(ph, &version);
- if (ret)
- return ret;
-
dev_info(ph->dev, "NXP SM BBM Version %d.%d\n",
- PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+ PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
binfo = devm_kzalloc(ph->dev, sizeof(*binfo), GFP_KERNEL);
if (!binfo)
@@ -364,7 +358,7 @@ static int scmi_imx_bbm_protocol_init(const struct scmi_protocol_handle *ph)
if (ret)
return ret;
- return ph->set_priv(ph, binfo, version);
+ return ph->set_priv(ph, binfo);
}
static const struct scmi_protocol scmi_imx_bbm = {
diff --git a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c
index 66f47f5371e5..753274af11d2 100644
--- a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c
+++ b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c
@@ -233,15 +233,10 @@ static int scmi_imx_cpu_attributes_get(const struct scmi_protocol_handle *ph,
static int scmi_imx_cpu_protocol_init(const struct scmi_protocol_handle *ph)
{
struct scmi_imx_cpu_info *info;
- u32 version;
int ret, i;
- ret = ph->xops->version_get(ph, &version);
- if (ret)
- return ret;
-
dev_info(ph->dev, "NXP SM CPU Protocol Version %d.%d\n",
- PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+ PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
info = devm_kzalloc(ph->dev, sizeof(*info), GFP_KERNEL);
if (!info)
@@ -257,7 +252,7 @@ static int scmi_imx_cpu_protocol_init(const struct scmi_protocol_handle *ph)
return ret;
}
- return ph->set_priv(ph, info, version);
+ return ph->set_priv(ph, info);
}
static const struct scmi_protocol scmi_imx_cpu = {
diff --git a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-lmm.c b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-lmm.c
index b519c67fe920..c56ae247774d 100644
--- a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-lmm.c
+++ b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-lmm.c
@@ -226,15 +226,10 @@ static int scmi_imx_lmm_protocol_attributes_get(const struct scmi_protocol_handl
static int scmi_imx_lmm_protocol_init(const struct scmi_protocol_handle *ph)
{
struct scmi_imx_lmm_priv *info;
- u32 version;
int ret;
- ret = ph->xops->version_get(ph, &version);
- if (ret)
- return ret;
-
dev_info(ph->dev, "NXP SM LMM Version %d.%d\n",
- PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+ PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
info = devm_kzalloc(ph->dev, sizeof(*info), GFP_KERNEL);
if (!info)
@@ -244,7 +239,7 @@ static int scmi_imx_lmm_protocol_init(const struct scmi_protocol_handle *ph)
if (ret)
return ret;
- return ph->set_priv(ph, info, version);
+ return ph->set_priv(ph, info);
}
static const struct scmi_protocol scmi_imx_lmm = {
diff --git a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c
index 700a3f24f4ef..0ada753367ef 100644
--- a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c
+++ b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c
@@ -28,11 +28,11 @@ enum scmi_imx_misc_protocol_cmd {
SCMI_IMX_MISC_DISCOVER_BUILD_INFO = 0x6,
SCMI_IMX_MISC_CTRL_NOTIFY = 0x8,
SCMI_IMX_MISC_CFG_INFO_GET = 0xC,
+ SCMI_IMX_MISC_SYSLOG_GET = 0xD,
SCMI_IMX_MISC_BOARD_INFO = 0xE,
};
struct scmi_imx_misc_info {
- u32 version;
u32 nr_dev_ctrl;
u32 nr_brd_ctrl;
u32 nr_reason;
@@ -89,6 +89,19 @@ struct scmi_imx_misc_cfg_info_out {
u8 cfgname[MISC_MAX_CFGNAME];
};
+struct scmi_imx_misc_syslog_in {
+ __le32 flags;
+ __le32 index;
+};
+
+#define REMAINING(x) le32_get_bits((x), GENMASK(31, 20))
+#define RETURNED(x) le32_get_bits((x), GENMASK(11, 0))
+
+struct scmi_imx_misc_syslog_out {
+ __le32 numlogflags;
+ __le32 syslog[];
+};
+
static int scmi_imx_misc_attributes_get(const struct scmi_protocol_handle *ph,
struct scmi_imx_misc_info *mi)
{
@@ -371,24 +384,88 @@ static int scmi_imx_misc_cfg_info_get(const struct scmi_protocol_handle *ph)
return ret;
}
+struct scmi_imx_misc_syslog_ipriv {
+ u32 *array;
+ u16 *size;
+};
+
+static void iter_misc_syslog_prepare_message(void *message, u32 desc_index,
+ const void *priv)
+{
+ struct scmi_imx_misc_syslog_in *msg = message;
+
+ msg->flags = cpu_to_le32(0);
+ msg->index = cpu_to_le32(desc_index);
+}
+
+static int iter_misc_syslog_update_state(struct scmi_iterator_state *st,
+ const void *response, void *priv)
+{
+ const struct scmi_imx_misc_syslog_out *r = response;
+ struct scmi_imx_misc_syslog_ipriv *p = priv;
+
+ st->num_returned = RETURNED(r->numlogflags);
+ st->num_remaining = REMAINING(r->numlogflags);
+ *p->size = st->num_returned + st->num_remaining;
+
+ return 0;
+}
+
+static int
+iter_misc_syslog_process_response(const struct scmi_protocol_handle *ph,
+ const void *response,
+ struct scmi_iterator_state *st, void *priv)
+{
+ const struct scmi_imx_misc_syslog_out *r = response;
+ struct scmi_imx_misc_syslog_ipriv *p = priv;
+
+ p->array[st->desc_index + st->loop_idx] =
+ le32_to_cpu(r->syslog[st->loop_idx]);
+
+ return 0;
+}
+
+static int scmi_imx_misc_syslog_get(const struct scmi_protocol_handle *ph, u16 *size,
+ void *array)
+{
+ struct scmi_iterator_ops ops = {
+ .prepare_message = iter_misc_syslog_prepare_message,
+ .update_state = iter_misc_syslog_update_state,
+ .process_response = iter_misc_syslog_process_response,
+ };
+ struct scmi_imx_misc_syslog_ipriv ipriv = {
+ .array = array,
+ .size = size,
+ };
+ void *iter;
+
+ if (!array || !size || !*size)
+ return -EINVAL;
+
+ iter = ph->hops->iter_response_init(ph, &ops, *size, SCMI_IMX_MISC_SYSLOG_GET,
+ sizeof(struct scmi_imx_misc_syslog_in),
+ &ipriv);
+ if (IS_ERR(iter))
+ return PTR_ERR(iter);
+
+ /* If firmware return NOT SUPPORTED, propagate value to caller */
+ return ph->hops->iter_response_run(iter);
+}
+
static const struct scmi_imx_misc_proto_ops scmi_imx_misc_proto_ops = {
.misc_ctrl_set = scmi_imx_misc_ctrl_set,
.misc_ctrl_get = scmi_imx_misc_ctrl_get,
.misc_ctrl_req_notify = scmi_imx_misc_ctrl_notify,
+ .misc_syslog = scmi_imx_misc_syslog_get,
};
static int scmi_imx_misc_protocol_init(const struct scmi_protocol_handle *ph)
{
struct scmi_imx_misc_info *minfo;
- u32 version;
int ret;
- ret = ph->xops->version_get(ph, &version);
- if (ret)
- return ret;
-
dev_info(ph->dev, "NXP SM MISC Version %d.%d\n",
- PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+ PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
minfo = devm_kzalloc(ph->dev, sizeof(*minfo), GFP_KERNEL);
if (!minfo)
@@ -410,7 +487,7 @@ static int scmi_imx_misc_protocol_init(const struct scmi_protocol_handle *ph)
if (ret && ret != -EOPNOTSUPP)
return ret;
- return ph->set_priv(ph, minfo, version);
+ return ph->set_priv(ph, minfo);
}
static const struct scmi_protocol scmi_imx_misc = {
diff --git a/drivers/firmware/arm_scmi/voltage.c b/drivers/firmware/arm_scmi/voltage.c
index 17127880e10a..b9391c1ee8a0 100644
--- a/drivers/firmware/arm_scmi/voltage.c
+++ b/drivers/firmware/arm_scmi/voltage.c
@@ -66,7 +66,6 @@ struct scmi_resp_voltage_level_set_complete {
};
struct voltage_info {
- unsigned int version;
unsigned int num_domains;
struct scmi_voltage_info *domains;
};
@@ -243,7 +242,7 @@ static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph,
* If supported overwrite short name with the extended one;
* on error just carry on and use already provided short name.
*/
- if (PROTOCOL_REV_MAJOR(vinfo->version) >= 0x2) {
+ if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2) {
if (SUPPORTS_EXTENDED_NAMES(attributes))
ph->hops->extended_name_get(ph,
VOLTAGE_DOMAIN_NAME_GET,
@@ -405,20 +404,14 @@ static const struct scmi_voltage_proto_ops voltage_proto_ops = {
static int scmi_voltage_protocol_init(const struct scmi_protocol_handle *ph)
{
int ret;
- u32 version;
struct voltage_info *vinfo;
- ret = ph->xops->version_get(ph, &version);
- if (ret)
- return ret;
-
dev_dbg(ph->dev, "Voltage Version %d.%d\n",
- PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+ PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
vinfo = devm_kzalloc(ph->dev, sizeof(*vinfo), GFP_KERNEL);
if (!vinfo)
return -ENOMEM;
- vinfo->version = version;
ret = scmi_protocol_attributes_get(ph, vinfo);
if (ret)
@@ -437,7 +430,7 @@ static int scmi_voltage_protocol_init(const struct scmi_protocol_handle *ph)
dev_warn(ph->dev, "No Voltage domains found.\n");
}
- return ph->set_priv(ph, vinfo, version);
+ return ph->set_priv(ph, vinfo);
}
static const struct scmi_protocol scmi_voltage = {
diff --git a/drivers/firmware/broadcom/tee_bnxt_fw.c b/drivers/firmware/broadcom/tee_bnxt_fw.c
index 40e3183a3d11..a706c84eb2b6 100644
--- a/drivers/firmware/broadcom/tee_bnxt_fw.c
+++ b/drivers/firmware/broadcom/tee_bnxt_fw.c
@@ -181,9 +181,9 @@ static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
return (ver->impl_id == TEE_IMPL_ID_OPTEE);
}
-static int tee_bnxt_fw_probe(struct device *dev)
+static int tee_bnxt_fw_probe(struct tee_client_device *bnxt_device)
{
- struct tee_client_device *bnxt_device = to_tee_client_device(dev);
+ struct device *dev = &bnxt_device->dev;
int ret, err = -ENODEV;
struct tee_ioctl_open_session_arg sess_arg;
struct tee_shm *fw_shm_pool;
@@ -231,17 +231,15 @@ out_ctx:
return err;
}
-static int tee_bnxt_fw_remove(struct device *dev)
+static void tee_bnxt_fw_remove(struct tee_client_device *bnxt_device)
{
tee_shm_free(pvt_data.fw_shm_pool);
tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
tee_client_close_context(pvt_data.ctx);
pvt_data.ctx = NULL;
-
- return 0;
}
-static void tee_bnxt_fw_shutdown(struct device *dev)
+static void tee_bnxt_fw_shutdown(struct tee_client_device *bnxt_device)
{
tee_shm_free(pvt_data.fw_shm_pool);
tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
@@ -258,28 +256,16 @@ static const struct tee_client_device_id tee_bnxt_fw_id_table[] = {
MODULE_DEVICE_TABLE(tee, tee_bnxt_fw_id_table);
static struct tee_client_driver tee_bnxt_fw_driver = {
+ .probe = tee_bnxt_fw_probe,
+ .remove = tee_bnxt_fw_remove,
+ .shutdown = tee_bnxt_fw_shutdown,
.id_table = tee_bnxt_fw_id_table,
.driver = {
.name = KBUILD_MODNAME,
- .bus = &tee_bus_type,
- .probe = tee_bnxt_fw_probe,
- .remove = tee_bnxt_fw_remove,
- .shutdown = tee_bnxt_fw_shutdown,
},
};
-static int __init tee_bnxt_fw_mod_init(void)
-{
- return driver_register(&tee_bnxt_fw_driver.driver);
-}
-
-static void __exit tee_bnxt_fw_mod_exit(void)
-{
- driver_unregister(&tee_bnxt_fw_driver.driver);
-}
-
-module_init(tee_bnxt_fw_mod_init);
-module_exit(tee_bnxt_fw_mod_exit);
+module_tee_client_driver(tee_bnxt_fw_driver);
MODULE_AUTHOR("Vikas Gupta <vikas.gupta@broadcom.com>");
MODULE_DESCRIPTION("Broadcom bnxt firmware manager");
diff --git a/drivers/firmware/efi/stmm/tee_stmm_efi.c b/drivers/firmware/efi/stmm/tee_stmm_efi.c
index 65c0fe1ba275..7b04dd649629 100644
--- a/drivers/firmware/efi/stmm/tee_stmm_efi.c
+++ b/drivers/firmware/efi/stmm/tee_stmm_efi.c
@@ -520,8 +520,9 @@ static void tee_stmm_restore_efivars_generic_ops(void)
efivars_generic_ops_register();
}
-static int tee_stmm_efi_probe(struct device *dev)
+static int tee_stmm_efi_probe(struct tee_client_device *tee_dev)
{
+ struct device *dev = &tee_dev->dev;
struct tee_ioctl_open_session_arg sess_arg;
efi_status_t ret;
int rc;
@@ -571,37 +572,23 @@ static int tee_stmm_efi_probe(struct device *dev)
return 0;
}
-static int tee_stmm_efi_remove(struct device *dev)
+static void tee_stmm_efi_remove(struct tee_client_device *dev)
{
tee_stmm_restore_efivars_generic_ops();
-
- return 0;
}
MODULE_DEVICE_TABLE(tee, tee_stmm_efi_id_table);
static struct tee_client_driver tee_stmm_efi_driver = {
.id_table = tee_stmm_efi_id_table,
+ .probe = tee_stmm_efi_probe,
+ .remove = tee_stmm_efi_remove,
.driver = {
.name = "tee-stmm-efi",
- .bus = &tee_bus_type,
- .probe = tee_stmm_efi_probe,
- .remove = tee_stmm_efi_remove,
},
};
-static int __init tee_stmm_efi_mod_init(void)
-{
- return driver_register(&tee_stmm_efi_driver.driver);
-}
-
-static void __exit tee_stmm_efi_mod_exit(void)
-{
- driver_unregister(&tee_stmm_efi_driver.driver);
-}
-
-module_init(tee_stmm_efi_mod_init);
-module_exit(tee_stmm_efi_mod_exit);
+module_tee_client_driver(tee_stmm_efi_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ilias Apalodimas <ilias.apalodimas@linaro.org>");
diff --git a/drivers/firmware/imx/sm-misc.c b/drivers/firmware/imx/sm-misc.c
index fc3ee12c2be8..0a8ada329c9d 100644
--- a/drivers/firmware/imx/sm-misc.c
+++ b/drivers/firmware/imx/sm-misc.c
@@ -3,12 +3,16 @@
* Copyright 2024 NXP
*/
+#include <linux/debugfs.h>
+#include <linux/device/devres.h>
#include <linux/firmware/imx/sm.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/scmi_protocol.h>
#include <linux/scmi_imx_protocol.h>
+#include <linux/seq_file.h>
+#include <linux/sizes.h>
static const struct scmi_imx_misc_proto_ops *imx_misc_ctrl_ops;
static struct scmi_protocol_handle *ph;
@@ -44,10 +48,38 @@ static int scmi_imx_misc_ctrl_notifier(struct notifier_block *nb,
return 0;
}
+static int syslog_show(struct seq_file *file, void *priv)
+{
+ /* 4KB is large enough for syslog */
+ void *syslog __free(kfree) = kmalloc(SZ_4K, GFP_KERNEL);
+ /* syslog API use num words, not num bytes */
+ u16 size = SZ_4K / 4;
+ int ret;
+
+ if (!ph)
+ return -ENODEV;
+
+ ret = imx_misc_ctrl_ops->misc_syslog(ph, &size, syslog);
+ if (ret)
+ return ret;
+
+ seq_hex_dump(file, " ", DUMP_PREFIX_NONE, 16, sizeof(u32), syslog, size * 4, false);
+ seq_putc(file, '\n');
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(syslog);
+
+static void scmi_imx_misc_put(void *p)
+{
+ debugfs_remove((struct dentry *)p);
+}
+
static int scmi_imx_misc_ctrl_probe(struct scmi_device *sdev)
{
const struct scmi_handle *handle = sdev->handle;
struct device_node *np = sdev->dev.of_node;
+ struct dentry *scmi_imx_dentry;
u32 src_id, flags;
int ret, i, num;
@@ -98,7 +130,10 @@ static int scmi_imx_misc_ctrl_probe(struct scmi_device *sdev)
}
}
- return 0;
+ scmi_imx_dentry = debugfs_create_dir("scmi_imx", NULL);
+ debugfs_create_file("syslog", 0444, scmi_imx_dentry, &sdev->dev, &syslog_fops);
+
+ return devm_add_action_or_reset(&sdev->dev, scmi_imx_misc_put, scmi_imx_dentry);
}
static const struct scmi_device_id scmi_id_table[] = {
diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c
index 1a6f85e463e0..8e3c2ac40341 100644
--- a/drivers/firmware/qcom/qcom_scm.c
+++ b/drivers/firmware/qcom/qcom_scm.c
@@ -27,21 +27,29 @@
#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
+#include <linux/remoteproc.h>
#include <linux/sizes.h>
#include <linux/types.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
#include "qcom_scm.h"
#include "qcom_tzmem.h"
static u32 download_mode;
+#define GIC_SPI_BASE 32
+#define GIC_MAX_SPI 1019 // SPIs in GICv3 spec range from 32..1019
+#define GIC_ESPI_BASE 4096
+#define GIC_MAX_ESPI 5119 // ESPIs in GICv3 spec range from 4096..5119
+
struct qcom_scm {
struct device *dev;
struct clk *core_clk;
struct clk *iface_clk;
struct clk *bus_clk;
struct icc_path *path;
- struct completion waitq_comp;
+ struct completion *waitq_comps;
struct reset_controller_dev reset;
/* control access to the interconnect path */
@@ -51,6 +59,7 @@ struct qcom_scm {
u64 dload_mode_addr;
struct qcom_tzmem_pool *mempool;
+ unsigned int wq_cnt;
};
struct qcom_scm_current_perm_info {
@@ -111,6 +120,8 @@ enum qcom_scm_qseecom_tz_cmd_info {
QSEECOM_TZ_CMD_INFO_VERSION = 3,
};
+#define RSCTABLE_BUFFER_NOT_SUFFICIENT 20
+
#define QSEECOM_MAX_APP_NAME_SIZE 64
#define SHMBRIDGE_RESULT_NOTSUPP 4
@@ -130,6 +141,8 @@ static const u8 qcom_scm_cpu_warm_bits[QCOM_SCM_BOOT_MAX_CPUS] = {
#define QCOM_DLOAD_MINIDUMP 2
#define QCOM_DLOAD_BOTHDUMP 3
+#define QCOM_SCM_DEFAULT_WAITQ_COUNT 1
+
static const char * const qcom_scm_convention_names[] = {
[SMC_CONVENTION_UNKNOWN] = "unknown",
[SMC_CONVENTION_ARM_32] = "smc arm 32",
@@ -559,15 +572,104 @@ static void qcom_scm_set_download_mode(u32 dload_mode)
}
/**
+ * devm_qcom_scm_pas_context_alloc() - Allocate peripheral authentication service
+ * context for a given peripheral
+ *
+ * PAS context is device-resource managed, so the caller does not need
+ * to worry about freeing the context memory.
+ *
+ * @dev: PAS firmware device
+ * @pas_id: peripheral authentication service id
+ * @mem_phys: Subsystem reserve memory start address
+ * @mem_size: Subsystem reserve memory size
+ *
+ * Returns: The new PAS context, or ERR_PTR() on failure.
+ */
+struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev,
+ u32 pas_id,
+ phys_addr_t mem_phys,
+ size_t mem_size)
+{
+ struct qcom_scm_pas_context *ctx;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return ERR_PTR(-ENOMEM);
+
+ ctx->dev = dev;
+ ctx->pas_id = pas_id;
+ ctx->mem_phys = mem_phys;
+ ctx->mem_size = mem_size;
+
+ return ctx;
+}
+EXPORT_SYMBOL_GPL(devm_qcom_scm_pas_context_alloc);
+
+static int __qcom_scm_pas_init_image(u32 pas_id, dma_addr_t mdata_phys,
+ struct qcom_scm_res *res)
+{
+ struct qcom_scm_desc desc = {
+ .svc = QCOM_SCM_SVC_PIL,
+ .cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE,
+ .arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW),
+ .args[0] = pas_id,
+ .owner = ARM_SMCCC_OWNER_SIP,
+ };
+ int ret;
+
+ ret = qcom_scm_clk_enable();
+ if (ret)
+ return ret;
+
+ ret = qcom_scm_bw_enable();
+ if (ret)
+ goto disable_clk;
+
+ desc.args[1] = mdata_phys;
+
+ ret = qcom_scm_call(__scm->dev, &desc, res);
+ qcom_scm_bw_disable();
+
+disable_clk:
+ qcom_scm_clk_disable();
+
+ return ret;
+}
+
+static int qcom_scm_pas_prep_and_init_image(struct qcom_scm_pas_context *ctx,
+ const void *metadata, size_t size)
+{
+ struct qcom_scm_res res;
+ phys_addr_t mdata_phys;
+ void *mdata_buf;
+ int ret;
+
+ mdata_buf = qcom_tzmem_alloc(__scm->mempool, size, GFP_KERNEL);
+ if (!mdata_buf)
+ return -ENOMEM;
+
+ memcpy(mdata_buf, metadata, size);
+ mdata_phys = qcom_tzmem_to_phys(mdata_buf);
+
+ ret = __qcom_scm_pas_init_image(ctx->pas_id, mdata_phys, &res);
+ if (ret < 0)
+ qcom_tzmem_free(mdata_buf);
+ else
+ ctx->ptr = mdata_buf;
+
+ return ret ? : res.result[0];
+}
+
+/**
* qcom_scm_pas_init_image() - Initialize peripheral authentication service
* state machine for a given peripheral, using the
* metadata
- * @peripheral: peripheral id
+ * @pas_id: peripheral authentication service id
* @metadata: pointer to memory containing ELF header, program header table
* and optional blob of data used for authenticating the metadata
* and the rest of the firmware
* @size: size of the metadata
- * @ctx: optional metadata context
+ * @ctx: optional pas context
*
* Return: 0 on success.
*
@@ -575,20 +677,16 @@ static void qcom_scm_set_download_mode(u32 dload_mode)
* track the metadata allocation, this needs to be released by invoking
* qcom_scm_pas_metadata_release() by the caller.
*/
-int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size,
- struct qcom_scm_pas_metadata *ctx)
+int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size,
+ struct qcom_scm_pas_context *ctx)
{
+ struct qcom_scm_res res;
dma_addr_t mdata_phys;
void *mdata_buf;
int ret;
- struct qcom_scm_desc desc = {
- .svc = QCOM_SCM_SVC_PIL,
- .cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE,
- .arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW),
- .args[0] = peripheral,
- .owner = ARM_SMCCC_OWNER_SIP,
- };
- struct qcom_scm_res res;
+
+ if (ctx && ctx->use_tzmem)
+ return qcom_scm_pas_prep_and_init_image(ctx, metadata, size);
/*
* During the scm call memory protection will be enabled for the meta
@@ -609,23 +707,7 @@ int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size,
memcpy(mdata_buf, metadata, size);
- ret = qcom_scm_clk_enable();
- if (ret)
- goto out;
-
- ret = qcom_scm_bw_enable();
- if (ret)
- goto disable_clk;
-
- desc.args[1] = mdata_phys;
-
- ret = qcom_scm_call(__scm->dev, &desc, &res);
- qcom_scm_bw_disable();
-
-disable_clk:
- qcom_scm_clk_disable();
-
-out:
+ ret = __qcom_scm_pas_init_image(pas_id, mdata_phys, &res);
if (ret < 0 || !ctx) {
dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys);
} else if (ctx) {
@@ -640,38 +722,39 @@ EXPORT_SYMBOL_GPL(qcom_scm_pas_init_image);
/**
* qcom_scm_pas_metadata_release() - release metadata context
- * @ctx: metadata context
+ * @ctx: pas context
*/
-void qcom_scm_pas_metadata_release(struct qcom_scm_pas_metadata *ctx)
+void qcom_scm_pas_metadata_release(struct qcom_scm_pas_context *ctx)
{
if (!ctx->ptr)
return;
- dma_free_coherent(__scm->dev, ctx->size, ctx->ptr, ctx->phys);
+ if (ctx->use_tzmem)
+ qcom_tzmem_free(ctx->ptr);
+ else
+ dma_free_coherent(__scm->dev, ctx->size, ctx->ptr, ctx->phys);
ctx->ptr = NULL;
- ctx->phys = 0;
- ctx->size = 0;
}
EXPORT_SYMBOL_GPL(qcom_scm_pas_metadata_release);
/**
* qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral
* for firmware loading
- * @peripheral: peripheral id
+ * @pas_id: peripheral authentication service id
* @addr: start address of memory area to prepare
* @size: size of the memory area to prepare
*
* Returns 0 on success.
*/
-int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size)
+int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size)
{
int ret;
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_PIL,
.cmd = QCOM_SCM_PIL_PAS_MEM_SETUP,
.arginfo = QCOM_SCM_ARGS(3),
- .args[0] = peripheral,
+ .args[0] = pas_id,
.args[1] = addr,
.args[2] = size,
.owner = ARM_SMCCC_OWNER_SIP,
@@ -696,21 +779,189 @@ disable_clk:
}
EXPORT_SYMBOL_GPL(qcom_scm_pas_mem_setup);
+static void *__qcom_scm_pas_get_rsc_table(u32 pas_id, void *input_rt_tzm,
+ size_t input_rt_size,
+ size_t *output_rt_size)
+{
+ struct qcom_scm_desc desc = {
+ .svc = QCOM_SCM_SVC_PIL,
+ .cmd = QCOM_SCM_PIL_PAS_GET_RSCTABLE,
+ .arginfo = QCOM_SCM_ARGS(5, QCOM_SCM_VAL, QCOM_SCM_RO, QCOM_SCM_VAL,
+ QCOM_SCM_RW, QCOM_SCM_VAL),
+ .args[0] = pas_id,
+ .owner = ARM_SMCCC_OWNER_SIP,
+ };
+ struct qcom_scm_res res;
+ void *output_rt_tzm;
+ int ret;
+
+ output_rt_tzm = qcom_tzmem_alloc(__scm->mempool, *output_rt_size, GFP_KERNEL);
+ if (!output_rt_tzm)
+ return ERR_PTR(-ENOMEM);
+
+ desc.args[1] = qcom_tzmem_to_phys(input_rt_tzm);
+ desc.args[2] = input_rt_size;
+ desc.args[3] = qcom_tzmem_to_phys(output_rt_tzm);
+ desc.args[4] = *output_rt_size;
+
+ /*
+ * Whether SMC fail or pass, res.result[2] will hold actual resource table
+ * size.
+ *
+ * If passed 'output_rt_size' buffer size is not sufficient to hold the
+ * resource table TrustZone sends, response code in res.result[1] as
+ * RSCTABLE_BUFFER_NOT_SUFFICIENT so that caller can retry this SMC call
+ * with output_rt_tzm buffer with res.result[2] size however, It should not
+ * be of unresonable size.
+ */
+ ret = qcom_scm_call(__scm->dev, &desc, &res);
+ if (!ret && res.result[2] > SZ_1G) {
+ ret = -E2BIG;
+ goto free_output_rt;
+ }
+
+ *output_rt_size = res.result[2];
+ if (ret && res.result[1] == RSCTABLE_BUFFER_NOT_SUFFICIENT)
+ ret = -EOVERFLOW;
+
+free_output_rt:
+ if (ret)
+ qcom_tzmem_free(output_rt_tzm);
+
+ return ret ? ERR_PTR(ret) : output_rt_tzm;
+}
+
+/**
+ * qcom_scm_pas_get_rsc_table() - Retrieve the resource table in passed output buffer
+ * for a given peripheral.
+ *
+ * Qualcomm remote processor may rely on both static and dynamic resources for
+ * its functionality. Static resources typically refer to memory-mapped addresses
+ * required by the subsystem and are often embedded within the firmware binary
+ * and dynamic resources, such as shared memory in DDR etc., are determined at
+ * runtime during the boot process.
+ *
+ * On Qualcomm Technologies devices, it's possible that static resources are not
+ * embedded in the firmware binary and instead are provided by TrustZone However,
+ * dynamic resources are always expected to come from TrustZone. This indicates
+ * that for Qualcomm devices, all resources (static and dynamic) will be provided
+ * by TrustZone via the SMC call.
+ *
+ * If the remote processor firmware binary does contain static resources, they
+ * should be passed in input_rt. These will be forwarded to TrustZone for
+ * authentication. TrustZone will then append the dynamic resources and return
+ * the complete resource table in output_rt_tzm.
+ *
+ * If the remote processor firmware binary does not include a resource table,
+ * the caller of this function should set input_rt as NULL and input_rt_size
+ * as zero respectively.
+ *
+ * More about documentation on resource table data structures can be found in
+ * include/linux/remoteproc.h
+ *
+ * @ctx: PAS context
+ * @pas_id: peripheral authentication service id
+ * @input_rt: resource table buffer which is present in firmware binary
+ * @input_rt_size: size of the resource table present in firmware binary
+ * @output_rt_size: TrustZone expects caller should pass worst case size for
+ * the output_rt_tzm.
+ *
+ * Return:
+ * On success, returns a pointer to the allocated buffer containing the final
+ * resource table and output_rt_size will have actual resource table size from
+ * TrustZone. The caller is responsible for freeing the buffer. On failure,
+ * returns ERR_PTR(-errno).
+ */
+struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *ctx,
+ void *input_rt,
+ size_t input_rt_size,
+ size_t *output_rt_size)
+{
+ struct resource_table empty_rsc = {};
+ size_t size = SZ_16K;
+ void *output_rt_tzm;
+ void *input_rt_tzm;
+ void *tbl_ptr;
+ int ret;
+
+ ret = qcom_scm_clk_enable();
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = qcom_scm_bw_enable();
+ if (ret)
+ goto disable_clk;
+
+ /*
+ * TrustZone can not accept buffer as NULL value as argument hence,
+ * we need to pass a input buffer indicating that subsystem firmware
+ * does not have resource table by filling resource table structure.
+ */
+ if (!input_rt) {
+ input_rt = &empty_rsc;
+ input_rt_size = sizeof(empty_rsc);
+ }
+
+ input_rt_tzm = qcom_tzmem_alloc(__scm->mempool, input_rt_size, GFP_KERNEL);
+ if (!input_rt_tzm) {
+ ret = -ENOMEM;
+ goto disable_scm_bw;
+ }
+
+ memcpy(input_rt_tzm, input_rt, input_rt_size);
+
+ output_rt_tzm = __qcom_scm_pas_get_rsc_table(ctx->pas_id, input_rt_tzm,
+ input_rt_size, &size);
+ if (PTR_ERR(output_rt_tzm) == -EOVERFLOW)
+ /* Try again with the size requested by the TZ */
+ output_rt_tzm = __qcom_scm_pas_get_rsc_table(ctx->pas_id,
+ input_rt_tzm,
+ input_rt_size,
+ &size);
+ if (IS_ERR(output_rt_tzm)) {
+ ret = PTR_ERR(output_rt_tzm);
+ goto free_input_rt;
+ }
+
+ tbl_ptr = kzalloc(size, GFP_KERNEL);
+ if (!tbl_ptr) {
+ qcom_tzmem_free(output_rt_tzm);
+ ret = -ENOMEM;
+ goto free_input_rt;
+ }
+
+ memcpy(tbl_ptr, output_rt_tzm, size);
+ *output_rt_size = size;
+ qcom_tzmem_free(output_rt_tzm);
+
+free_input_rt:
+ qcom_tzmem_free(input_rt_tzm);
+
+disable_scm_bw:
+ qcom_scm_bw_disable();
+
+disable_clk:
+ qcom_scm_clk_disable();
+
+ return ret ? ERR_PTR(ret) : tbl_ptr;
+}
+EXPORT_SYMBOL_GPL(qcom_scm_pas_get_rsc_table);
+
/**
* qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware
* and reset the remote processor
- * @peripheral: peripheral id
+ * @pas_id: peripheral authentication service id
*
* Return 0 on success.
*/
-int qcom_scm_pas_auth_and_reset(u32 peripheral)
+int qcom_scm_pas_auth_and_reset(u32 pas_id)
{
int ret;
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_PIL,
.cmd = QCOM_SCM_PIL_PAS_AUTH_AND_RESET,
.arginfo = QCOM_SCM_ARGS(1),
- .args[0] = peripheral,
+ .args[0] = pas_id,
.owner = ARM_SMCCC_OWNER_SIP,
};
struct qcom_scm_res res;
@@ -734,19 +985,66 @@ disable_clk:
EXPORT_SYMBOL_GPL(qcom_scm_pas_auth_and_reset);
/**
+ * qcom_scm_pas_prepare_and_auth_reset() - Prepare, authenticate, and reset the
+ * remote processor
+ *
+ * @ctx: Context saved during call to qcom_scm_pas_context_init()
+ *
+ * This function performs the necessary steps to prepare a PAS subsystem,
+ * authenticate it using the provided metadata, and initiate a reset sequence.
+ *
+ * It should be used when Linux is in control setting up the IOMMU hardware
+ * for remote subsystem during secure firmware loading processes. The preparation
+ * step sets up a shmbridge over the firmware memory before TrustZone accesses the
+ * firmware memory region for authentication. The authentication step verifies
+ * the integrity and authenticity of the firmware or configuration using secure
+ * metadata. Finally, the reset step ensures the subsystem starts in a clean and
+ * sane state.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx)
+{
+ u64 handle;
+ int ret;
+
+ /*
+ * When Linux running @ EL1, Gunyah hypervisor running @ EL2 traps the
+ * auth_and_reset call and create an shmbridge on the remote subsystem
+ * memory region and then invokes a call to TrustZone to authenticate.
+ */
+ if (!ctx->use_tzmem)
+ return qcom_scm_pas_auth_and_reset(ctx->pas_id);
+
+ /*
+ * When Linux runs @ EL2 Linux must create the shmbridge itself and then
+ * subsequently call TrustZone for authenticate and reset.
+ */
+ ret = qcom_tzmem_shm_bridge_create(ctx->mem_phys, ctx->mem_size, &handle);
+ if (ret)
+ return ret;
+
+ ret = qcom_scm_pas_auth_and_reset(ctx->pas_id);
+ qcom_tzmem_shm_bridge_delete(handle);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(qcom_scm_pas_prepare_and_auth_reset);
+
+/**
* qcom_scm_pas_shutdown() - Shut down the remote processor
- * @peripheral: peripheral id
+ * @pas_id: peripheral authentication service id
*
* Returns 0 on success.
*/
-int qcom_scm_pas_shutdown(u32 peripheral)
+int qcom_scm_pas_shutdown(u32 pas_id)
{
int ret;
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_PIL,
.cmd = QCOM_SCM_PIL_PAS_SHUTDOWN,
.arginfo = QCOM_SCM_ARGS(1),
- .args[0] = peripheral,
+ .args[0] = pas_id,
.owner = ARM_SMCCC_OWNER_SIP,
};
struct qcom_scm_res res;
@@ -772,18 +1070,18 @@ EXPORT_SYMBOL_GPL(qcom_scm_pas_shutdown);
/**
* qcom_scm_pas_supported() - Check if the peripheral authentication service is
* available for the given peripherial
- * @peripheral: peripheral id
+ * @pas_id: peripheral authentication service id
*
* Returns true if PAS is supported for this peripheral, otherwise false.
*/
-bool qcom_scm_pas_supported(u32 peripheral)
+bool qcom_scm_pas_supported(u32 pas_id)
{
int ret;
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_PIL,
.cmd = QCOM_SCM_PIL_PAS_IS_SUPPORTED,
.arginfo = QCOM_SCM_ARGS(1),
- .args[0] = peripheral,
+ .args[0] = pas_id,
.owner = ARM_SMCCC_OWNER_SIP,
};
struct qcom_scm_res res;
@@ -2007,6 +2305,7 @@ static const struct of_device_id qcom_scm_qseecom_allowlist[] __maybe_unused = {
{ .compatible = "lenovo,yoga-slim7x" },
{ .compatible = "microsoft,arcata", },
{ .compatible = "microsoft,blackrock" },
+ { .compatible = "microsoft,denali", },
{ .compatible = "microsoft,romulus13", },
{ .compatible = "microsoft,romulus15", },
{ .compatible = "qcom,hamoa-iot-evk" },
@@ -2208,42 +2507,107 @@ bool qcom_scm_is_available(void)
}
EXPORT_SYMBOL_GPL(qcom_scm_is_available);
-static int qcom_scm_assert_valid_wq_ctx(u32 wq_ctx)
+static int qcom_scm_fill_irq_fwspec_params(struct irq_fwspec *fwspec, u32 hwirq)
{
- /* FW currently only supports a single wq_ctx (zero).
- * TODO: Update this logic to include dynamic allocation and lookup of
- * completion structs when FW supports more wq_ctx values.
- */
- if (wq_ctx != 0) {
- dev_err(__scm->dev, "Firmware unexpectedly passed non-zero wq_ctx\n");
- return -EINVAL;
+ if (hwirq >= GIC_SPI_BASE && hwirq <= GIC_MAX_SPI) {
+ fwspec->param[0] = GIC_SPI;
+ fwspec->param[1] = hwirq - GIC_SPI_BASE;
+ } else if (hwirq >= GIC_ESPI_BASE && hwirq <= GIC_MAX_ESPI) {
+ fwspec->param[0] = GIC_ESPI;
+ fwspec->param[1] = hwirq - GIC_ESPI_BASE;
+ } else {
+ WARN(1, "Unexpected hwirq: %d\n", hwirq);
+ return -ENXIO;
}
+ fwspec->param[2] = IRQ_TYPE_EDGE_RISING;
+ fwspec->param_count = 3;
+
return 0;
}
-int qcom_scm_wait_for_wq_completion(u32 wq_ctx)
+static int qcom_scm_query_waitq_count(struct qcom_scm *scm)
{
+ struct qcom_scm_desc desc = {
+ .svc = QCOM_SCM_SVC_WAITQ,
+ .cmd = QCOM_SCM_WAITQ_GET_INFO,
+ .owner = ARM_SMCCC_OWNER_SIP
+ };
+ struct qcom_scm_res res;
int ret;
- ret = qcom_scm_assert_valid_wq_ctx(wq_ctx);
+ ret = qcom_scm_call_atomic(scm->dev, &desc, &res);
if (ret)
return ret;
- wait_for_completion(&__scm->waitq_comp);
-
- return 0;
+ return res.result[0] & GENMASK(7, 0);
}
-static int qcom_scm_waitq_wakeup(unsigned int wq_ctx)
+static int qcom_scm_get_waitq_irq(struct qcom_scm *scm)
{
+ struct qcom_scm_desc desc = {
+ .svc = QCOM_SCM_SVC_WAITQ,
+ .cmd = QCOM_SCM_WAITQ_GET_INFO,
+ .owner = ARM_SMCCC_OWNER_SIP
+ };
+ struct device_node *parent_irq_node;
+ struct irq_fwspec fwspec;
+ struct qcom_scm_res res;
+ u32 hwirq;
int ret;
- ret = qcom_scm_assert_valid_wq_ctx(wq_ctx);
+ ret = qcom_scm_call_atomic(scm->dev, &desc, &res);
+ if (ret)
+ return ret;
+
+ hwirq = res.result[1] & GENMASK(15, 0);
+ ret = qcom_scm_fill_irq_fwspec_params(&fwspec, hwirq);
if (ret)
return ret;
- complete(&__scm->waitq_comp);
+ parent_irq_node = of_irq_find_parent(scm->dev->of_node);
+ if (!parent_irq_node)
+ return -ENODEV;
+
+ fwspec.fwnode = of_fwnode_handle(parent_irq_node);
+
+ return irq_create_fwspec_mapping(&fwspec);
+}
+
+static struct completion *qcom_scm_get_completion(u32 wq_ctx)
+{
+ struct completion *wq;
+
+ if (WARN_ON_ONCE(wq_ctx >= __scm->wq_cnt))
+ return ERR_PTR(-EINVAL);
+
+ wq = &__scm->waitq_comps[wq_ctx];
+
+ return wq;
+}
+
+int qcom_scm_wait_for_wq_completion(u32 wq_ctx)
+{
+ struct completion *wq;
+
+ wq = qcom_scm_get_completion(wq_ctx);
+ if (IS_ERR(wq))
+ return PTR_ERR(wq);
+
+ wait_for_completion_state(wq, TASK_IDLE);
+
+ return 0;
+}
+
+static int qcom_scm_waitq_wakeup(unsigned int wq_ctx)
+{
+ struct completion *wq;
+
+ wq = qcom_scm_get_completion(wq_ctx);
+ if (IS_ERR(wq))
+ return PTR_ERR(wq);
+
+ complete(wq);
return 0;
}
@@ -2319,6 +2683,7 @@ static int qcom_scm_probe(struct platform_device *pdev)
struct qcom_tzmem_pool_config pool_config;
struct qcom_scm *scm;
int irq, ret;
+ int i;
scm = devm_kzalloc(&pdev->dev, sizeof(*scm), GFP_KERNEL);
if (!scm)
@@ -2329,7 +2694,6 @@ static int qcom_scm_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
- init_completion(&scm->waitq_comp);
mutex_init(&scm->scm_bw_lock);
scm->path = devm_of_icc_get(&pdev->dev, NULL);
@@ -2381,7 +2745,20 @@ static int qcom_scm_probe(struct platform_device *pdev)
return dev_err_probe(scm->dev, PTR_ERR(scm->mempool),
"Failed to create the SCM memory pool\n");
- irq = platform_get_irq_optional(pdev, 0);
+ ret = qcom_scm_query_waitq_count(scm);
+ scm->wq_cnt = ret < 0 ? QCOM_SCM_DEFAULT_WAITQ_COUNT : ret;
+ scm->waitq_comps = devm_kcalloc(&pdev->dev, scm->wq_cnt, sizeof(*scm->waitq_comps),
+ GFP_KERNEL);
+ if (!scm->waitq_comps)
+ return -ENOMEM;
+
+ for (i = 0; i < scm->wq_cnt; i++)
+ init_completion(&scm->waitq_comps[i]);
+
+ irq = qcom_scm_get_waitq_irq(scm);
+ if (irq < 0)
+ irq = platform_get_irq_optional(pdev, 0);
+
if (irq < 0) {
if (irq != -ENXIO)
return irq;
diff --git a/drivers/firmware/qcom/qcom_scm.h b/drivers/firmware/qcom/qcom_scm.h
index a56c8212cc0c..caab80a73e17 100644
--- a/drivers/firmware/qcom/qcom_scm.h
+++ b/drivers/firmware/qcom/qcom_scm.h
@@ -105,6 +105,7 @@ int qcom_scm_shm_bridge_enable(struct device *scm_dev);
#define QCOM_SCM_PIL_PAS_SHUTDOWN 0x06
#define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07
#define QCOM_SCM_PIL_PAS_MSS_RESET 0x0a
+#define QCOM_SCM_PIL_PAS_GET_RSCTABLE 0x21
#define QCOM_SCM_SVC_IO 0x05
#define QCOM_SCM_IO_READ 0x01
@@ -152,6 +153,7 @@ int qcom_scm_shm_bridge_enable(struct device *scm_dev);
#define QCOM_SCM_SVC_WAITQ 0x24
#define QCOM_SCM_WAITQ_RESUME 0x02
#define QCOM_SCM_WAITQ_GET_WQ_CTX 0x03
+#define QCOM_SCM_WAITQ_GET_INFO 0x04
#define QCOM_SCM_SVC_GPU 0x28
#define QCOM_SCM_SVC_GPU_INIT_REGS 0x01
diff --git a/drivers/firmware/ti_sci.h b/drivers/firmware/ti_sci.h
index 91f234550c43..4616127e33ff 100644
--- a/drivers/firmware/ti_sci.h
+++ b/drivers/firmware/ti_sci.h
@@ -580,13 +580,13 @@ struct ti_sci_msg_resp_get_clock_freq {
} __packed;
/**
- * struct tisci_msg_req_prepare_sleep - Request for TISCI_MSG_PREPARE_SLEEP.
+ * struct ti_sci_msg_req_prepare_sleep - Request for TISCI_MSG_PREPARE_SLEEP.
*
- * @hdr TISCI header to provide ACK/NAK flags to the host.
- * @mode Low power mode to enter.
- * @ctx_lo Low 32-bits of physical pointer to address to use for context save.
- * @ctx_hi High 32-bits of physical pointer to address to use for context save.
- * @debug_flags Flags that can be set to halt the sequence during suspend or
+ * @hdr: TISCI header to provide ACK/NAK flags to the host.
+ * @mode: Low power mode to enter.
+ * @ctx_lo: Low 32-bits of physical pointer to address to use for context save.
+ * @ctx_hi: High 32-bits of physical pointer to address to use for context save.
+ * @debug_flags: Flags that can be set to halt the sequence during suspend or
* resume to allow JTAG connection and debug.
*
* This message is used as the first step of entering a low power mode. It
@@ -610,7 +610,7 @@ struct ti_sci_msg_req_prepare_sleep {
} __packed;
/**
- * struct tisci_msg_set_io_isolation_req - Request for TI_SCI_MSG_SET_IO_ISOLATION.
+ * struct ti_sci_msg_req_set_io_isolation - Request for TI_SCI_MSG_SET_IO_ISOLATION.
*
* @hdr: Generic header
* @state: The deseared state of the IO isolation.
@@ -676,7 +676,7 @@ struct ti_sci_msg_req_lpm_set_device_constraint {
* TISCI_MSG_LPM_SET_LATENCY_CONSTRAINT.
*
* @hdr: TISCI header to provide ACK/NAK flags to the host.
- * @wkup_latency: The maximum acceptable latency to wake up from low power mode
+ * @latency: The maximum acceptable latency to wake up from low power mode
* in milliseconds. The deeper the state, the higher the latency.
* @state: The desired state of wakeup latency constraint: set or clear.
* @rsvd: Reserved for future use.
@@ -855,7 +855,7 @@ struct ti_sci_msg_rm_ring_cfg_req {
* UDMAP transmit channels mapped to source threads will have their
* TCHAN_THRD_ID register programmed with the destination thread if the pairing
* is successful.
-
+ *
* @dst_thread: PSI-L destination thread ID within the PSI-L System thread map.
* PSI-L destination threads start at index 0x8000. The request is NACK'd if
* the destination thread is not greater than or equal to 0x8000.
@@ -1000,7 +1000,8 @@ struct rm_ti_sci_msg_udmap_rx_flow_opt_cfg {
} __packed;
/**
- * Configures a Navigator Subsystem UDMAP transmit channel
+ * struct ti_sci_msg_rm_udmap_tx_ch_cfg_req - Configures a
+ * Navigator Subsystem UDMAP transmit channel
*
* Configures the non-real-time registers of a Navigator Subsystem UDMAP
* transmit channel. The channel index must be assigned to the host defined
@@ -1128,7 +1129,8 @@ struct ti_sci_msg_rm_udmap_tx_ch_cfg_req {
} __packed;
/**
- * Configures a Navigator Subsystem UDMAP receive channel
+ * struct ti_sci_msg_rm_udmap_rx_ch_cfg_req - Configures a
+ * Navigator Subsystem UDMAP receive channel
*
* Configures the non-real-time registers of a Navigator Subsystem UDMAP
* receive channel. The channel index must be assigned to the host defined
@@ -1247,7 +1249,8 @@ struct ti_sci_msg_rm_udmap_rx_ch_cfg_req {
} __packed;
/**
- * Configures a Navigator Subsystem UDMAP receive flow
+ * struct ti_sci_msg_rm_udmap_flow_cfg_req - Configures a
+ * Navigator Subsystem UDMAP receive flow
*
* Configures a Navigator Subsystem UDMAP receive flow's registers.
* Configuration does not include the flow registers which handle size-based
@@ -1258,7 +1261,7 @@ struct ti_sci_msg_rm_udmap_rx_ch_cfg_req {
*
* @hdr: Standard TISCI header
*
- * @valid_params
+ * @valid_params:
* Bitfield defining validity of rx flow configuration parameters. The
* rx flow configuration fields are not valid, and will not be used for flow
* configuration, if their corresponding valid bit is zero. Valid bit usage:
diff --git a/drivers/hwspinlock/omap_hwspinlock.c b/drivers/hwspinlock/omap_hwspinlock.c
index 3a9a5678737b..1832e0c3af6b 100644
--- a/drivers/hwspinlock/omap_hwspinlock.c
+++ b/drivers/hwspinlock/omap_hwspinlock.c
@@ -88,7 +88,9 @@ static int omap_hwspinlock_probe(struct platform_device *pdev)
* make sure the module is enabled and clocked before reading
* the module SYSSTATUS register
*/
- devm_pm_runtime_enable(&pdev->dev);
+ ret = devm_pm_runtime_enable(&pdev->dev);
+ if (ret)
+ return ret;
ret = pm_runtime_resume_and_get(&pdev->dev);
if (ret < 0)
return ret;
diff --git a/drivers/irqchip/irq-ls-extirq.c b/drivers/irqchip/irq-ls-extirq.c
index 96f9c20621cf..a7e9c3885b09 100644
--- a/drivers/irqchip/irq-ls-extirq.c
+++ b/drivers/irqchip/irq-ls-extirq.c
@@ -125,45 +125,32 @@ static const struct irq_domain_ops extirq_domain_ops = {
static int
ls_extirq_parse_map(struct ls_extirq_data *priv, struct device_node *node)
{
- const __be32 *map;
- u32 mapsize;
+ struct of_imap_parser imap_parser;
+ struct of_imap_item imap_item;
int ret;
- map = of_get_property(node, "interrupt-map", &mapsize);
- if (!map)
- return -ENOENT;
- if (mapsize % sizeof(*map))
- return -EINVAL;
- mapsize /= sizeof(*map);
+ ret = of_imap_parser_init(&imap_parser, node, &imap_item);
+ if (ret)
+ return ret;
- while (mapsize) {
+ for_each_of_imap_item(&imap_parser, &imap_item) {
struct device_node *ipar;
- u32 hwirq, intsize, j;
+ u32 hwirq;
+ int i;
- if (mapsize < 3)
- return -EINVAL;
- hwirq = be32_to_cpup(map);
- if (hwirq >= MAXIRQ)
+ hwirq = imap_item.child_imap[0];
+ if (hwirq >= MAXIRQ) {
+ of_node_put(imap_item.parent_args.np);
return -EINVAL;
+ }
priv->nirq = max(priv->nirq, hwirq + 1);
- ipar = of_find_node_by_phandle(be32_to_cpup(map + 2));
- map += 3;
- mapsize -= 3;
- if (!ipar)
- return -EINVAL;
- priv->map[hwirq].fwnode = &ipar->fwnode;
- ret = of_property_read_u32(ipar, "#interrupt-cells", &intsize);
- if (ret)
- return ret;
-
- if (intsize > mapsize)
- return -EINVAL;
+ ipar = of_node_get(imap_item.parent_args.np);
+ priv->map[hwirq].fwnode = of_fwnode_handle(ipar);
- priv->map[hwirq].param_count = intsize;
- for (j = 0; j < intsize; ++j)
- priv->map[hwirq].param[j] = be32_to_cpup(map++);
- mapsize -= intsize;
+ priv->map[hwirq].param_count = imap_item.parent_args.args_count;
+ for (i = 0; i < priv->map[hwirq].param_count; i++)
+ priv->map[hwirq].param[i] = imap_item.parent_args.args[i];
}
return 0;
}
diff --git a/drivers/irqchip/irq-renesas-rza1.c b/drivers/irqchip/irq-renesas-rza1.c
index 6047a524ac77..370d968b2398 100644
--- a/drivers/irqchip/irq-renesas-rza1.c
+++ b/drivers/irqchip/irq-renesas-rza1.c
@@ -142,47 +142,36 @@ static const struct irq_domain_ops rza1_irqc_domain_ops = {
static int rza1_irqc_parse_map(struct rza1_irqc_priv *priv,
struct device_node *gic_node)
{
+ struct of_imap_parser imap_parser;
struct device *dev = priv->dev;
- unsigned int imaplen, i, j;
+ struct of_imap_item imap_item;
struct device_node *ipar;
- const __be32 *imap;
- u32 intsize;
+ unsigned int j;
+ u32 i = 0;
int ret;
- imap = of_get_property(dev->of_node, "interrupt-map", &imaplen);
- if (!imap)
- return -EINVAL;
-
- for (i = 0; i < IRQC_NUM_IRQ; i++) {
- if (imaplen < 3)
- return -EINVAL;
+ ret = of_imap_parser_init(&imap_parser, dev->of_node, &imap_item);
+ if (ret)
+ return ret;
+ for_each_of_imap_item(&imap_parser, &imap_item) {
/* Check interrupt number, ignore sense */
- if (be32_to_cpup(imap) != i)
+ if (imap_item.child_imap[0] != i) {
+ of_node_put(imap_item.parent_args.np);
return -EINVAL;
+ }
- ipar = of_find_node_by_phandle(be32_to_cpup(imap + 2));
+ ipar = imap_item.parent_args.np;
if (ipar != gic_node) {
of_node_put(ipar);
return -EINVAL;
}
- imap += 3;
- imaplen -= 3;
-
- ret = of_property_read_u32(ipar, "#interrupt-cells", &intsize);
- of_node_put(ipar);
- if (ret)
- return ret;
-
- if (imaplen < intsize)
- return -EINVAL;
-
- priv->map[i].args_count = intsize;
- for (j = 0; j < intsize; j++)
- priv->map[i].args[j] = be32_to_cpup(imap++);
+ priv->map[i].args_count = imap_item.parent_args.args_count;
+ for (j = 0; j < priv->map[i].args_count; j++)
+ priv->map[i].args[j] = imap_item.parent_args.args[j];
- imaplen -= intsize;
+ i++;
}
return 0;
diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c
index 5791f80f995a..1bf6984948ef 100644
--- a/drivers/mailbox/mtk-cmdq-mailbox.c
+++ b/drivers/mailbox/mtk-cmdq-mailbox.c
@@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/sizes.h>
#include <linux/mailbox_controller.h>
#include <linux/mailbox/mtk-cmdq-mailbox.h>
#include <linux/of.h>
@@ -43,6 +44,13 @@
#define GCE_CTRL_BY_SW GENMASK(2, 0)
#define GCE_DDR_EN GENMASK(18, 16)
+#define GCE_VM_ID_MAP(n) (0x5018 + (n) / 10 * 4)
+#define GCE_VM_ID_MAP_THR_FLD_SHIFT(n) ((n) % 10 * 3)
+#define GCE_VM_ID_MAP_HOST_VM GENMASK(2, 0)
+#define GCE_VM_CPR_GSIZE 0x50c4
+#define GCE_VM_CPR_GSIZE_FLD_SHIFT(vm_id) ((vm_id) * 4)
+#define GCE_VM_CPR_GSIZE_MAX GENMASK(3, 0)
+
#define CMDQ_THR_ACTIVE_SLOT_CYCLES 0x3200
#define CMDQ_THR_ENABLED 0x1
#define CMDQ_THR_DISABLED 0x0
@@ -87,22 +95,33 @@ struct cmdq {
struct gce_plat {
u32 thread_nr;
u8 shift;
+ dma_addr_t mminfra_offset;
bool control_by_sw;
bool sw_ddr_en;
+ bool gce_vm;
u32 gce_num;
};
static inline u32 cmdq_convert_gce_addr(dma_addr_t addr, const struct gce_plat *pdata)
{
/* Convert DMA addr (PA or IOVA) to GCE readable addr */
- return addr >> pdata->shift;
+ return (addr + pdata->mminfra_offset) >> pdata->shift;
}
static inline dma_addr_t cmdq_revert_gce_addr(u32 addr, const struct gce_plat *pdata)
{
/* Revert GCE readable addr to DMA addr (PA or IOVA) */
- return (dma_addr_t)addr << pdata->shift;
+ return ((dma_addr_t)addr << pdata->shift) - pdata->mminfra_offset;
+}
+
+void cmdq_get_mbox_priv(struct mbox_chan *chan, struct cmdq_mbox_priv *priv)
+{
+ struct cmdq *cmdq = container_of(chan->mbox, struct cmdq, mbox);
+
+ priv->shift_pa = cmdq->pdata->shift;
+ priv->mminfra_offset = cmdq->pdata->mminfra_offset;
}
+EXPORT_SYMBOL(cmdq_get_mbox_priv);
u8 cmdq_get_shift_pa(struct mbox_chan *chan)
{
@@ -112,6 +131,45 @@ u8 cmdq_get_shift_pa(struct mbox_chan *chan)
}
EXPORT_SYMBOL(cmdq_get_shift_pa);
+static void cmdq_vm_init(struct cmdq *cmdq)
+{
+ int i;
+ u32 vm_cpr_gsize = 0, vm_id_map = 0;
+ u32 *vm_map = NULL;
+
+ if (!cmdq->pdata->gce_vm)
+ return;
+
+ vm_map = kcalloc(cmdq->pdata->thread_nr, sizeof(*vm_map), GFP_KERNEL);
+ if (!vm_map)
+ return;
+
+ /* only configure the max CPR SRAM size to host vm (vm_id = 0) currently */
+ vm_cpr_gsize = GCE_VM_CPR_GSIZE_MAX << GCE_VM_CPR_GSIZE_FLD_SHIFT(0);
+
+ /* set all thread mapping to host vm currently */
+ for (i = 0; i < cmdq->pdata->thread_nr; i++)
+ vm_map[i] = GCE_VM_ID_MAP_HOST_VM << GCE_VM_ID_MAP_THR_FLD_SHIFT(i);
+
+ /* set the amount of CPR SRAM to allocate to each VM */
+ writel(vm_cpr_gsize, cmdq->base + GCE_VM_CPR_GSIZE);
+
+ /* config CPR_GSIZE before setting VM_ID_MAP to avoid data leakage */
+ for (i = 0; i < cmdq->pdata->thread_nr; i++) {
+ vm_id_map |= vm_map[i];
+ /* config every 10 threads, e.g., thread id=0~9, 10~19, ..., into one register */
+ if ((i + 1) % 10 == 0) {
+ writel(vm_id_map, cmdq->base + GCE_VM_ID_MAP(i));
+ vm_id_map = 0;
+ }
+ }
+ /* config remaining threads settings */
+ if (cmdq->pdata->thread_nr % 10 != 0)
+ writel(vm_id_map, cmdq->base + GCE_VM_ID_MAP(cmdq->pdata->thread_nr - 1));
+
+ kfree(vm_map);
+}
+
static void cmdq_gctl_value_toggle(struct cmdq *cmdq, bool ddr_enable)
{
u32 val = cmdq->pdata->control_by_sw ? GCE_CTRL_BY_SW : 0;
@@ -156,6 +214,7 @@ static void cmdq_init(struct cmdq *cmdq)
WARN_ON(clk_bulk_enable(cmdq->pdata->gce_num, cmdq->clocks));
+ cmdq_vm_init(cmdq);
cmdq_gctl_value_toggle(cmdq, true);
writel(CMDQ_THR_ACTIVE_SLOT_CYCLES, cmdq->base + CMDQ_THR_SLOT_CYCLES);
@@ -782,6 +841,16 @@ static const struct gce_plat gce_plat_mt8195 = {
.gce_num = 2
};
+static const struct gce_plat gce_plat_mt8196 = {
+ .thread_nr = 32,
+ .shift = 3,
+ .mminfra_offset = SZ_2G,
+ .control_by_sw = true,
+ .sw_ddr_en = true,
+ .gce_vm = true,
+ .gce_num = 2
+};
+
static const struct of_device_id cmdq_of_ids[] = {
{.compatible = "mediatek,mt6779-gce", .data = (void *)&gce_plat_mt6779},
{.compatible = "mediatek,mt8173-gce", .data = (void *)&gce_plat_mt8173},
@@ -790,6 +859,7 @@ static const struct of_device_id cmdq_of_ids[] = {
{.compatible = "mediatek,mt8188-gce", .data = (void *)&gce_plat_mt8188},
{.compatible = "mediatek,mt8192-gce", .data = (void *)&gce_plat_mt8192},
{.compatible = "mediatek,mt8195-gce", .data = (void *)&gce_plat_mt8195},
+ {.compatible = "mediatek,mt8196-gce", .data = (void *)&gce_plat_mt8196},
{}
};
MODULE_DEVICE_TABLE(of, cmdq_of_ids);
diff --git a/drivers/memory/mtk-smi.c b/drivers/memory/mtk-smi.c
index 733e22f695ab..aaeba8ab211e 100644
--- a/drivers/memory/mtk-smi.c
+++ b/drivers/memory/mtk-smi.c
@@ -595,25 +595,28 @@ static int mtk_smi_device_link_common(struct device *dev, struct device **com_de
smi_com_pdev = of_find_device_by_node(smi_com_node);
of_node_put(smi_com_node);
- if (smi_com_pdev) {
- /* smi common is the supplier, Make sure it is ready before */
- if (!platform_get_drvdata(smi_com_pdev)) {
- put_device(&smi_com_pdev->dev);
- return -EPROBE_DEFER;
- }
- smi_com_dev = &smi_com_pdev->dev;
- link = device_link_add(dev, smi_com_dev,
- DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS);
- if (!link) {
- dev_err(dev, "Unable to link smi-common dev\n");
- put_device(&smi_com_pdev->dev);
- return -ENODEV;
- }
- *com_dev = smi_com_dev;
- } else {
+ if (!smi_com_pdev) {
dev_err(dev, "Failed to get the smi_common device\n");
return -EINVAL;
}
+
+ /* smi common is the supplier, Make sure it is ready before */
+ if (!platform_get_drvdata(smi_com_pdev)) {
+ put_device(&smi_com_pdev->dev);
+ return -EPROBE_DEFER;
+ }
+
+ smi_com_dev = &smi_com_pdev->dev;
+ link = device_link_add(dev, smi_com_dev,
+ DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS);
+ if (!link) {
+ dev_err(dev, "Unable to link smi-common dev\n");
+ put_device(&smi_com_pdev->dev);
+ return -ENODEV;
+ }
+
+ *com_dev = smi_com_dev;
+
return 0;
}
@@ -674,6 +677,7 @@ static int mtk_smi_larb_probe(struct platform_device *pdev)
err_pm_disable:
pm_runtime_disable(dev);
device_link_remove(dev, larb->smi_common_dev);
+ put_device(larb->smi_common_dev);
return ret;
}
@@ -684,6 +688,7 @@ static void mtk_smi_larb_remove(struct platform_device *pdev)
device_link_remove(&pdev->dev, larb->smi_common_dev);
pm_runtime_disable(&pdev->dev);
component_del(&pdev->dev, &mtk_smi_larb_component_ops);
+ put_device(larb->smi_common_dev);
}
static int __maybe_unused mtk_smi_larb_resume(struct device *dev)
@@ -917,6 +922,7 @@ static void mtk_smi_common_remove(struct platform_device *pdev)
if (common->plat->type == MTK_SMI_GEN2_SUB_COMM)
device_link_remove(&pdev->dev, common->smi_common_dev);
pm_runtime_disable(&pdev->dev);
+ put_device(common->smi_common_dev);
}
static int __maybe_unused mtk_smi_common_resume(struct device *dev)
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index e3816819dbfe..f374d8b212b8 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -157,6 +157,76 @@ const __be32 *of_irq_parse_imap_parent(const __be32 *imap, int len, struct of_ph
return imap;
}
+int of_imap_parser_init(struct of_imap_parser *parser, struct device_node *node,
+ struct of_imap_item *item)
+{
+ int imaplen;
+ u32 tmp;
+ int ret;
+
+ /*
+ * parent_offset is the offset where the parent part is starting.
+ * In other words, the offset where the parent interrupt controller
+ * phandle is present.
+ *
+ * Compute this offset (child #interrupt-cells + child #address-cells)
+ */
+ parser->parent_offset = of_bus_n_addr_cells(node);
+
+ ret = of_property_read_u32(node, "#interrupt-cells", &tmp);
+ if (ret)
+ return ret;
+
+ parser->parent_offset += tmp;
+
+ if (WARN(parser->parent_offset > ARRAY_SIZE(item->child_imap),
+ "child part size = %u, cannot fit in array of %zu items",
+ parser->parent_offset, ARRAY_SIZE(item->child_imap)))
+ return -EINVAL;
+
+ parser->imap = of_get_property(node, "interrupt-map", &imaplen);
+ if (!parser->imap)
+ return -ENOENT;
+
+ imaplen /= sizeof(*parser->imap);
+ parser->imap_end = parser->imap + imaplen;
+
+ memset(item, 0, sizeof(*item));
+ item->child_imap_count = parser->parent_offset;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_imap_parser_init);
+
+struct of_imap_item *of_imap_parser_one(struct of_imap_parser *parser,
+ struct of_imap_item *item)
+{
+ const __be32 *imap_parent, *imap_next;
+ int i;
+
+ /* Release previously get parent node */
+ of_node_put(item->parent_args.np);
+
+ if (parser->imap + parser->parent_offset + 1 >= parser->imap_end)
+ return NULL;
+
+ imap_parent = parser->imap + parser->parent_offset;
+
+ imap_next = of_irq_parse_imap_parent(imap_parent,
+ parser->imap_end - imap_parent,
+ &item->parent_args);
+ if (!imap_next)
+ return NULL;
+
+ for (i = 0; i < parser->parent_offset; i++)
+ item->child_imap[i] = be32_to_cpu(*(parser->imap + i));
+
+ parser->imap = imap_next;
+
+ return item;
+}
+EXPORT_SYMBOL_GPL(of_imap_parser_one);
+
/**
* of_irq_parse_raw - Low level interrupt tree parsing
* @addr: address specifier (start of "reg" property of the device) in be32 format
diff --git a/drivers/of/unittest-data/tests-interrupts.dtsi b/drivers/of/unittest-data/tests-interrupts.dtsi
index 4ccb54f91c30..974f888c9b15 100644
--- a/drivers/of/unittest-data/tests-interrupts.dtsi
+++ b/drivers/of/unittest-data/tests-interrupts.dtsi
@@ -50,6 +50,15 @@
interrupt-map = <0x5000 1 2 &test_intc0 15>;
};
+ intmap2 {
+ #interrupt-cells = <2>;
+ #address-cells = <0>;
+ interrupt-map = <1 11 &test_intc0 100>,
+ <2 22 &test_intc1 200 201 202>,
+ <3 33 &test_intc2 300 301>,
+ <4 44 &test_intc2 400 401>;
+ };
+
test_intc_intmap0: intc-intmap0 {
#interrupt-cells = <1>;
#address-cells = <1>;
diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c
index 3b773aaf9d05..7eccb5d9135f 100644
--- a/drivers/of/unittest.c
+++ b/drivers/of/unittest.c
@@ -1654,6 +1654,121 @@ static void __init of_unittest_parse_interrupts_extended(void)
of_node_put(np);
}
+struct of_unittest_expected_imap_item {
+ u32 child_imap_count;
+ u32 child_imap[2];
+ const char *parent_path;
+ int parent_args_count;
+ u32 parent_args[3];
+};
+
+static const struct of_unittest_expected_imap_item of_unittest_expected_imap_items[] = {
+ {
+ .child_imap_count = 2,
+ .child_imap = {1, 11},
+ .parent_path = "/testcase-data/interrupts/intc0",
+ .parent_args_count = 1,
+ .parent_args = {100},
+ }, {
+ .child_imap_count = 2,
+ .child_imap = {2, 22},
+ .parent_path = "/testcase-data/interrupts/intc1",
+ .parent_args_count = 3,
+ .parent_args = {200, 201, 202},
+ }, {
+ .child_imap_count = 2,
+ .child_imap = {3, 33},
+ .parent_path = "/testcase-data/interrupts/intc2",
+ .parent_args_count = 2,
+ .parent_args = {300, 301},
+ }, {
+ .child_imap_count = 2,
+ .child_imap = {4, 44},
+ .parent_path = "/testcase-data/interrupts/intc2",
+ .parent_args_count = 2,
+ .parent_args = {400, 401},
+ }
+};
+
+static void __init of_unittest_parse_interrupt_map(void)
+{
+ const struct of_unittest_expected_imap_item *expected_item;
+ struct device_node *imap_np, *expected_parent_np;
+ struct of_imap_parser imap_parser;
+ struct of_imap_item imap_item;
+ int count, ret, i;
+
+ if (of_irq_workarounds & (OF_IMAP_NO_PHANDLE | OF_IMAP_OLDWORLD_MAC))
+ return;
+
+ imap_np = of_find_node_by_path("/testcase-data/interrupts/intmap2");
+ if (!imap_np) {
+ pr_err("missing testcase data\n");
+ return;
+ }
+
+ ret = of_imap_parser_init(&imap_parser, imap_np, &imap_item);
+ if (unittest(!ret, "of_imap_parser_init(%pOF) returned error %d\n",
+ imap_np, ret))
+ goto end;
+
+ expected_item = of_unittest_expected_imap_items;
+ count = 0;
+
+ for_each_of_imap_item(&imap_parser, &imap_item) {
+ if (unittest(count < ARRAY_SIZE(of_unittest_expected_imap_items),
+ "imap item number %d not expected. Max number %zu\n",
+ count, ARRAY_SIZE(of_unittest_expected_imap_items) - 1)) {
+ of_node_put(imap_item.parent_args.np);
+ goto end;
+ }
+
+ expected_parent_np = of_find_node_by_path(expected_item->parent_path);
+ if (unittest(expected_parent_np,
+ "missing dependent testcase data (%s)\n",
+ expected_item->parent_path)) {
+ of_node_put(imap_item.parent_args.np);
+ goto end;
+ }
+
+ unittest(imap_item.child_imap_count == expected_item->child_imap_count,
+ "imap[%d] child_imap_count = %u, expected %u\n",
+ count, imap_item.child_imap_count,
+ expected_item->child_imap_count);
+
+ for (i = 0; i < expected_item->child_imap_count; i++)
+ unittest(imap_item.child_imap[i] == expected_item->child_imap[i],
+ "imap[%d] child_imap[%d] = %u, expected %u\n",
+ count, i, imap_item.child_imap[i],
+ expected_item->child_imap[i]);
+
+ unittest(imap_item.parent_args.np == expected_parent_np,
+ "imap[%d] parent np = %pOF, expected %pOF\n",
+ count, imap_item.parent_args.np, expected_parent_np);
+
+ unittest(imap_item.parent_args.args_count == expected_item->parent_args_count,
+ "imap[%d] parent param_count = %d, expected %d\n",
+ count, imap_item.parent_args.args_count,
+ expected_item->parent_args_count);
+
+ for (i = 0; i < expected_item->parent_args_count; i++)
+ unittest(imap_item.parent_args.args[i] == expected_item->parent_args[i],
+ "imap[%d] parent param[%d] = %u, expected %u\n",
+ count, i, imap_item.parent_args.args[i],
+ expected_item->parent_args[i]);
+
+ of_node_put(expected_parent_np);
+ count++;
+ expected_item++;
+ }
+
+ unittest(count == ARRAY_SIZE(of_unittest_expected_imap_items),
+ "Missing items. %d parsed, expected %zu\n",
+ count, ARRAY_SIZE(of_unittest_expected_imap_items));
+end:
+ of_node_put(imap_np);
+}
+
#if IS_ENABLED(CONFIG_OF_DYNAMIC)
static void __init of_unittest_irq_refcount(void)
{
@@ -4393,6 +4508,7 @@ static int __init of_unittest(void)
of_unittest_changeset_prop();
of_unittest_parse_interrupts();
of_unittest_parse_interrupts_extended();
+ of_unittest_parse_interrupt_map();
of_unittest_irq_refcount();
of_unittest_dma_get_max_cpu_address();
of_unittest_parse_dma_ranges();
diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c
index 52680ac99589..46204da046fa 100644
--- a/drivers/remoteproc/qcom_q6v5_pas.c
+++ b/drivers/remoteproc/qcom_q6v5_pas.c
@@ -11,6 +11,7 @@
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/interrupt.h>
+#include <linux/iommu.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -117,8 +118,8 @@ struct qcom_pas {
struct qcom_rproc_ssr ssr_subdev;
struct qcom_sysmon *sysmon;
- struct qcom_scm_pas_metadata pas_metadata;
- struct qcom_scm_pas_metadata dtb_pas_metadata;
+ struct qcom_scm_pas_context *pas_ctx;
+ struct qcom_scm_pas_context *dtb_pas_ctx;
};
static void qcom_pas_segment_dump(struct rproc *rproc,
@@ -211,9 +212,9 @@ static int qcom_pas_unprepare(struct rproc *rproc)
* auth_and_reset() was successful, but in other cases clean it up
* here.
*/
- qcom_scm_pas_metadata_release(&pas->pas_metadata);
+ qcom_scm_pas_metadata_release(pas->pas_ctx);
if (pas->dtb_pas_id)
- qcom_scm_pas_metadata_release(&pas->dtb_pas_metadata);
+ qcom_scm_pas_metadata_release(pas->dtb_pas_ctx);
return 0;
}
@@ -239,15 +240,9 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw)
return ret;
}
- ret = qcom_mdt_pas_init(pas->dev, pas->dtb_firmware, pas->dtb_firmware_name,
- pas->dtb_pas_id, pas->dtb_mem_phys,
- &pas->dtb_pas_metadata);
- if (ret)
- goto release_dtb_firmware;
-
- ret = qcom_mdt_load_no_init(pas->dev, pas->dtb_firmware, pas->dtb_firmware_name,
- pas->dtb_mem_region, pas->dtb_mem_phys,
- pas->dtb_mem_size, &pas->dtb_mem_reloc);
+ ret = qcom_mdt_pas_load(pas->dtb_pas_ctx, pas->dtb_firmware,
+ pas->dtb_firmware_name, pas->dtb_mem_region,
+ &pas->dtb_mem_reloc);
if (ret)
goto release_dtb_metadata;
}
@@ -255,14 +250,28 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw)
return 0;
release_dtb_metadata:
- qcom_scm_pas_metadata_release(&pas->dtb_pas_metadata);
-
-release_dtb_firmware:
+ qcom_scm_pas_metadata_release(pas->dtb_pas_ctx);
release_firmware(pas->dtb_firmware);
return ret;
}
+static void qcom_pas_unmap_carveout(struct rproc *rproc, phys_addr_t mem_phys, size_t size)
+{
+ if (rproc->has_iommu)
+ iommu_unmap(rproc->domain, mem_phys, size);
+}
+
+static int qcom_pas_map_carveout(struct rproc *rproc, phys_addr_t mem_phys, size_t size)
+{
+ int ret = 0;
+
+ if (rproc->has_iommu)
+ ret = iommu_map(rproc->domain, mem_phys, mem_phys, size,
+ IOMMU_READ | IOMMU_WRITE, GFP_KERNEL);
+ return ret;
+}
+
static int qcom_pas_start(struct rproc *rproc)
{
struct qcom_pas *pas = rproc->priv;
@@ -297,54 +306,62 @@ static int qcom_pas_start(struct rproc *rproc)
}
if (pas->dtb_pas_id) {
- ret = qcom_scm_pas_auth_and_reset(pas->dtb_pas_id);
+ ret = qcom_pas_map_carveout(rproc, pas->dtb_mem_phys, pas->dtb_mem_size);
+ if (ret)
+ goto disable_px_supply;
+
+ ret = qcom_scm_pas_prepare_and_auth_reset(pas->dtb_pas_ctx);
if (ret) {
dev_err(pas->dev,
"failed to authenticate dtb image and release reset\n");
- goto disable_px_supply;
+ goto unmap_dtb_carveout;
}
}
- ret = qcom_mdt_pas_init(pas->dev, pas->firmware, rproc->firmware, pas->pas_id,
- pas->mem_phys, &pas->pas_metadata);
- if (ret)
- goto disable_px_supply;
-
- ret = qcom_mdt_load_no_init(pas->dev, pas->firmware, rproc->firmware,
- pas->mem_region, pas->mem_phys, pas->mem_size,
- &pas->mem_reloc);
+ ret = qcom_mdt_pas_load(pas->pas_ctx, pas->firmware, rproc->firmware,
+ pas->mem_region, &pas->mem_reloc);
if (ret)
goto release_pas_metadata;
qcom_pil_info_store(pas->info_name, pas->mem_phys, pas->mem_size);
- ret = qcom_scm_pas_auth_and_reset(pas->pas_id);
+ ret = qcom_pas_map_carveout(rproc, pas->mem_phys, pas->mem_size);
+ if (ret)
+ goto release_pas_metadata;
+
+ ret = qcom_scm_pas_prepare_and_auth_reset(pas->pas_ctx);
if (ret) {
dev_err(pas->dev,
"failed to authenticate image and release reset\n");
- goto release_pas_metadata;
+ goto unmap_carveout;
}
ret = qcom_q6v5_wait_for_start(&pas->q6v5, msecs_to_jiffies(5000));
if (ret == -ETIMEDOUT) {
dev_err(pas->dev, "start timed out\n");
qcom_scm_pas_shutdown(pas->pas_id);
- goto release_pas_metadata;
+ goto unmap_carveout;
}
- qcom_scm_pas_metadata_release(&pas->pas_metadata);
+ qcom_scm_pas_metadata_release(pas->pas_ctx);
if (pas->dtb_pas_id)
- qcom_scm_pas_metadata_release(&pas->dtb_pas_metadata);
+ qcom_scm_pas_metadata_release(pas->dtb_pas_ctx);
/* firmware is used to pass reference from qcom_pas_start(), drop it now */
pas->firmware = NULL;
return 0;
+unmap_carveout:
+ qcom_pas_unmap_carveout(rproc, pas->mem_phys, pas->mem_size);
release_pas_metadata:
- qcom_scm_pas_metadata_release(&pas->pas_metadata);
+ qcom_scm_pas_metadata_release(pas->pas_ctx);
+ if (pas->dtb_pas_id)
+ qcom_scm_pas_metadata_release(pas->dtb_pas_ctx);
+
+unmap_dtb_carveout:
if (pas->dtb_pas_id)
- qcom_scm_pas_metadata_release(&pas->dtb_pas_metadata);
+ qcom_pas_unmap_carveout(rproc, pas->dtb_mem_phys, pas->dtb_mem_size);
disable_px_supply:
if (pas->px_supply)
regulator_disable(pas->px_supply);
@@ -400,8 +417,12 @@ static int qcom_pas_stop(struct rproc *rproc)
ret = qcom_scm_pas_shutdown(pas->dtb_pas_id);
if (ret)
dev_err(pas->dev, "failed to shutdown dtb: %d\n", ret);
+
+ qcom_pas_unmap_carveout(rproc, pas->dtb_mem_phys, pas->dtb_mem_size);
}
+ qcom_pas_unmap_carveout(rproc, pas->mem_phys, pas->mem_size);
+
handover = qcom_q6v5_unprepare(&pas->q6v5);
if (handover)
qcom_pas_handover(&pas->q6v5);
@@ -427,6 +448,61 @@ static void *qcom_pas_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is
return pas->mem_region + offset;
}
+static int qcom_pas_parse_firmware(struct rproc *rproc, const struct firmware *fw)
+{
+ struct qcom_pas *pas = rproc->priv;
+ struct resource_table *table = NULL;
+ size_t output_rt_size;
+ void *output_rt;
+ size_t table_sz;
+ int ret;
+
+ ret = qcom_register_dump_segments(rproc, fw);
+ if (ret) {
+ dev_err(pas->dev, "Error in registering dump segments\n");
+ return ret;
+ }
+
+ if (!rproc->has_iommu)
+ return 0;
+
+ ret = rproc_elf_load_rsc_table(rproc, fw);
+ if (ret)
+ dev_dbg(&rproc->dev, "Failed to load resource table from firmware\n");
+
+ table = rproc->table_ptr;
+ table_sz = rproc->table_sz;
+
+ /*
+ * The resources consumed by Qualcomm remote processors fall into two categories:
+ * static (such as the memory carveouts for the rproc firmware) and dynamic (like
+ * shared memory pools). Both are managed by a Qualcomm hypervisor (such as QHEE
+ * or Gunyah), if one is present. Otherwise, a resource table must be retrieved
+ * via an SCM call. That table will list all dynamic resources (if any) and possibly
+ * the static ones. The static resources may also come from a resource table embedded
+ * in the rproc firmware instead.
+ *
+ * Here, we call rproc_elf_load_rsc_table() to check firmware binary has resources
+ * or not and if it is not having then we pass NULL and zero as input resource
+ * table pointer and size respectively to the argument of qcom_scm_pas_get_rsc_table()
+ * and this is even true for Qualcomm remote processor who does follow remoteproc
+ * framework.
+ */
+ output_rt = qcom_scm_pas_get_rsc_table(pas->pas_ctx, table, table_sz, &output_rt_size);
+ ret = IS_ERR(output_rt) ? PTR_ERR(output_rt) : 0;
+ if (ret) {
+ dev_err(pas->dev, "Error in getting resource table: %d\n", ret);
+ return ret;
+ }
+
+ kfree(rproc->cached_table);
+ rproc->cached_table = output_rt;
+ rproc->table_ptr = rproc->cached_table;
+ rproc->table_sz = output_rt_size;
+
+ return ret;
+}
+
static unsigned long qcom_pas_panic(struct rproc *rproc)
{
struct qcom_pas *pas = rproc->priv;
@@ -439,7 +515,7 @@ static const struct rproc_ops qcom_pas_ops = {
.start = qcom_pas_start,
.stop = qcom_pas_stop,
.da_to_va = qcom_pas_da_to_va,
- .parse_fw = qcom_register_dump_segments,
+ .parse_fw = qcom_pas_parse_firmware,
.load = qcom_pas_load,
.panic = qcom_pas_panic,
};
@@ -449,7 +525,7 @@ static const struct rproc_ops qcom_pas_minidump_ops = {
.start = qcom_pas_start,
.stop = qcom_pas_stop,
.da_to_va = qcom_pas_da_to_va,
- .parse_fw = qcom_register_dump_segments,
+ .parse_fw = qcom_pas_parse_firmware,
.load = qcom_pas_load,
.panic = qcom_pas_panic,
.coredump = qcom_pas_minidump,
@@ -697,6 +773,7 @@ static int qcom_pas_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ rproc->has_iommu = of_property_present(pdev->dev.of_node, "iommus");
rproc->auto_boot = desc->auto_boot;
rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
@@ -760,6 +837,24 @@ static int qcom_pas_probe(struct platform_device *pdev)
}
qcom_add_ssr_subdev(rproc, &pas->ssr_subdev, desc->ssr_name);
+
+ pas->pas_ctx = devm_qcom_scm_pas_context_alloc(pas->dev, pas->pas_id,
+ pas->mem_phys, pas->mem_size);
+ if (IS_ERR(pas->pas_ctx)) {
+ ret = PTR_ERR(pas->pas_ctx);
+ goto remove_ssr_sysmon;
+ }
+
+ pas->dtb_pas_ctx = devm_qcom_scm_pas_context_alloc(pas->dev, pas->dtb_pas_id,
+ pas->dtb_mem_phys,
+ pas->dtb_mem_size);
+ if (IS_ERR(pas->dtb_pas_ctx)) {
+ ret = PTR_ERR(pas->dtb_pas_ctx);
+ goto remove_ssr_sysmon;
+ }
+
+ pas->pas_ctx->use_tzmem = rproc->has_iommu;
+ pas->dtb_pas_ctx->use_tzmem = rproc->has_iommu;
ret = rproc_add(rproc);
if (ret)
goto remove_ssr_sysmon;
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index 6e5d6deffa7d..7ce151f6a7e4 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -161,7 +161,7 @@ config RESET_K210
config RESET_K230
tristate "Reset controller driver for Canaan Kendryte K230 SoC"
depends on ARCH_CANAAN || COMPILE_TEST
- depends on OF
+ default ARCH_CANAAN
help
Support for the Canaan Kendryte K230 RISC-V SoC reset controller.
Say Y if you want to control reset signals provided by this
@@ -299,15 +299,6 @@ config RESET_SOCFPGA
This enables the reset driver for the SoCFPGA ARMv7 platforms. This
driver gets initialized early during platform init calls.
-config RESET_SPACEMIT
- tristate "SpacemiT reset driver"
- depends on ARCH_SPACEMIT || COMPILE_TEST
- select AUXILIARY_BUS
- default ARCH_SPACEMIT
- help
- This enables the reset controller driver for SpacemiT SoCs,
- including the K1.
-
config RESET_SUNPLUS
bool "Sunplus SoCs Reset Driver" if COMPILE_TEST
default ARCH_SUNPLUS
@@ -406,9 +397,10 @@ config RESET_ZYNQMP
This enables the reset controller driver for Xilinx ZynqMP SoCs.
source "drivers/reset/amlogic/Kconfig"
+source "drivers/reset/hisilicon/Kconfig"
+source "drivers/reset/spacemit/Kconfig"
source "drivers/reset/starfive/Kconfig"
source "drivers/reset/sti/Kconfig"
-source "drivers/reset/hisilicon/Kconfig"
source "drivers/reset/tegra/Kconfig"
endif
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 9c3e484dfd81..fc0cc99f8514 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -2,6 +2,7 @@
obj-y += core.o
obj-y += amlogic/
obj-y += hisilicon/
+obj-y += spacemit/
obj-y += starfive/
obj-y += sti/
obj-y += tegra/
@@ -38,7 +39,6 @@ obj-$(CONFIG_RESET_RZV2H_USB2PHY) += reset-rzv2h-usb2phy.o
obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
-obj-$(CONFIG_RESET_SPACEMIT) += reset-spacemit.o
obj-$(CONFIG_RESET_SUNPLUS) += reset-sunplus.o
obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o
obj-$(CONFIG_RESET_TH1520) += reset-th1520.o
diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index 0135dd0ae204..0666dfc41ca9 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -868,11 +868,11 @@ static int reset_add_gpio_aux_device(struct device *parent,
*/
static int __reset_add_reset_gpio_device(const struct of_phandle_args *args)
{
- struct property_entry properties[2] = { };
+ struct property_entry properties[3] = { };
unsigned int offset, of_flags, lflags;
struct reset_gpio_lookup *rgpio_dev;
struct device *parent;
- int id, ret;
+ int id, ret, prop = 0;
/*
* Currently only #gpio-cells=2 is supported with the meaning of:
@@ -923,7 +923,8 @@ static int __reset_add_reset_gpio_device(const struct of_phandle_args *args)
lflags = GPIO_PERSISTENT | (of_flags & GPIO_ACTIVE_LOW);
parent = gpio_device_to_device(gdev);
- properties[0] = PROPERTY_ENTRY_GPIO("reset-gpios", parent->fwnode, offset, lflags);
+ properties[prop++] = PROPERTY_ENTRY_STRING("compatible", "reset-gpio");
+ properties[prop++] = PROPERTY_ENTRY_GPIO("reset-gpios", parent->fwnode, offset, lflags);
id = ida_alloc(&reset_gpio_ida, GFP_KERNEL);
if (id < 0)
diff --git a/drivers/reset/reset-gpio.c b/drivers/reset/reset-gpio.c
index e5512b3b596b..0a1610d9e78a 100644
--- a/drivers/reset/reset-gpio.c
+++ b/drivers/reset/reset-gpio.c
@@ -22,9 +22,7 @@ static int reset_gpio_assert(struct reset_controller_dev *rc, unsigned long id)
{
struct reset_gpio_priv *priv = rc_to_reset_gpio(rc);
- gpiod_set_value_cansleep(priv->reset, 1);
-
- return 0;
+ return gpiod_set_value_cansleep(priv->reset, 1);
}
static int reset_gpio_deassert(struct reset_controller_dev *rc,
@@ -32,9 +30,7 @@ static int reset_gpio_deassert(struct reset_controller_dev *rc,
{
struct reset_gpio_priv *priv = rc_to_reset_gpio(rc);
- gpiod_set_value_cansleep(priv->reset, 0);
-
- return 0;
+ return gpiod_set_value_cansleep(priv->reset, 0);
}
static int reset_gpio_status(struct reset_controller_dev *rc, unsigned long id)
@@ -111,6 +107,7 @@ static struct auxiliary_driver reset_gpio_driver = {
.id_table = reset_gpio_ids,
.driver = {
.name = "reset-gpio",
+ .suppress_bind_attrs = true,
},
};
module_auxiliary_driver(reset_gpio_driver);
diff --git a/drivers/reset/reset-imx8mp-audiomix.c b/drivers/reset/reset-imx8mp-audiomix.c
index eceb37ff5dc5..b7fa3110f282 100644
--- a/drivers/reset/reset-imx8mp-audiomix.c
+++ b/drivers/reset/reset-imx8mp-audiomix.c
@@ -3,22 +3,23 @@
* Copyright 2024 NXP
*/
+#include <dt-bindings/reset/fsl,imx8ulp-sim-lpav.h>
#include <dt-bindings/reset/imx8mp-reset-audiomix.h>
#include <linux/auxiliary_bus.h>
+#include <linux/bits.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/regmap.h>
#include <linux/reset-controller.h>
#define IMX8MP_AUDIOMIX_EARC_RESET_OFFSET 0x200
-#define IMX8MP_AUDIOMIX_EARC_RESET_MASK BIT(0)
-#define IMX8MP_AUDIOMIX_EARC_PHY_RESET_MASK BIT(1)
-
#define IMX8MP_AUDIOMIX_DSP_RUNSTALL_OFFSET 0x108
-#define IMX8MP_AUDIOMIX_DSP_RUNSTALL_MASK BIT(5)
+
+#define IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET 0x8
struct imx8mp_reset_map {
unsigned int offset;
@@ -26,28 +27,76 @@ struct imx8mp_reset_map {
bool active_low;
};
-static const struct imx8mp_reset_map reset_map[] = {
+struct imx8mp_reset_info {
+ const struct imx8mp_reset_map *map;
+ int num_lines;
+};
+
+static const struct imx8mp_reset_map imx8mp_reset_map[] = {
[IMX8MP_AUDIOMIX_EARC_RESET] = {
.offset = IMX8MP_AUDIOMIX_EARC_RESET_OFFSET,
- .mask = IMX8MP_AUDIOMIX_EARC_RESET_MASK,
+ .mask = BIT(0),
.active_low = true,
},
[IMX8MP_AUDIOMIX_EARC_PHY_RESET] = {
.offset = IMX8MP_AUDIOMIX_EARC_RESET_OFFSET,
- .mask = IMX8MP_AUDIOMIX_EARC_PHY_RESET_MASK,
+ .mask = BIT(1),
.active_low = true,
},
[IMX8MP_AUDIOMIX_DSP_RUNSTALL] = {
.offset = IMX8MP_AUDIOMIX_DSP_RUNSTALL_OFFSET,
- .mask = IMX8MP_AUDIOMIX_DSP_RUNSTALL_MASK,
+ .mask = BIT(5),
.active_low = false,
},
};
+static const struct imx8mp_reset_info imx8mp_reset_info = {
+ .map = imx8mp_reset_map,
+ .num_lines = ARRAY_SIZE(imx8mp_reset_map),
+};
+
+static const struct imx8mp_reset_map imx8ulp_reset_map[] = {
+ [IMX8ULP_SIM_LPAV_HIFI4_DSP_DBG_RST] = {
+ .offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET,
+ .mask = BIT(25),
+ .active_low = false,
+ },
+ [IMX8ULP_SIM_LPAV_HIFI4_DSP_RST] = {
+ .offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET,
+ .mask = BIT(16),
+ .active_low = false,
+ },
+ [IMX8ULP_SIM_LPAV_HIFI4_DSP_STALL] = {
+ .offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET,
+ .mask = BIT(13),
+ .active_low = false,
+ },
+ [IMX8ULP_SIM_LPAV_DSI_RST_BYTE_N] = {
+ .offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET,
+ .mask = BIT(5),
+ .active_low = true,
+ },
+ [IMX8ULP_SIM_LPAV_DSI_RST_ESC_N] = {
+ .offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET,
+ .mask = BIT(4),
+ .active_low = true,
+ },
+ [IMX8ULP_SIM_LPAV_DSI_RST_DPI_N] = {
+ .offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET,
+ .mask = BIT(3),
+ .active_low = true,
+ },
+};
+
+static const struct imx8mp_reset_info imx8ulp_reset_info = {
+ .map = imx8ulp_reset_map,
+ .num_lines = ARRAY_SIZE(imx8ulp_reset_map),
+};
+
struct imx8mp_audiomix_reset {
struct reset_controller_dev rcdev;
- spinlock_t lock; /* protect register read-modify-write cycle */
- void __iomem *base;
+ struct regmap *regmap;
+ const struct imx8mp_reset_map *map;
};
static struct imx8mp_audiomix_reset *to_imx8mp_audiomix_reset(struct reset_controller_dev *rcdev)
@@ -59,26 +108,15 @@ static int imx8mp_audiomix_update(struct reset_controller_dev *rcdev,
unsigned long id, bool assert)
{
struct imx8mp_audiomix_reset *priv = to_imx8mp_audiomix_reset(rcdev);
- void __iomem *reg_addr = priv->base;
- unsigned int mask, offset, active_low;
- unsigned long reg, flags;
+ const struct imx8mp_reset_map *reset_map = priv->map;
+ unsigned int mask, offset, active_low, val;
mask = reset_map[id].mask;
offset = reset_map[id].offset;
active_low = reset_map[id].active_low;
+ val = (active_low ^ assert) ? mask : ~mask;
- spin_lock_irqsave(&priv->lock, flags);
-
- reg = readl(reg_addr + offset);
- if (active_low ^ assert)
- reg |= mask;
- else
- reg &= ~mask;
- writel(reg, reg_addr + offset);
-
- spin_unlock_irqrestore(&priv->lock, flags);
-
- return 0;
+ return regmap_update_bits(priv->regmap, offset, mask, val);
}
static int imx8mp_audiomix_reset_assert(struct reset_controller_dev *rcdev,
@@ -98,52 +136,96 @@ static const struct reset_control_ops imx8mp_audiomix_reset_ops = {
.deassert = imx8mp_audiomix_reset_deassert,
};
+static const struct regmap_config regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+/* assumption: registered only if not using parent regmap */
+static void imx8mp_audiomix_reset_iounmap(void *data)
+{
+ void __iomem *base = (void __iomem *)data;
+
+ iounmap(base);
+}
+
+static int imx8mp_audiomix_reset_get_regmap(struct imx8mp_audiomix_reset *priv)
+{
+ void __iomem *base;
+ struct device *dev;
+ int ret;
+
+ dev = priv->rcdev.dev;
+
+ /* try to use the parent's regmap */
+ priv->regmap = dev_get_regmap(dev->parent, NULL);
+ if (priv->regmap)
+ return 0;
+
+ /* ... if that's not possible then initialize the regmap right now */
+ base = of_iomap(dev->parent->of_node, 0);
+ if (!base)
+ return dev_err_probe(dev, -ENOMEM, "failed to iomap address space\n");
+
+ ret = devm_add_action_or_reset(dev,
+ imx8mp_audiomix_reset_iounmap,
+ (void __force *)base);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register action\n");
+
+ priv->regmap = devm_regmap_init_mmio(dev, base, &regmap_config);
+ if (IS_ERR(priv->regmap))
+ return dev_err_probe(dev, PTR_ERR(priv->regmap),
+ "failed to initialize regmap\n");
+
+ return 0;
+}
+
static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev,
const struct auxiliary_device_id *id)
{
+ const struct imx8mp_reset_info *rinfo;
struct imx8mp_audiomix_reset *priv;
struct device *dev = &adev->dev;
int ret;
+ rinfo = (void *)id->driver_data;
+
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
- spin_lock_init(&priv->lock);
-
priv->rcdev.owner = THIS_MODULE;
- priv->rcdev.nr_resets = ARRAY_SIZE(reset_map);
+ priv->map = rinfo->map;
+ priv->rcdev.nr_resets = rinfo->num_lines;
priv->rcdev.ops = &imx8mp_audiomix_reset_ops;
priv->rcdev.of_node = dev->parent->of_node;
priv->rcdev.dev = dev;
priv->rcdev.of_reset_n_cells = 1;
- priv->base = of_iomap(dev->parent->of_node, 0);
- if (!priv->base)
- return -ENOMEM;
dev_set_drvdata(dev, priv);
+ ret = imx8mp_audiomix_reset_get_regmap(priv);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get regmap\n");
+
ret = devm_reset_controller_register(dev, &priv->rcdev);
if (ret)
- goto out_unmap;
+ return dev_err_probe(dev, ret,
+ "failed to register reset controller\n");
return 0;
-
-out_unmap:
- iounmap(priv->base);
- return ret;
-}
-
-static void imx8mp_audiomix_reset_remove(struct auxiliary_device *adev)
-{
- struct imx8mp_audiomix_reset *priv = dev_get_drvdata(&adev->dev);
-
- iounmap(priv->base);
}
static const struct auxiliary_device_id imx8mp_audiomix_reset_ids[] = {
{
.name = "clk_imx8mp_audiomix.reset",
+ .driver_data = (kernel_ulong_t)&imx8mp_reset_info,
+ },
+ {
+ .name = "clk_imx8ulp_sim_lpav.reset",
+ .driver_data = (kernel_ulong_t)&imx8ulp_reset_info,
},
{ }
};
@@ -151,7 +233,6 @@ MODULE_DEVICE_TABLE(auxiliary, imx8mp_audiomix_reset_ids);
static struct auxiliary_driver imx8mp_audiomix_reset_driver = {
.probe = imx8mp_audiomix_reset_probe,
- .remove = imx8mp_audiomix_reset_remove,
.id_table = imx8mp_audiomix_reset_ids,
};
diff --git a/drivers/reset/reset-rzg2l-usbphy-ctrl.c b/drivers/reset/reset-rzg2l-usbphy-ctrl.c
index 4ecb9acb2641..32bc268c9149 100644
--- a/drivers/reset/reset-rzg2l-usbphy-ctrl.c
+++ b/drivers/reset/reset-rzg2l-usbphy-ctrl.c
@@ -36,6 +36,7 @@ struct rzg2l_usbphy_ctrl_priv {
struct reset_control *rstc;
void __iomem *base;
struct platform_device *vdev;
+ struct regmap_field *pwrrdy;
spinlock_t lock;
};
@@ -92,6 +93,19 @@ static int rzg2l_usbphy_ctrl_status(struct reset_controller_dev *rcdev,
return !!(readl(priv->base + RESET) & port_mask);
}
+/* put pll and phy into reset state */
+static void rzg2l_usbphy_ctrl_init(struct rzg2l_usbphy_ctrl_priv *priv)
+{
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ val = readl(priv->base + RESET);
+ val |= RESET_SEL_PLLRESET | RESET_PLLRESET | PHY_RESET_PORT2 | PHY_RESET_PORT1;
+ writel(val, priv->base + RESET);
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
#define RZG2L_USBPHY_CTRL_PWRRDY 1
static const struct of_device_id rzg2l_usbphy_ctrl_match_table[] = {
@@ -117,13 +131,13 @@ static const struct regmap_config rzg2l_usb_regconf = {
.max_register = 1,
};
-static void rzg2l_usbphy_ctrl_set_pwrrdy(struct regmap_field *pwrrdy,
- bool power_on)
+static int rzg2l_usbphy_ctrl_set_pwrrdy(struct regmap_field *pwrrdy,
+ bool power_on)
{
u32 val = power_on ? 0 : 1;
/* The initialization path guarantees that the mask is 1 bit long. */
- regmap_field_update_bits(pwrrdy, 1, val);
+ return regmap_field_update_bits(pwrrdy, 1, val);
}
static void rzg2l_usbphy_ctrl_pwrrdy_off(void *data)
@@ -131,13 +145,14 @@ static void rzg2l_usbphy_ctrl_pwrrdy_off(void *data)
rzg2l_usbphy_ctrl_set_pwrrdy(data, false);
}
-static int rzg2l_usbphy_ctrl_pwrrdy_init(struct device *dev)
+static int rzg2l_usbphy_ctrl_pwrrdy_init(struct device *dev,
+ struct rzg2l_usbphy_ctrl_priv *priv)
{
- struct regmap_field *pwrrdy;
struct reg_field field;
struct regmap *regmap;
const int *data;
u32 args[2];
+ int ret;
data = device_get_match_data(dev);
if ((uintptr_t)data != RZG2L_USBPHY_CTRL_PWRRDY)
@@ -157,13 +172,15 @@ static int rzg2l_usbphy_ctrl_pwrrdy_init(struct device *dev)
field.lsb = __ffs(args[1]);
field.msb = __fls(args[1]);
- pwrrdy = devm_regmap_field_alloc(dev, regmap, field);
- if (IS_ERR(pwrrdy))
- return PTR_ERR(pwrrdy);
+ priv->pwrrdy = devm_regmap_field_alloc(dev, regmap, field);
+ if (IS_ERR(priv->pwrrdy))
+ return PTR_ERR(priv->pwrrdy);
- rzg2l_usbphy_ctrl_set_pwrrdy(pwrrdy, true);
+ ret = rzg2l_usbphy_ctrl_set_pwrrdy(priv->pwrrdy, true);
+ if (ret)
+ return ret;
- return devm_add_action_or_reset(dev, rzg2l_usbphy_ctrl_pwrrdy_off, pwrrdy);
+ return devm_add_action_or_reset(dev, rzg2l_usbphy_ctrl_pwrrdy_off, priv->pwrrdy);
}
static int rzg2l_usbphy_ctrl_probe(struct platform_device *pdev)
@@ -172,9 +189,7 @@ static int rzg2l_usbphy_ctrl_probe(struct platform_device *pdev)
struct rzg2l_usbphy_ctrl_priv *priv;
struct platform_device *vdev;
struct regmap *regmap;
- unsigned long flags;
int error;
- u32 val;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -188,7 +203,7 @@ static int rzg2l_usbphy_ctrl_probe(struct platform_device *pdev)
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- error = rzg2l_usbphy_ctrl_pwrrdy_init(dev);
+ error = rzg2l_usbphy_ctrl_pwrrdy_init(dev, priv);
if (error)
return error;
@@ -211,12 +226,7 @@ static int rzg2l_usbphy_ctrl_probe(struct platform_device *pdev)
goto err_pm_disable_reset_deassert;
}
- /* put pll and phy into reset state */
- spin_lock_irqsave(&priv->lock, flags);
- val = readl(priv->base + RESET);
- val |= RESET_SEL_PLLRESET | RESET_PLLRESET | PHY_RESET_PORT2 | PHY_RESET_PORT1;
- writel(val, priv->base + RESET);
- spin_unlock_irqrestore(&priv->lock, flags);
+ rzg2l_usbphy_ctrl_init(priv);
priv->rcdev.ops = &rzg2l_usbphy_ctrl_reset_ops;
priv->rcdev.of_reset_n_cells = 1;
@@ -263,10 +273,72 @@ static void rzg2l_usbphy_ctrl_remove(struct platform_device *pdev)
reset_control_assert(priv->rstc);
}
+static int rzg2l_usbphy_ctrl_suspend(struct device *dev)
+{
+ struct rzg2l_usbphy_ctrl_priv *priv = dev_get_drvdata(dev);
+ u32 val;
+ int ret;
+
+ val = readl(priv->base + RESET);
+ if (!(val & PHY_RESET_PORT2) || !(val & PHY_RESET_PORT1))
+ WARN(1, "Suspend with resets de-asserted\n");
+
+ pm_runtime_put_sync(dev);
+
+ ret = reset_control_assert(priv->rstc);
+ if (ret)
+ goto rpm_resume;
+
+ ret = rzg2l_usbphy_ctrl_set_pwrrdy(priv->pwrrdy, false);
+ if (ret)
+ goto reset_deassert;
+
+ return 0;
+
+reset_deassert:
+ reset_control_deassert(priv->rstc);
+rpm_resume:
+ pm_runtime_resume_and_get(dev);
+ return ret;
+}
+
+static int rzg2l_usbphy_ctrl_resume(struct device *dev)
+{
+ struct rzg2l_usbphy_ctrl_priv *priv = dev_get_drvdata(dev);
+ int ret;
+
+ ret = rzg2l_usbphy_ctrl_set_pwrrdy(priv->pwrrdy, true);
+ if (ret)
+ return ret;
+
+ ret = reset_control_deassert(priv->rstc);
+ if (ret)
+ goto pwrrdy_off;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ goto reset_assert;
+
+ rzg2l_usbphy_ctrl_init(priv);
+
+ return 0;
+
+reset_assert:
+ reset_control_assert(priv->rstc);
+pwrrdy_off:
+ rzg2l_usbphy_ctrl_set_pwrrdy(priv->pwrrdy, false);
+ return ret;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(rzg2l_usbphy_ctrl_pm_ops,
+ rzg2l_usbphy_ctrl_suspend,
+ rzg2l_usbphy_ctrl_resume);
+
static struct platform_driver rzg2l_usbphy_ctrl_driver = {
.driver = {
.name = "rzg2l_usbphy_ctrl",
.of_match_table = rzg2l_usbphy_ctrl_match_table,
+ .pm = pm_ptr(&rzg2l_usbphy_ctrl_pm_ops),
},
.probe = rzg2l_usbphy_ctrl_probe,
.remove = rzg2l_usbphy_ctrl_remove,
diff --git a/drivers/reset/spacemit/Kconfig b/drivers/reset/spacemit/Kconfig
new file mode 100644
index 000000000000..545d6b41c6ca
--- /dev/null
+++ b/drivers/reset/spacemit/Kconfig
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menu "Reset support for SpacemiT platforms"
+ depends on ARCH_SPACEMIT || COMPILE_TEST
+
+config RESET_SPACEMIT_COMMON
+ tristate
+ select AUXILIARY_BUS
+ help
+ Common reset controller infrastructure for SpacemiT SoCs.
+ This provides shared code and helper functions used by
+ reset drivers for various SpacemiT SoC families.
+
+config RESET_SPACEMIT_K1
+ tristate "Support for SpacemiT K1 SoC"
+ depends on SPACEMIT_K1_CCU
+ select RESET_SPACEMIT_COMMON
+ default SPACEMIT_K1_CCU
+ help
+ Support for reset controller in SpacemiT K1 SoC.
+ This driver works with the SpacemiT K1 clock controller
+ unit (CCU) driver to provide reset control functionality
+ for various peripherals and subsystems in the SoC.
+
+config RESET_SPACEMIT_K3
+ tristate "Support for SpacemiT K3 SoC"
+ depends on SPACEMIT_K3_CCU
+ select RESET_SPACEMIT_COMMON
+ default SPACEMIT_K3_CCU
+ help
+ Support for reset controller in SpacemiT K3 SoC.
+ This driver works with the SpacemiT K3 clock controller
+ unit (CCU) driver to provide reset control functionality
+ for various peripherals and subsystems in the SoC.
+
+endmenu
diff --git a/drivers/reset/spacemit/Makefile b/drivers/reset/spacemit/Makefile
new file mode 100644
index 000000000000..00669132c6ac
--- /dev/null
+++ b/drivers/reset/spacemit/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_RESET_SPACEMIT_COMMON) += reset-spacemit-common.o
+
+obj-$(CONFIG_RESET_SPACEMIT_K1) += reset-spacemit-k1.o
+obj-$(CONFIG_RESET_SPACEMIT_K3) += reset-spacemit-k3.o
diff --git a/drivers/reset/spacemit/reset-spacemit-common.c b/drivers/reset/spacemit/reset-spacemit-common.c
new file mode 100644
index 000000000000..0626633a5e7d
--- /dev/null
+++ b/drivers/reset/spacemit/reset-spacemit-common.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/* SpacemiT reset controller driver - common implementation */
+
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/module.h>
+
+#include <soc/spacemit/ccu.h>
+
+#include "reset-spacemit-common.h"
+
+static int spacemit_reset_update(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct ccu_reset_controller *controller;
+ const struct ccu_reset_data *data;
+ u32 mask;
+ u32 val;
+
+ controller = container_of(rcdev, struct ccu_reset_controller, rcdev);
+ data = &controller->data->reset_data[id];
+ mask = data->assert_mask | data->deassert_mask;
+ val = assert ? data->assert_mask : data->deassert_mask;
+
+ return regmap_update_bits(controller->regmap, data->offset, mask, val);
+}
+
+static int spacemit_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return spacemit_reset_update(rcdev, id, true);
+}
+
+static int spacemit_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return spacemit_reset_update(rcdev, id, false);
+}
+
+static const struct reset_control_ops spacemit_reset_control_ops = {
+ .assert = spacemit_reset_assert,
+ .deassert = spacemit_reset_deassert,
+};
+
+static int spacemit_reset_controller_register(struct device *dev,
+ struct ccu_reset_controller *controller)
+{
+ struct reset_controller_dev *rcdev = &controller->rcdev;
+
+ rcdev->ops = &spacemit_reset_control_ops;
+ rcdev->owner = dev->driver->owner;
+ rcdev->of_node = dev->of_node;
+ rcdev->nr_resets = controller->data->count;
+
+ return devm_reset_controller_register(dev, &controller->rcdev);
+}
+
+int spacemit_reset_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct spacemit_ccu_adev *rdev = to_spacemit_ccu_adev(adev);
+ struct ccu_reset_controller *controller;
+ struct device *dev = &adev->dev;
+
+ controller = devm_kzalloc(dev, sizeof(*controller), GFP_KERNEL);
+ if (!controller)
+ return -ENOMEM;
+ controller->data = (const struct ccu_reset_controller_data *)id->driver_data;
+ controller->regmap = rdev->regmap;
+
+ return spacemit_reset_controller_register(dev, controller);
+}
+EXPORT_SYMBOL_NS_GPL(spacemit_reset_probe, "RESET_SPACEMIT");
+
+MODULE_DESCRIPTION("SpacemiT reset controller driver - common code");
+MODULE_LICENSE("GPL");
diff --git a/drivers/reset/spacemit/reset-spacemit-common.h b/drivers/reset/spacemit/reset-spacemit-common.h
new file mode 100644
index 000000000000..ffaf2f86eb39
--- /dev/null
+++ b/drivers/reset/spacemit/reset-spacemit-common.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * SpacemiT reset controller driver - common definitions
+ */
+
+#ifndef _RESET_SPACEMIT_COMMON_H_
+#define _RESET_SPACEMIT_COMMON_H_
+
+#include <linux/auxiliary_bus.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+#include <linux/types.h>
+
+struct ccu_reset_data {
+ u32 offset;
+ u32 assert_mask;
+ u32 deassert_mask;
+};
+
+struct ccu_reset_controller_data {
+ const struct ccu_reset_data *reset_data; /* array */
+ size_t count;
+};
+
+struct ccu_reset_controller {
+ struct reset_controller_dev rcdev;
+ const struct ccu_reset_controller_data *data;
+ struct regmap *regmap;
+};
+
+#define RESET_DATA(_offset, _assert_mask, _deassert_mask) \
+ { \
+ .offset = (_offset), \
+ .assert_mask = (_assert_mask), \
+ .deassert_mask = (_deassert_mask), \
+ }
+
+/* Common probe function */
+int spacemit_reset_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id);
+
+#endif /* _RESET_SPACEMIT_COMMON_H_ */
diff --git a/drivers/reset/reset-spacemit.c b/drivers/reset/spacemit/reset-spacemit-k1.c
index e1272aff28f7..8f3b5329ea5f 100644
--- a/drivers/reset/reset-spacemit.c
+++ b/drivers/reset/spacemit/reset-spacemit-k1.c
@@ -1,41 +1,13 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* SpacemiT reset controller driver */
+/* SpacemiT K1 reset controller driver */
-#include <linux/auxiliary_bus.h>
-#include <linux/container_of.h>
-#include <linux/device.h>
#include <linux/module.h>
-#include <linux/regmap.h>
-#include <linux/reset-controller.h>
-#include <linux/types.h>
-#include <soc/spacemit/k1-syscon.h>
#include <dt-bindings/clock/spacemit,k1-syscon.h>
+#include <soc/spacemit/k1-syscon.h>
-struct ccu_reset_data {
- u32 offset;
- u32 assert_mask;
- u32 deassert_mask;
-};
-
-struct ccu_reset_controller_data {
- const struct ccu_reset_data *reset_data; /* array */
- size_t count;
-};
-
-struct ccu_reset_controller {
- struct reset_controller_dev rcdev;
- const struct ccu_reset_controller_data *data;
- struct regmap *regmap;
-};
-
-#define RESET_DATA(_offset, _assert_mask, _deassert_mask) \
- { \
- .offset = (_offset), \
- .assert_mask = (_assert_mask), \
- .deassert_mask = (_deassert_mask), \
- }
+#include "reset-spacemit-common.h"
static const struct ccu_reset_data k1_mpmu_resets[] = {
[RESET_WDT] = RESET_DATA(MPMU_WDTPCR, BIT(2), 0),
@@ -214,91 +186,30 @@ static const struct ccu_reset_controller_data k1_apbc2_reset_data = {
.count = ARRAY_SIZE(k1_apbc2_resets),
};
-static int spacemit_reset_update(struct reset_controller_dev *rcdev,
- unsigned long id, bool assert)
-{
- struct ccu_reset_controller *controller;
- const struct ccu_reset_data *data;
- u32 mask;
- u32 val;
-
- controller = container_of(rcdev, struct ccu_reset_controller, rcdev);
- data = &controller->data->reset_data[id];
- mask = data->assert_mask | data->deassert_mask;
- val = assert ? data->assert_mask : data->deassert_mask;
-
- return regmap_update_bits(controller->regmap, data->offset, mask, val);
-}
-
-static int spacemit_reset_assert(struct reset_controller_dev *rcdev,
- unsigned long id)
-{
- return spacemit_reset_update(rcdev, id, true);
-}
-
-static int spacemit_reset_deassert(struct reset_controller_dev *rcdev,
- unsigned long id)
-{
- return spacemit_reset_update(rcdev, id, false);
-}
-
-static const struct reset_control_ops spacemit_reset_control_ops = {
- .assert = spacemit_reset_assert,
- .deassert = spacemit_reset_deassert,
-};
-
-static int spacemit_reset_controller_register(struct device *dev,
- struct ccu_reset_controller *controller)
-{
- struct reset_controller_dev *rcdev = &controller->rcdev;
-
- rcdev->ops = &spacemit_reset_control_ops;
- rcdev->owner = THIS_MODULE;
- rcdev->of_node = dev->of_node;
- rcdev->nr_resets = controller->data->count;
-
- return devm_reset_controller_register(dev, &controller->rcdev);
-}
-
-static int spacemit_reset_probe(struct auxiliary_device *adev,
- const struct auxiliary_device_id *id)
-{
- struct spacemit_ccu_adev *rdev = to_spacemit_ccu_adev(adev);
- struct ccu_reset_controller *controller;
- struct device *dev = &adev->dev;
-
- controller = devm_kzalloc(dev, sizeof(*controller), GFP_KERNEL);
- if (!controller)
- return -ENOMEM;
- controller->data = (const struct ccu_reset_controller_data *)id->driver_data;
- controller->regmap = rdev->regmap;
-
- return spacemit_reset_controller_register(dev, controller);
-}
-
#define K1_AUX_DEV_ID(_unit) \
{ \
- .name = "spacemit_ccu_k1." #_unit "-reset", \
+ .name = "spacemit_ccu.k1-" #_unit "-reset", \
.driver_data = (kernel_ulong_t)&k1_ ## _unit ## _reset_data, \
}
-static const struct auxiliary_device_id spacemit_reset_ids[] = {
+static const struct auxiliary_device_id spacemit_k1_reset_ids[] = {
K1_AUX_DEV_ID(mpmu),
K1_AUX_DEV_ID(apbc),
K1_AUX_DEV_ID(apmu),
K1_AUX_DEV_ID(rcpu),
K1_AUX_DEV_ID(rcpu2),
K1_AUX_DEV_ID(apbc2),
- { },
+ { /* sentinel */ }
};
-MODULE_DEVICE_TABLE(auxiliary, spacemit_reset_ids);
+MODULE_DEVICE_TABLE(auxiliary, spacemit_k1_reset_ids);
static struct auxiliary_driver spacemit_k1_reset_driver = {
.probe = spacemit_reset_probe,
- .id_table = spacemit_reset_ids,
+ .id_table = spacemit_k1_reset_ids,
};
module_auxiliary_driver(spacemit_k1_reset_driver);
+MODULE_IMPORT_NS("RESET_SPACEMIT");
MODULE_AUTHOR("Alex Elder <elder@kernel.org>");
-MODULE_DESCRIPTION("SpacemiT reset controller driver");
+MODULE_DESCRIPTION("SpacemiT K1 reset controller driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/reset/spacemit/reset-spacemit-k3.c b/drivers/reset/spacemit/reset-spacemit-k3.c
new file mode 100644
index 000000000000..e9e32e4c1ba5
--- /dev/null
+++ b/drivers/reset/spacemit/reset-spacemit-k3.c
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/* SpacemiT K3 reset controller driver */
+
+#include <linux/module.h>
+
+#include <dt-bindings/reset/spacemit,k3-resets.h>
+#include <soc/spacemit/k3-syscon.h>
+
+#include "reset-spacemit-common.h"
+
+static const struct ccu_reset_data k3_mpmu_resets[] = {
+ [RESET_MPMU_WDT] = RESET_DATA(MPMU_WDTPCR, BIT(2), 0),
+ [RESET_MPMU_RIPC] = RESET_DATA(MPMU_RIPCCR, BIT(2), 0),
+};
+
+static const struct ccu_reset_controller_data k3_mpmu_reset_data = {
+ .reset_data = k3_mpmu_resets,
+ .count = ARRAY_SIZE(k3_mpmu_resets),
+};
+
+static const struct ccu_reset_data k3_apbc_resets[] = {
+ [RESET_APBC_UART0] = RESET_DATA(APBC_UART0_CLK_RST, BIT(2), 0),
+ [RESET_APBC_UART2] = RESET_DATA(APBC_UART2_CLK_RST, BIT(2), 0),
+ [RESET_APBC_UART3] = RESET_DATA(APBC_UART3_CLK_RST, BIT(2), 0),
+ [RESET_APBC_UART4] = RESET_DATA(APBC_UART4_CLK_RST, BIT(2), 0),
+ [RESET_APBC_UART5] = RESET_DATA(APBC_UART5_CLK_RST, BIT(2), 0),
+ [RESET_APBC_UART6] = RESET_DATA(APBC_UART6_CLK_RST, BIT(2), 0),
+ [RESET_APBC_UART7] = RESET_DATA(APBC_UART7_CLK_RST, BIT(2), 0),
+ [RESET_APBC_UART8] = RESET_DATA(APBC_UART8_CLK_RST, BIT(2), 0),
+ [RESET_APBC_UART9] = RESET_DATA(APBC_UART9_CLK_RST, BIT(2), 0),
+ [RESET_APBC_UART10] = RESET_DATA(APBC_UART10_CLK_RST, BIT(2), 0),
+ [RESET_APBC_GPIO] = RESET_DATA(APBC_GPIO_CLK_RST, BIT(2), 0),
+ [RESET_APBC_PWM0] = RESET_DATA(APBC_PWM0_CLK_RST, BIT(2), 0),
+ [RESET_APBC_PWM1] = RESET_DATA(APBC_PWM1_CLK_RST, BIT(2), 0),
+ [RESET_APBC_PWM2] = RESET_DATA(APBC_PWM2_CLK_RST, BIT(2), 0),
+ [RESET_APBC_PWM3] = RESET_DATA(APBC_PWM3_CLK_RST, BIT(2), 0),
+ [RESET_APBC_PWM4] = RESET_DATA(APBC_PWM4_CLK_RST, BIT(2), 0),
+ [RESET_APBC_PWM5] = RESET_DATA(APBC_PWM5_CLK_RST, BIT(2), 0),
+ [RESET_APBC_PWM6] = RESET_DATA(APBC_PWM6_CLK_RST, BIT(2), 0),
+ [RESET_APBC_PWM7] = RESET_DATA(APBC_PWM7_CLK_RST, BIT(2), 0),
+ [RESET_APBC_PWM8] = RESET_DATA(APBC_PWM8_CLK_RST, BIT(2), 0),
+ [RESET_APBC_PWM9] = RESET_DATA(APBC_PWM9_CLK_RST, BIT(2), 0),
+ [RESET_APBC_PWM10] = RESET_DATA(APBC_PWM10_CLK_RST, BIT(2), 0),
+ [RESET_APBC_PWM11] = RESET_DATA(APBC_PWM11_CLK_RST, BIT(2), 0),
+ [RESET_APBC_PWM12] = RESET_DATA(APBC_PWM12_CLK_RST, BIT(2), 0),
+ [RESET_APBC_PWM13] = RESET_DATA(APBC_PWM13_CLK_RST, BIT(2), 0),
+ [RESET_APBC_PWM14] = RESET_DATA(APBC_PWM14_CLK_RST, BIT(2), 0),
+ [RESET_APBC_PWM15] = RESET_DATA(APBC_PWM15_CLK_RST, BIT(2), 0),
+ [RESET_APBC_PWM16] = RESET_DATA(APBC_PWM16_CLK_RST, BIT(2), 0),
+ [RESET_APBC_PWM17] = RESET_DATA(APBC_PWM17_CLK_RST, BIT(2), 0),
+ [RESET_APBC_PWM18] = RESET_DATA(APBC_PWM18_CLK_RST, BIT(2), 0),
+ [RESET_APBC_PWM19] = RESET_DATA(APBC_PWM19_CLK_RST, BIT(2), 0),
+ [RESET_APBC_SPI0] = RESET_DATA(APBC_SSP0_CLK_RST, BIT(2), 0),
+ [RESET_APBC_SPI1] = RESET_DATA(APBC_SSP1_CLK_RST, BIT(2), 0),
+ [RESET_APBC_SPI3] = RESET_DATA(APBC_SSP3_CLK_RST, BIT(2), 0),
+ [RESET_APBC_RTC] = RESET_DATA(APBC_RTC_CLK_RST, BIT(2), 0),
+ [RESET_APBC_TWSI0] = RESET_DATA(APBC_TWSI0_CLK_RST, BIT(2), 0),
+ [RESET_APBC_TWSI1] = RESET_DATA(APBC_TWSI1_CLK_RST, BIT(2), 0),
+ [RESET_APBC_TWSI2] = RESET_DATA(APBC_TWSI2_CLK_RST, BIT(2), 0),
+ [RESET_APBC_TWSI4] = RESET_DATA(APBC_TWSI4_CLK_RST, BIT(2), 0),
+ [RESET_APBC_TWSI5] = RESET_DATA(APBC_TWSI5_CLK_RST, BIT(2), 0),
+ [RESET_APBC_TWSI6] = RESET_DATA(APBC_TWSI6_CLK_RST, BIT(2), 0),
+ [RESET_APBC_TWSI8] = RESET_DATA(APBC_TWSI8_CLK_RST, BIT(2), 0),
+ [RESET_APBC_TIMERS0] = RESET_DATA(APBC_TIMERS0_CLK_RST, BIT(2), 0),
+ [RESET_APBC_TIMERS1] = RESET_DATA(APBC_TIMERS1_CLK_RST, BIT(2), 0),
+ [RESET_APBC_TIMERS2] = RESET_DATA(APBC_TIMERS2_CLK_RST, BIT(2), 0),
+ [RESET_APBC_TIMERS3] = RESET_DATA(APBC_TIMERS3_CLK_RST, BIT(2), 0),
+ [RESET_APBC_TIMERS4] = RESET_DATA(APBC_TIMERS4_CLK_RST, BIT(2), 0),
+ [RESET_APBC_TIMERS5] = RESET_DATA(APBC_TIMERS5_CLK_RST, BIT(2), 0),
+ [RESET_APBC_TIMERS6] = RESET_DATA(APBC_TIMERS6_CLK_RST, BIT(2), 0),
+ [RESET_APBC_TIMERS7] = RESET_DATA(APBC_TIMERS7_CLK_RST, BIT(2), 0),
+ [RESET_APBC_AIB] = RESET_DATA(APBC_AIB_CLK_RST, BIT(2), 0),
+ [RESET_APBC_ONEWIRE] = RESET_DATA(APBC_ONEWIRE_CLK_RST, BIT(2), 0),
+ [RESET_APBC_I2S0] = RESET_DATA(APBC_SSPA0_CLK_RST, BIT(2), 0),
+ [RESET_APBC_I2S1] = RESET_DATA(APBC_SSPA1_CLK_RST, BIT(2), 0),
+ [RESET_APBC_I2S2] = RESET_DATA(APBC_SSPA2_CLK_RST, BIT(2), 0),
+ [RESET_APBC_I2S3] = RESET_DATA(APBC_SSPA3_CLK_RST, BIT(2), 0),
+ [RESET_APBC_I2S4] = RESET_DATA(APBC_SSPA4_CLK_RST, BIT(2), 0),
+ [RESET_APBC_I2S5] = RESET_DATA(APBC_SSPA5_CLK_RST, BIT(2), 0),
+ [RESET_APBC_DRO] = RESET_DATA(APBC_DRO_CLK_RST, BIT(2), 0),
+ [RESET_APBC_IR0] = RESET_DATA(APBC_IR0_CLK_RST, BIT(2), 0),
+ [RESET_APBC_IR1] = RESET_DATA(APBC_IR1_CLK_RST, BIT(2), 0),
+ [RESET_APBC_TSEN] = RESET_DATA(APBC_TSEN_CLK_RST, BIT(2), 0),
+ [RESET_IPC_AP2AUD] = RESET_DATA(APBC_IPC_AP2AUD_CLK_RST, BIT(2), 0),
+ [RESET_APBC_CAN0] = RESET_DATA(APBC_CAN0_CLK_RST, BIT(2), 0),
+ [RESET_APBC_CAN1] = RESET_DATA(APBC_CAN1_CLK_RST, BIT(2), 0),
+ [RESET_APBC_CAN2] = RESET_DATA(APBC_CAN2_CLK_RST, BIT(2), 0),
+ [RESET_APBC_CAN3] = RESET_DATA(APBC_CAN3_CLK_RST, BIT(2), 0),
+ [RESET_APBC_CAN4] = RESET_DATA(APBC_CAN4_CLK_RST, BIT(2), 0),
+};
+
+static const struct ccu_reset_controller_data k3_apbc_reset_data = {
+ .reset_data = k3_apbc_resets,
+ .count = ARRAY_SIZE(k3_apbc_resets),
+};
+
+static const struct ccu_reset_data k3_apmu_resets[] = {
+ [RESET_APMU_CSI] = RESET_DATA(APMU_CSI_CCIC2_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_APMU_CCIC2PHY] = RESET_DATA(APMU_CSI_CCIC2_CLK_RES_CTRL, 0, BIT(2)),
+ [RESET_APMU_CCIC3PHY] = RESET_DATA(APMU_CSI_CCIC2_CLK_RES_CTRL, 0, BIT(29)),
+ [RESET_APMU_ISP_CIBUS] = RESET_DATA(APMU_ISP_CLK_RES_CTRL, 0, BIT(16)),
+ [RESET_APMU_DSI_ESC] = RESET_DATA(APMU_LCD_CLK_RES_CTRL1, 0, BIT(3)),
+ [RESET_APMU_LCD] = RESET_DATA(APMU_LCD_CLK_RES_CTRL1, 0, BIT(4)),
+ [RESET_APMU_V2D] = RESET_DATA(APMU_LCD_CLK_RES_CTRL1, 0, BIT(27)),
+ [RESET_APMU_LCD_MCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL2, 0, BIT(9)),
+ [RESET_APMU_LCD_DSCCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL2, 0, BIT(15)),
+ [RESET_APMU_SC2_HCLK] = RESET_DATA(APMU_CCIC_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_APMU_CCIC_4X] = RESET_DATA(APMU_CCIC_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_APMU_CCIC1_PHY] = RESET_DATA(APMU_CCIC_CLK_RES_CTRL, 0, BIT(2)),
+ [RESET_APMU_SDH_AXI] = RESET_DATA(APMU_SDH0_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_APMU_SDH0] = RESET_DATA(APMU_SDH0_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_APMU_SDH1] = RESET_DATA(APMU_SDH1_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_APMU_SDH2] = RESET_DATA(APMU_SDH2_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_APMU_USB2] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0,
+ BIT(1)|BIT(2)|BIT(3)),
+ [RESET_APMU_USB3_PORTA] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0,
+ BIT(5)|BIT(6)|BIT(7)),
+ [RESET_APMU_USB3_PORTB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0,
+ BIT(9)|BIT(10)|BIT(11)),
+ [RESET_APMU_USB3_PORTC] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0,
+ BIT(13)|BIT(14)|BIT(15)),
+ [RESET_APMU_USB3_PORTD] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0,
+ BIT(17)|BIT(18)|BIT(19)),
+ [RESET_APMU_QSPI] = RESET_DATA(APMU_QSPI_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_APMU_QSPI_BUS] = RESET_DATA(APMU_QSPI_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_APMU_DMA] = RESET_DATA(APMU_DMA_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_APMU_AES_WTM] = RESET_DATA(APMU_AES_CLK_RES_CTRL, 0, BIT(4)),
+ [RESET_APMU_MCB_DCLK] = RESET_DATA(APMU_MCB_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_APMU_MCB_ACLK] = RESET_DATA(APMU_MCB_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_APMU_VPU] = RESET_DATA(APMU_VPU_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_APMU_DTC] = RESET_DATA(APMU_DTC_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_APMU_GPU] = RESET_DATA(APMU_GPU_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_APMU_MC] = RESET_DATA(APMU_PMUA_MC_CTRL, 0, BIT(0)),
+ [RESET_APMU_CPU0_POP] = RESET_DATA(APMU_PMU_CC2_AP, BIT(0), 0),
+ [RESET_APMU_CPU0_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(1), 0),
+ [RESET_APMU_CPU1_POP] = RESET_DATA(APMU_PMU_CC2_AP, BIT(3), 0),
+ [RESET_APMU_CPU1_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(4), 0),
+ [RESET_APMU_CPU2_POP] = RESET_DATA(APMU_PMU_CC2_AP, BIT(6), 0),
+ [RESET_APMU_CPU2_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(7), 0),
+ [RESET_APMU_CPU3_POP] = RESET_DATA(APMU_PMU_CC2_AP, BIT(9), 0),
+ [RESET_APMU_CPU3_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(10), 0),
+ [RESET_APMU_C0_MPSUB_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(12), 0),
+ [RESET_APMU_CPU4_POP] = RESET_DATA(APMU_PMU_CC2_AP, BIT(16), 0),
+ [RESET_APMU_CPU4_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(17), 0),
+ [RESET_APMU_CPU5_POP] = RESET_DATA(APMU_PMU_CC2_AP, BIT(19), 0),
+ [RESET_APMU_CPU5_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(20), 0),
+ [RESET_APMU_CPU6_POP] = RESET_DATA(APMU_PMU_CC2_AP, BIT(22), 0),
+ [RESET_APMU_CPU6_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(23), 0),
+ [RESET_APMU_CPU7_POP] = RESET_DATA(APMU_PMU_CC2_AP, BIT(25), 0),
+ [RESET_APMU_CPU7_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(26), 0),
+ [RESET_APMU_C1_MPSUB_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(28), 0),
+ [RESET_APMU_MPSUB_DBG] = RESET_DATA(APMU_PMU_CC2_AP, BIT(29), 0),
+ [RESET_APMU_UCIE] = RESET_DATA(APMU_UCIE_CTRL,
+ BIT(1) | BIT(2) | BIT(3), 0),
+ [RESET_APMU_RCPU] = RESET_DATA(APMU_RCPU_CLK_RES_CTRL, 0,
+ BIT(3) | BIT(2) | BIT(0)),
+ [RESET_APMU_DSI4LN2_ESCCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL3, 0, BIT(3)),
+ [RESET_APMU_DSI4LN2_LCD_SW] = RESET_DATA(APMU_LCD_CLK_RES_CTRL3, 0, BIT(4)),
+ [RESET_APMU_DSI4LN2_LCD_MCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL4, 0, BIT(9)),
+ [RESET_APMU_DSI4LN2_LCD_DSCCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL4, 0, BIT(15)),
+ [RESET_APMU_DSI4LN2_DPU_ACLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL5, 0, BIT(0)),
+ [RESET_APMU_DPU_ACLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL5, 0, BIT(15)),
+ [RESET_APMU_UFS_ACLK] = RESET_DATA(APMU_UFS_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_APMU_EDP0] = RESET_DATA(APMU_LCD_EDP_CTRL, 0, BIT(0)),
+ [RESET_APMU_EDP1] = RESET_DATA(APMU_LCD_EDP_CTRL, 0, BIT(16)),
+ [RESET_APMU_PCIE_PORTA] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_A, 0,
+ BIT(5) | BIT(4) | BIT(3)),
+ [RESET_APMU_PCIE_PORTB] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_B, 0,
+ BIT(5) | BIT(4) | BIT(3)),
+ [RESET_APMU_PCIE_PORTC] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_C, 0,
+ BIT(5) | BIT(4) | BIT(3)),
+ [RESET_APMU_PCIE_PORTD] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_D, 0,
+ BIT(5) | BIT(4) | BIT(3)),
+ [RESET_APMU_PCIE_PORTE] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_E, 0,
+ BIT(5) | BIT(4) | BIT(3)),
+ [RESET_APMU_EMAC0] = RESET_DATA(APMU_EMAC0_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_APMU_EMAC1] = RESET_DATA(APMU_EMAC1_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_APMU_EMAC2] = RESET_DATA(APMU_EMAC2_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_APMU_ESPI_MCLK] = RESET_DATA(APMU_ESPI_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_APMU_ESPI_SCLK] = RESET_DATA(APMU_ESPI_CLK_RES_CTRL, 0, BIT(2)),
+};
+
+static const struct ccu_reset_controller_data k3_apmu_reset_data = {
+ .reset_data = k3_apmu_resets,
+ .count = ARRAY_SIZE(k3_apmu_resets),
+};
+
+static const struct ccu_reset_data k3_dciu_resets[] = {
+ [RESET_DCIU_HDMA] = RESET_DATA(DCIU_DMASYS_RSTN, 0, BIT(0)),
+ [RESET_DCIU_DMA350] = RESET_DATA(DCIU_DMASYS_SDMA_RSTN, 0, BIT(0)),
+ [RESET_DCIU_DMA350_0] = RESET_DATA(DCIU_DMASYS_S0_RSTN, 0, BIT(0)),
+ [RESET_DCIU_DMA350_1] = RESET_DATA(DCIU_DMASYS_S1_RSTN, 0, BIT(0)),
+ [RESET_DCIU_AXIDMA0] = RESET_DATA(DCIU_DMASYS_A0_RSTN, 0, BIT(0)),
+ [RESET_DCIU_AXIDMA1] = RESET_DATA(DCIU_DMASYS_A1_RSTN, 0, BIT(0)),
+ [RESET_DCIU_AXIDMA2] = RESET_DATA(DCIU_DMASYS_A2_RSTN, 0, BIT(0)),
+ [RESET_DCIU_AXIDMA3] = RESET_DATA(DCIU_DMASYS_A3_RSTN, 0, BIT(0)),
+ [RESET_DCIU_AXIDMA4] = RESET_DATA(DCIU_DMASYS_A4_RSTN, 0, BIT(0)),
+ [RESET_DCIU_AXIDMA5] = RESET_DATA(DCIU_DMASYS_A5_RSTN, 0, BIT(0)),
+ [RESET_DCIU_AXIDMA6] = RESET_DATA(DCIU_DMASYS_A6_RSTN, 0, BIT(0)),
+ [RESET_DCIU_AXIDMA7] = RESET_DATA(DCIU_DMASYS_A7_RSTN, 0, BIT(0)),
+};
+
+static const struct ccu_reset_controller_data k3_dciu_reset_data = {
+ .reset_data = k3_dciu_resets,
+ .count = ARRAY_SIZE(k3_dciu_resets),
+};
+
+#define K3_AUX_DEV_ID(_unit) \
+ { \
+ .name = "spacemit_ccu.k3-" #_unit "-reset", \
+ .driver_data = (kernel_ulong_t)&k3_ ## _unit ## _reset_data, \
+ }
+
+static const struct auxiliary_device_id spacemit_k3_reset_ids[] = {
+ K3_AUX_DEV_ID(mpmu),
+ K3_AUX_DEV_ID(apbc),
+ K3_AUX_DEV_ID(apmu),
+ K3_AUX_DEV_ID(dciu),
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(auxiliary, spacemit_k3_reset_ids);
+
+static struct auxiliary_driver spacemit_k3_reset_driver = {
+ .probe = spacemit_reset_probe,
+ .id_table = spacemit_k3_reset_ids,
+};
+module_auxiliary_driver(spacemit_k3_reset_driver);
+
+MODULE_IMPORT_NS("RESET_SPACEMIT");
+MODULE_AUTHOR("Guodong Xu <guodong@riscstar.com>");
+MODULE_DESCRIPTION("SpacemiT K3 reset controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-optee.c b/drivers/rtc/rtc-optee.c
index 184c6d142801..eefde789d194 100644
--- a/drivers/rtc/rtc-optee.c
+++ b/drivers/rtc/rtc-optee.c
@@ -547,9 +547,9 @@ static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
return 0;
}
-static int optee_rtc_probe(struct device *dev)
+static int optee_rtc_probe(struct tee_client_device *rtc_device)
{
- struct tee_client_device *rtc_device = to_tee_client_device(dev);
+ struct device *dev = &rtc_device->dev;
struct tee_ioctl_open_session_arg sess2_arg = {0};
struct tee_ioctl_open_session_arg sess_arg = {0};
struct optee_rtc *priv;
@@ -682,8 +682,9 @@ out_ctx:
return err;
}
-static int optee_rtc_remove(struct device *dev)
+static void optee_rtc_remove(struct tee_client_device *rtc_device)
{
+ struct device *dev = &rtc_device->dev;
struct optee_rtc *priv = dev_get_drvdata(dev);
if (priv->features & TA_RTC_FEATURE_ALARM) {
@@ -696,8 +697,6 @@ static int optee_rtc_remove(struct device *dev)
tee_shm_free(priv->shm);
tee_client_close_session(priv->ctx, priv->session_id);
tee_client_close_context(priv->ctx);
-
- return 0;
}
static int optee_rtc_suspend(struct device *dev)
@@ -724,27 +723,15 @@ MODULE_DEVICE_TABLE(tee, optee_rtc_id_table);
static struct tee_client_driver optee_rtc_driver = {
.id_table = optee_rtc_id_table,
+ .probe = optee_rtc_probe,
+ .remove = optee_rtc_remove,
.driver = {
.name = "optee_rtc",
- .bus = &tee_bus_type,
- .probe = optee_rtc_probe,
- .remove = optee_rtc_remove,
.pm = pm_sleep_ptr(&optee_rtc_pm_ops),
},
};
-static int __init optee_rtc_mod_init(void)
-{
- return driver_register(&optee_rtc_driver.driver);
-}
-
-static void __exit optee_rtc_mod_exit(void)
-{
- driver_unregister(&optee_rtc_driver.driver);
-}
-
-module_init(optee_rtc_mod_init);
-module_exit(optee_rtc_mod_exit);
+module_tee_client_driver(optee_rtc_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Clément Léger <clement.leger@bootlin.com>");
diff --git a/drivers/soc/amlogic/meson-gx-socinfo.c b/drivers/soc/amlogic/meson-gx-socinfo.c
index 2a54ca43cd13..dcb75baaff24 100644
--- a/drivers/soc/amlogic/meson-gx-socinfo.c
+++ b/drivers/soc/amlogic/meson-gx-socinfo.c
@@ -85,6 +85,7 @@ static const struct meson_gx_package_id {
{ "S905D3", 0x2b, 0x30, 0x3f },
{ "A113L", 0x2c, 0x0, 0xf8 },
{ "S805X2", 0x37, 0x2, 0xf },
+ { "S905Y4", 0x37, 0x3, 0xf },
{ "C308L", 0x3d, 0x1, 0xf },
{ "A311D2", 0x36, 0x1, 0xf },
{ "A113X2", 0x3c, 0x1, 0xf },
diff --git a/drivers/soc/apple/rtkit.c b/drivers/soc/apple/rtkit.c
index b8d4da147d23..4ad4f964fde7 100644
--- a/drivers/soc/apple/rtkit.c
+++ b/drivers/soc/apple/rtkit.c
@@ -851,6 +851,22 @@ int apple_rtkit_shutdown(struct apple_rtkit *rtk)
}
EXPORT_SYMBOL_GPL(apple_rtkit_shutdown);
+int apple_rtkit_poweroff(struct apple_rtkit *rtk)
+{
+ int ret;
+
+ ret = apple_rtkit_set_ap_power_state(rtk, APPLE_RTKIT_PWR_STATE_OFF);
+ if (ret)
+ return ret;
+
+ ret = apple_rtkit_set_iop_power_state(rtk, APPLE_RTKIT_PWR_STATE_OFF);
+ if (ret)
+ return ret;
+
+ return apple_rtkit_reinit(rtk);
+}
+EXPORT_SYMBOL_GPL(apple_rtkit_poweroff);
+
int apple_rtkit_idle(struct apple_rtkit *rtk)
{
int ret;
diff --git a/drivers/soc/dove/pmu.c b/drivers/soc/dove/pmu.c
index 7bbd3f940e4d..dd8ade8e9ee8 100644
--- a/drivers/soc/dove/pmu.c
+++ b/drivers/soc/dove/pmu.c
@@ -371,7 +371,7 @@ int __init dove_init_pmu_legacy(const struct dove_pmu_initdata *initdata)
*/
int __init dove_init_pmu(void)
{
- struct device_node *np_pmu, *domains_node, *np;
+ struct device_node *np_pmu, *domains_node;
struct pmu_data *pmu;
int ret, parent_irq;
@@ -404,21 +404,18 @@ int __init dove_init_pmu(void)
pmu_reset_init(pmu);
- for_each_available_child_of_node(domains_node, np) {
+ for_each_available_child_of_node_scoped(domains_node, np) {
struct of_phandle_args args;
struct pmu_domain *domain;
domain = kzalloc(sizeof(*domain), GFP_KERNEL);
- if (!domain) {
- of_node_put(np);
+ if (!domain)
break;
- }
domain->pmu = pmu;
domain->base.name = kasprintf(GFP_KERNEL, "%pOFn", np);
if (!domain->base.name) {
kfree(domain);
- of_node_put(np);
break;
}
diff --git a/drivers/soc/fsl/qe/Makefile b/drivers/soc/fsl/qe/Makefile
index ec8506e13113..901a9c40d5eb 100644
--- a/drivers/soc/fsl/qe/Makefile
+++ b/drivers/soc/fsl/qe/Makefile
@@ -11,4 +11,4 @@ obj-$(CONFIG_UCC_SLOW) += ucc_slow.o
obj-$(CONFIG_UCC_FAST) += ucc_fast.o
obj-$(CONFIG_QE_TDM) += qe_tdm.o
obj-$(CONFIG_QE_USB) += usb.o
-obj-$(CONFIG_QE_GPIO) += gpio.o
+obj-$(CONFIG_QE_GPIO) += gpio.o qe_ports_ic.o
diff --git a/drivers/soc/fsl/qe/qe_ports_ic.c b/drivers/soc/fsl/qe/qe_ports_ic.c
new file mode 100644
index 000000000000..8e2107e2cde5
--- /dev/null
+++ b/drivers/soc/fsl/qe/qe_ports_ic.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * QUICC ENGINE I/O Ports Interrupt Controller
+ *
+ * Copyright (c) 2025 Christophe Leroy CS GROUP France (christophe.leroy@csgroup.eu)
+ */
+
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/platform_device.h>
+
+/* QE IC registers offset */
+#define CEPIER 0x0c
+#define CEPIMR 0x10
+#define CEPICR 0x14
+
+struct qepic_data {
+ void __iomem *reg;
+ struct irq_domain *host;
+};
+
+static void qepic_mask(struct irq_data *d)
+{
+ struct qepic_data *data = irq_data_get_irq_chip_data(d);
+
+ clrbits32(data->reg + CEPIMR, 1 << (31 - irqd_to_hwirq(d)));
+}
+
+static void qepic_unmask(struct irq_data *d)
+{
+ struct qepic_data *data = irq_data_get_irq_chip_data(d);
+
+ setbits32(data->reg + CEPIMR, 1 << (31 - irqd_to_hwirq(d)));
+}
+
+static void qepic_end(struct irq_data *d)
+{
+ struct qepic_data *data = irq_data_get_irq_chip_data(d);
+
+ out_be32(data->reg + CEPIER, 1 << (31 - irqd_to_hwirq(d)));
+}
+
+static int qepic_set_type(struct irq_data *d, unsigned int flow_type)
+{
+ struct qepic_data *data = irq_data_get_irq_chip_data(d);
+ unsigned int vec = (unsigned int)irqd_to_hwirq(d);
+
+ switch (flow_type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_EDGE_FALLING:
+ setbits32(data->reg + CEPICR, 1 << (31 - vec));
+ return 0;
+ case IRQ_TYPE_EDGE_BOTH:
+ case IRQ_TYPE_NONE:
+ clrbits32(data->reg + CEPICR, 1 << (31 - vec));
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static struct irq_chip qepic = {
+ .name = "QEPIC",
+ .irq_mask = qepic_mask,
+ .irq_unmask = qepic_unmask,
+ .irq_eoi = qepic_end,
+ .irq_set_type = qepic_set_type,
+};
+
+static int qepic_get_irq(struct irq_desc *desc)
+{
+ struct qepic_data *data = irq_desc_get_handler_data(desc);
+ u32 event = in_be32(data->reg + CEPIER);
+
+ if (!event)
+ return -1;
+
+ return irq_find_mapping(data->host, 32 - ffs(event));
+}
+
+static void qepic_cascade(struct irq_desc *desc)
+{
+ generic_handle_irq(qepic_get_irq(desc));
+}
+
+static int qepic_host_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw)
+{
+ irq_set_chip_data(virq, h->host_data);
+ irq_set_chip_and_handler(virq, &qepic, handle_fasteoi_irq);
+ return 0;
+}
+
+static const struct irq_domain_ops qepic_host_ops = {
+ .map = qepic_host_map,
+};
+
+static int qepic_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct qepic_data *data;
+ int irq;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->reg = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(data->reg))
+ return PTR_ERR(data->reg);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ data->host = irq_domain_add_linear(dev->of_node, 32, &qepic_host_ops, data);
+ if (!data->host)
+ return -ENODEV;
+
+ irq_set_chained_handler_and_data(irq, qepic_cascade, data);
+
+ return 0;
+}
+
+static const struct of_device_id qepic_match[] = {
+ {
+ .compatible = "fsl,mpc8323-qe-ports-ic",
+ },
+ {},
+};
+
+static struct platform_driver qepic_driver = {
+ .driver = {
+ .name = "qe_ports_ic",
+ .of_match_table = qepic_match,
+ },
+ .probe = qepic_probe,
+};
+
+static int __init qepic_init(void)
+{
+ return platform_driver_register(&qepic_driver);
+}
+arch_initcall(qepic_init);
diff --git a/drivers/soc/fsl/qe/qmc.c b/drivers/soc/fsl/qe/qmc.c
index da5ea6d35618..c4587b32a59b 100644
--- a/drivers/soc/fsl/qe/qmc.c
+++ b/drivers/soc/fsl/qe/qmc.c
@@ -1284,31 +1284,26 @@ static unsigned int qmc_nb_chans(struct qmc *qmc)
static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np)
{
- struct device_node *chan_np;
struct qmc_chan *chan;
const char *mode;
u32 chan_id;
u64 ts_mask;
int ret;
- for_each_available_child_of_node(np, chan_np) {
+ for_each_available_child_of_node_scoped(np, chan_np) {
ret = of_property_read_u32(chan_np, "reg", &chan_id);
if (ret) {
dev_err(qmc->dev, "%pOF: failed to read reg\n", chan_np);
- of_node_put(chan_np);
return ret;
}
if (chan_id > 63) {
dev_err(qmc->dev, "%pOF: Invalid chan_id\n", chan_np);
- of_node_put(chan_np);
return -EINVAL;
}
chan = devm_kzalloc(qmc->dev, sizeof(*chan), GFP_KERNEL);
- if (!chan) {
- of_node_put(chan_np);
+ if (!chan)
return -ENOMEM;
- }
chan->id = chan_id;
spin_lock_init(&chan->ts_lock);
@@ -1319,7 +1314,6 @@ static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np)
if (ret) {
dev_err(qmc->dev, "%pOF: failed to read fsl,tx-ts-mask\n",
chan_np);
- of_node_put(chan_np);
return ret;
}
chan->tx_ts_mask_avail = ts_mask;
@@ -1329,7 +1323,6 @@ static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np)
if (ret) {
dev_err(qmc->dev, "%pOF: failed to read fsl,rx-ts-mask\n",
chan_np);
- of_node_put(chan_np);
return ret;
}
chan->rx_ts_mask_avail = ts_mask;
@@ -1340,7 +1333,6 @@ static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np)
if (ret && ret != -EINVAL) {
dev_err(qmc->dev, "%pOF: failed to read fsl,operational-mode\n",
chan_np);
- of_node_put(chan_np);
return ret;
}
if (!strcmp(mode, "transparent")) {
@@ -1350,7 +1342,6 @@ static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np)
} else {
dev_err(qmc->dev, "%pOF: Invalid fsl,operational-mode (%s)\n",
chan_np, mode);
- of_node_put(chan_np);
return -EINVAL;
}
diff --git a/drivers/soc/imx/soc-imx8m.c b/drivers/soc/imx/soc-imx8m.c
index 04a1b60f2f2b..8e2322999f09 100644
--- a/drivers/soc/imx/soc-imx8m.c
+++ b/drivers/soc/imx/soc-imx8m.c
@@ -148,7 +148,11 @@ static int imx8m_soc_prepare(struct platform_device *pdev, const char *ocotp_com
goto err_clk;
}
- return clk_prepare_enable(drvdata->clk);
+ ret = clk_prepare_enable(drvdata->clk);
+ if (ret)
+ goto err_clk;
+
+ return 0;
err_clk:
iounmap(drvdata->ocotp_base);
diff --git a/drivers/soc/imx/soc-imx9.c b/drivers/soc/imx/soc-imx9.c
index b46d22cf0212..d67bc7402b10 100644
--- a/drivers/soc/imx/soc-imx9.c
+++ b/drivers/soc/imx/soc-imx9.c
@@ -12,12 +12,13 @@
#include <linux/sys_soc.h>
#define IMX_SIP_GET_SOC_INFO 0xc2000006
-#define SOC_ID(x) (((x) & 0xFFFF) >> 8)
+#define SOC_ID(x) (((x) & 0xFF) ? ((x) & 0xFFFF) >> 4 : ((x) & 0xFFFF) >> 8)
#define SOC_REV_MAJOR(x) ((((x) >> 28) & 0xF) - 0x9)
#define SOC_REV_MINOR(x) (((x) >> 24) & 0xF)
static int imx9_soc_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
struct soc_device_attribute *attr;
struct arm_smccc_res res;
struct soc_device *sdev;
@@ -25,17 +26,15 @@ static int imx9_soc_probe(struct platform_device *pdev)
u64 uid127_64, uid63_0;
int err;
- attr = kzalloc(sizeof(*attr), GFP_KERNEL);
+ attr = devm_kzalloc(dev, sizeof(*attr), GFP_KERNEL);
if (!attr)
return -ENOMEM;
err = of_property_read_string(of_root, "model", &attr->machine);
- if (err) {
- pr_err("%s: missing model property: %d\n", __func__, err);
- goto attr;
- }
+ if (err)
+ return dev_err_probe(dev, err, "%s: missing model property\n", __func__);
- attr->family = kasprintf(GFP_KERNEL, "Freescale i.MX");
+ attr->family = devm_kasprintf(dev, GFP_KERNEL, "Freescale i.MX");
/*
* Retrieve the soc id, rev & uid info:
@@ -45,46 +44,33 @@ static int imx9_soc_probe(struct platform_device *pdev)
* res.a3: uid[63:0];
*/
arm_smccc_smc(IMX_SIP_GET_SOC_INFO, 0, 0, 0, 0, 0, 0, 0, &res);
- if (res.a0 != SMCCC_RET_SUCCESS) {
- pr_err("%s: SMC failed: 0x%lx\n", __func__, res.a0);
- err = -EINVAL;
- goto family;
- }
+ if (res.a0 != SMCCC_RET_SUCCESS)
+ return dev_err_probe(dev, -EINVAL, "%s: SMC failed: 0x%lx\n", __func__, res.a0);
soc_id = SOC_ID(res.a1);
rev_major = SOC_REV_MAJOR(res.a1);
rev_minor = SOC_REV_MINOR(res.a1);
- attr->soc_id = kasprintf(GFP_KERNEL, "i.MX%2x", soc_id);
- attr->revision = kasprintf(GFP_KERNEL, "%d.%d", rev_major, rev_minor);
+ attr->soc_id = devm_kasprintf(dev, GFP_KERNEL, "i.MX%2x", soc_id);
+ attr->revision = devm_kasprintf(dev, GFP_KERNEL, "%d.%d", rev_major, rev_minor);
uid127_64 = res.a2;
uid63_0 = res.a3;
- attr->serial_number = kasprintf(GFP_KERNEL, "%016llx%016llx", uid127_64, uid63_0);
+ attr->serial_number = devm_kasprintf(dev, GFP_KERNEL, "%016llx%016llx", uid127_64, uid63_0);
sdev = soc_device_register(attr);
- if (IS_ERR(sdev)) {
- err = PTR_ERR(sdev);
- pr_err("%s failed to register SoC as a device: %d\n", __func__, err);
- goto serial_number;
- }
+ if (IS_ERR(sdev))
+ return dev_err_probe(dev, PTR_ERR(sdev),
+ "%s failed to register SoC as a device\n", __func__);
return 0;
-
-serial_number:
- kfree(attr->serial_number);
- kfree(attr->revision);
- kfree(attr->soc_id);
-family:
- kfree(attr->family);
-attr:
- kfree(attr);
- return err;
}
static __maybe_unused const struct of_device_id imx9_soc_match[] = {
{ .compatible = "fsl,imx93", },
+ { .compatible = "fsl,imx94", },
{ .compatible = "fsl,imx95", },
+ { .compatible = "fsl,imx952", },
{ }
};
diff --git a/drivers/soc/mediatek/mtk-cmdq-helper.c b/drivers/soc/mediatek/mtk-cmdq-helper.c
index 455221e8de24..67e5879374ac 100644
--- a/drivers/soc/mediatek/mtk-cmdq-helper.c
+++ b/drivers/soc/mediatek/mtk-cmdq-helper.c
@@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/mailbox_controller.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/soc/mediatek/mtk-cmdq.h>
#define CMDQ_WRITE_ENABLE_MASK BIT(0)
@@ -60,20 +61,41 @@ int cmdq_dev_get_client_reg(struct device *dev,
struct cmdq_client_reg *client_reg, int idx)
{
struct of_phandle_args spec;
+ struct resource res;
int err;
if (!client_reg)
return -ENOENT;
+ err = of_address_to_resource(dev->of_node, 0, &res);
+ if (err) {
+ dev_err(dev, "Missing reg in %s node\n", dev->of_node->full_name);
+ return -EINVAL;
+ }
+ client_reg->pa_base = res.start;
+
err = of_parse_phandle_with_fixed_args(dev->of_node,
"mediatek,gce-client-reg",
3, idx, &spec);
if (err < 0) {
- dev_warn(dev,
+ dev_dbg(dev,
"error %d can't parse gce-client-reg property (%d)",
err, idx);
- return err;
+ /* make subsys invalid */
+ client_reg->subsys = CMDQ_SUBSYS_INVALID;
+
+ /*
+ * All GCEs support writing register PA with mask without subsys,
+ * but this requires extra GCE instructions to convert the PA into
+ * a format that GCE can handle, which is less performance than
+ * directly using subsys. Therefore, when subsys is available,
+ * we prefer to use subsys for writing register PA.
+ */
+ client_reg->pkt_write = cmdq_pkt_write_pa;
+ client_reg->pkt_write_mask = cmdq_pkt_write_mask_pa;
+
+ return 0;
}
client_reg->subsys = (u8)spec.args[0];
@@ -81,6 +103,9 @@ int cmdq_dev_get_client_reg(struct device *dev,
client_reg->size = (u16)spec.args[2];
of_node_put(spec.np);
+ client_reg->pkt_write = cmdq_pkt_write_subsys;
+ client_reg->pkt_write_mask = cmdq_pkt_write_mask_subsys;
+
return 0;
}
EXPORT_SYMBOL(cmdq_dev_get_client_reg);
@@ -140,6 +165,7 @@ int cmdq_pkt_create(struct cmdq_client *client, struct cmdq_pkt *pkt, size_t siz
}
pkt->pa_base = dma_addr;
+ cmdq_get_mbox_priv(client->chan, &pkt->priv);
return 0;
}
@@ -201,6 +227,26 @@ int cmdq_pkt_write(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value)
}
EXPORT_SYMBOL(cmdq_pkt_write);
+int cmdq_pkt_write_pa(struct cmdq_pkt *pkt, u8 subsys /*unused*/, u32 pa_base,
+ u16 offset, u32 value)
+{
+ int err;
+
+ err = cmdq_pkt_assign(pkt, CMDQ_THR_SPR_IDX0, CMDQ_ADDR_HIGH(pa_base));
+ if (err < 0)
+ return err;
+
+ return cmdq_pkt_write_s_value(pkt, CMDQ_THR_SPR_IDX0, CMDQ_ADDR_LOW(offset), value);
+}
+EXPORT_SYMBOL(cmdq_pkt_write_pa);
+
+int cmdq_pkt_write_subsys(struct cmdq_pkt *pkt, u8 subsys, u32 pa_base /*unused*/,
+ u16 offset, u32 value)
+{
+ return cmdq_pkt_write(pkt, subsys, offset, value);
+}
+EXPORT_SYMBOL(cmdq_pkt_write_subsys);
+
int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys,
u16 offset, u32 value, u32 mask)
{
@@ -218,6 +264,27 @@ int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys,
}
EXPORT_SYMBOL(cmdq_pkt_write_mask);
+int cmdq_pkt_write_mask_pa(struct cmdq_pkt *pkt, u8 subsys /*unused*/, u32 pa_base,
+ u16 offset, u32 value, u32 mask)
+{
+ int err;
+
+ err = cmdq_pkt_assign(pkt, CMDQ_THR_SPR_IDX0, CMDQ_ADDR_HIGH(pa_base));
+ if (err < 0)
+ return err;
+
+ return cmdq_pkt_write_s_mask_value(pkt, CMDQ_THR_SPR_IDX0,
+ CMDQ_ADDR_LOW(offset), value, mask);
+}
+EXPORT_SYMBOL(cmdq_pkt_write_mask_pa);
+
+int cmdq_pkt_write_mask_subsys(struct cmdq_pkt *pkt, u8 subsys, u32 pa_base /*unused*/,
+ u16 offset, u32 value, u32 mask)
+{
+ return cmdq_pkt_write_mask(pkt, subsys, offset, value, mask);
+}
+EXPORT_SYMBOL(cmdq_pkt_write_mask_subsys);
+
int cmdq_pkt_read_s(struct cmdq_pkt *pkt, u16 high_addr_reg_idx, u16 addr_low,
u16 reg_idx)
{
@@ -305,6 +372,7 @@ int cmdq_pkt_mem_move(struct cmdq_pkt *pkt, dma_addr_t src_addr, dma_addr_t dst_
int ret;
/* read the value of src_addr into high_addr_reg_idx */
+ src_addr += pkt->priv.mminfra_offset;
ret = cmdq_pkt_assign(pkt, high_addr_reg_idx, CMDQ_ADDR_HIGH(src_addr));
if (ret < 0)
return ret;
@@ -313,6 +381,7 @@ int cmdq_pkt_mem_move(struct cmdq_pkt *pkt, dma_addr_t src_addr, dma_addr_t dst_
return ret;
/* write the value of value_reg_idx into dst_addr */
+ dst_addr += pkt->priv.mminfra_offset;
ret = cmdq_pkt_assign(pkt, high_addr_reg_idx, CMDQ_ADDR_HIGH(dst_addr));
if (ret < 0)
return ret;
@@ -438,7 +507,7 @@ int cmdq_pkt_poll_addr(struct cmdq_pkt *pkt, dma_addr_t addr, u32 value, u32 mas
inst.op = CMDQ_CODE_MASK;
inst.dst_t = CMDQ_REG_TYPE;
inst.sop = CMDQ_POLL_ADDR_GPR;
- inst.value = addr;
+ inst.value = addr + pkt->priv.mminfra_offset;
ret = cmdq_pkt_append_command(pkt, inst);
if (ret < 0)
return ret;
@@ -498,7 +567,7 @@ int cmdq_pkt_jump_abs(struct cmdq_pkt *pkt, dma_addr_t addr, u8 shift_pa)
struct cmdq_instruction inst = {
.op = CMDQ_CODE_JUMP,
.offset = CMDQ_JUMP_ABSOLUTE,
- .value = addr >> shift_pa
+ .value = (addr + pkt->priv.mminfra_offset) >> pkt->priv.shift_pa
};
return cmdq_pkt_append_command(pkt, inst);
}
diff --git a/drivers/soc/mediatek/mtk-dvfsrc.c b/drivers/soc/mediatek/mtk-dvfsrc.c
index 41add5636b03..548a28f50242 100644
--- a/drivers/soc/mediatek/mtk-dvfsrc.c
+++ b/drivers/soc/mediatek/mtk-dvfsrc.c
@@ -7,6 +7,7 @@
#include <linux/arm-smccc.h>
#include <linux/bitfield.h>
+#include <linux/clk.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -15,11 +16,17 @@
#include <linux/soc/mediatek/dvfsrc.h>
#include <linux/soc/mediatek/mtk_sip_svc.h>
+/* DVFSRC_BASIC_CONTROL */
+#define DVFSRC_V4_BASIC_CTRL_OPP_COUNT GENMASK(26, 20)
+
/* DVFSRC_LEVEL */
#define DVFSRC_V1_LEVEL_TARGET_LEVEL GENMASK(15, 0)
#define DVFSRC_TGT_LEVEL_IDLE 0x00
#define DVFSRC_V1_LEVEL_CURRENT_LEVEL GENMASK(31, 16)
+#define DVFSRC_V4_LEVEL_TARGET_LEVEL GENMASK(15, 8)
+#define DVFSRC_V4_LEVEL_TARGET_PRESENT BIT(16)
+
/* DVFSRC_SW_REQ, DVFSRC_SW_REQ2 */
#define DVFSRC_V1_SW_REQ2_DRAM_LEVEL GENMASK(1, 0)
#define DVFSRC_V1_SW_REQ2_VCORE_LEVEL GENMASK(3, 2)
@@ -27,24 +34,40 @@
#define DVFSRC_V2_SW_REQ_DRAM_LEVEL GENMASK(3, 0)
#define DVFSRC_V2_SW_REQ_VCORE_LEVEL GENMASK(6, 4)
+#define DVFSRC_V4_SW_REQ_EMI_LEVEL GENMASK(3, 0)
+#define DVFSRC_V4_SW_REQ_DRAM_LEVEL GENMASK(15, 12)
+
/* DVFSRC_VCORE */
#define DVFSRC_V2_VCORE_REQ_VSCP_LEVEL GENMASK(14, 12)
+/* DVFSRC_TARGET_GEAR */
+#define DVFSRC_V4_GEAR_TARGET_DRAM GENMASK(7, 0)
+#define DVFSRC_V4_GEAR_TARGET_VCORE GENMASK(15, 8)
+
+/* DVFSRC_GEAR_INFO */
+#define DVFSRC_V4_GEAR_INFO_REG_WIDTH 0x4
+#define DVFSRC_V4_GEAR_INFO_REG_LEVELS 64
+#define DVFSRC_V4_GEAR_INFO_VCORE GENMASK(3, 0)
+#define DVFSRC_V4_GEAR_INFO_EMI GENMASK(7, 4)
+#define DVFSRC_V4_GEAR_INFO_DRAM GENMASK(15, 12)
+
#define DVFSRC_POLL_TIMEOUT_US 1000
#define STARTUP_TIME_US 1
#define MTK_SIP_DVFSRC_INIT 0x0
#define MTK_SIP_DVFSRC_START 0x1
-struct dvfsrc_bw_constraints {
- u16 max_dram_nom_bw;
- u16 max_dram_peak_bw;
- u16 max_dram_hrt_bw;
+enum mtk_dvfsrc_bw_type {
+ DVFSRC_BW_AVG,
+ DVFSRC_BW_PEAK,
+ DVFSRC_BW_HRT,
+ DVFSRC_BW_MAX,
};
struct dvfsrc_opp {
u32 vcore_opp;
u32 dram_opp;
+ u32 emi_opp;
};
struct dvfsrc_opp_desc {
@@ -55,6 +78,7 @@ struct dvfsrc_opp_desc {
struct dvfsrc_soc_data;
struct mtk_dvfsrc {
struct device *dev;
+ struct clk *clk;
struct platform_device *icc;
struct platform_device *regulator;
const struct dvfsrc_soc_data *dvd;
@@ -65,11 +89,16 @@ struct mtk_dvfsrc {
struct dvfsrc_soc_data {
const int *regs;
+ const u8 *bw_units;
+ const bool has_emi_ddr;
const struct dvfsrc_opp_desc *opps_desc;
+ u32 (*calc_dram_bw)(struct mtk_dvfsrc *dvfsrc, enum mtk_dvfsrc_bw_type type, u64 bw);
u32 (*get_target_level)(struct mtk_dvfsrc *dvfsrc);
u32 (*get_current_level)(struct mtk_dvfsrc *dvfsrc);
u32 (*get_vcore_level)(struct mtk_dvfsrc *dvfsrc);
u32 (*get_vscp_level)(struct mtk_dvfsrc *dvfsrc);
+ u32 (*get_opp_count)(struct mtk_dvfsrc *dvfsrc);
+ int (*get_hw_opps)(struct mtk_dvfsrc *dvfsrc);
void (*set_dram_bw)(struct mtk_dvfsrc *dvfsrc, u64 bw);
void (*set_dram_peak_bw)(struct mtk_dvfsrc *dvfsrc, u64 bw);
void (*set_dram_hrt_bw)(struct mtk_dvfsrc *dvfsrc, u64 bw);
@@ -78,7 +107,22 @@ struct dvfsrc_soc_data {
void (*set_vscp_level)(struct mtk_dvfsrc *dvfsrc, u32 level);
int (*wait_for_opp_level)(struct mtk_dvfsrc *dvfsrc, u32 level);
int (*wait_for_vcore_level)(struct mtk_dvfsrc *dvfsrc, u32 level);
- const struct dvfsrc_bw_constraints *bw_constraints;
+
+ /**
+ * @bw_max_constraints - array of maximum bandwidth for this hardware
+ *
+ * indexed by &enum mtk_dvfsrc_bw_type, storing the maximum permissible
+ * hardware value for each bandwidth type.
+ */
+ const u32 *const bw_max_constraints;
+
+ /**
+ * @bw_min_constraints - array of minimum bandwidth for this hardware
+ *
+ * indexed by &enum mtk_dvfsrc_bw_type, storing the minimum permissible
+ * hardware value for each bandwidth type.
+ */
+ const u32 *const bw_min_constraints;
};
static u32 dvfsrc_readl(struct mtk_dvfsrc *dvfs, u32 offset)
@@ -92,6 +136,7 @@ static void dvfsrc_writel(struct mtk_dvfsrc *dvfs, u32 offset, u32 val)
}
enum dvfsrc_regs {
+ DVFSRC_BASIC_CONTROL,
DVFSRC_SW_REQ,
DVFSRC_SW_REQ2,
DVFSRC_LEVEL,
@@ -99,7 +144,11 @@ enum dvfsrc_regs {
DVFSRC_SW_BW,
DVFSRC_SW_PEAK_BW,
DVFSRC_SW_HRT_BW,
+ DVFSRC_SW_EMI_BW,
DVFSRC_VCORE,
+ DVFSRC_TARGET_GEAR,
+ DVFSRC_GEAR_INFO_L,
+ DVFSRC_GEAR_INFO_H,
DVFSRC_REGS_MAX,
};
@@ -120,6 +169,22 @@ static const int dvfsrc_mt8195_regs[] = {
[DVFSRC_TARGET_LEVEL] = 0xd48,
};
+static const int dvfsrc_mt8196_regs[] = {
+ [DVFSRC_BASIC_CONTROL] = 0x0,
+ [DVFSRC_SW_REQ] = 0x18,
+ [DVFSRC_VCORE] = 0x80,
+ [DVFSRC_GEAR_INFO_L] = 0xfc,
+ [DVFSRC_SW_BW] = 0x1e8,
+ [DVFSRC_SW_PEAK_BW] = 0x1f4,
+ [DVFSRC_SW_HRT_BW] = 0x20c,
+ [DVFSRC_LEVEL] = 0x5f0,
+ [DVFSRC_TARGET_LEVEL] = 0x5f0,
+ [DVFSRC_SW_REQ2] = 0x604,
+ [DVFSRC_SW_EMI_BW] = 0x60c,
+ [DVFSRC_TARGET_GEAR] = 0x6ac,
+ [DVFSRC_GEAR_INFO_H] = 0x6b0,
+};
+
static const struct dvfsrc_opp *dvfsrc_get_current_opp(struct mtk_dvfsrc *dvfsrc)
{
u32 level = dvfsrc->dvd->get_current_level(dvfsrc);
@@ -127,6 +192,20 @@ static const struct dvfsrc_opp *dvfsrc_get_current_opp(struct mtk_dvfsrc *dvfsrc
return &dvfsrc->curr_opps->opps[level];
}
+static u32 dvfsrc_get_current_target_vcore_gear(struct mtk_dvfsrc *dvfsrc)
+{
+ u32 val = dvfsrc_readl(dvfsrc, DVFSRC_TARGET_GEAR);
+
+ return FIELD_GET(DVFSRC_V4_GEAR_TARGET_VCORE, val);
+}
+
+static u32 dvfsrc_get_current_target_dram_gear(struct mtk_dvfsrc *dvfsrc)
+{
+ u32 val = dvfsrc_readl(dvfsrc, DVFSRC_TARGET_GEAR);
+
+ return FIELD_GET(DVFSRC_V4_GEAR_TARGET_DRAM, val);
+}
+
static bool dvfsrc_is_idle(struct mtk_dvfsrc *dvfsrc)
{
if (!dvfsrc->dvd->get_target_level)
@@ -183,6 +262,24 @@ static int dvfsrc_wait_for_opp_level_v2(struct mtk_dvfsrc *dvfsrc, u32 level)
return 0;
}
+static int dvfsrc_wait_for_vcore_level_v4(struct mtk_dvfsrc *dvfsrc, u32 level)
+{
+ u32 val;
+
+ return readx_poll_timeout_atomic(dvfsrc_get_current_target_vcore_gear,
+ dvfsrc, val, val >= level,
+ STARTUP_TIME_US, DVFSRC_POLL_TIMEOUT_US);
+}
+
+static int dvfsrc_wait_for_opp_level_v4(struct mtk_dvfsrc *dvfsrc, u32 level)
+{
+ u32 val;
+
+ return readx_poll_timeout_atomic(dvfsrc_get_current_target_dram_gear,
+ dvfsrc, val, val >= level,
+ STARTUP_TIME_US, DVFSRC_POLL_TIMEOUT_US);
+}
+
static u32 dvfsrc_get_target_level_v1(struct mtk_dvfsrc *dvfsrc)
{
u32 val = dvfsrc_readl(dvfsrc, DVFSRC_LEVEL);
@@ -216,6 +313,27 @@ static u32 dvfsrc_get_current_level_v2(struct mtk_dvfsrc *dvfsrc)
return 0;
}
+static u32 dvfsrc_get_target_level_v4(struct mtk_dvfsrc *dvfsrc)
+{
+ u32 val = dvfsrc_readl(dvfsrc, DVFSRC_TARGET_LEVEL);
+
+ if (val & DVFSRC_V4_LEVEL_TARGET_PRESENT)
+ return FIELD_GET(DVFSRC_V4_LEVEL_TARGET_LEVEL, val) + 1;
+ return 0;
+}
+
+static u32 dvfsrc_get_current_level_v4(struct mtk_dvfsrc *dvfsrc)
+{
+ u32 level = dvfsrc_readl(dvfsrc, DVFSRC_LEVEL) + 1;
+
+ /* Valid levels */
+ if (level < dvfsrc->curr_opps->num_opp)
+ return dvfsrc->curr_opps->num_opp - level;
+
+ /* Zero for level 0 or invalid level */
+ return 0;
+}
+
static u32 dvfsrc_get_vcore_level_v1(struct mtk_dvfsrc *dvfsrc)
{
u32 val = dvfsrc_readl(dvfsrc, DVFSRC_SW_REQ2);
@@ -267,39 +385,69 @@ static void dvfsrc_set_vscp_level_v2(struct mtk_dvfsrc *dvfsrc, u32 level)
dvfsrc_writel(dvfsrc, DVFSRC_VCORE, val);
}
+static u32 dvfsrc_get_opp_count_v4(struct mtk_dvfsrc *dvfsrc)
+{
+ u32 val = dvfsrc_readl(dvfsrc, DVFSRC_BASIC_CONTROL);
+
+ return FIELD_GET(DVFSRC_V4_BASIC_CTRL_OPP_COUNT, val) + 1;
+}
+
+static u32
+dvfsrc_calc_dram_bw_v1(struct mtk_dvfsrc *dvfsrc, enum mtk_dvfsrc_bw_type type, u64 bw)
+{
+ return clamp_val(div_u64(bw, 100 * 1000), dvfsrc->dvd->bw_min_constraints[type],
+ dvfsrc->dvd->bw_max_constraints[type]);
+}
+
+/**
+ * dvfsrc_calc_dram_bw_v4 - convert kbps to hardware register bandwidth value
+ * @dvfsrc: pointer to the &struct mtk_dvfsrc of this driver instance
+ * @type: one of %DVFSRC_BW_AVG, %DVFSRC_BW_PEAK, or %DVFSRC_BW_HRT
+ * @bw: the bandwidth in kilobits per second
+ *
+ * Returns the hardware register value appropriate for expressing @bw, clamped
+ * to hardware limits.
+ */
+static u32
+dvfsrc_calc_dram_bw_v4(struct mtk_dvfsrc *dvfsrc, enum mtk_dvfsrc_bw_type type, u64 bw)
+{
+ u8 bw_unit = dvfsrc->dvd->bw_units[type];
+ u64 bw_mbps;
+ u32 bw_hw;
+
+ if (type < DVFSRC_BW_AVG || type >= DVFSRC_BW_MAX)
+ return 0;
+
+ bw_mbps = div_u64(bw, 1000);
+ bw_hw = div_u64((bw_mbps + bw_unit - 1), bw_unit);
+ return clamp_val(bw_hw, dvfsrc->dvd->bw_min_constraints[type],
+ dvfsrc->dvd->bw_max_constraints[type]);
+}
+
static void __dvfsrc_set_dram_bw_v1(struct mtk_dvfsrc *dvfsrc, u32 reg,
- u16 max_bw, u16 min_bw, u64 bw)
+ enum mtk_dvfsrc_bw_type type, u64 bw)
{
- u32 new_bw = (u32)div_u64(bw, 100 * 1000);
+ u32 bw_hw = dvfsrc->dvd->calc_dram_bw(dvfsrc, type, bw);
- /* If bw constraints (in mbps) are defined make sure to respect them */
- if (max_bw)
- new_bw = min(new_bw, max_bw);
- if (min_bw && new_bw > 0)
- new_bw = max(new_bw, min_bw);
+ dvfsrc_writel(dvfsrc, reg, bw_hw);
- dvfsrc_writel(dvfsrc, reg, new_bw);
+ if (type == DVFSRC_BW_AVG && dvfsrc->dvd->has_emi_ddr)
+ dvfsrc_writel(dvfsrc, DVFSRC_SW_EMI_BW, bw_hw);
}
static void dvfsrc_set_dram_bw_v1(struct mtk_dvfsrc *dvfsrc, u64 bw)
{
- u64 max_bw = dvfsrc->dvd->bw_constraints->max_dram_nom_bw;
-
- __dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_BW, max_bw, 0, bw);
+ __dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_BW, DVFSRC_BW_AVG, bw);
};
static void dvfsrc_set_dram_peak_bw_v1(struct mtk_dvfsrc *dvfsrc, u64 bw)
{
- u64 max_bw = dvfsrc->dvd->bw_constraints->max_dram_peak_bw;
-
- __dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_PEAK_BW, max_bw, 0, bw);
+ __dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_PEAK_BW, DVFSRC_BW_PEAK, bw);
}
static void dvfsrc_set_dram_hrt_bw_v1(struct mtk_dvfsrc *dvfsrc, u64 bw)
{
- u64 max_bw = dvfsrc->dvd->bw_constraints->max_dram_hrt_bw;
-
- __dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_HRT_BW, max_bw, 0, bw);
+ __dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_HRT_BW, DVFSRC_BW_HRT, bw);
}
static void dvfsrc_set_opp_level_v1(struct mtk_dvfsrc *dvfsrc, u32 level)
@@ -315,6 +463,100 @@ static void dvfsrc_set_opp_level_v1(struct mtk_dvfsrc *dvfsrc, u32 level)
dvfsrc_writel(dvfsrc, DVFSRC_SW_REQ, val);
}
+static u32 dvfsrc_get_opp_gear(struct mtk_dvfsrc *dvfsrc, u8 level)
+{
+ u32 reg_ofst, val;
+ u8 idx;
+
+ /* Calculate register offset and index for requested gear */
+ if (level < DVFSRC_V4_GEAR_INFO_REG_LEVELS) {
+ reg_ofst = dvfsrc->dvd->regs[DVFSRC_GEAR_INFO_L];
+ idx = level;
+ } else {
+ reg_ofst = dvfsrc->dvd->regs[DVFSRC_GEAR_INFO_H];
+ idx = level - DVFSRC_V4_GEAR_INFO_REG_LEVELS;
+ }
+ reg_ofst += DVFSRC_V4_GEAR_INFO_REG_WIDTH * (level / 2);
+
+ /* Read the corresponding gear register */
+ val = readl(dvfsrc->regs + reg_ofst);
+
+ /* Each register contains two sets of data, 16 bits per gear */
+ val >>= 16 * (idx % 2);
+
+ return val;
+}
+
+static int dvfsrc_get_hw_opps_v4(struct mtk_dvfsrc *dvfsrc)
+{
+ struct dvfsrc_opp *dvfsrc_opps;
+ struct dvfsrc_opp_desc *desc;
+ u32 num_opps, gear_info;
+ u8 num_vcore, num_dram;
+ u8 num_emi;
+ int i;
+
+ num_opps = dvfsrc_get_opp_count_v4(dvfsrc);
+ if (num_opps == 0) {
+ dev_err(dvfsrc->dev, "No OPPs programmed in DVFSRC MCU.\n");
+ return -EINVAL;
+ }
+
+ /*
+ * The first 16 bits set in the gear info table says how many OPPs
+ * and how many vcore, dram and emi table entries are available.
+ */
+ gear_info = dvfsrc_readl(dvfsrc, DVFSRC_GEAR_INFO_L);
+ if (gear_info == 0) {
+ dev_err(dvfsrc->dev, "No gear info in DVFSRC MCU.\n");
+ return -EINVAL;
+ }
+
+ num_vcore = FIELD_GET(DVFSRC_V4_GEAR_INFO_VCORE, gear_info) + 1;
+ num_dram = FIELD_GET(DVFSRC_V4_GEAR_INFO_DRAM, gear_info) + 1;
+ num_emi = FIELD_GET(DVFSRC_V4_GEAR_INFO_EMI, gear_info) + 1;
+ dev_info(dvfsrc->dev,
+ "Discovered %u gears and %u vcore, %u dram, %u emi table entries.\n",
+ num_opps, num_vcore, num_dram, num_emi);
+
+ /* Allocate everything now as anything else after that cannot fail */
+ desc = devm_kzalloc(dvfsrc->dev, sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ return -ENOMEM;
+
+ dvfsrc_opps = devm_kcalloc(dvfsrc->dev, num_opps + 1,
+ sizeof(*dvfsrc_opps), GFP_KERNEL);
+ if (!dvfsrc_opps)
+ return -ENOMEM;
+
+ /* Read the OPP table gear indices */
+ for (i = 0; i <= num_opps; i++) {
+ gear_info = dvfsrc_get_opp_gear(dvfsrc, num_opps - i);
+ dvfsrc_opps[i].vcore_opp = FIELD_GET(DVFSRC_V4_GEAR_INFO_VCORE, gear_info);
+ dvfsrc_opps[i].dram_opp = FIELD_GET(DVFSRC_V4_GEAR_INFO_DRAM, gear_info);
+ dvfsrc_opps[i].emi_opp = FIELD_GET(DVFSRC_V4_GEAR_INFO_EMI, gear_info);
+ };
+ desc->num_opp = num_opps + 1;
+ desc->opps = dvfsrc_opps;
+
+ /* Assign to main structure now that everything is done! */
+ dvfsrc->curr_opps = desc;
+
+ return 0;
+}
+
+static void dvfsrc_set_dram_level_v4(struct mtk_dvfsrc *dvfsrc, u32 level)
+{
+ u32 val = dvfsrc_readl(dvfsrc, DVFSRC_SW_REQ);
+
+ val &= ~DVFSRC_V4_SW_REQ_DRAM_LEVEL;
+ val |= FIELD_PREP(DVFSRC_V4_SW_REQ_DRAM_LEVEL, level);
+
+ dev_dbg(dvfsrc->dev, "%s level=%u\n", __func__, level);
+
+ dvfsrc_writel(dvfsrc, DVFSRC_SW_REQ, val);
+}
+
int mtk_dvfsrc_send_request(const struct device *dev, u32 cmd, u64 data)
{
struct mtk_dvfsrc *dvfsrc = dev_get_drvdata(dev);
@@ -422,6 +664,11 @@ static int mtk_dvfsrc_probe(struct platform_device *pdev)
if (IS_ERR(dvfsrc->regs))
return PTR_ERR(dvfsrc->regs);
+ dvfsrc->clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(dvfsrc->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(dvfsrc->clk),
+ "Couldn't get and enable DVFSRC clock\n");
+
arm_smccc_smc(MTK_SIP_DVFSRC_VCOREFS_CONTROL, MTK_SIP_DVFSRC_INIT,
0, 0, 0, 0, 0, 0, &ares);
if (ares.a0)
@@ -430,7 +677,14 @@ static int mtk_dvfsrc_probe(struct platform_device *pdev)
dvfsrc->dram_type = ares.a1;
dev_dbg(&pdev->dev, "DRAM Type: %d\n", dvfsrc->dram_type);
- dvfsrc->curr_opps = &dvfsrc->dvd->opps_desc[dvfsrc->dram_type];
+ /* Newer versions of the DVFSRC MCU have pre-programmed gear tables */
+ if (dvfsrc->dvd->get_hw_opps) {
+ ret = dvfsrc->dvd->get_hw_opps(dvfsrc);
+ if (ret)
+ return ret;
+ } else {
+ dvfsrc->curr_opps = &dvfsrc->dvd->opps_desc[dvfsrc->dram_type];
+ }
platform_set_drvdata(pdev, dvfsrc);
ret = devm_of_platform_populate(&pdev->dev);
@@ -440,17 +694,28 @@ static int mtk_dvfsrc_probe(struct platform_device *pdev)
/* Everything is set up - make it run! */
arm_smccc_smc(MTK_SIP_DVFSRC_VCOREFS_CONTROL, MTK_SIP_DVFSRC_START,
0, 0, 0, 0, 0, 0, &ares);
- if (ares.a0)
+ if (ares.a0 & BIT(0))
return dev_err_probe(&pdev->dev, -EINVAL, "Cannot start DVFSRC: %lu\n", ares.a0);
return 0;
}
-static const struct dvfsrc_bw_constraints dvfsrc_bw_constr_v1 = { 0, 0, 0 };
-static const struct dvfsrc_bw_constraints dvfsrc_bw_constr_v2 = {
- .max_dram_nom_bw = 255,
- .max_dram_peak_bw = 255,
- .max_dram_hrt_bw = 1023,
+static const u32 dvfsrc_bw_min_constr_none[DVFSRC_BW_MAX] = {
+ [DVFSRC_BW_AVG] = 0,
+ [DVFSRC_BW_PEAK] = 0,
+ [DVFSRC_BW_HRT] = 0,
+};
+
+static const u32 dvfsrc_bw_max_constr_v1[DVFSRC_BW_MAX] = {
+ [DVFSRC_BW_AVG] = U32_MAX,
+ [DVFSRC_BW_PEAK] = U32_MAX,
+ [DVFSRC_BW_HRT] = U32_MAX,
+};
+
+static const u32 dvfsrc_bw_max_constr_v2[DVFSRC_BW_MAX] = {
+ [DVFSRC_BW_AVG] = 65535,
+ [DVFSRC_BW_PEAK] = 65535,
+ [DVFSRC_BW_HRT] = 1023,
};
static const struct dvfsrc_opp dvfsrc_opp_mt6893_lp4[] = {
@@ -483,7 +748,8 @@ static const struct dvfsrc_soc_data mt6893_data = {
.set_vscp_level = dvfsrc_set_vscp_level_v2,
.wait_for_opp_level = dvfsrc_wait_for_opp_level_v2,
.wait_for_vcore_level = dvfsrc_wait_for_vcore_level_v1,
- .bw_constraints = &dvfsrc_bw_constr_v2,
+ .bw_max_constraints = dvfsrc_bw_max_constr_v2,
+ .bw_min_constraints = dvfsrc_bw_min_constr_none,
};
static const struct dvfsrc_opp dvfsrc_opp_mt8183_lp4[] = {
@@ -512,6 +778,7 @@ static const struct dvfsrc_opp_desc dvfsrc_opp_mt8183_desc[] = {
static const struct dvfsrc_soc_data mt8183_data = {
.opps_desc = dvfsrc_opp_mt8183_desc,
.regs = dvfsrc_mt8183_regs,
+ .calc_dram_bw = dvfsrc_calc_dram_bw_v1,
.get_target_level = dvfsrc_get_target_level_v1,
.get_current_level = dvfsrc_get_current_level_v1,
.get_vcore_level = dvfsrc_get_vcore_level_v1,
@@ -520,7 +787,8 @@ static const struct dvfsrc_soc_data mt8183_data = {
.set_vcore_level = dvfsrc_set_vcore_level_v1,
.wait_for_opp_level = dvfsrc_wait_for_opp_level_v1,
.wait_for_vcore_level = dvfsrc_wait_for_vcore_level_v1,
- .bw_constraints = &dvfsrc_bw_constr_v1,
+ .bw_max_constraints = dvfsrc_bw_max_constr_v1,
+ .bw_min_constraints = dvfsrc_bw_min_constr_none,
};
static const struct dvfsrc_opp dvfsrc_opp_mt8195_lp4[] = {
@@ -542,6 +810,7 @@ static const struct dvfsrc_opp_desc dvfsrc_opp_mt8195_desc[] = {
static const struct dvfsrc_soc_data mt8195_data = {
.opps_desc = dvfsrc_opp_mt8195_desc,
.regs = dvfsrc_mt8195_regs,
+ .calc_dram_bw = dvfsrc_calc_dram_bw_v1,
.get_target_level = dvfsrc_get_target_level_v2,
.get_current_level = dvfsrc_get_current_level_v2,
.get_vcore_level = dvfsrc_get_vcore_level_v2,
@@ -553,13 +822,44 @@ static const struct dvfsrc_soc_data mt8195_data = {
.set_vscp_level = dvfsrc_set_vscp_level_v2,
.wait_for_opp_level = dvfsrc_wait_for_opp_level_v2,
.wait_for_vcore_level = dvfsrc_wait_for_vcore_level_v1,
- .bw_constraints = &dvfsrc_bw_constr_v2,
+ .bw_max_constraints = dvfsrc_bw_max_constr_v2,
+ .bw_min_constraints = dvfsrc_bw_min_constr_none,
+};
+
+static const u8 mt8196_bw_units[] = {
+ [DVFSRC_BW_AVG] = 64,
+ [DVFSRC_BW_PEAK] = 64,
+ [DVFSRC_BW_HRT] = 30,
+};
+
+static const struct dvfsrc_soc_data mt8196_data = {
+ .regs = dvfsrc_mt8196_regs,
+ .bw_units = mt8196_bw_units,
+ .has_emi_ddr = true,
+ .get_target_level = dvfsrc_get_target_level_v4,
+ .get_current_level = dvfsrc_get_current_level_v4,
+ .get_vcore_level = dvfsrc_get_vcore_level_v2,
+ .get_vscp_level = dvfsrc_get_vscp_level_v2,
+ .get_opp_count = dvfsrc_get_opp_count_v4,
+ .get_hw_opps = dvfsrc_get_hw_opps_v4,
+ .calc_dram_bw = dvfsrc_calc_dram_bw_v4,
+ .set_dram_bw = dvfsrc_set_dram_bw_v1,
+ .set_dram_peak_bw = dvfsrc_set_dram_peak_bw_v1,
+ .set_dram_hrt_bw = dvfsrc_set_dram_hrt_bw_v1,
+ .set_opp_level = dvfsrc_set_dram_level_v4,
+ .set_vcore_level = dvfsrc_set_vcore_level_v2,
+ .set_vscp_level = dvfsrc_set_vscp_level_v2,
+ .wait_for_opp_level = dvfsrc_wait_for_opp_level_v4,
+ .wait_for_vcore_level = dvfsrc_wait_for_vcore_level_v4,
+ .bw_max_constraints = dvfsrc_bw_max_constr_v2,
+ .bw_min_constraints = dvfsrc_bw_min_constr_none,
};
static const struct of_device_id mtk_dvfsrc_of_match[] = {
{ .compatible = "mediatek,mt6893-dvfsrc", .data = &mt6893_data },
{ .compatible = "mediatek,mt8183-dvfsrc", .data = &mt8183_data },
{ .compatible = "mediatek,mt8195-dvfsrc", .data = &mt8195_data },
+ { .compatible = "mediatek,mt8196-dvfsrc", .data = &mt8196_data },
{ /* sentinel */ }
};
diff --git a/drivers/soc/mediatek/mtk-socinfo.c b/drivers/soc/mediatek/mtk-socinfo.c
index 978c43e9115a..424a1eb82c20 100644
--- a/drivers/soc/mediatek/mtk-socinfo.c
+++ b/drivers/soc/mediatek/mtk-socinfo.c
@@ -59,6 +59,7 @@ static struct socinfo_data socinfo_data_table[] = {
MTK_SOCINFO_ENTRY("MT8195", "MT8195TV/EZA", "Kompanio 1380", 0x81950400, CELL_NOT_USED),
MTK_SOCINFO_ENTRY("MT8195", "MT8195TV/EHZA", "Kompanio 1380", 0x81950404, CELL_NOT_USED),
MTK_SOCINFO_ENTRY("MT8370", "MT8370AV/AZA", "Genio 510", 0x83700000, 0x00000081),
+ MTK_SOCINFO_ENTRY("MT8371", "MT8371AV/AZA", "Genio 520", 0x83710000, 0x00000081),
MTK_SOCINFO_ENTRY("MT8390", "MT8390AV/AZA", "Genio 700", 0x83900000, 0x00000080),
MTK_SOCINFO_ENTRY("MT8391", "MT8391AV/AZA", "Genio 720", 0x83910000, 0x00000080),
MTK_SOCINFO_ENTRY("MT8395", "MT8395AV/ZA", "Genio 1200", 0x83950100, CELL_NOT_USED),
diff --git a/drivers/soc/mediatek/mtk-svs.c b/drivers/soc/mediatek/mtk-svs.c
index f45537546553..99edecb204f2 100644
--- a/drivers/soc/mediatek/mtk-svs.c
+++ b/drivers/soc/mediatek/mtk-svs.c
@@ -9,6 +9,7 @@
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/completion.h>
+#include <linux/cleanup.h>
#include <linux/cpu.h>
#include <linux/cpuidle.h>
#include <linux/debugfs.h>
@@ -789,7 +790,7 @@ static ssize_t svs_enable_debug_write(struct file *filp,
struct svs_bank *svsb = file_inode(filp)->i_private;
struct svs_platform *svsp = dev_get_drvdata(svsb->dev);
int enabled, ret;
- char *buf = NULL;
+ char *buf __free(kfree) = NULL;
if (count >= PAGE_SIZE)
return -EINVAL;
@@ -807,8 +808,6 @@ static ssize_t svs_enable_debug_write(struct file *filp,
svsb->mode_support = SVSB_MODE_ALL_DISABLE;
}
- kfree(buf);
-
return count;
}
diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c
index ae66c2623d25..84a75d8c4b70 100644
--- a/drivers/soc/qcom/cmd-db.c
+++ b/drivers/soc/qcom/cmd-db.c
@@ -349,15 +349,16 @@ static int cmd_db_dev_probe(struct platform_device *pdev)
return -EINVAL;
}
- cmd_db_header = memremap(rmem->base, rmem->size, MEMREMAP_WC);
- if (!cmd_db_header) {
- ret = -ENOMEM;
+ cmd_db_header = devm_memremap(&pdev->dev, rmem->base, rmem->size, MEMREMAP_WC);
+ if (IS_ERR(cmd_db_header)) {
+ ret = PTR_ERR(cmd_db_header);
cmd_db_header = NULL;
return ret;
}
if (!cmd_db_magic_matches(cmd_db_header)) {
dev_err(&pdev->dev, "Invalid Command DB Magic\n");
+ cmd_db_header = NULL;
return -EINVAL;
}
diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c
index 13e174267294..1abfda7a58f2 100644
--- a/drivers/soc/qcom/llcc-qcom.c
+++ b/drivers/soc/qcom/llcc-qcom.c
@@ -182,6 +182,197 @@ enum llcc_reg_offset {
LLCC_TRP_WRS_CACHEABLE_EN,
};
+static const struct llcc_slice_config glymur_data[] = {
+ {
+ .usecase_id = LLCC_CPUSS,
+ .slice_id = 1,
+ .max_cap = 7680,
+ .priority = 1,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ .activate_on_init = true,
+ }, {
+ .usecase_id = LLCC_VIDSC0,
+ .slice_id = 2,
+ .max_cap = 512,
+ .priority = 3,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_AUDIO,
+ .slice_id = 6,
+ .max_cap = 1024,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_VIDSC1,
+ .slice_id = 4,
+ .max_cap = 512,
+ .priority = 3,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_CMPT,
+ .slice_id = 10,
+ .max_cap = 7680,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_GPUHTW,
+ .slice_id = 11,
+ .max_cap = 512,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_GPU,
+ .slice_id = 9,
+ .max_cap = 7680,
+ .priority = 1,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .write_scid_en = true,
+ .write_scid_cacheable_en = true,
+ .stale_en = true,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_MMUHWT,
+ .slice_id = 18,
+ .max_cap = 768,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ .activate_on_init = true,
+ }, {
+ .usecase_id = LLCC_AUDHW,
+ .slice_id = 22,
+ .max_cap = 1024,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_CVP,
+ .slice_id = 8,
+ .max_cap = 64,
+ .priority = 3,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_WRCACHE,
+ .slice_id = 31,
+ .max_cap = 1536,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ .activate_on_init = true,
+ }, {
+ .usecase_id = LLCC_CMPTHCP,
+ .slice_id = 17,
+ .max_cap = 256,
+ .priority = 3,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_LCPDARE,
+ .slice_id = 30,
+ .max_cap = 768,
+ .priority = 3,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .alloc_oneway_en = true,
+ .vict_prio = true,
+ .activate_on_init = true,
+ }, {
+ .usecase_id = LLCC_AENPU,
+ .slice_id = 3,
+ .max_cap = 3072,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .cache_mode = 2,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_ISLAND1,
+ .slice_id = 12,
+ .max_cap = 5632,
+ .priority = 7,
+ .fixed_size = true,
+ .bonus_ways = 0x0,
+ .res_ways = 0x7FF,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_VIDVSP,
+ .slice_id = 28,
+ .max_cap = 256,
+ .priority = 3,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_OOBM_NS,
+ .slice_id = 5,
+ .max_cap = 512,
+ .priority = 1,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_CPUSS_OPP,
+ .slice_id = 32,
+ .max_cap = 0,
+ .fixed_size = true,
+ .bonus_ways = 0x0,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ .activate_on_init = true,
+ }, {
+ .usecase_id = LLCC_PCIE_TCU,
+ .slice_id = 19,
+ .max_cap = 256,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ .activate_on_init = true,
+ }, {
+ .usecase_id = LLCC_VIDSC_VSP1,
+ .slice_id = 29,
+ .max_cap = 256,
+ .priority = 3,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ }
+};
+
static const struct llcc_slice_config ipq5424_data[] = {
{
.usecase_id = LLCC_CPUSS,
@@ -3872,6 +4063,16 @@ static const struct qcom_llcc_config kaanapali_cfg[] = {
},
};
+static const struct qcom_llcc_config glymur_cfg[] = {
+ {
+ .sct_data = glymur_data,
+ .size = ARRAY_SIZE(glymur_data),
+ .reg_offset = llcc_v6_reg_offset,
+ .edac_reg_offset = &llcc_v2_1_edac_reg_offset,
+ .no_edac = true,
+ },
+};
+
static const struct qcom_llcc_config qcs615_cfg[] = {
{
.sct_data = qcs615_data,
@@ -4103,6 +4304,11 @@ static const struct qcom_sct_config kaanapali_cfgs = {
.num_config = ARRAY_SIZE(kaanapali_cfg),
};
+static const struct qcom_sct_config glymur_cfgs = {
+ .llcc_config = glymur_cfg,
+ .num_config = ARRAY_SIZE(glymur_cfg),
+};
+
static const struct qcom_sct_config qcs615_cfgs = {
.llcc_config = qcs615_cfg,
.num_config = ARRAY_SIZE(qcs615_cfg),
@@ -4941,6 +5147,7 @@ err:
}
static const struct of_device_id qcom_llcc_of_match[] = {
+ { .compatible = "qcom,glymur-llcc", .data = &glymur_cfgs },
{ .compatible = "qcom,ipq5424-llcc", .data = &ipq5424_cfgs},
{ .compatible = "qcom,kaanapali-llcc", .data = &kaanapali_cfgs},
{ .compatible = "qcom,qcs615-llcc", .data = &qcs615_cfgs},
diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c
index c239107cb930..c004d444d698 100644
--- a/drivers/soc/qcom/mdt_loader.c
+++ b/drivers/soc/qcom/mdt_loader.c
@@ -227,20 +227,9 @@ void *qcom_mdt_read_metadata(const struct firmware *fw, size_t *data_len,
}
EXPORT_SYMBOL_GPL(qcom_mdt_read_metadata);
-/**
- * qcom_mdt_pas_init() - initialize PAS region for firmware loading
- * @dev: device handle to associate resources with
- * @fw: firmware object for the mdt file
- * @fw_name: name of the firmware, for construction of segment file names
- * @pas_id: PAS identifier
- * @mem_phys: physical address of allocated memory region
- * @ctx: PAS metadata context, to be released by caller
- *
- * Returns 0 on success, negative errno otherwise.
- */
-int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw,
- const char *fw_name, int pas_id, phys_addr_t mem_phys,
- struct qcom_scm_pas_metadata *ctx)
+static int __qcom_mdt_pas_init(struct device *dev, const struct firmware *fw,
+ const char *fw_name, int pas_id, phys_addr_t mem_phys,
+ struct qcom_scm_pas_context *ctx)
{
const struct elf32_phdr *phdrs;
const struct elf32_phdr *phdr;
@@ -302,7 +291,6 @@ int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw,
out:
return ret;
}
-EXPORT_SYMBOL_GPL(qcom_mdt_pas_init);
static bool qcom_mdt_bins_are_split(const struct firmware *fw)
{
@@ -469,7 +457,7 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw,
{
int ret;
- ret = qcom_mdt_pas_init(dev, fw, fw_name, pas_id, mem_phys, NULL);
+ ret = __qcom_mdt_pas_init(dev, fw, fw_name, pas_id, mem_phys, NULL);
if (ret)
return ret;
@@ -478,5 +466,36 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw,
}
EXPORT_SYMBOL_GPL(qcom_mdt_load);
+/**
+ * qcom_mdt_pas_load - Loads and authenticates the metadata of the firmware
+ * (typically contained in the .mdt file), followed by loading the actual
+ * firmware segments (e.g., .bXX files). Authentication of the segments done
+ * by a separate call.
+ *
+ * The PAS context must be initialized using qcom_scm_pas_context_init()
+ * prior to invoking this function.
+ *
+ * @ctx: Pointer to the PAS (Peripheral Authentication Service) context
+ * @fw: Firmware object representing the .mdt file
+ * @firmware: Name of the firmware used to construct segment file names
+ * @mem_region: Memory region allocated for loading the firmware
+ * @reloc_base: Physical address adjusted after relocation
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int qcom_mdt_pas_load(struct qcom_scm_pas_context *ctx, const struct firmware *fw,
+ const char *firmware, void *mem_region, phys_addr_t *reloc_base)
+{
+ int ret;
+
+ ret = __qcom_mdt_pas_init(ctx->dev, fw, firmware, ctx->pas_id, ctx->mem_phys, ctx);
+ if (ret)
+ return ret;
+
+ return qcom_mdt_load_no_init(ctx->dev, fw, firmware, mem_region, ctx->mem_phys,
+ ctx->mem_size, reloc_base);
+}
+EXPORT_SYMBOL_GPL(qcom_mdt_pas_load);
+
MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/pmic_glink_altmode.c b/drivers/soc/qcom/pmic_glink_altmode.c
index 7f11acd33323..d0afdcb96ee1 100644
--- a/drivers/soc/qcom/pmic_glink_altmode.c
+++ b/drivers/soc/qcom/pmic_glink_altmode.c
@@ -14,10 +14,12 @@
#include <linux/soc/qcom/pdr.h>
#include <drm/bridge/aux-bridge.h>
+#include <linux/usb/pd.h>
#include <linux/usb/typec_altmode.h>
#include <linux/usb/typec_dp.h>
#include <linux/usb/typec_mux.h>
#include <linux/usb/typec_retimer.h>
+#include <linux/usb/typec_tbt.h>
#include <linux/soc/qcom/pmic_glink.h>
@@ -37,11 +39,38 @@ struct usbc_write_req {
__le32 reserved;
};
-#define NOTIFY_PAYLOAD_SIZE 16
+struct usbc_sc8280x_dp_data {
+ u8 pin_assignment : 6;
+ u8 hpd_state : 1;
+ u8 hpd_irq : 1;
+ u8 res[7];
+};
+
+/* Used for both TBT and USB4 notifications */
+struct usbc_sc8280x_tbt_data {
+ u8 usb_speed : 3;
+ u8 cable_type : 3;
+ /* This field is NOP on USB4, all cables support rounded rates by spec */
+ u8 rounded_cable : 1;
+ u8 power_limited : 1;
+ u8 res[11];
+};
+
struct usbc_notify {
struct pmic_glink_hdr hdr;
- char payload[NOTIFY_PAYLOAD_SIZE];
- u32 reserved;
+ u8 port_idx;
+ u8 orientation;
+ u8 mux_ctrl;
+#define MUX_CTRL_STATE_NO_CONN 0
+#define MUX_CTRL_STATE_TUNNELING 4
+
+ u8 res;
+ __le16 vid;
+ __le16 svid;
+ union usbc_sc8280x_extended_data {
+ struct usbc_sc8280x_dp_data dp;
+ struct usbc_sc8280x_tbt_data tbt;
+ } extended_data;
};
struct usbc_sc8180x_notify {
@@ -74,6 +103,7 @@ struct pmic_glink_altmode_port {
struct typec_retimer *typec_retimer;
struct typec_retimer_state retimer_state;
struct typec_altmode dp_alt;
+ struct typec_altmode tbt_alt;
struct work_struct work;
@@ -81,10 +111,12 @@ struct pmic_glink_altmode_port {
enum typec_orientation orientation;
u16 svid;
+ struct usbc_sc8280x_tbt_data tbt_data;
u8 dp_data;
u8 mode;
u8 hpd_state;
u8 hpd_irq;
+ u8 mux_ctrl;
};
#define work_to_altmode(w) container_of((w), struct pmic_glink_altmode, enable_work)
@@ -170,6 +202,102 @@ static void pmic_glink_altmode_enable_dp(struct pmic_glink_altmode *altmode,
dev_err(altmode->dev, "failed to setup retimer to DP: %d\n", ret);
}
+static void pmic_glink_altmode_enable_tbt(struct pmic_glink_altmode *altmode,
+ struct pmic_glink_altmode_port *port)
+{
+ struct usbc_sc8280x_tbt_data *tbt = &port->tbt_data;
+ struct typec_thunderbolt_data tbt_data = {};
+ u32 cable_speed;
+ int ret;
+
+ /* Device Discover Mode VDO */
+ tbt_data.device_mode = TBT_MODE;
+ tbt_data.device_mode |= TBT_SET_ADAPTER(TBT_ADAPTER_TBT3);
+
+ /* Cable Discover Mode VDO */
+ tbt_data.cable_mode = TBT_MODE;
+
+ if (tbt->usb_speed == 0) {
+ cable_speed = TBT_CABLE_USB3_PASSIVE;
+ } else if (tbt->usb_speed == 1) {
+ cable_speed = TBT_CABLE_10_AND_20GBPS;
+ } else {
+ dev_err(altmode->dev,
+ "Got illegal TBT3 cable speed value (%u), falling back to passive\n",
+ tbt->usb_speed);
+ cable_speed = TBT_CABLE_USB3_PASSIVE;
+ }
+ tbt_data.cable_mode |= TBT_SET_CABLE_SPEED(cable_speed);
+
+ if (tbt->cable_type) {
+ tbt_data.cable_mode |= TBT_CABLE_ACTIVE_PASSIVE;
+ tbt_data.cable_mode |= TBT_SET_CABLE_ROUNDED(tbt->rounded_cable);
+ }
+
+ /* Enter Mode VDO */
+ tbt_data.enter_vdo |= TBT_MODE;
+ tbt_data.enter_vdo |= TBT_ENTER_MODE_CABLE_SPEED(cable_speed);
+
+ if (tbt->cable_type) {
+ tbt_data.enter_vdo |= TBT_CABLE_ACTIVE_PASSIVE;
+ tbt_data.enter_vdo |= TBT_SET_CABLE_ROUNDED(tbt->rounded_cable);
+ }
+
+ port->state.alt = &port->tbt_alt;
+ port->state.data = &tbt_data;
+ port->state.mode = TYPEC_MODAL_STATE(port->mode);
+
+ ret = typec_mux_set(port->typec_mux, &port->state);
+ if (ret)
+ dev_err(altmode->dev, "failed to switch mux to USB: %d\n", ret);
+
+ port->retimer_state.alt = &port->tbt_alt;
+ port->retimer_state.data = &tbt_data;
+ port->retimer_state.mode = TYPEC_MODAL_STATE(port->mode);
+
+ ret = typec_retimer_set(port->typec_retimer, &port->retimer_state);
+ if (ret)
+ dev_err(altmode->dev, "failed to setup retimer to USB: %d\n", ret);
+}
+
+static void pmic_glink_altmode_enable_usb4(struct pmic_glink_altmode *altmode,
+ struct pmic_glink_altmode_port *port)
+{
+ struct usbc_sc8280x_tbt_data *tbt = &port->tbt_data;
+ struct enter_usb_data data = {};
+ int ret;
+
+ data.eudo = FIELD_PREP(EUDO_USB_MODE_MASK, EUDO_USB_MODE_USB4);
+
+ if (tbt->usb_speed == 0) {
+ data.eudo |= FIELD_PREP(EUDO_CABLE_SPEED_MASK, EUDO_CABLE_SPEED_USB4_GEN2);
+ } else if (tbt->usb_speed == 1) {
+ data.eudo |= FIELD_PREP(EUDO_CABLE_SPEED_MASK, EUDO_CABLE_SPEED_USB4_GEN3);
+ } else {
+ pr_err("Got illegal USB4 cable speed value (%u), falling back to G2\n",
+ tbt->usb_speed);
+ data.eudo |= FIELD_PREP(EUDO_CABLE_SPEED_MASK, EUDO_CABLE_SPEED_USB4_GEN2);
+ }
+
+ data.eudo |= FIELD_PREP(EUDO_CABLE_TYPE_MASK, tbt->cable_type);
+
+ port->state.alt = NULL;
+ port->state.data = &data;
+ port->state.mode = TYPEC_MODE_USB4;
+
+ ret = typec_mux_set(port->typec_mux, &port->state);
+ if (ret)
+ dev_err(altmode->dev, "failed to switch mux to USB: %d\n", ret);
+
+ port->retimer_state.alt = NULL;
+ port->retimer_state.data = &data;
+ port->retimer_state.mode = TYPEC_MODE_USB4;
+
+ ret = typec_retimer_set(port->typec_retimer, &port->retimer_state);
+ if (ret)
+ dev_err(altmode->dev, "failed to setup retimer to USB: %d\n", ret);
+}
+
static void pmic_glink_altmode_enable_usb(struct pmic_glink_altmode *altmode,
struct pmic_glink_altmode_port *port)
{
@@ -222,15 +350,15 @@ static void pmic_glink_altmode_worker(struct work_struct *work)
typec_switch_set(alt_port->typec_switch, alt_port->orientation);
- if (alt_port->svid == USB_TYPEC_DP_SID) {
- if (alt_port->mode == 0xff) {
- pmic_glink_altmode_safe(altmode, alt_port);
- } else {
- pmic_glink_altmode_enable_dp(altmode, alt_port,
- alt_port->mode,
- alt_port->hpd_state,
- alt_port->hpd_irq);
- }
+ if (alt_port->mux_ctrl == MUX_CTRL_STATE_NO_CONN) {
+ pmic_glink_altmode_safe(altmode, alt_port);
+ } else if (alt_port->svid == USB_TYPEC_TBT_SID) {
+ pmic_glink_altmode_enable_tbt(altmode, alt_port);
+ } else if (alt_port->svid == USB_TYPEC_DP_SID) {
+ pmic_glink_altmode_enable_dp(altmode, alt_port,
+ alt_port->mode,
+ alt_port->hpd_state,
+ alt_port->hpd_irq);
if (alt_port->hpd_state)
conn_status = connector_status_connected;
@@ -238,6 +366,8 @@ static void pmic_glink_altmode_worker(struct work_struct *work)
conn_status = connector_status_disconnected;
drm_aux_hpd_bridge_notify(&alt_port->bridge->dev, conn_status);
+ } else if (alt_port->mux_ctrl == MUX_CTRL_STATE_TUNNELING) {
+ pmic_glink_altmode_enable_usb4(altmode, alt_port);
} else {
pmic_glink_altmode_enable_usb(altmode, alt_port);
}
@@ -314,11 +444,10 @@ static void pmic_glink_altmode_sc8280xp_notify(struct pmic_glink_altmode *altmod
u16 svid, const void *data, size_t len)
{
struct pmic_glink_altmode_port *alt_port;
+ const struct usbc_sc8280x_tbt_data *tbt;
+ const struct usbc_sc8280x_dp_data *dp;
const struct usbc_notify *notify;
u8 orientation;
- u8 hpd_state;
- u8 hpd_irq;
- u8 mode;
u8 port;
if (len != sizeof(*notify)) {
@@ -329,11 +458,8 @@ static void pmic_glink_altmode_sc8280xp_notify(struct pmic_glink_altmode *altmod
notify = data;
- port = notify->payload[0];
- orientation = notify->payload[1];
- mode = FIELD_GET(SC8280XP_DPAM_MASK, notify->payload[8]) - DPAM_HPD_A;
- hpd_state = FIELD_GET(SC8280XP_HPD_STATE_MASK, notify->payload[8]);
- hpd_irq = FIELD_GET(SC8280XP_HPD_IRQ_MASK, notify->payload[8]);
+ port = notify->port_idx;
+ orientation = notify->orientation;
if (port >= ARRAY_SIZE(altmode->ports) || !altmode->ports[port].altmode) {
dev_dbg(altmode->dev, "notification on undefined port %d\n", port);
@@ -343,9 +469,21 @@ static void pmic_glink_altmode_sc8280xp_notify(struct pmic_glink_altmode *altmod
alt_port = &altmode->ports[port];
alt_port->orientation = pmic_glink_altmode_orientation(orientation);
alt_port->svid = svid;
- alt_port->mode = mode;
- alt_port->hpd_state = hpd_state;
- alt_port->hpd_irq = hpd_irq;
+ alt_port->mux_ctrl = notify->mux_ctrl;
+
+ if (svid == USB_TYPEC_DP_SID) {
+ dp = &notify->extended_data.dp;
+
+ alt_port->mode = dp->pin_assignment - DPAM_HPD_A;
+ alt_port->hpd_state = dp->hpd_state;
+ alt_port->hpd_irq = dp->hpd_irq;
+ } else if (alt_port->mux_ctrl == MUX_CTRL_STATE_TUNNELING) {
+ /* Valid for both USB4 and TBT3 */
+ tbt = &notify->extended_data.tbt;
+
+ alt_port->tbt_data = *tbt;
+ }
+
schedule_work(&alt_port->work);
}
@@ -471,6 +609,10 @@ static int pmic_glink_altmode_probe(struct auxiliary_device *adev,
alt_port->dp_alt.mode = USB_TYPEC_DP_MODE;
alt_port->dp_alt.active = 1;
+ alt_port->tbt_alt.svid = USB_TYPEC_TBT_SID;
+ alt_port->tbt_alt.mode = TYPEC_TBT_MODE;
+ alt_port->tbt_alt.active = 1;
+
alt_port->typec_mux = fwnode_typec_mux_get(fwnode);
if (IS_ERR(alt_port->typec_mux)) {
fwnode_handle_put(fwnode);
diff --git a/drivers/soc/qcom/qmi_encdec.c b/drivers/soc/qcom/qmi_encdec.c
index 7660a960fb45..28ce6f130b6a 100644
--- a/drivers/soc/qcom/qmi_encdec.c
+++ b/drivers/soc/qcom/qmi_encdec.c
@@ -23,18 +23,60 @@
*p_length |= ((u8)*p_src) << 8; \
} while (0)
-#define QMI_ENCDEC_ENCODE_N_BYTES(p_dst, p_src, size) \
+#define QMI_ENCDEC_ENCODE_U8(p_dst, p_src) \
do { \
- memcpy(p_dst, p_src, size); \
- p_dst = (u8 *)p_dst + size; \
- p_src = (u8 *)p_src + size; \
+ memcpy(p_dst, p_src, sizeof(u8)); \
+ p_dst = (u8 *)p_dst + sizeof(u8); \
+ p_src = (u8 *)p_src + sizeof(u8); \
} while (0)
-#define QMI_ENCDEC_DECODE_N_BYTES(p_dst, p_src, size) \
+#define QMI_ENCDEC_ENCODE_U16(p_dst, p_src) \
do { \
- memcpy(p_dst, p_src, size); \
- p_dst = (u8 *)p_dst + size; \
- p_src = (u8 *)p_src + size; \
+ *(__le16 *)p_dst = __cpu_to_le16(*(u16 *)p_src); \
+ p_dst = (u8 *)p_dst + sizeof(u16); \
+ p_src = (u8 *)p_src + sizeof(u16); \
+} while (0)
+
+#define QMI_ENCDEC_ENCODE_U32(p_dst, p_src) \
+do { \
+ *(__le32 *)p_dst = __cpu_to_le32(*(u32 *)p_src); \
+ p_dst = (u8 *)p_dst + sizeof(u32); \
+ p_src = (u8 *)p_src + sizeof(u32); \
+} while (0)
+
+#define QMI_ENCDEC_ENCODE_U64(p_dst, p_src) \
+do { \
+ *(__le64 *)p_dst = __cpu_to_le64(*(u64 *)p_src); \
+ p_dst = (u8 *)p_dst + sizeof(u64); \
+ p_src = (u8 *)p_src + sizeof(u64); \
+} while (0)
+
+#define QMI_ENCDEC_DECODE_U8(p_dst, p_src) \
+do { \
+ memcpy(p_dst, p_src, sizeof(u8)); \
+ p_dst = (u8 *)p_dst + sizeof(u8); \
+ p_src = (u8 *)p_src + sizeof(u8); \
+} while (0)
+
+#define QMI_ENCDEC_DECODE_U16(p_dst, p_src) \
+do { \
+ *(u16 *)p_dst = __le16_to_cpu(*(__le16 *)p_src); \
+ p_dst = (u8 *)p_dst + sizeof(u16); \
+ p_src = (u8 *)p_src + sizeof(u16); \
+} while (0)
+
+#define QMI_ENCDEC_DECODE_U32(p_dst, p_src) \
+do { \
+ *(u32 *)p_dst = __le32_to_cpu(*(__le32 *)p_src); \
+ p_dst = (u8 *)p_dst + sizeof(u32); \
+ p_src = (u8 *)p_src + sizeof(u32); \
+} while (0)
+
+#define QMI_ENCDEC_DECODE_U64(p_dst, p_src) \
+do { \
+ *(u64 *)p_dst = __le64_to_cpu(*(__le64 *)p_src); \
+ p_dst = (u8 *)p_dst + sizeof(u64); \
+ p_src = (u8 *)p_src + sizeof(u64); \
} while (0)
#define UPDATE_ENCODE_VARIABLES(temp_si, buf_dst, \
@@ -161,7 +203,8 @@ static int qmi_calc_min_msg_len(const struct qmi_elem_info *ei_array,
* of primary data type which include u8 - u64 or similar. This
* function returns the number of bytes of encoded information.
*
- * Return: The number of bytes of encoded information.
+ * Return: The number of bytes of encoded information on success or negative
+ * errno on error.
*/
static int qmi_encode_basic_elem(void *buf_dst, const void *buf_src,
u32 elem_len, u32 elem_size)
@@ -169,7 +212,24 @@ static int qmi_encode_basic_elem(void *buf_dst, const void *buf_src,
u32 i, rc = 0;
for (i = 0; i < elem_len; i++) {
- QMI_ENCDEC_ENCODE_N_BYTES(buf_dst, buf_src, elem_size);
+ switch (elem_size) {
+ case sizeof(u8):
+ QMI_ENCDEC_ENCODE_U8(buf_dst, buf_src);
+ break;
+ case sizeof(u16):
+ QMI_ENCDEC_ENCODE_U16(buf_dst, buf_src);
+ break;
+ case sizeof(u32):
+ QMI_ENCDEC_ENCODE_U32(buf_dst, buf_src);
+ break;
+ case sizeof(u64):
+ QMI_ENCDEC_ENCODE_U64(buf_dst, buf_src);
+ break;
+ default:
+ pr_err("%s: Unrecognized element size\n", __func__);
+ return -EINVAL;
+ }
+
rc += elem_size;
}
@@ -267,11 +327,15 @@ static int qmi_encode_string_elem(const struct qmi_elem_info *ei_array,
}
rc = qmi_encode_basic_elem(buf_dst, &string_len,
1, string_len_sz);
+ if (rc < 0)
+ return rc;
encoded_bytes += rc;
}
rc = qmi_encode_basic_elem(buf_dst + encoded_bytes, buf_src,
string_len, temp_ei->elem_size);
+ if (rc < 0)
+ return rc;
encoded_bytes += rc;
return encoded_bytes;
@@ -333,6 +397,8 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf,
case QMI_OPT_FLAG:
rc = qmi_encode_basic_elem(&opt_flag_value, buf_src,
1, sizeof(u8));
+ if (rc < 0)
+ return rc;
if (opt_flag_value)
temp_ei = temp_ei + 1;
else
@@ -340,6 +406,7 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf,
break;
case QMI_DATA_LEN:
+ memcpy(&data_len_value, buf_src, sizeof(u32));
data_len_sz = temp_ei->elem_size == sizeof(u8) ?
sizeof(u8) : sizeof(u16);
/* Check to avoid out of range buffer access */
@@ -350,15 +417,17 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf,
return -ETOOSMALL;
}
if (data_len_sz == sizeof(u8)) {
- val8 = *(u8 *)buf_src;
- data_len_value = (u32)val8;
+ val8 = data_len_value;
rc = qmi_encode_basic_elem(buf_dst, &val8,
1, data_len_sz);
+ if (rc < 0)
+ return rc;
} else {
- val16 = *(u16 *)buf_src;
- data_len_value = (u32)le16_to_cpu(val16);
+ val16 = data_len_value;
rc = qmi_encode_basic_elem(buf_dst, &val16,
1, data_len_sz);
+ if (rc < 0)
+ return rc;
}
UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst,
encoded_bytes, tlv_len,
@@ -386,6 +455,8 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf,
rc = qmi_encode_basic_elem(buf_dst, buf_src,
data_len_value,
temp_ei->elem_size);
+ if (rc < 0)
+ return rc;
UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst,
encoded_bytes, tlv_len,
encode_tlv, rc);
@@ -444,7 +515,8 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf,
* of primary data type which include u8 - u64 or similar. This
* function returns the number of bytes of decoded information.
*
- * Return: The total size of the decoded data elements, in bytes.
+ * Return: The total size of the decoded data elements, in bytes, on success or
+ * negative errno on error.
*/
static int qmi_decode_basic_elem(void *buf_dst, const void *buf_src,
u32 elem_len, u32 elem_size)
@@ -452,7 +524,24 @@ static int qmi_decode_basic_elem(void *buf_dst, const void *buf_src,
u32 i, rc = 0;
for (i = 0; i < elem_len; i++) {
- QMI_ENCDEC_DECODE_N_BYTES(buf_dst, buf_src, elem_size);
+ switch (elem_size) {
+ case sizeof(u8):
+ QMI_ENCDEC_DECODE_U8(buf_dst, buf_src);
+ break;
+ case sizeof(u16):
+ QMI_ENCDEC_DECODE_U16(buf_dst, buf_src);
+ break;
+ case sizeof(u32):
+ QMI_ENCDEC_DECODE_U32(buf_dst, buf_src);
+ break;
+ case sizeof(u64):
+ QMI_ENCDEC_DECODE_U64(buf_dst, buf_src);
+ break;
+ default:
+ pr_err("%s: Unrecognized element size\n", __func__);
+ return -EINVAL;
+ }
+
rc += elem_size;
}
@@ -544,10 +633,14 @@ static int qmi_decode_string_elem(const struct qmi_elem_info *ei_array,
if (string_len_sz == sizeof(u8)) {
rc = qmi_decode_basic_elem(&val8, buf_src,
1, string_len_sz);
+ if (rc < 0)
+ return rc;
string_len = (u32)val8;
} else {
rc = qmi_decode_basic_elem(&val16, buf_src,
1, string_len_sz);
+ if (rc < 0)
+ return rc;
string_len = (u32)val16;
}
decoded_bytes += rc;
@@ -565,6 +658,8 @@ static int qmi_decode_string_elem(const struct qmi_elem_info *ei_array,
rc = qmi_decode_basic_elem(buf_dst, buf_src + decoded_bytes,
string_len, temp_ei->elem_size);
+ if (rc < 0)
+ return rc;
*((char *)buf_dst + string_len) = '\0';
decoded_bytes += rc;
@@ -625,7 +720,6 @@ static int qmi_decode(const struct qmi_elem_info *ei_array, void *out_c_struct,
int rc;
u8 val8;
u16 val16;
- u32 val32;
while (decoded_bytes < in_buf_len) {
if (dec_level >= 2 && temp_ei->data_type == QMI_EOTI)
@@ -667,14 +761,17 @@ static int qmi_decode(const struct qmi_elem_info *ei_array, void *out_c_struct,
if (data_len_sz == sizeof(u8)) {
rc = qmi_decode_basic_elem(&val8, buf_src,
1, data_len_sz);
+ if (rc < 0)
+ return rc;
data_len_value = (u32)val8;
} else {
rc = qmi_decode_basic_elem(&val16, buf_src,
1, data_len_sz);
+ if (rc < 0)
+ return rc;
data_len_value = (u32)val16;
}
- val32 = cpu_to_le32(data_len_value);
- memcpy(buf_dst, &val32, sizeof(u32));
+ memcpy(buf_dst, &data_len_value, sizeof(u32));
temp_ei = temp_ei + 1;
buf_dst = out_c_struct + temp_ei->offset;
tlv_len -= data_len_sz;
@@ -701,6 +798,8 @@ static int qmi_decode(const struct qmi_elem_info *ei_array, void *out_c_struct,
rc = qmi_decode_basic_elem(buf_dst, buf_src,
data_len_value,
temp_ei->elem_size);
+ if (rc < 0)
+ return rc;
UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc);
break;
diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
index c18a0c946f76..d5c94b47f431 100644
--- a/drivers/soc/qcom/smem.c
+++ b/drivers/soc/qcom/smem.c
@@ -1219,7 +1219,9 @@ static int qcom_smem_probe(struct platform_device *pdev)
smem->item_count = qcom_smem_get_item_count(smem);
break;
case SMEM_GLOBAL_HEAP_VERSION:
- qcom_smem_map_global(smem, size);
+ ret = qcom_smem_map_global(smem, size);
+ if (ret < 0)
+ return ret;
smem->item_count = SMEM_ITEM_COUNT;
break;
default:
diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig
index 52a4b1463804..1e50dc7c31cd 100644
--- a/drivers/soc/renesas/Kconfig
+++ b/drivers/soc/renesas/Kconfig
@@ -62,6 +62,7 @@ config ARCH_RZN1
select PM
select PM_GENERIC_DOMAINS
select ARM_AMBA
+ select RZN1_IRQMUX if GPIO_DWAPB
if ARM && ARCH_RENESAS
@@ -430,6 +431,7 @@ config ARCH_R9A09G077
config ARCH_R9A09G087
bool "ARM64 Platform support for R9A09G087 (RZ/N2H)"
default y if ARCH_RENESAS
+ select RENESAS_RZT2H_ICU
help
This enables support for the Renesas RZ/N2H SoC variants.
@@ -461,6 +463,9 @@ config PWC_RZV2M
config RST_RCAR
bool "Reset Controller support for R-Car" if COMPILE_TEST
+config RZN1_IRQMUX
+ bool "Renesas RZ/N1 GPIO IRQ multiplexer support" if COMPILE_TEST
+
config SYSC_RZ
bool "System controller for RZ SoCs" if COMPILE_TEST
select MFD_SYSCON
diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile
index 3bdcc6a395d5..33d44d964d61 100644
--- a/drivers/soc/renesas/Makefile
+++ b/drivers/soc/renesas/Makefile
@@ -14,4 +14,5 @@ obj-$(CONFIG_SYS_R9A09G057) += r9a09g057-sys.o
# Family
obj-$(CONFIG_PWC_RZV2M) += pwc-rzv2m.o
obj-$(CONFIG_RST_RCAR) += rcar-rst.o
+obj-$(CONFIG_RZN1_IRQMUX) += rzn1_irqmux.o
obj-$(CONFIG_SYSC_RZ) += rz-sysc.o
diff --git a/drivers/soc/renesas/rzn1_irqmux.c b/drivers/soc/renesas/rzn1_irqmux.c
new file mode 100644
index 000000000000..b50b295f83d7
--- /dev/null
+++ b/drivers/soc/renesas/rzn1_irqmux.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * RZ/N1 GPIO Interrupt Multiplexer
+ *
+ * Copyright 2025 Schneider Electric
+ * Author: Herve Codina <herve.codina@bootlin.com>
+ */
+
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+/*
+ * Up to 8 output lines are connected to GIC SPI interrupt controller
+ * starting at IRQ 103.
+ */
+#define RZN1_IRQMUX_GIC_SPI_BASE 103
+#define RZN1_IRQMUX_NUM_OUTPUTS 8
+
+static int rzn1_irqmux_parent_args_to_line_index(struct device *dev,
+ const struct of_phandle_args *parent_args)
+{
+ /*
+ * The parent interrupt should be one of the GIC controller.
+ * Three arguments must be provided.
+ * - args[0]: GIC_SPI
+ * - args[1]: The GIC interrupt number
+ * - args[2]: The interrupt flags
+ *
+ * We retrieve the line index based on the GIC interrupt number
+ * provided.
+ */
+
+ if (parent_args->args_count != 3 || parent_args->args[0] != GIC_SPI) {
+ dev_err(dev, "Invalid interrupt-map item\n");
+ return -EINVAL;
+ }
+
+ if (parent_args->args[1] < RZN1_IRQMUX_GIC_SPI_BASE ||
+ parent_args->args[1] >= RZN1_IRQMUX_GIC_SPI_BASE + RZN1_IRQMUX_NUM_OUTPUTS) {
+ dev_err(dev, "Invalid GIC interrupt %u\n", parent_args->args[1]);
+ return -EINVAL;
+ }
+
+ return parent_args->args[1] - RZN1_IRQMUX_GIC_SPI_BASE;
+}
+
+static int rzn1_irqmux_probe(struct platform_device *pdev)
+{
+ DECLARE_BITMAP(index_done, RZN1_IRQMUX_NUM_OUTPUTS) = {};
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct of_imap_parser imap_parser;
+ struct of_imap_item imap_item;
+ u32 __iomem *regs;
+ int index;
+ int ret;
+ u32 tmp;
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ /* We support only #interrupt-cells = <1> and #address-cells = <0> */
+ ret = of_property_read_u32(np, "#interrupt-cells", &tmp);
+ if (ret)
+ return ret;
+ if (tmp != 1)
+ return -EINVAL;
+
+ ret = of_property_read_u32(np, "#address-cells", &tmp);
+ if (ret)
+ return ret;
+ if (tmp != 0)
+ return -EINVAL;
+
+ ret = of_imap_parser_init(&imap_parser, np, &imap_item);
+ if (ret)
+ return ret;
+
+ for_each_of_imap_item(&imap_parser, &imap_item) {
+ index = rzn1_irqmux_parent_args_to_line_index(dev, &imap_item.parent_args);
+ if (index < 0) {
+ of_node_put(imap_item.parent_args.np);
+ return index;
+ }
+
+ if (test_and_set_bit(index, index_done)) {
+ of_node_put(imap_item.parent_args.np);
+ dev_err(dev, "Mux output line %d already defined in interrupt-map\n",
+ index);
+ return -EINVAL;
+ }
+
+ /*
+ * The child #address-cells is 0 (already checked). The first
+ * value in imap item is the src hwirq.
+ */
+ writel(imap_item.child_imap[0], regs + index);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id rzn1_irqmux_of_match[] = {
+ { .compatible = "renesas,rzn1-gpioirqmux", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rzn1_irqmux_of_match);
+
+static struct platform_driver rzn1_irqmux_driver = {
+ .probe = rzn1_irqmux_probe,
+ .driver = {
+ .name = "rzn1_irqmux",
+ .of_match_table = rzn1_irqmux_of_match,
+ },
+};
+module_platform_driver(rzn1_irqmux_driver);
+
+MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
+MODULE_DESCRIPTION("Renesas RZ/N1 GPIO IRQ Multiplexer Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/soc/rockchip/grf.c b/drivers/soc/rockchip/grf.c
index 27bfa09ff251..04937c40da47 100644
--- a/drivers/soc/rockchip/grf.c
+++ b/drivers/soc/rockchip/grf.c
@@ -146,7 +146,7 @@ static const struct rockchip_grf_info rk3576_sysgrf __initconst = {
.num_values = ARRAY_SIZE(rk3576_defaults_sys_grf),
};
-#define RK3576_IOCGRF_MISC_CON 0x04F0
+#define RK3576_IOCGRF_MISC_CON 0x40F0
static const struct rockchip_grf_value rk3576_defaults_ioc_grf[] __initconst = {
{ "jtag switching", RK3576_IOCGRF_MISC_CON, FIELD_PREP_WM16_CONST(BIT(1), 0) },
@@ -217,34 +217,33 @@ static int __init rockchip_grf_init(void)
struct regmap *grf;
int ret, i;
- np = of_find_matching_node_and_match(NULL, rockchip_grf_dt_match,
- &match);
- if (!np)
- return -ENODEV;
- if (!match || !match->data) {
- pr_err("%s: missing grf data\n", __func__);
- of_node_put(np);
- return -EINVAL;
- }
-
- grf_info = match->data;
-
- grf = syscon_node_to_regmap(np);
- of_node_put(np);
- if (IS_ERR(grf)) {
- pr_err("%s: could not get grf syscon\n", __func__);
- return PTR_ERR(grf);
- }
-
- for (i = 0; i < grf_info->num_values; i++) {
- const struct rockchip_grf_value *val = &grf_info->values[i];
-
- pr_debug("%s: adjusting %s in %#6x to %#10x\n", __func__,
- val->desc, val->reg, val->val);
- ret = regmap_write(grf, val->reg, val->val);
- if (ret < 0)
- pr_err("%s: write to %#6x failed with %d\n",
- __func__, val->reg, ret);
+ for_each_matching_node_and_match(np, rockchip_grf_dt_match, &match) {
+ if (!of_device_is_available(np))
+ continue;
+ if (!match || !match->data) {
+ pr_err("%s: missing grf data\n", __func__);
+ of_node_put(np);
+ return -EINVAL;
+ }
+
+ grf_info = match->data;
+
+ grf = syscon_node_to_regmap(np);
+ if (IS_ERR(grf)) {
+ pr_err("%s: could not get grf syscon\n", __func__);
+ return PTR_ERR(grf);
+ }
+
+ for (i = 0; i < grf_info->num_values; i++) {
+ const struct rockchip_grf_value *val = &grf_info->values[i];
+
+ pr_debug("%s: adjusting %s in %#6x to %#10x\n", __func__,
+ val->desc, val->reg, val->val);
+ ret = regmap_write(grf, val->reg, val->val);
+ if (ret < 0)
+ pr_err("%s: write to %#6x failed with %d\n",
+ __func__, val->reg, ret);
+ }
}
return 0;
diff --git a/drivers/soc/samsung/exynos-chipid.c b/drivers/soc/samsung/exynos-chipid.c
index d3b4b5508e0c..6ef9751e2509 100644
--- a/drivers/soc/samsung/exynos-chipid.c
+++ b/drivers/soc/samsung/exynos-chipid.c
@@ -14,7 +14,9 @@
#include <linux/array_size.h>
#include <linux/device.h>
-#include <linux/errno.h>
+#include <linux/device/devres.h>
+#include <linux/err.h>
+#include <linux/ioport.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -27,9 +29,11 @@
#include "exynos-asv.h"
struct exynos_chipid_variant {
- unsigned int rev_reg; /* revision register offset */
+ unsigned int main_rev_reg; /* main revision register offset */
+ unsigned int sub_rev_reg; /* sub revision register offset */
unsigned int main_rev_shift; /* main revision offset in rev_reg */
unsigned int sub_rev_shift; /* sub revision offset in rev_reg */
+ bool efuse;
};
struct exynos_chipid_info {
@@ -68,9 +72,11 @@ static const struct exynos_soc_id {
{ "EXYNOS990", 0xE9830000 },
{ "EXYNOSAUTOV9", 0xAAA80000 },
{ "EXYNOSAUTOV920", 0x0A920000 },
+ /* Compatible with: google,gs101-otp */
+ { "GS101", 0x9845000 },
};
-static const char *product_id_to_soc_id(unsigned int product_id)
+static const char *exynos_product_id_to_name(unsigned int product_id)
{
int i;
@@ -80,8 +86,8 @@ static const char *product_id_to_soc_id(unsigned int product_id)
return NULL;
}
-static int exynos_chipid_get_chipid_info(struct regmap *regmap,
- const struct exynos_chipid_variant *data,
+static int exynos_chipid_get_chipid_info(struct device *dev,
+ struct regmap *regmap, const struct exynos_chipid_variant *data,
struct exynos_chipid_info *soc_info)
{
int ret;
@@ -89,21 +95,61 @@ static int exynos_chipid_get_chipid_info(struct regmap *regmap,
ret = regmap_read(regmap, EXYNOS_CHIPID_REG_PRO_ID, &val);
if (ret < 0)
- return ret;
+ return dev_err_probe(dev, ret, "failed to read Product ID\n");
soc_info->product_id = val & EXYNOS_MASK;
- if (data->rev_reg != EXYNOS_CHIPID_REG_PRO_ID) {
- ret = regmap_read(regmap, data->rev_reg, &val);
+ if (data->sub_rev_reg == EXYNOS_CHIPID_REG_PRO_ID) {
+ /* exynos4210 case */
+ main_rev = (val >> data->main_rev_shift) & EXYNOS_REV_PART_MASK;
+ sub_rev = (val >> data->sub_rev_shift) & EXYNOS_REV_PART_MASK;
+ } else {
+ unsigned int val2;
+
+ ret = regmap_read(regmap, data->sub_rev_reg, &val2);
if (ret < 0)
- return ret;
+ return dev_err_probe(dev, ret,
+ "failed to read revision\n");
+
+ if (data->main_rev_reg == EXYNOS_CHIPID_REG_PRO_ID)
+ /* gs101 case */
+ main_rev = (val >> data->main_rev_shift) & EXYNOS_REV_PART_MASK;
+ else
+ /* exynos850 case */
+ main_rev = (val2 >> data->main_rev_shift) & EXYNOS_REV_PART_MASK;
+
+ sub_rev = (val2 >> data->sub_rev_shift) & EXYNOS_REV_PART_MASK;
}
- main_rev = (val >> data->main_rev_shift) & EXYNOS_REV_PART_MASK;
- sub_rev = (val >> data->sub_rev_shift) & EXYNOS_REV_PART_MASK;
+
soc_info->revision = (main_rev << EXYNOS_REV_PART_SHIFT) | sub_rev;
return 0;
}
+static struct regmap *exynos_chipid_get_efuse_regmap(struct platform_device *pdev)
+{
+ struct resource *res;
+ void __iomem *base;
+
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(base))
+ return ERR_CAST(base);
+
+ const struct regmap_config reg_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .use_relaxed_mmio = true,
+ .max_register = (resource_size(res) - reg_config.reg_stride),
+ };
+
+ return devm_regmap_init_mmio_clk(&pdev->dev, "pclk", base, &reg_config);
+}
+
+static void exynos_chipid_unregister_soc(void *data)
+{
+ soc_device_unregister(data);
+}
+
static int exynos_chipid_probe(struct platform_device *pdev)
{
const struct exynos_chipid_variant *drv_data;
@@ -117,13 +163,19 @@ static int exynos_chipid_probe(struct platform_device *pdev)
drv_data = of_device_get_match_data(dev);
if (!drv_data)
- return -EINVAL;
+ return dev_err_probe(dev, -EINVAL,
+ "failed to get match data\n");
+
+ if (drv_data->efuse)
+ regmap = exynos_chipid_get_efuse_regmap(pdev);
+ else
+ regmap = device_node_to_regmap(dev->of_node);
- regmap = device_node_to_regmap(dev->of_node);
if (IS_ERR(regmap))
- return PTR_ERR(regmap);
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "failed to get regmap\n");
- ret = exynos_chipid_get_chipid_info(regmap, drv_data, &soc_info);
+ ret = exynos_chipid_get_chipid_info(dev, regmap, drv_data, &soc_info);
if (ret < 0)
return ret;
@@ -141,55 +193,55 @@ static int exynos_chipid_probe(struct platform_device *pdev)
soc_info.revision);
if (!soc_dev_attr->revision)
return -ENOMEM;
- soc_dev_attr->soc_id = product_id_to_soc_id(soc_info.product_id);
- if (!soc_dev_attr->soc_id) {
- pr_err("Unknown SoC\n");
- return -ENODEV;
- }
+
+ soc_dev_attr->soc_id = exynos_product_id_to_name(soc_info.product_id);
+ if (!soc_dev_attr->soc_id)
+ return dev_err_probe(dev, -ENODEV, "Unknown SoC\n");
/* please note that the actual registration will be deferred */
soc_dev = soc_device_register(soc_dev_attr);
if (IS_ERR(soc_dev))
- return PTR_ERR(soc_dev);
+ return dev_err_probe(dev, PTR_ERR(soc_dev),
+ "failed to register to the soc interface\n");
- ret = exynos_asv_init(dev, regmap);
+ ret = devm_add_action_or_reset(dev, exynos_chipid_unregister_soc,
+ soc_dev);
if (ret)
- goto err;
+ return dev_err_probe(dev, ret, "failed to add devm action\n");
- platform_set_drvdata(pdev, soc_dev);
+ ret = exynos_asv_init(dev, regmap);
+ if (ret)
+ return ret;
- dev_info(dev, "Exynos: CPU[%s] PRO_ID[0x%x] REV[0x%x] Detected\n",
- soc_dev_attr->soc_id, soc_info.product_id, soc_info.revision);
+ dev_dbg(dev, "Exynos: CPU[%s] PRO_ID[0x%x] REV[0x%x] Detected\n",
+ soc_dev_attr->soc_id, soc_info.product_id, soc_info.revision);
return 0;
-
-err:
- soc_device_unregister(soc_dev);
-
- return ret;
-}
-
-static void exynos_chipid_remove(struct platform_device *pdev)
-{
- struct soc_device *soc_dev = platform_get_drvdata(pdev);
-
- soc_device_unregister(soc_dev);
}
static const struct exynos_chipid_variant exynos4210_chipid_drv_data = {
- .rev_reg = 0x0,
.main_rev_shift = 4,
.sub_rev_shift = 0,
};
static const struct exynos_chipid_variant exynos850_chipid_drv_data = {
- .rev_reg = 0x10,
+ .main_rev_reg = 0x10,
+ .sub_rev_reg = 0x10,
.main_rev_shift = 20,
.sub_rev_shift = 16,
};
+static const struct exynos_chipid_variant gs101_chipid_drv_data = {
+ .sub_rev_reg = 0x10,
+ .sub_rev_shift = 16,
+ .efuse = true,
+};
+
static const struct of_device_id exynos_chipid_of_device_ids[] = {
{
+ .compatible = "google,gs101-otp",
+ .data = &gs101_chipid_drv_data,
+ }, {
.compatible = "samsung,exynos4210-chipid",
.data = &exynos4210_chipid_drv_data,
}, {
@@ -206,7 +258,6 @@ static struct platform_driver exynos_chipid_driver = {
.of_match_table = exynos_chipid_of_device_ids,
},
.probe = exynos_chipid_probe,
- .remove = exynos_chipid_remove,
};
module_platform_driver(exynos_chipid_driver);
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index f3760a3b3026..9cdbd8ba94be 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -28,6 +28,7 @@
#include <linux/iopoll.h>
#include <linux/irqdomain.h>
#include <linux/irq.h>
+#include <linux/irq_work.h>
#include <linux/kernel.h>
#include <linux/of_address.h>
#include <linux/of_clk.h>
@@ -201,18 +202,20 @@
#define TEGRA_SMC_PMC_WRITE 0xbb
struct pmc_clk {
- struct clk_hw hw;
- unsigned long offs;
- u32 mux_shift;
- u32 force_en_shift;
+ struct clk_hw hw;
+ struct tegra_pmc *pmc;
+ unsigned long offs;
+ u32 mux_shift;
+ u32 force_en_shift;
};
#define to_pmc_clk(_hw) container_of(_hw, struct pmc_clk, hw)
struct pmc_clk_gate {
- struct clk_hw hw;
- unsigned long offs;
- u32 shift;
+ struct clk_hw hw;
+ struct tegra_pmc *pmc;
+ unsigned long offs;
+ u32 shift;
};
#define to_pmc_clk_gate(_hw) container_of(_hw, struct pmc_clk_gate, hw)
@@ -265,6 +268,17 @@ static const struct pmc_clk_init_data tegra_pmc_clks_data[] = {
},
};
+struct tegra_pmc_core_pd {
+ struct generic_pm_domain genpd;
+ struct tegra_pmc *pmc;
+};
+
+static inline struct tegra_pmc_core_pd *
+to_core_pd(struct generic_pm_domain *genpd)
+{
+ return container_of(genpd, struct tegra_pmc_core_pd, genpd);
+}
+
struct tegra_powergate {
struct generic_pm_domain genpd;
struct tegra_pmc *pmc;
@@ -467,7 +481,13 @@ struct tegra_pmc {
unsigned long *wake_type_dual_edge_map;
unsigned long *wake_sw_status_map;
unsigned long *wake_cntrl_level_map;
+
+ struct notifier_block reboot_notifier;
struct syscore syscore;
+
+ /* Pending wake IRQ processing */
+ struct irq_work wake_work;
+ u32 *wake_status;
};
static struct tegra_pmc *pmc = &(struct tegra_pmc) {
@@ -541,12 +561,7 @@ static void tegra_pmc_scratch_writel(struct tegra_pmc *pmc, u32 value,
writel(value, pmc->scratch + offset);
}
-/*
- * TODO Figure out a way to call this with the struct tegra_pmc * passed in.
- * This currently doesn't work because readx_poll_timeout() can only operate
- * on functions that take a single argument.
- */
-static inline bool tegra_powergate_state(int id)
+static inline bool tegra_powergate_state(struct tegra_pmc *pmc, int id)
{
if (id == TEGRA_POWERGATE_3D && pmc->soc->has_gpu_clamps)
return (tegra_pmc_readl(pmc, GPU_RG_CNTRL) & 0x1) == 0;
@@ -598,8 +613,9 @@ static int tegra20_powergate_set(struct tegra_pmc *pmc, unsigned int id,
tegra_pmc_writel(pmc, PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
/* wait for PMC to execute the command */
- ret = readx_poll_timeout(tegra_powergate_state, id, status,
- status == new_state, 1, 10);
+ ret = read_poll_timeout(tegra_powergate_state, status,
+ status == new_state, 1, 10, false,
+ pmc, id);
} while (ret == -ETIMEDOUT && retries--);
return ret;
@@ -631,8 +647,9 @@ static int tegra114_powergate_set(struct tegra_pmc *pmc, unsigned int id,
return err;
/* wait for PMC to execute the command */
- err = readx_poll_timeout(tegra_powergate_state, id, status,
- status == new_state, 10, 100000);
+ err = read_poll_timeout(tegra_powergate_state, status,
+ status == new_state, 10, 100000, false,
+ pmc, id);
if (err)
return err;
@@ -655,7 +672,7 @@ static int tegra_powergate_set(struct tegra_pmc *pmc, unsigned int id,
mutex_lock(&pmc->powergates_lock);
- if (tegra_powergate_state(id) == new_state) {
+ if (tegra_powergate_state(pmc, id) == new_state) {
mutex_unlock(&pmc->powergates_lock);
return 0;
}
@@ -940,30 +957,122 @@ static int tegra_genpd_power_off(struct generic_pm_domain *domain)
return err;
}
+static void tegra_pmc_put_device(void *data)
+{
+ struct tegra_pmc *pmc = data;
+
+ put_device(pmc->dev);
+}
+
+static const struct of_device_id tegra_pmc_match[];
+
+static struct tegra_pmc *tegra_pmc_get(struct device *dev)
+{
+ struct platform_device *pdev;
+ struct device_node *np;
+ struct tegra_pmc *pmc;
+
+ np = of_parse_phandle(dev->of_node, "nvidia,pmc", 0);
+ if (!np) {
+ struct device_node *parent = of_node_get(dev->of_node);
+
+ while ((parent = of_get_next_parent(parent)) != NULL) {
+ np = of_find_matching_node(parent, tegra_pmc_match);
+ if (np)
+ break;
+ }
+
+ of_node_put(parent);
+
+ if (!np)
+ return ERR_PTR(-ENODEV);
+ }
+
+ pdev = of_find_device_by_node(np);
+ of_node_put(np);
+
+ if (!pdev)
+ return ERR_PTR(-ENODEV);
+
+ pmc = platform_get_drvdata(pdev);
+ if (!pmc) {
+ put_device(&pdev->dev);
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ return pmc;
+}
+
/**
- * tegra_powergate_power_on() - power on partition
+ * tegra_pmc_get() - find the PMC for a given device
+ * @dev: device for which to find the PMC
+ *
+ * Returns a pointer to the PMC on success or an ERR_PTR()-encoded error code
+ * otherwise.
+ */
+struct tegra_pmc *devm_tegra_pmc_get(struct device *dev)
+{
+ struct tegra_pmc *pmc;
+ int err;
+
+ pmc = tegra_pmc_get(dev);
+ if (IS_ERR(pmc))
+ return pmc;
+
+ err = devm_add_action_or_reset(dev, tegra_pmc_put_device, pmc);
+ if (err < 0)
+ return ERR_PTR(err);
+
+ return pmc;
+}
+EXPORT_SYMBOL(devm_tegra_pmc_get);
+
+/**
+ * tegra_pmc_powergate_power_on() - power on partition
+ * @pmc: power management controller
* @id: partition ID
*/
-int tegra_powergate_power_on(unsigned int id)
+int tegra_pmc_powergate_power_on(struct tegra_pmc *pmc, unsigned int id)
{
if (!tegra_powergate_is_available(pmc, id))
return -EINVAL;
return tegra_powergate_set(pmc, id, true);
}
+EXPORT_SYMBOL(tegra_pmc_powergate_power_on);
+
+/**
+ * tegra_powergate_power_on() - power on partition
+ * @id: partition ID
+ */
+int tegra_powergate_power_on(unsigned int id)
+{
+ return tegra_pmc_powergate_power_on(pmc, id);
+}
EXPORT_SYMBOL(tegra_powergate_power_on);
/**
- * tegra_powergate_power_off() - power off partition
+ * tegra_pmc_powergate_power_off() - power off partition
+ * @pmc: power management controller
* @id: partition ID
*/
-int tegra_powergate_power_off(unsigned int id)
+int tegra_pmc_powergate_power_off(struct tegra_pmc *pmc, unsigned int id)
{
if (!tegra_powergate_is_available(pmc, id))
return -EINVAL;
return tegra_powergate_set(pmc, id, false);
}
+EXPORT_SYMBOL(tegra_pmc_powergate_power_off);
+
+/**
+ * tegra_powergate_power_off() - power off partition
+ * @id: partition ID
+ */
+int tegra_powergate_power_off(unsigned int id)
+{
+ return tegra_pmc_powergate_power_off(pmc, id);
+}
EXPORT_SYMBOL(tegra_powergate_power_off);
/**
@@ -976,32 +1085,45 @@ static int tegra_powergate_is_powered(struct tegra_pmc *pmc, unsigned int id)
if (!tegra_powergate_is_valid(pmc, id))
return -EINVAL;
- return tegra_powergate_state(id);
+ return tegra_powergate_state(pmc, id);
}
/**
- * tegra_powergate_remove_clamping() - remove power clamps for partition
+ * tegra_pmc_powergate_remove_clamping() - remove power clamps for partition
+ * @pmc: power management controller
* @id: partition ID
*/
-int tegra_powergate_remove_clamping(unsigned int id)
+int tegra_pmc_powergate_remove_clamping(struct tegra_pmc *pmc, unsigned int id)
{
if (!tegra_powergate_is_available(pmc, id))
return -EINVAL;
return __tegra_powergate_remove_clamping(pmc, id);
}
+EXPORT_SYMBOL(tegra_pmc_powergate_remove_clamping);
+
+/**
+ * tegra_powergate_remove_clamping() - remove power clamps for partition
+ * @id: partition ID
+ */
+int tegra_powergate_remove_clamping(unsigned int id)
+{
+ return tegra_pmc_powergate_remove_clamping(pmc, id);
+}
EXPORT_SYMBOL(tegra_powergate_remove_clamping);
/**
- * tegra_powergate_sequence_power_up() - power up partition
+ * tegra_pmc_powergate_sequence_power_up() - power up partition
+ * @pmc: power management controller
* @id: partition ID
* @clk: clock for partition
* @rst: reset for partition
*
* Must be called with clk disabled, and returns with clk enabled.
*/
-int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk,
- struct reset_control *rst)
+int tegra_pmc_powergate_sequence_power_up(struct tegra_pmc *pmc,
+ unsigned int id, struct clk *clk,
+ struct reset_control *rst)
{
struct tegra_powergate *pg;
int err;
@@ -1035,6 +1157,21 @@ int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk,
return err;
}
+EXPORT_SYMBOL(tegra_pmc_powergate_sequence_power_up);
+
+/**
+ * tegra_powergate_sequence_power_up() - power up partition
+ * @id: partition ID
+ * @clk: clock for partition
+ * @rst: reset for partition
+ *
+ * Must be called with clk disabled, and returns with clk enabled.
+ */
+int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk,
+ struct reset_control *rst)
+{
+ return tegra_pmc_powergate_sequence_power_up(pmc, id, clk, rst);
+}
EXPORT_SYMBOL(tegra_powergate_sequence_power_up);
/**
@@ -1099,7 +1236,8 @@ int tegra_pmc_cpu_remove_clamping(unsigned int cpuid)
return tegra_powergate_remove_clamping(id);
}
-static void tegra_pmc_program_reboot_reason(const char *cmd)
+static void tegra_pmc_program_reboot_reason(struct tegra_pmc *pmc,
+ const char *cmd)
{
u32 value;
@@ -1123,17 +1261,15 @@ static void tegra_pmc_program_reboot_reason(const char *cmd)
static int tegra_pmc_reboot_notify(struct notifier_block *this,
unsigned long action, void *data)
{
+ struct tegra_pmc *pmc = container_of(this, struct tegra_pmc,
+ reboot_notifier);
if (action == SYS_RESTART)
- tegra_pmc_program_reboot_reason(data);
+ tegra_pmc_program_reboot_reason(pmc, data);
return NOTIFY_DONE;
}
-static struct notifier_block tegra_pmc_reboot_notifier = {
- .notifier_call = tegra_pmc_reboot_notify,
-};
-
-static void tegra_pmc_restart(void)
+static void tegra_pmc_restart(struct tegra_pmc *pmc)
{
u32 value;
@@ -1145,13 +1281,17 @@ static void tegra_pmc_restart(void)
static int tegra_pmc_restart_handler(struct sys_off_data *data)
{
- tegra_pmc_restart();
+ struct tegra_pmc *pmc = data->cb_data;
+
+ tegra_pmc_restart(pmc);
return NOTIFY_DONE;
}
static int tegra_pmc_power_off_handler(struct sys_off_data *data)
{
+ struct tegra_pmc *pmc = data->cb_data;
+
/*
* Reboot Nexus 7 into special bootloader mode if USB cable is
* connected in order to display battery status and power off.
@@ -1161,7 +1301,7 @@ static int tegra_pmc_power_off_handler(struct sys_off_data *data)
const u32 go_to_charger_mode = 0xa5a55a5a;
tegra_pmc_writel(pmc, go_to_charger_mode, PMC_SCRATCH37);
- tegra_pmc_restart();
+ tegra_pmc_restart(pmc);
}
return NOTIFY_DONE;
@@ -1169,6 +1309,7 @@ static int tegra_pmc_power_off_handler(struct sys_off_data *data)
static int powergate_show(struct seq_file *s, void *data)
{
+ struct tegra_pmc *pmc = data;
unsigned int i;
int status;
@@ -1377,6 +1518,8 @@ static int
tegra_pmc_core_pd_set_performance_state(struct generic_pm_domain *genpd,
unsigned int level)
{
+ struct tegra_pmc_core_pd *pd = to_core_pd(genpd);
+ struct tegra_pmc *pmc = pd->pmc;
struct dev_pm_opp *opp;
int err;
@@ -1404,30 +1547,31 @@ tegra_pmc_core_pd_set_performance_state(struct generic_pm_domain *genpd,
static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np)
{
- struct generic_pm_domain *genpd;
const char *rname[] = { "core", NULL};
+ struct tegra_pmc_core_pd *pd;
int err;
- genpd = devm_kzalloc(pmc->dev, sizeof(*genpd), GFP_KERNEL);
- if (!genpd)
+ pd = devm_kzalloc(pmc->dev, sizeof(*pd), GFP_KERNEL);
+ if (!pd)
return -ENOMEM;
- genpd->name = "core";
- genpd->flags = GENPD_FLAG_NO_SYNC_STATE;
- genpd->set_performance_state = tegra_pmc_core_pd_set_performance_state;
+ pd->genpd.name = "core";
+ pd->genpd.flags = GENPD_FLAG_NO_SYNC_STATE;
+ pd->genpd.set_performance_state = tegra_pmc_core_pd_set_performance_state;
+ pd->pmc = pmc;
err = devm_pm_opp_set_regulators(pmc->dev, rname);
if (err)
return dev_err_probe(pmc->dev, err,
"failed to set core OPP regulator\n");
- err = pm_genpd_init(genpd, NULL, false);
+ err = pm_genpd_init(&pd->genpd, NULL, false);
if (err) {
dev_err(pmc->dev, "failed to init core genpd: %d\n", err);
return err;
}
- err = of_genpd_add_provider_simple(np, genpd);
+ err = of_genpd_add_provider_simple(np, &pd->genpd);
if (err) {
dev_err(pmc->dev, "failed to add core genpd: %d\n", err);
goto remove_genpd;
@@ -1436,7 +1580,7 @@ static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np)
return 0;
remove_genpd:
- pm_genpd_remove(genpd);
+ pm_genpd_remove(&pd->genpd);
return err;
}
@@ -1499,7 +1643,7 @@ static void tegra_powergate_remove(struct generic_pm_domain *genpd)
kfree(pg->clks);
- set_bit(pg->id, pmc->powergates_available);
+ set_bit(pg->id, pg->pmc->powergates_available);
kfree(pg);
}
@@ -1603,11 +1747,12 @@ static void tegra_io_pad_unprepare(struct tegra_pmc *pmc)
/**
* tegra_io_pad_power_enable() - enable power to I/O pad
+ * @pmc: power management controller
* @id: Tegra I/O pad ID for which to enable power
*
* Returns: 0 on success or a negative error code on failure.
*/
-int tegra_io_pad_power_enable(enum tegra_io_pad id)
+int tegra_pmc_io_pad_power_enable(struct tegra_pmc *pmc, enum tegra_io_pad id)
{
const struct tegra_io_pad_soc *pad;
unsigned long request, status;
@@ -1642,15 +1787,28 @@ unlock:
mutex_unlock(&pmc->powergates_lock);
return err;
}
+EXPORT_SYMBOL(tegra_pmc_io_pad_power_enable);
+
+/**
+ * tegra_io_pad_power_enable() - enable power to I/O pad
+ * @id: Tegra I/O pad ID for which to enable power
+ *
+ * Returns: 0 on success or a negative error code on failure.
+ */
+int tegra_io_pad_power_enable(enum tegra_io_pad id)
+{
+ return tegra_pmc_io_pad_power_enable(pmc, id);
+}
EXPORT_SYMBOL(tegra_io_pad_power_enable);
/**
- * tegra_io_pad_power_disable() - disable power to I/O pad
+ * tegra_pmc_io_pad_power_disable() - disable power to I/O pad
+ * @pmc: power management controller
* @id: Tegra I/O pad ID for which to disable power
*
* Returns: 0 on success or a negative error code on failure.
*/
-int tegra_io_pad_power_disable(enum tegra_io_pad id)
+int tegra_pmc_io_pad_power_disable(struct tegra_pmc *pmc, enum tegra_io_pad id)
{
const struct tegra_io_pad_soc *pad;
unsigned long request, status;
@@ -1685,6 +1843,18 @@ unlock:
mutex_unlock(&pmc->powergates_lock);
return err;
}
+EXPORT_SYMBOL(tegra_pmc_io_pad_power_disable);
+
+/**
+ * tegra_io_pad_power_disable() - disable power to I/O pad
+ * @id: Tegra I/O pad ID for which to disable power
+ *
+ * Returns: 0 on success or a negative error code on failure.
+ */
+int tegra_io_pad_power_disable(enum tegra_io_pad id)
+{
+ return tegra_pmc_io_pad_power_disable(pmc, id);
+}
EXPORT_SYMBOL(tegra_io_pad_power_disable);
static int tegra_io_pad_is_powered(struct tegra_pmc *pmc, enum tegra_io_pad id)
@@ -1905,6 +2075,50 @@ static int tegra_pmc_parse_dt(struct tegra_pmc *pmc, struct device_node *np)
return 0;
}
+/* translate sc7 wake sources back into IRQs to catch edge triggered wakeups */
+static void tegra186_pmc_wake_handler(struct irq_work *work)
+{
+ struct tegra_pmc *pmc = container_of(work, struct tegra_pmc, wake_work);
+ unsigned int i, wake;
+
+ for (i = 0; i < pmc->soc->max_wake_vectors; i++) {
+ unsigned long status = pmc->wake_status[i];
+
+ for_each_set_bit(wake, &status, 32) {
+ irq_hw_number_t hwirq = wake + (i * 32);
+ struct irq_desc *desc;
+ unsigned int irq;
+
+ irq = irq_find_mapping(pmc->domain, hwirq);
+ if (!irq) {
+ dev_warn(pmc->dev,
+ "No IRQ found for WAKE#%lu!\n",
+ hwirq);
+ continue;
+ }
+
+ dev_dbg(pmc->dev,
+ "Resume caused by WAKE#%lu mapped to IRQ#%u\n",
+ hwirq, irq);
+
+ desc = irq_to_desc(irq);
+ if (!desc) {
+ dev_warn(pmc->dev,
+ "No descriptor found for IRQ#%u\n",
+ irq);
+ continue;
+ }
+
+ if (!desc->action || !desc->action->name)
+ continue;
+
+ generic_handle_irq(irq);
+ }
+
+ pmc->wake_status[i] = 0;
+ }
+}
+
static int tegra_pmc_init(struct tegra_pmc *pmc)
{
if (pmc->soc->max_wake_events > 0) {
@@ -1923,6 +2137,18 @@ static int tegra_pmc_init(struct tegra_pmc *pmc)
pmc->wake_cntrl_level_map = bitmap_zalloc(pmc->soc->max_wake_events, GFP_KERNEL);
if (!pmc->wake_cntrl_level_map)
return -ENOMEM;
+
+ pmc->wake_status = kcalloc(pmc->soc->max_wake_vectors, sizeof(u32), GFP_KERNEL);
+ if (!pmc->wake_status)
+ return -ENOMEM;
+
+ /*
+ * Initialize IRQ work for processing wake IRQs. Must use
+ * HARD_IRQ variant to run in hard IRQ context on PREEMPT_RT
+ * because we call generic_handle_irq() which requires hard
+ * IRQ context.
+ */
+ pmc->wake_work = IRQ_WORK_INIT_HARD(tegra186_pmc_wake_handler);
}
if (pmc->soc->init)
@@ -2104,9 +2330,9 @@ static int tegra_io_pad_pinconf_set(struct pinctrl_dev *pctl_dev,
switch (param) {
case PIN_CONFIG_MODE_LOW_POWER:
if (arg)
- err = tegra_io_pad_power_disable(pad->id);
+ err = tegra_pmc_io_pad_power_disable(pmc, pad->id);
else
- err = tegra_io_pad_power_enable(pad->id);
+ err = tegra_pmc_io_pad_power_enable(pmc, pad->id);
if (err)
return err;
break;
@@ -2163,6 +2389,7 @@ static int tegra_pmc_pinctrl_init(struct tegra_pmc *pmc)
static ssize_t reset_reason_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ struct tegra_pmc *pmc = dev_get_drvdata(dev);
u32 value;
value = tegra_pmc_readl(pmc, pmc->soc->regs->rst_status);
@@ -2180,6 +2407,7 @@ static DEVICE_ATTR_RO(reset_reason);
static ssize_t reset_level_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ struct tegra_pmc *pmc = dev_get_drvdata(dev);
u32 value;
value = tegra_pmc_readl(pmc, pmc->soc->regs->rst_status);
@@ -2543,7 +2771,7 @@ static int tegra_pmc_clk_notify_cb(struct notifier_block *nb,
return NOTIFY_OK;
}
-static void pmc_clk_fence_udelay(u32 offset)
+static void pmc_clk_fence_udelay(struct tegra_pmc *pmc, u32 offset)
{
tegra_pmc_readl(pmc, offset);
/* pmc clk propagation delay 2 us */
@@ -2555,7 +2783,7 @@ static u8 pmc_clk_mux_get_parent(struct clk_hw *hw)
struct pmc_clk *clk = to_pmc_clk(hw);
u32 val;
- val = tegra_pmc_readl(pmc, clk->offs) >> clk->mux_shift;
+ val = tegra_pmc_readl(clk->pmc, clk->offs) >> clk->mux_shift;
val &= PMC_CLK_OUT_MUX_MASK;
return val;
@@ -2566,11 +2794,11 @@ static int pmc_clk_mux_set_parent(struct clk_hw *hw, u8 index)
struct pmc_clk *clk = to_pmc_clk(hw);
u32 val;
- val = tegra_pmc_readl(pmc, clk->offs);
+ val = tegra_pmc_readl(clk->pmc, clk->offs);
val &= ~(PMC_CLK_OUT_MUX_MASK << clk->mux_shift);
val |= index << clk->mux_shift;
- tegra_pmc_writel(pmc, val, clk->offs);
- pmc_clk_fence_udelay(clk->offs);
+ tegra_pmc_writel(clk->pmc, val, clk->offs);
+ pmc_clk_fence_udelay(clk->pmc, clk->offs);
return 0;
}
@@ -2580,26 +2808,27 @@ static int pmc_clk_is_enabled(struct clk_hw *hw)
struct pmc_clk *clk = to_pmc_clk(hw);
u32 val;
- val = tegra_pmc_readl(pmc, clk->offs) & BIT(clk->force_en_shift);
+ val = tegra_pmc_readl(clk->pmc, clk->offs) & BIT(clk->force_en_shift);
return val ? 1 : 0;
}
-static void pmc_clk_set_state(unsigned long offs, u32 shift, int state)
+static void pmc_clk_set_state(struct tegra_pmc *pmc, unsigned long offs,
+ u32 shift, int state)
{
u32 val;
val = tegra_pmc_readl(pmc, offs);
val = state ? (val | BIT(shift)) : (val & ~BIT(shift));
tegra_pmc_writel(pmc, val, offs);
- pmc_clk_fence_udelay(offs);
+ pmc_clk_fence_udelay(pmc, offs);
}
static int pmc_clk_enable(struct clk_hw *hw)
{
struct pmc_clk *clk = to_pmc_clk(hw);
- pmc_clk_set_state(clk->offs, clk->force_en_shift, 1);
+ pmc_clk_set_state(clk->pmc, clk->offs, clk->force_en_shift, 1);
return 0;
}
@@ -2608,7 +2837,7 @@ static void pmc_clk_disable(struct clk_hw *hw)
{
struct pmc_clk *clk = to_pmc_clk(hw);
- pmc_clk_set_state(clk->offs, clk->force_en_shift, 0);
+ pmc_clk_set_state(clk->pmc, clk->offs, clk->force_en_shift, 0);
}
static const struct clk_ops pmc_clk_ops = {
@@ -2640,6 +2869,7 @@ tegra_pmc_clk_out_register(struct tegra_pmc *pmc,
CLK_SET_PARENT_GATE;
pmc_clk->hw.init = &init;
+ pmc_clk->pmc = pmc;
pmc_clk->offs = offset;
pmc_clk->mux_shift = data->mux_shift;
pmc_clk->force_en_shift = data->force_en_shift;
@@ -2650,15 +2880,16 @@ tegra_pmc_clk_out_register(struct tegra_pmc *pmc,
static int pmc_clk_gate_is_enabled(struct clk_hw *hw)
{
struct pmc_clk_gate *gate = to_pmc_clk_gate(hw);
+ u32 value = tegra_pmc_readl(gate->pmc, gate->offs);
- return tegra_pmc_readl(pmc, gate->offs) & BIT(gate->shift) ? 1 : 0;
+ return value & BIT(gate->shift) ? 1 : 0;
}
static int pmc_clk_gate_enable(struct clk_hw *hw)
{
struct pmc_clk_gate *gate = to_pmc_clk_gate(hw);
- pmc_clk_set_state(gate->offs, gate->shift, 1);
+ pmc_clk_set_state(gate->pmc, gate->offs, gate->shift, 1);
return 0;
}
@@ -2667,7 +2898,7 @@ static void pmc_clk_gate_disable(struct clk_hw *hw)
{
struct pmc_clk_gate *gate = to_pmc_clk_gate(hw);
- pmc_clk_set_state(gate->offs, gate->shift, 0);
+ pmc_clk_set_state(gate->pmc, gate->offs, gate->shift, 0);
}
static const struct clk_ops pmc_clk_gate_ops = {
@@ -2695,6 +2926,7 @@ tegra_pmc_clk_gate_register(struct tegra_pmc *pmc, const char *name,
init.flags = 0;
gate->hw.init = &init;
+ gate->pmc = pmc;
gate->offs = offset;
gate->shift = shift;
@@ -2858,6 +3090,8 @@ static int tegra_pmc_regmap_init(struct tegra_pmc *pmc)
static void tegra_pmc_reset_suspend_mode(void *data)
{
+ struct tegra_pmc *pmc = data;
+
pmc->suspend_mode = TEGRA_SUSPEND_NOT_READY;
}
@@ -2880,7 +3114,7 @@ static int tegra_pmc_probe(struct platform_device *pdev)
return err;
err = devm_add_action_or_reset(&pdev->dev, tegra_pmc_reset_suspend_mode,
- NULL);
+ pmc);
if (err)
return err;
@@ -2931,8 +3165,10 @@ static int tegra_pmc_probe(struct platform_device *pdev)
* CPU without resetting everything else.
*/
if (pmc->scratch) {
+ pmc->reboot_notifier.notifier_call = tegra_pmc_reboot_notify;
+
err = devm_register_reboot_notifier(&pdev->dev,
- &tegra_pmc_reboot_notifier);
+ &pmc->reboot_notifier);
if (err) {
dev_err(&pdev->dev,
"unable to register reboot notifier, %d\n",
@@ -2944,7 +3180,8 @@ static int tegra_pmc_probe(struct platform_device *pdev)
err = devm_register_sys_off_handler(&pdev->dev,
SYS_OFF_MODE_RESTART,
SYS_OFF_PRIO_LOW,
- tegra_pmc_restart_handler, NULL);
+ tegra_pmc_restart_handler,
+ pmc);
if (err) {
dev_err(&pdev->dev, "failed to register sys-off handler: %d\n",
err);
@@ -2958,7 +3195,8 @@ static int tegra_pmc_probe(struct platform_device *pdev)
err = devm_register_sys_off_handler(&pdev->dev,
SYS_OFF_MODE_POWER_OFF,
SYS_OFF_PRIO_FIRMWARE,
- tegra_pmc_power_off_handler, NULL);
+ tegra_pmc_power_off_handler,
+ pmc);
if (err) {
dev_err(&pdev->dev, "failed to register sys-off handler: %d\n",
err);
@@ -3024,7 +3262,7 @@ static int tegra_pmc_probe(struct platform_device *pdev)
if (pmc->soc->set_wake_filters)
pmc->soc->set_wake_filters(pmc);
- debugfs_create_file("powergate", 0444, NULL, NULL, &powergate_fops);
+ debugfs_create_file("powergate", 0444, NULL, pmc, &powergate_fops);
return 0;
@@ -3129,47 +3367,33 @@ static void wke_clear_wake_status(struct tegra_pmc *pmc)
}
}
-/* translate sc7 wake sources back into IRQs to catch edge triggered wakeups */
-static void tegra186_pmc_process_wake_events(struct tegra_pmc *pmc, unsigned int index,
- unsigned long status)
-{
- unsigned int wake;
-
- dev_dbg(pmc->dev, "Wake[%d:%d] status=%#lx\n", (index * 32) + 31, index * 32, status);
-
- for_each_set_bit(wake, &status, 32) {
- irq_hw_number_t hwirq = wake + 32 * index;
- struct irq_desc *desc;
- unsigned int irq;
-
- irq = irq_find_mapping(pmc->domain, hwirq);
-
- desc = irq_to_desc(irq);
- if (!desc || !desc->action || !desc->action->name) {
- dev_dbg(pmc->dev, "Resume caused by WAKE%ld, IRQ %d\n", hwirq, irq);
- continue;
- }
-
- dev_dbg(pmc->dev, "Resume caused by WAKE%ld, %s\n", hwirq, desc->action->name);
- generic_handle_irq(irq);
- }
-}
-
static void tegra186_pmc_wake_syscore_resume(void *data)
{
- u32 status, mask;
+ struct tegra_pmc *pmc = data;
unsigned int i;
+ u32 mask;
for (i = 0; i < pmc->soc->max_wake_vectors; i++) {
mask = readl(pmc->wake + WAKE_AOWAKE_TIER2_ROUTING(i));
- status = readl(pmc->wake + WAKE_AOWAKE_STATUS_R(i)) & mask;
-
- tegra186_pmc_process_wake_events(pmc, i, status);
+ pmc->wake_status[i] = readl(pmc->wake + WAKE_AOWAKE_STATUS_R(i)) & mask;
}
+
+ /* Schedule IRQ work to process wake IRQs (if any) */
+ irq_work_queue(&pmc->wake_work);
}
static int tegra186_pmc_wake_syscore_suspend(void *data)
{
+ struct tegra_pmc *pmc = data;
+ unsigned int i;
+
+ /* Check if there are unhandled wake IRQs */
+ for (i = 0; i < pmc->soc->max_wake_vectors; i++)
+ if (pmc->wake_status[i])
+ dev_warn(pmc->dev,
+ "Unhandled wake IRQs pending vector[%u]: 0x%x\n",
+ i, pmc->wake_status[i]);
+
wke_read_sw_wake_status(pmc);
/* flip the wakeup trigger for dual-edge triggered pads
@@ -3843,6 +4067,7 @@ static const struct tegra_pmc_regs tegra186_pmc_regs = {
static void tegra186_pmc_init(struct tegra_pmc *pmc)
{
pmc->syscore.ops = &tegra186_pmc_wake_syscore_ops;
+ pmc->syscore.data = pmc;
register_syscore(&pmc->syscore);
}
diff --git a/drivers/soc/ti/Kconfig b/drivers/soc/ti/Kconfig
index 1a93001c9e36..163aadd589d3 100644
--- a/drivers/soc/ti/Kconfig
+++ b/drivers/soc/ti/Kconfig
@@ -62,7 +62,7 @@ config TI_K3_RINGACC
If unsure, say N.
config TI_K3_SOCINFO
- bool
+ bool "K3 SoC Information driver" if COMPILE_TEST
depends on ARCH_K3 || COMPILE_TEST
select SOC_BUS
select MFD_SYSCON
diff --git a/drivers/soc/ti/k3-socinfo.c b/drivers/soc/ti/k3-socinfo.c
index 50c170a995f9..42275cb5ba1c 100644
--- a/drivers/soc/ti/k3-socinfo.c
+++ b/drivers/soc/ti/k3-socinfo.c
@@ -141,7 +141,7 @@ static int k3_chipinfo_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
- regmap = regmap_init_mmio(dev, base, &k3_chipinfo_regmap_cfg);
+ regmap = devm_regmap_init_mmio(dev, base, &k3_chipinfo_regmap_cfg);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
diff --git a/drivers/soc/ti/knav_dma.c b/drivers/soc/ti/knav_dma.c
index 553ae7ee20f1..e5f5e3142fc4 100644
--- a/drivers/soc/ti/knav_dma.c
+++ b/drivers/soc/ti/knav_dma.c
@@ -706,20 +706,15 @@ static int knav_dma_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = pdev->dev.of_node;
- struct device_node *child;
int ret = 0;
- if (!node) {
- dev_err(&pdev->dev, "could not find device info\n");
- return -EINVAL;
- }
+ if (!node)
+ return dev_err_probe(dev, -EINVAL, "could not find device info\n");
kdev = devm_kzalloc(dev,
sizeof(struct knav_dma_pool_device), GFP_KERNEL);
- if (!kdev) {
- dev_err(dev, "could not allocate driver mem\n");
+ if (!kdev)
return -ENOMEM;
- }
kdev->dev = dev;
INIT_LIST_HEAD(&kdev->list);
@@ -727,23 +722,21 @@ static int knav_dma_probe(struct platform_device *pdev)
pm_runtime_enable(kdev->dev);
ret = pm_runtime_resume_and_get(kdev->dev);
if (ret < 0) {
- dev_err(kdev->dev, "unable to enable pktdma, err %d\n", ret);
+ dev_err(dev, "unable to enable pktdma, err %d\n", ret);
goto err_pm_disable;
}
/* Initialise all packet dmas */
- for_each_child_of_node(node, child) {
+ for_each_child_of_node_scoped(node, child) {
ret = dma_init(node, child);
if (ret) {
- of_node_put(child);
- dev_err(&pdev->dev, "init failed with %d\n", ret);
+ dev_err(dev, "init failed with %d\n", ret);
break;
}
}
if (list_empty(&kdev->list)) {
- dev_err(dev, "no valid dma instance\n");
- ret = -ENODEV;
+ ret = dev_err_probe(dev, -ENODEV, "no valid dma instance\n");
goto err_put_sync;
}
diff --git a/drivers/soc/ti/knav_qmss_queue.c b/drivers/soc/ti/knav_qmss_queue.c
index 6e56e7609ccd..86d7a9c9ae01 100644
--- a/drivers/soc/ti/knav_qmss_queue.c
+++ b/drivers/soc/ti/knav_qmss_queue.c
@@ -1079,7 +1079,6 @@ static int knav_queue_setup_regions(struct knav_device *kdev,
struct device_node *regions __free(device_node) =
of_get_child_by_name(node, "descriptor-regions");
struct knav_region *region;
- struct device_node *child;
u32 temp[2];
int ret;
@@ -1087,13 +1086,10 @@ static int knav_queue_setup_regions(struct knav_device *kdev,
return dev_err_probe(dev, -ENODEV,
"descriptor-regions not specified\n");
- for_each_child_of_node(regions, child) {
+ for_each_child_of_node_scoped(regions, child) {
region = devm_kzalloc(dev, sizeof(*region), GFP_KERNEL);
- if (!region) {
- of_node_put(child);
- dev_err(dev, "out of memory allocating region\n");
+ if (!region)
return -ENOMEM;
- }
region->name = knav_queue_find_name(child);
of_property_read_u32(child, "id", &region->id);
@@ -1397,7 +1393,6 @@ static int knav_queue_init_qmgrs(struct knav_device *kdev,
struct device_node *qmgrs __free(device_node) =
of_get_child_by_name(node, "qmgrs");
struct knav_qmgr_info *qmgr;
- struct device_node *child;
u32 temp[2];
int ret;
@@ -1405,13 +1400,10 @@ static int knav_queue_init_qmgrs(struct knav_device *kdev,
return dev_err_probe(dev, -ENODEV,
"queue manager info not specified\n");
- for_each_child_of_node(qmgrs, child) {
+ for_each_child_of_node_scoped(qmgrs, child) {
qmgr = devm_kzalloc(dev, sizeof(*qmgr), GFP_KERNEL);
- if (!qmgr) {
- of_node_put(child);
- dev_err(dev, "out of memory allocating qmgr\n");
+ if (!qmgr)
return -ENOMEM;
- }
ret = of_property_read_u32_array(child, "managed-queues",
temp, 2);
@@ -1503,15 +1495,12 @@ static int knav_queue_init_pdsps(struct knav_device *kdev,
{
struct device *dev = kdev->dev;
struct knav_pdsp_info *pdsp;
- struct device_node *child;
- for_each_child_of_node(pdsps, child) {
+ for_each_child_of_node_scoped(pdsps, child) {
pdsp = devm_kzalloc(dev, sizeof(*pdsp), GFP_KERNEL);
- if (!pdsp) {
- of_node_put(child);
- dev_err(dev, "out of memory allocating pdsp\n");
+ if (!pdsp)
return -ENOMEM;
- }
+
pdsp->name = knav_queue_find_name(child);
pdsp->iram =
knav_queue_map_reg(kdev, child,
diff --git a/drivers/soc/ti/pruss.c b/drivers/soc/ti/pruss.c
index 038576805bfa..0fd59c73f585 100644
--- a/drivers/soc/ti/pruss.c
+++ b/drivers/soc/ti/pruss.c
@@ -366,12 +366,10 @@ static int pruss_clk_mux_setup(struct pruss *pruss, struct clk *clk_mux,
ret = devm_add_action_or_reset(dev, pruss_of_free_clk_provider,
clk_mux_np);
- if (ret) {
+ if (ret)
dev_err(dev, "failed to add clkmux free action %d", ret);
- goto put_clk_mux_np;
- }
- return 0;
+ return ret;
put_clk_mux_np:
of_node_put(clk_mux_np);
diff --git a/drivers/soc/xilinx/zynqmp_power.c b/drivers/soc/xilinx/zynqmp_power.c
index 9b7b2858b22a..9085db1b480a 100644
--- a/drivers/soc/xilinx/zynqmp_power.c
+++ b/drivers/soc/xilinx/zynqmp_power.c
@@ -82,7 +82,7 @@ static void subsystem_restart_event_callback(const u32 *payload, void *data)
memcpy(zynqmp_pm_init_restart_work->args, &payload[0],
sizeof(zynqmp_pm_init_restart_work->args));
- queue_work(system_unbound_wq, &zynqmp_pm_init_restart_work->callback_work);
+ queue_work(system_dfl_wq, &zynqmp_pm_init_restart_work->callback_work);
}
static void suspend_event_callback(const u32 *payload, void *data)
@@ -95,7 +95,7 @@ static void suspend_event_callback(const u32 *payload, void *data)
memcpy(zynqmp_pm_init_suspend_work->args, &payload[1],
sizeof(zynqmp_pm_init_suspend_work->args));
- queue_work(system_unbound_wq, &zynqmp_pm_init_suspend_work->callback_work);
+ queue_work(system_dfl_wq, &zynqmp_pm_init_suspend_work->callback_work);
}
static irqreturn_t zynqmp_pm_isr(int irq, void *data)
@@ -140,7 +140,7 @@ static void ipi_receive_callback(struct mbox_client *cl, void *data)
memcpy(zynqmp_pm_init_suspend_work->args, &payload[1],
sizeof(zynqmp_pm_init_suspend_work->args));
- queue_work(system_unbound_wq,
+ queue_work(system_dfl_wq,
&zynqmp_pm_init_suspend_work->callback_work);
/* Send NULL message to mbox controller to ack the message */
diff --git a/drivers/tee/amdtee/call.c b/drivers/tee/amdtee/call.c
index 4c21b02be4af..460b0c9e511f 100644
--- a/drivers/tee/amdtee/call.c
+++ b/drivers/tee/amdtee/call.c
@@ -15,7 +15,7 @@
static int tee_params_to_amd_params(struct tee_param *tee, u32 count,
struct tee_operation *amd)
{
- int i, ret = 0;
+ int i;
u32 type;
if (!count)
@@ -66,13 +66,13 @@ static int tee_params_to_amd_params(struct tee_param *tee, u32 count,
i, amd->params[i].val.b);
}
}
- return ret;
+ return 0;
}
static int amd_params_to_tee_params(struct tee_param *tee, u32 count,
struct tee_operation *amd)
{
- int i, ret = 0;
+ int i;
u32 type;
if (!count)
@@ -118,7 +118,7 @@ static int amd_params_to_tee_params(struct tee_param *tee, u32 count,
i, amd->params[i].val.b);
}
}
- return ret;
+ return 0;
}
static DEFINE_MUTEX(ta_refcount_mutex);
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index 5b62139714ce..2d807bc748bc 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
}
+int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
+{
+ struct optee *optee = tee_get_drvdata(teedev);
+ u64 build_id;
+
+ if (!optee)
+ return -ENODEV;
+ if (!buf || !len)
+ return -EINVAL;
+
+ build_id = optee->revision.os_build_id;
+ if (build_id)
+ scnprintf(buf, len, "%u.%u (%016llx)",
+ optee->revision.os_major,
+ optee->revision.os_minor,
+ (unsigned long long)build_id);
+ else
+ scnprintf(buf, len, "%u.%u", optee->revision.os_major,
+ optee->revision.os_minor);
+
+ return 0;
+}
+
static void optee_bus_scan(struct work_struct *work)
{
WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
index bf8390789ecf..8fc72aa95722 100644
--- a/drivers/tee/optee/ffa_abi.c
+++ b/drivers/tee/optee/ffa_abi.c
@@ -775,6 +775,39 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
* with a matching configuration.
*/
+static bool optee_ffa_get_os_revision(struct ffa_device *ffa_dev,
+ const struct ffa_ops *ops,
+ struct optee_revision *revision)
+{
+ const struct ffa_msg_ops *msg_ops = ops->msg_ops;
+ struct ffa_send_direct_data data = {
+ .data0 = OPTEE_FFA_GET_OS_VERSION,
+ };
+ int rc;
+
+ msg_ops->mode_32bit_set(ffa_dev);
+
+ rc = msg_ops->sync_send_receive(ffa_dev, &data);
+ if (rc) {
+ pr_err("Unexpected error %d\n", rc);
+ return false;
+ }
+
+ if (revision) {
+ revision->os_major = data.data0;
+ revision->os_minor = data.data1;
+ revision->os_build_id = data.data2;
+ }
+
+ if (data.data2)
+ pr_info("revision %lu.%lu (%08lx)",
+ data.data0, data.data1, data.data2);
+ else
+ pr_info("revision %lu.%lu", data.data0, data.data1);
+
+ return true;
+}
+
static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
const struct ffa_ops *ops)
{
@@ -798,20 +831,6 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
return false;
}
- data = (struct ffa_send_direct_data){
- .data0 = OPTEE_FFA_GET_OS_VERSION,
- };
- rc = msg_ops->sync_send_receive(ffa_dev, &data);
- if (rc) {
- pr_err("Unexpected error %d\n", rc);
- return false;
- }
- if (data.data2)
- pr_info("revision %lu.%lu (%08lx)",
- data.data0, data.data1, data.data2);
- else
- pr_info("revision %lu.%lu", data.data0, data.data1);
-
return true;
}
@@ -900,6 +919,7 @@ static int optee_ffa_open(struct tee_context *ctx)
static const struct tee_driver_ops optee_ffa_clnt_ops = {
.get_version = optee_ffa_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_ffa_open,
.release = optee_release,
.open_session = optee_open_session,
@@ -918,6 +938,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
static const struct tee_driver_ops optee_ffa_supp_ops = {
.get_version = optee_ffa_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_ffa_open,
.release = optee_release_supp,
.supp_recv = optee_supp_recv,
@@ -1060,6 +1081,11 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
if (!optee)
return -ENOMEM;
+ if (!optee_ffa_get_os_revision(ffa_dev, ffa_ops, &optee->revision)) {
+ rc = -EINVAL;
+ goto err_free_optee;
+ }
+
pool = optee_ffa_shm_pool_alloc_pages();
if (IS_ERR(pool)) {
rc = PTR_ERR(pool);
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index db9ea673fbca..acd3051c4879 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -172,6 +172,24 @@ struct optee_ffa {
struct optee;
/**
+ * struct optee_revision - OP-TEE OS revision reported by secure world
+ * @os_major: OP-TEE OS major version
+ * @os_minor: OP-TEE OS minor version
+ * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
+ *
+ * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
+ * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
+ * FF-A ABI version.
+ */
+struct optee_revision {
+ u32 os_major;
+ u32 os_minor;
+ u64 os_build_id;
+};
+
+int optee_get_revision(struct tee_device *teedev, char *buf, size_t len);
+
+/**
* struct optee_ops - OP-TEE driver internal operations
* @do_call_with_arg: enters OP-TEE in secure world
* @to_msg_param: converts from struct tee_param to OPTEE_MSG parameters
@@ -249,6 +267,7 @@ struct optee {
bool in_kernel_rpmb_routing;
struct work_struct scan_bus_work;
struct work_struct rpmb_scan_bus_work;
+ struct optee_revision revision;
};
struct optee_session {
diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c
index ebbbd42b0e3e..1758eb7e6e8b 100644
--- a/drivers/tee/optee/rpc.c
+++ b/drivers/tee/optee/rpc.c
@@ -43,7 +43,7 @@ static void handle_rpc_func_cmd_i2c_transfer(struct tee_context *ctx,
struct i2c_msg msg = { };
size_t i;
int ret = -EOPNOTSUPP;
- u8 attr[] = {
+ static const u8 attr[] = {
TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT,
@@ -247,8 +247,8 @@ void optee_rpc_cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm)
param.u.value.c = 0;
/*
- * Match the tee_shm_get_from_id() in cmd_alloc_suppl() as secure
- * world has released its reference.
+ * Match the tee_shm_get_from_id() in optee_rpc_cmd_alloc_suppl()
+ * as secure world has released its reference.
*
* It's better to do this before sending the request to supplicant
* as we'd like to let the process doing the initial allocation to
diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
index 0be663fcd52b..51fae1ab8ef8 100644
--- a/drivers/tee/optee/smc_abi.c
+++ b/drivers/tee/optee/smc_abi.c
@@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx)
static const struct tee_driver_ops optee_clnt_ops = {
.get_version = optee_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_smc_open,
.release = optee_release,
.open_session = optee_open_session,
@@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = {
static const struct tee_driver_ops optee_supp_ops = {
.get_version = optee_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_smc_open,
.release = optee_release_supp,
.supp_recv = optee_supp_recv,
@@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
}
#endif
-static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
+static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
+ struct optee_revision *revision)
{
union {
struct arm_smccc_res smccc;
@@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
&res.smccc);
+ if (revision) {
+ revision->os_major = res.result.major;
+ revision->os_minor = res.result.minor;
+ revision->os_build_id = res.result.build_id;
+ }
+
if (res.result.build_id)
pr_info("revision %lu.%lu (%0*lx)", res.result.major,
res.result.minor, (int)sizeof(res.result.build_id) * 2,
@@ -1745,8 +1754,6 @@ static int optee_probe(struct platform_device *pdev)
return -EINVAL;
}
- optee_msg_get_os_revision(invoke_fn);
-
if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
pr_warn("api revision mismatch\n");
return -EINVAL;
@@ -1815,6 +1822,8 @@ static int optee_probe(struct platform_device *pdev)
goto err_free_shm_pool;
}
+ optee_msg_get_os_revision(invoke_fn, &optee->revision);
+
optee->ops = &optee_ops;
optee->smc.invoke_fn = invoke_fn;
optee->smc.sec_caps = sec_caps;
diff --git a/drivers/tee/qcomtee/call.c b/drivers/tee/qcomtee/call.c
index 65f9140d4e1f..8f8830f0df26 100644
--- a/drivers/tee/qcomtee/call.c
+++ b/drivers/tee/qcomtee/call.c
@@ -395,9 +395,7 @@ static int qcomtee_object_invoke(struct tee_context *ctx,
struct tee_ioctl_object_invoke_arg *arg,
struct tee_param *params)
{
- struct qcomtee_object_invoke_ctx *oic __free(kfree) = NULL;
struct qcomtee_context_data *ctxdata = ctx->data;
- struct qcomtee_arg *u __free(kfree) = NULL;
struct qcomtee_object *object;
int i, ret, result;
@@ -412,12 +410,14 @@ static int qcomtee_object_invoke(struct tee_context *ctx,
}
/* Otherwise, invoke a QTEE object: */
- oic = qcomtee_object_invoke_ctx_alloc(ctx);
+ struct qcomtee_object_invoke_ctx *oic __free(kfree) =
+ qcomtee_object_invoke_ctx_alloc(ctx);
if (!oic)
return -ENOMEM;
/* +1 for ending QCOMTEE_ARG_TYPE_INV. */
- u = kcalloc(arg->num_params + 1, sizeof(*u), GFP_KERNEL);
+ struct qcomtee_arg *u __free(kfree) = kcalloc(arg->num_params + 1, sizeof(*u),
+ GFP_KERNEL);
if (!u)
return -ENOMEM;
@@ -562,9 +562,8 @@ static int qcomtee_supp_send(struct tee_context *ctx, u32 errno, u32 num_params,
static int qcomtee_open(struct tee_context *ctx)
{
- struct qcomtee_context_data *ctxdata __free(kfree) = NULL;
-
- ctxdata = kzalloc(sizeof(*ctxdata), GFP_KERNEL);
+ struct qcomtee_context_data *ctxdata __free(kfree) = kzalloc(sizeof(*ctxdata),
+ GFP_KERNEL);
if (!ctxdata)
return -ENOMEM;
@@ -645,12 +644,12 @@ static void qcomtee_get_version(struct tee_device *teedev,
static void qcomtee_get_qtee_feature_list(struct tee_context *ctx, u32 id,
u32 *version)
{
- struct qcomtee_object_invoke_ctx *oic __free(kfree) = NULL;
struct qcomtee_object *client_env, *service;
struct qcomtee_arg u[3] = { 0 };
int result;
- oic = qcomtee_object_invoke_ctx_alloc(ctx);
+ struct qcomtee_object_invoke_ctx *oic __free(kfree) =
+ qcomtee_object_invoke_ctx_alloc(ctx);
if (!oic)
return;
diff --git a/drivers/tee/qcomtee/mem_obj.c b/drivers/tee/qcomtee/mem_obj.c
index 228a3e30a31b..a16f8fc39b8d 100644
--- a/drivers/tee/qcomtee/mem_obj.c
+++ b/drivers/tee/qcomtee/mem_obj.c
@@ -88,11 +88,11 @@ int qcomtee_memobj_param_to_object(struct qcomtee_object **object,
struct tee_param *param,
struct tee_context *ctx)
{
- struct qcomtee_mem_object *mem_object __free(kfree) = NULL;
struct tee_shm *shm;
int err;
- mem_object = kzalloc(sizeof(*mem_object), GFP_KERNEL);
+ struct qcomtee_mem_object *mem_object __free(kfree) = kzalloc(sizeof(*mem_object),
+ GFP_KERNEL);
if (!mem_object)
return -ENOMEM;
diff --git a/drivers/tee/qcomtee/user_obj.c b/drivers/tee/qcomtee/user_obj.c
index 0139905f2684..6aa3aefd67f0 100644
--- a/drivers/tee/qcomtee/user_obj.c
+++ b/drivers/tee/qcomtee/user_obj.c
@@ -228,10 +228,10 @@ static int qcomtee_user_object_dispatch(struct qcomtee_object_invoke_ctx *oic,
{
struct qcomtee_user_object *uo = to_qcomtee_user_object(object);
struct qcomtee_context_data *ctxdata = uo->ctx->data;
- struct qcomtee_ureq *ureq __free(kfree) = NULL;
int errno;
- ureq = kzalloc(sizeof(*ureq), GFP_KERNEL);
+ struct qcomtee_ureq *ureq __free(kfree) = kzalloc(sizeof(*ureq),
+ GFP_KERNEL);
if (!ureq)
return -ENOMEM;
@@ -367,10 +367,10 @@ int qcomtee_user_param_to_object(struct qcomtee_object **object,
struct tee_param *param,
struct tee_context *ctx)
{
- struct qcomtee_user_object *user_object __free(kfree) = NULL;
int err;
- user_object = kzalloc(sizeof(*user_object), GFP_KERNEL);
+ struct qcomtee_user_object *user_object __free(kfree) =
+ kzalloc(sizeof(*user_object), GFP_KERNEL);
if (!user_object)
return -ENOMEM;
diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
index d65d47cc154e..6c49e2e383c6 100644
--- a/drivers/tee/tee_core.c
+++ b/drivers/tee/tee_core.c
@@ -1146,7 +1146,56 @@ static struct attribute *tee_dev_attrs[] = {
NULL
};
-ATTRIBUTE_GROUPS(tee_dev);
+static const struct attribute_group tee_dev_group = {
+ .attrs = tee_dev_attrs,
+};
+
+static ssize_t revision_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tee_device *teedev = container_of(dev, struct tee_device, dev);
+ char version[TEE_REVISION_STR_SIZE];
+ int ret;
+
+ if (!teedev->desc->ops->get_tee_revision)
+ return -ENODEV;
+
+ ret = teedev->desc->ops->get_tee_revision(teedev, version,
+ sizeof(version));
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%s\n", version);
+}
+static DEVICE_ATTR_RO(revision);
+
+static struct attribute *tee_revision_attrs[] = {
+ &dev_attr_revision.attr,
+ NULL
+};
+
+static umode_t tee_revision_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct tee_device *teedev = container_of(dev, struct tee_device, dev);
+
+ if (teedev->desc->ops->get_tee_revision)
+ return attr->mode;
+
+ return 0;
+}
+
+static const struct attribute_group tee_revision_group = {
+ .attrs = tee_revision_attrs,
+ .is_visible = tee_revision_attr_is_visible,
+};
+
+static const struct attribute_group *tee_dev_groups[] = {
+ &tee_dev_group,
+ &tee_revision_group,
+ NULL
+};
static const struct class tee_class = {
.name = "tee",
@@ -1398,13 +1447,97 @@ static int tee_client_device_uevent(const struct device *dev,
return add_uevent_var(env, "MODALIAS=tee:%pUb", dev_id);
}
+static int tee_client_device_probe(struct device *dev)
+{
+ struct tee_client_device *tcdev = to_tee_client_device(dev);
+ struct tee_client_driver *drv = to_tee_client_driver(dev->driver);
+
+ if (drv->probe)
+ return drv->probe(tcdev);
+ else
+ return 0;
+}
+
+static void tee_client_device_remove(struct device *dev)
+{
+ struct tee_client_device *tcdev = to_tee_client_device(dev);
+ struct tee_client_driver *drv = to_tee_client_driver(dev->driver);
+
+ if (drv->remove)
+ drv->remove(tcdev);
+}
+
+static void tee_client_device_shutdown(struct device *dev)
+{
+ struct tee_client_device *tcdev = to_tee_client_device(dev);
+ struct tee_client_driver *drv = to_tee_client_driver(dev->driver);
+
+ if (dev->driver && drv->shutdown)
+ drv->shutdown(tcdev);
+}
+
const struct bus_type tee_bus_type = {
.name = "tee",
.match = tee_client_device_match,
.uevent = tee_client_device_uevent,
+ .probe = tee_client_device_probe,
+ .remove = tee_client_device_remove,
+ .shutdown = tee_client_device_shutdown,
};
EXPORT_SYMBOL_GPL(tee_bus_type);
+static int tee_client_device_probe_legacy(struct tee_client_device *tcdev)
+{
+ struct device *dev = &tcdev->dev;
+ struct device_driver *driver = dev->driver;
+
+ return driver->probe(dev);
+}
+
+static void tee_client_device_remove_legacy(struct tee_client_device *tcdev)
+{
+ struct device *dev = &tcdev->dev;
+ struct device_driver *driver = dev->driver;
+
+ driver->remove(dev);
+}
+
+static void tee_client_device_shutdown_legacy(struct tee_client_device *tcdev)
+{
+ struct device *dev = &tcdev->dev;
+ struct device_driver *driver = dev->driver;
+
+ driver->shutdown(dev);
+}
+
+int __tee_client_driver_register(struct tee_client_driver *tee_driver,
+ struct module *owner)
+{
+ tee_driver->driver.owner = owner;
+ tee_driver->driver.bus = &tee_bus_type;
+
+ /*
+ * Drivers that have callbacks set for tee_driver->driver need updating
+ * to use the callbacks in tee_driver instead. driver_register() warns
+ * about that, so no need to warn here, too.
+ */
+ if (!tee_driver->probe && tee_driver->driver.probe)
+ tee_driver->probe = tee_client_device_probe_legacy;
+ if (!tee_driver->remove && tee_driver->driver.remove)
+ tee_driver->remove = tee_client_device_remove_legacy;
+ if (!tee_driver->shutdown && tee_driver->driver.probe)
+ tee_driver->shutdown = tee_client_device_shutdown_legacy;
+
+ return driver_register(&tee_driver->driver);
+}
+EXPORT_SYMBOL_GPL(__tee_client_driver_register);
+
+void tee_client_driver_unregister(struct tee_client_driver *tee_driver)
+{
+ driver_unregister(&tee_driver->driver);
+}
+EXPORT_SYMBOL_GPL(tee_client_driver_unregister);
+
static int __init tee_init(void)
{
int rc;