diff options
Diffstat (limited to 'drivers')
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, ®map_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 = ¬ify->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 = ¬ify->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, ®_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", ®ion->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; |
