The addition of per message-id fastchannel support check exposed a SCP firmware bug which leads to a device crash on X1E platforms. The X1E firmware supports fastchannel on LEVEL_GET but fails to have this set in the protocol message attributes and the fallback to regular messaging leads to a device crash since that implementation has a bug for all the X1E devices in the wild. Fix this by introducing a quirk that selectively skips the per message-id fastchannel check only for the LEVEL_GET message on X1E platforms. Signed-off-by: Sibi Sankar <quic_sibis@xxxxxxxxxxx> --- drivers/firmware/arm_scmi/driver.c | 5 +++-- drivers/firmware/arm_scmi/perf.c | 30 +++++++++++++++++++++------ drivers/firmware/arm_scmi/powercap.c | 8 +++---- drivers/firmware/arm_scmi/protocols.h | 2 +- 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 9313b9020fc1..b182fa8e8ccb 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -1903,7 +1903,8 @@ static void scmi_common_fastchannel_init(const struct scmi_protocol_handle *ph, u8 describe_id, u32 message_id, u32 valid_size, u32 domain, void __iomem **p_addr, - struct scmi_fc_db_info **p_db, u32 *rate_limit) + struct scmi_fc_db_info **p_db, u32 *rate_limit, + bool skip_check) { int ret; u32 flags; @@ -1919,7 +1920,7 @@ scmi_common_fastchannel_init(const struct scmi_protocol_handle *ph, /* Check if the MSG_ID supports fastchannel */ ret = scmi_protocol_msg_check(ph, message_id, &attributes); - if (!ret && !MSG_SUPPORTS_FASTCHANNEL(attributes)) + if (!ret && !MSG_SUPPORTS_FASTCHANNEL(attributes) && !skip_check) return; if (!p_addr) { diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index c7e5a34b254b..5b4559d0b054 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -48,6 +48,10 @@ enum { PERF_FC_MAX, }; +enum { + PERF_QUIRK_SKIP_FASTCHANNEL_LEVEL_GET, +}; + struct scmi_opp { u32 perf; u32 power; @@ -183,6 +187,7 @@ struct scmi_perf_info { enum scmi_power_scale power_scale; u64 stats_addr; u32 stats_size; + u32 quirks; bool notify_lvl_cmd; bool notify_lim_cmd; struct perf_dom_info *dom_info; @@ -838,9 +843,10 @@ static int scmi_perf_level_limits_notify(const struct scmi_protocol_handle *ph, } static void scmi_perf_domain_init_fc(const struct scmi_protocol_handle *ph, - struct perf_dom_info *dom) + struct perf_dom_info *dom, u32 quirks) { struct scmi_fc_info *fc; + bool quirk_level_get = !!(quirks & BIT(PERF_QUIRK_SKIP_FASTCHANNEL_LEVEL_GET)); fc = devm_kcalloc(ph->dev, PERF_FC_MAX, sizeof(*fc), GFP_KERNEL); if (!fc) @@ -849,26 +855,26 @@ static void scmi_perf_domain_init_fc(const struct scmi_protocol_handle *ph, ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL, PERF_LEVEL_GET, 4, dom->id, &fc[PERF_FC_LEVEL].get_addr, NULL, - &fc[PERF_FC_LEVEL].rate_limit); + &fc[PERF_FC_LEVEL].rate_limit, quirk_level_get); ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL, PERF_LIMITS_GET, 8, dom->id, &fc[PERF_FC_LIMIT].get_addr, NULL, - &fc[PERF_FC_LIMIT].rate_limit); + &fc[PERF_FC_LIMIT].rate_limit, false); if (dom->info.set_perf) ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL, PERF_LEVEL_SET, 4, dom->id, &fc[PERF_FC_LEVEL].set_addr, &fc[PERF_FC_LEVEL].set_db, - &fc[PERF_FC_LEVEL].rate_limit); + &fc[PERF_FC_LEVEL].rate_limit, false); if (dom->set_limits) ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL, PERF_LIMITS_SET, 8, dom->id, &fc[PERF_FC_LIMIT].set_addr, &fc[PERF_FC_LIMIT].set_db, - &fc[PERF_FC_LIMIT].rate_limit); + &fc[PERF_FC_LIMIT].rate_limit, false); dom->fc_info = fc; } @@ -1282,6 +1288,7 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph) { int domain, ret; u32 version; + struct device_node *np; struct scmi_perf_info *pinfo; ret = ph->xops->version_get(ph, &version); @@ -1297,6 +1304,17 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph) pinfo->version = version; + /* + * Some X1E devices support fastchannel for LEVEL_GET but erroneously + * says otherwise in the protocol message attributes. Add a quirk to + * force fastchannel on LEVEL_GET to prevent crashes on such devices. + */ + np = of_find_compatible_node(NULL, NULL, "qcom,x1e80100"); + if (np) { + pinfo->quirks = BIT(PERF_QUIRK_SKIP_FASTCHANNEL_LEVEL_GET); + of_node_put(np); + } + ret = scmi_perf_attributes_get(ph, pinfo); if (ret) return ret; @@ -1315,7 +1333,7 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph) scmi_perf_describe_levels_get(ph, dom, version); if (dom->perf_fastchannels) - scmi_perf_domain_init_fc(ph, dom); + scmi_perf_domain_init_fc(ph, dom, pinfo->quirks); } ret = devm_add_action_or_reset(ph->dev, scmi_perf_xa_destroy, pinfo); diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_scmi/powercap.c index 1fa79bba492e..90e92fc432e3 100644 --- a/drivers/firmware/arm_scmi/powercap.c +++ b/drivers/firmware/arm_scmi/powercap.c @@ -720,23 +720,23 @@ static void scmi_powercap_domain_init_fc(const struct scmi_protocol_handle *ph, POWERCAP_CAP_SET, 4, domain, &fc[POWERCAP_FC_CAP].set_addr, &fc[POWERCAP_FC_CAP].set_db, - &fc[POWERCAP_FC_CAP].rate_limit); + &fc[POWERCAP_FC_CAP].rate_limit, false); ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, POWERCAP_CAP_GET, 4, domain, &fc[POWERCAP_FC_CAP].get_addr, NULL, - &fc[POWERCAP_FC_CAP].rate_limit); + &fc[POWERCAP_FC_CAP].rate_limit, false); ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, POWERCAP_PAI_SET, 4, domain, &fc[POWERCAP_FC_PAI].set_addr, &fc[POWERCAP_FC_PAI].set_db, - &fc[POWERCAP_FC_PAI].rate_limit); + &fc[POWERCAP_FC_PAI].rate_limit, false); ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, POWERCAP_PAI_GET, 4, domain, &fc[POWERCAP_FC_PAI].get_addr, NULL, - &fc[POWERCAP_FC_PAI].rate_limit); + &fc[POWERCAP_FC_PAI].rate_limit, false); *p_fc = fc; } diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_scmi/protocols.h index d62c4469d1fd..fe4c724caba3 100644 --- a/drivers/firmware/arm_scmi/protocols.h +++ b/drivers/firmware/arm_scmi/protocols.h @@ -280,7 +280,7 @@ struct scmi_proto_helpers_ops { u32 valid_size, u32 domain, void __iomem **p_addr, struct scmi_fc_db_info **p_db, - u32 *rate_limit); + u32 *rate_limit, bool skip_check); void (*fastchannel_db_ring)(struct scmi_fc_db_info *db); int (*get_max_msg_size)(const struct scmi_protocol_handle *ph); }; -- 2.34.1