diff options
Diffstat (limited to 'drivers/firmware')
34 files changed, 1264 insertions, 953 deletions
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index b5957cc12fee..87383c05424b 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -28,8 +28,8 @@ #include <linux/hashtable.h> #include <linux/list.h> #include <linux/module.h> -#include <linux/of_address.h> -#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/platform_device.h> #include <linux/processor.h> #include <linux/refcount.h> #include <linux/slab.h> diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index ecf5c4de851b..c0cd556fbaae 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -2,19 +2,22 @@ /* * System Control and Management Interface (SCMI) Performance Protocol * - * Copyright (C) 2018-2022 ARM Ltd. + * Copyright (C) 2018-2023 ARM Ltd. */ #define pr_fmt(fmt) "SCMI Notifications PERF - " fmt #include <linux/bits.h> -#include <linux/of.h> +#include <linux/hashtable.h> #include <linux/io.h> +#include <linux/log2.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_opp.h> #include <linux/scmi_protocol.h> #include <linux/sort.h> +#include <linux/xarray.h> #include <trace/events/scmi.h> @@ -46,6 +49,9 @@ struct scmi_opp { u32 perf; u32 power; u32 trans_latency_us; + u32 indicative_freq; + u32 level_index; + struct hlist_node hash; }; struct scmi_msg_resp_perf_attributes { @@ -66,6 +72,7 @@ struct scmi_msg_resp_perf_domain_attributes { #define SUPPORTS_PERF_LEVEL_NOTIFY(x) ((x) & BIT(28)) #define SUPPORTS_PERF_FASTCHANNELS(x) ((x) & BIT(27)) #define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(26)) +#define SUPPORTS_LEVEL_INDEXING(x) ((x) & BIT(25)) __le32 rate_limit_us; __le32 sustained_freq_khz; __le32 sustained_perf_level; @@ -122,12 +129,27 @@ struct scmi_msg_resp_perf_describe_levels { } opp[]; }; +struct scmi_msg_resp_perf_describe_levels_v4 { + __le16 num_returned; + __le16 num_remaining; + struct { + __le32 perf_val; + __le32 power; + __le16 transition_latency_us; + __le16 reserved; + __le32 indicative_freq; + __le32 level_index; + } opp[]; +}; + struct perf_dom_info { + u32 id; bool set_limits; bool set_perf; bool perf_limit_notify; bool perf_level_notify; bool perf_fastchannels; + bool level_indexing_mode; u32 opp_count; u32 sustained_freq_khz; u32 sustained_perf_level; @@ -135,11 +157,26 @@ struct perf_dom_info { char name[SCMI_MAX_STR_SIZE]; struct scmi_opp opp[MAX_OPPS]; struct scmi_fc_info *fc_info; + struct xarray opps_by_idx; + struct xarray opps_by_lvl; + DECLARE_HASHTABLE(opps_by_freq, ilog2(MAX_OPPS)); }; +#define LOOKUP_BY_FREQ(__htp, __freq) \ +({ \ + /* u32 cast is needed to pick right hash func */ \ + u32 f_ = (u32)(__freq); \ + struct scmi_opp *_opp; \ + \ + hash_for_each_possible((__htp), _opp, hash, f_) \ + if (_opp->indicative_freq == f_) \ + break; \ + _opp; \ +}) + struct scmi_perf_info { u32 version; - int num_domains; + u16 num_domains; enum scmi_power_scale power_scale; u64 stats_addr; u32 stats_size; @@ -186,9 +223,20 @@ static int scmi_perf_attributes_get(const struct scmi_protocol_handle *ph, return ret; } +static void scmi_perf_xa_destroy(void *data) +{ + int domain; + struct scmi_perf_info *pinfo = data; + + for (domain = 0; domain < pinfo->num_domains; domain++) { + xa_destroy(&((pinfo->dom_info + domain)->opps_by_idx)); + xa_destroy(&((pinfo->dom_info + domain)->opps_by_lvl)); + } +} + static int scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph, - u32 domain, struct perf_dom_info *dom_info, + struct perf_dom_info *dom_info, u32 version) { int ret; @@ -197,11 +245,11 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph, struct scmi_msg_resp_perf_domain_attributes *attr; ret = ph->xops->xfer_get_init(ph, PERF_DOMAIN_ATTRIBUTES, - sizeof(domain), sizeof(*attr), &t); + sizeof(dom_info->id), sizeof(*attr), &t); if (ret) return ret; - put_unaligned_le32(domain, t->tx.buf); + put_unaligned_le32(dom_info->id, t->tx.buf); attr = t->rx.buf; ret = ph->xops->do_xfer(ph, t); @@ -213,6 +261,9 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph, dom_info->perf_limit_notify = SUPPORTS_PERF_LIMIT_NOTIFY(flags); dom_info->perf_level_notify = SUPPORTS_PERF_LEVEL_NOTIFY(flags); dom_info->perf_fastchannels = SUPPORTS_PERF_FASTCHANNELS(flags); + if (PROTOCOL_REV_MAJOR(version) >= 0x4) + dom_info->level_indexing_mode = + SUPPORTS_LEVEL_INDEXING(flags); dom_info->sustained_freq_khz = le32_to_cpu(attr->sustained_freq_khz); dom_info->sustained_perf_level = @@ -236,8 +287,15 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph, */ if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 && SUPPORTS_EXTENDED_NAMES(flags)) - ph->hops->extended_name_get(ph, PERF_DOMAIN_NAME_GET, domain, - dom_info->name, SCMI_MAX_STR_SIZE); + ph->hops->extended_name_get(ph, PERF_DOMAIN_NAME_GET, + dom_info->id, dom_info->name, + SCMI_MAX_STR_SIZE); + + if (dom_info->level_indexing_mode) { + xa_init(&dom_info->opps_by_idx); + xa_init(&dom_info->opps_by_lvl); + hash_init(dom_info->opps_by_freq); + } return ret; } @@ -250,7 +308,7 @@ static int opp_cmp_func(const void *opp1, const void *opp2) } struct scmi_perf_ipriv { - u32 domain; + u32 version; struct perf_dom_info *perf_dom; }; @@ -261,7 +319,7 @@ static void iter_perf_levels_prepare_message(void *message, struct scmi_msg_perf_describe_levels *msg = message; const struct scmi_perf_ipriv *p = priv; - msg->domain = cpu_to_le32(p->domain); + msg->domain = cpu_to_le32(p->perf_dom->id); /* Set the number of OPPs to be skipped/already read */ msg->level_index = cpu_to_le32(desc_index); } @@ -277,31 +335,63 @@ static int iter_perf_levels_update_state(struct scmi_iterator_state *st, return 0; } +static inline void +process_response_opp(struct scmi_opp *opp, unsigned int loop_idx, + const struct scmi_msg_resp_perf_describe_levels *r) +{ + opp->perf = le32_to_cpu(r->opp[loop_idx].perf_val); + opp->power = le32_to_cpu(r->opp[loop_idx].power); + opp->trans_latency_us = + le16_to_cpu(r->opp[loop_idx].transition_latency_us); +} + +static inline void +process_response_opp_v4(struct perf_dom_info *dom, struct scmi_opp *opp, + unsigned int loop_idx, + const struct scmi_msg_resp_perf_describe_levels_v4 *r) +{ + opp->perf = le32_to_cpu(r->opp[loop_idx].perf_val); + opp->power = le32_to_cpu(r->opp[loop_idx].power); + opp->trans_latency_us = + le16_to_cpu(r->opp[loop_idx].transition_latency_us); + + /* Note that PERF v4 reports always five 32-bit words */ + opp->indicative_freq = le32_to_cpu(r->opp[loop_idx].indicative_freq); + if (dom->level_indexing_mode) { + opp->level_index = le32_to_cpu(r->opp[loop_idx].level_index); + + xa_store(&dom->opps_by_idx, opp->level_index, opp, GFP_KERNEL); + xa_store(&dom->opps_by_lvl, opp->perf, opp, GFP_KERNEL); + hash_add(dom->opps_by_freq, &opp->hash, opp->indicative_freq); + } +} + static int iter_perf_levels_process_response(const struct scmi_protocol_handle *ph, const void *response, struct scmi_iterator_state *st, void *priv) { struct scmi_opp *opp; - const struct scmi_msg_resp_perf_describe_levels *r = response; struct scmi_perf_ipriv *p = priv; opp = &p->perf_dom->opp[st->desc_index + st->loop_idx]; - opp->perf = le32_to_cpu(r->opp[st->loop_idx].perf_val); - opp->power = le32_to_cpu(r->opp[st->loop_idx].power); - opp->trans_latency_us = - le16_to_cpu(r->opp[st->loop_idx].transition_latency_us); + if (PROTOCOL_REV_MAJOR(p->version) <= 0x3) + process_response_opp(opp, st->loop_idx, response); + else + process_response_opp_v4(p->perf_dom, opp, st->loop_idx, + response); p->perf_dom->opp_count++; - dev_dbg(ph->dev, "Level %d Power %d Latency %dus\n", - opp->perf, opp->power, opp->trans_latency_us); + dev_dbg(ph->dev, "Level %d Power %d Latency %dus Ifreq %d Index %d\n", + opp->perf, opp->power, opp->trans_latency_us, + opp->indicative_freq, opp->level_index); return 0; } static int -scmi_perf_describe_levels_get(const struct scmi_protocol_handle *ph, u32 domain, - struct perf_dom_info *perf_dom) +scmi_perf_describe_levels_get(const struct scmi_protocol_handle *ph, + struct perf_dom_info *perf_dom, u32 version) { int ret; void *iter; @@ -311,7 +401,7 @@ scmi_perf_describe_levels_get(const struct scmi_protocol_handle *ph, u32 domain, .process_response = iter_perf_levels_process_response, }; struct scmi_perf_ipriv ppriv = { - .domain = domain, + .version = version, .perf_dom = perf_dom, }; @@ -333,8 +423,8 @@ scmi_perf_describe_levels_get(const struct scmi_protocol_handle *ph, u32 domain, return ret; } -static int scmi_perf_mb_limits_set(const struct scmi_protocol_handle *ph, - u32 domain, u32 max_perf, u32 min_perf) +static int scmi_perf_msg_limits_set(const struct scmi_protocol_handle *ph, + u32 domain, u32 max_perf, u32 min_perf) { int ret; struct scmi_xfer *t; @@ -356,31 +446,73 @@ static int scmi_perf_mb_limits_set(const struct scmi_protocol_handle *ph, return ret; } -static int scmi_perf_limits_set(const struct scmi_protocol_handle *ph, - u32 domain, u32 max_perf, u32 min_perf) +static inline struct perf_dom_info * +scmi_perf_domain_lookup(const struct scmi_protocol_handle *ph, u32 domain) { struct scmi_perf_info *pi = ph->get_priv(ph); - struct perf_dom_info *dom = pi->dom_info + domain; - if (PROTOCOL_REV_MAJOR(pi->version) >= 0x3 && !max_perf && !min_perf) - return -EINVAL; + if (domain >= pi->num_domains) + return ERR_PTR(-EINVAL); + return pi->dom_info + domain; +} + +static int __scmi_perf_limits_set(const struct scmi_protocol_handle *ph, + struct perf_dom_info *dom, u32 max_perf, + u32 min_perf) +{ if (dom->fc_info && dom->fc_info[PERF_FC_LIMIT].set_addr) { struct scmi_fc_info *fci = &dom->fc_info[PERF_FC_LIMIT]; trace_scmi_fc_call(SCMI_PROTOCOL_PERF, PERF_LIMITS_SET, - domain, min_perf, max_perf); + dom->id, min_perf, max_perf); iowrite32(max_perf, fci->set_addr); iowrite32(min_perf, fci->set_addr + 4); ph->hops->fastchannel_db_ring(fci->set_db); return 0; } - return scmi_perf_mb_limits_set(ph, domain, max_perf, min_perf); + return scmi_perf_msg_limits_set(ph, dom->id, max_perf, min_perf); } -static int scmi_perf_mb_limits_get(const struct scmi_protocol_handle *ph, - u32 domain, u32 *max_perf, u32 *min_perf) +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); + if (IS_ERR(dom)) + return PTR_ERR(dom); + + if (PROTOCOL_REV_MAJOR(pi->version) >= 0x3 && !max_perf && !min_perf) + return -EINVAL; + + if (dom->level_indexing_mode) { + struct scmi_opp *opp; + + if (min_perf) { + opp = xa_load(&dom->opps_by_lvl, min_perf); + if (!opp) + return -EIO; + + min_perf = opp->level_index; + } + + if (max_perf) { + opp = xa_load(&dom->opps_by_lvl, max_perf); + if (!opp) + return -EIO; + + max_perf = opp->level_index; + } + } + + return __scmi_perf_limits_set(ph, dom, max_perf, min_perf); +} + +static int scmi_perf_msg_limits_get(const struct scmi_protocol_handle *ph, + u32 domain, u32 *max_perf, u32 *min_perf) { int ret; struct scmi_xfer *t; @@ -405,27 +537,58 @@ static int scmi_perf_mb_limits_get(const struct scmi_protocol_handle *ph, return ret; } -static int scmi_perf_limits_get(const struct scmi_protocol_handle *ph, - u32 domain, u32 *max_perf, u32 *min_perf) +static int __scmi_perf_limits_get(const struct scmi_protocol_handle *ph, + struct perf_dom_info *dom, u32 *max_perf, + u32 *min_perf) { - struct scmi_perf_info *pi = ph->get_priv(ph); - struct perf_dom_info *dom = pi->dom_info + domain; - if (dom->fc_info && dom->fc_info[PERF_FC_LIMIT].get_addr) { struct scmi_fc_info *fci = &dom->fc_info[PERF_FC_LIMIT]; *max_perf = ioread32(fci->get_addr); *min_perf = ioread32(fci->get_addr + 4); trace_scmi_fc_call(SCMI_PROTOCOL_PERF, PERF_LIMITS_GET, - domain, *min_perf, *max_perf); + dom->id, *min_perf, *max_perf); return 0; } - return scmi_perf_mb_limits_get(ph, domain, max_perf, min_perf); + return scmi_perf_msg_limits_get(ph, dom->id, max_perf, min_perf); } -static int scmi_perf_mb_level_set(const struct scmi_protocol_handle *ph, - u32 domain, u32 level, bool poll) +static int scmi_perf_limits_get(const struct scmi_protocol_handle *ph, + u32 domain, u32 *max_perf, u32 *min_perf) +{ + int ret; + struct perf_dom_info *dom; + + dom = scmi_perf_domain_lookup(ph, domain); + if (IS_ERR(dom)) + return PTR_ERR(dom); + + ret = __scmi_perf_limits_get(ph, dom, max_perf, min_perf); + if (ret) + return ret; + + if (dom->level_indexing_mode) { + struct scmi_opp *opp; + + opp = xa_load(&dom->opps_by_idx, *min_perf); + if (!opp) + return -EIO; + + *min_perf = opp->perf; + + opp = xa_load(&dom->opps_by_idx, *max_perf); + if (!opp) + return -EIO; + + *max_perf = opp->perf; + } + + return 0; +} + +static int scmi_perf_msg_level_set(const struct scmi_protocol_handle *ph, + u32 domain, u32 level, bool poll) { int ret; struct scmi_xfer *t; @@ -446,27 +609,47 @@ static int scmi_perf_mb_level_set(const struct scmi_protocol_handle *ph, return ret; } -static int scmi_perf_level_set(const struct scmi_protocol_handle *ph, - u32 domain, u32 level, bool poll) +static int __scmi_perf_level_set(const struct scmi_protocol_handle *ph, + struct perf_dom_info *dom, u32 level, + bool poll) { - struct scmi_perf_info *pi = ph->get_priv(ph); - struct perf_dom_info *dom = pi->dom_info + domain; - if (dom->fc_info && dom->fc_info[PERF_FC_LEVEL].set_addr) { struct scmi_fc_info *fci = &dom->fc_info[PERF_FC_LEVEL]; trace_scmi_fc_call(SCMI_PROTOCOL_PERF, PERF_LEVEL_SET, - domain, level, 0); + dom->id, level, 0); iowrite32(level, fci->set_addr); ph->hops->fastchannel_db_ring(fci->set_db); return 0; } - return scmi_perf_mb_level_set(ph, domain, level, poll); + return scmi_perf_msg_level_set(ph, dom->id, level, poll); } -static int scmi_perf_mb_level_get(const struct scmi_protocol_handle *ph, - u32 domain, u32 *level, bool poll) +static int scmi_perf_level_set(const struct scmi_protocol_handle *ph, + u32 domain, u32 level, bool poll) +{ + struct perf_dom_info *dom; + + dom = scmi_perf_domain_lookup(ph, domain); + if (IS_ERR(dom)) + return PTR_ERR(dom); + + if (dom->level_indexing_mode) { + struct scmi_opp *opp; + + opp = xa_load(&dom->opps_by_lvl, level); + if (!opp) + return -EIO; + + level = opp->level_index; + } + + return __scmi_perf_level_set(ph, dom, level, poll); +} + +static int scmi_perf_msg_level_get(const struct scmi_protocol_handle *ph, + u32 domain, u32 *level, bool poll) { int ret; struct scmi_xfer *t; @@ -487,20 +670,45 @@ static int scmi_perf_mb_level_get(const struct scmi_protocol_handle *ph, return ret; } -static int scmi_perf_level_get(const struct scmi_protocol_handle *ph, - u32 domain, u32 *level, bool poll) +static int __scmi_perf_level_get(const struct scmi_protocol_handle *ph, + struct perf_dom_info *dom, u32 *level, + bool poll) { - struct scmi_perf_info *pi = ph->get_priv(ph); - struct perf_dom_info *dom = pi->dom_info + domain; - if (dom->fc_info && dom->fc_info[PERF_FC_LEVEL].get_addr) { *level = ioread32(dom->fc_info[PERF_FC_LEVEL].get_addr); trace_scmi_fc_call(SCMI_PROTOCOL_PERF, PERF_LEVEL_GET, - domain, *level, 0); + dom->id, *level, 0); return 0; } - return scmi_perf_mb_level_get(ph, domain, level, poll); + return scmi_perf_msg_level_get(ph, dom->id, level, poll); +} + +static int scmi_perf_level_get(const struct scmi_protocol_handle *ph, + u32 domain, u32 *level, bool poll) +{ + int ret; + struct perf_dom_info *dom; + + dom = scmi_perf_domain_lookup(ph, domain); + if (IS_ERR(dom)) + return PTR_ERR(dom); + + ret = __scmi_perf_level_get(ph, dom, level, poll); + if (ret) + return ret; + + if (dom->level_indexing_mode) { + struct scmi_opp *opp; + + opp = xa_load(&dom->opps_by_idx, *level); + if (!opp) + return -EIO; + + *level = opp->perf; + } + + return 0; } static int scmi_perf_level_limits_notify(const struct scmi_protocol_handle *ph, @@ -574,27 +782,37 @@ static int scmi_dvfs_device_opps_add(const struct scmi_protocol_handle *ph, unsigned long freq; struct scmi_opp *opp; struct perf_dom_info *dom; - struct scmi_perf_info *pi = ph->get_priv(ph); domain = scmi_dev_domain_id(dev); if (domain < 0) - return domain; + return -EINVAL; - dom = pi->dom_info + domain; + dom = scmi_perf_domain_lookup(ph, domain); + if (IS_ERR(dom)) + return PTR_ERR(dom); for (opp = dom->opp, idx = 0; idx < dom->opp_count; idx++, opp++) { - freq = opp->perf * dom->mult_factor; + if (!dom->level_indexing_mode) + freq = opp->perf * dom->mult_factor; + else + freq = opp->indicative_freq * 1000; ret = dev_pm_opp_add(dev, freq, 0); if (ret) { dev_warn(dev, "failed to add opp %luHz\n", freq); while (idx-- > 0) { - freq = (--opp)->perf * dom->mult_factor; + if (!dom->level_indexing_mode) + freq = (--opp)->perf * dom->mult_factor; + else + freq = (--opp)->indicative_freq * 1000; dev_pm_opp_remove(dev, freq); } return ret; } + + dev_dbg(dev, "[%d][%s]:: Registered OPP[%d] %lu\n", + domain, dom->name, idx, freq); } return 0; } @@ -603,14 +821,17 @@ static int scmi_dvfs_transition_latency_get(const struct scmi_protocol_handle *ph, struct device *dev) { + int domain; struct perf_dom_info *dom; - struct scmi_perf_info *pi = ph->get_priv(ph); - int domain = scmi_dev_domain_id(dev); + domain = scmi_dev_domain_id(dev); if (domain < 0) - return domain; + return -EINVAL; + + dom = scmi_perf_domain_lookup(ph, domain); + if (IS_ERR(dom)) + return PTR_ERR(dom); - dom = pi->dom_info + domain; /* uS to nS */ return dom->opp[dom->opp_count - 1].trans_latency_us * 1000; } @@ -618,10 +839,26 @@ scmi_dvfs_transition_latency_get(const struct scmi_protocol_handle *ph, static int scmi_dvfs_freq_set(const struct scmi_protocol_handle *ph, u32 domain, unsigned long freq, bool poll) { - struct scmi_perf_info *pi = ph->get_priv(ph); - struct perf_dom_info *dom = pi->dom_info + domain; + unsigned int level; + struct perf_dom_info *dom; + + dom = scmi_perf_domain_lookup(ph, domain); + if (IS_ERR(dom)) + return PTR_ERR(dom); - return scmi_perf_level_set(ph, domain, freq / dom->mult_factor, poll); + if (!dom->level_indexing_mode) { + level = freq / dom->mult_factor; + } else { + struct scmi_opp *opp; + + opp = LOOKUP_BY_FREQ(dom->opps_by_freq, freq / 1000); + if (!opp) + return -EIO; + + level = opp->level_index; + } + + return __scmi_perf_level_set(ph, dom, level, poll); } static int scmi_dvfs_freq_get(const struct scmi_protocol_handle *ph, u32 domain, @@ -629,12 +866,27 @@ static int scmi_dvfs_freq_get(const struct scmi_protocol_handle *ph, u32 domain, { int ret; u32 level; - struct scmi_perf_info *pi = ph->get_priv(ph); - struct perf_dom_info *dom = pi->dom_info + domain; + struct perf_dom_info *dom; - ret = scmi_perf_level_get(ph, domain, &level, poll); - if (!ret) + dom = scmi_perf_domain_lookup(ph, domain); + if (IS_ERR(dom)) + return PTR_ERR(dom); + + ret = __scmi_perf_level_get(ph, dom, &level, poll); + if (ret) + return ret; + + if (!dom->level_indexing_mode) { *freq = level * dom->mult_factor; + } else { + struct scmi_opp *opp; + + opp = xa_load(&dom->opps_by_idx, level); + if (!opp) + return -EIO; + + *freq = opp->indicative_freq * 1000; + } return ret; } @@ -643,18 +895,21 @@ static int scmi_dvfs_est_power_get(const struct scmi_protocol_handle *ph, u32 domain, unsigned long *freq, unsigned long *power) { - struct scmi_perf_info *pi = ph->get_priv(ph); struct perf_dom_info *dom; unsigned long opp_freq; int idx, ret = -EINVAL; struct scmi_opp *opp; - dom = pi->dom_info + domain; - if (!dom) - return -EIO; + dom = scmi_perf_domain_lookup(ph, domain); + if (IS_ERR(dom)) + return PTR_ERR(dom); for (opp = dom->opp, idx = 0; idx < dom->opp_count; idx++, opp++) { - opp_freq = opp->perf * dom->mult_factor; + if (!dom->level_indexing_mode) + opp_freq = opp->perf * dom->mult_factor; + else + opp_freq = opp->indicative_freq * 1000; + if (opp_freq < *freq) continue; @@ -670,10 +925,16 @@ static int scmi_dvfs_est_power_get(const struct scmi_protocol_handle *ph, static bool scmi_fast_switch_possible(const struct scmi_protocol_handle *ph, struct device *dev) { + int domain; struct perf_dom_info *dom; - struct scmi_perf_info *pi = ph->get_priv(ph); - dom = pi->dom_info + scmi_dev_domain_id(dev); + domain = scmi_dev_domain_id(dev); + if (domain < 0) + return false; + + dom = scmi_perf_domain_lookup(ph, domain); + if (IS_ERR(dom)) + return false; return dom->fc_info && dom->fc_info[PERF_FC_LEVEL].set_addr; } @@ -831,13 +1092,18 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph) for (domain = 0; domain < pinfo->num_domains; domain++) { struct perf_dom_info *dom = pinfo->dom_info + domain; - scmi_perf_domain_attributes_get(ph, domain, dom, version); - scmi_perf_describe_levels_get(ph, domain, dom); + dom->id = domain; + scmi_perf_domain_attributes_get(ph, dom, version); + scmi_perf_describe_levels_get(ph, dom, version); if (dom->perf_fastchannels) - scmi_perf_domain_init_fc(ph, domain, &dom->fc_info); + scmi_perf_domain_init_fc(ph, dom->id, &dom->fc_info); } + ret = devm_add_action_or_reset(ph->dev, scmi_perf_xa_destroy, pinfo); + if (ret) + return ret; + pinfo->version = version; return ph->set_priv(ph, pinfo); diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c index f9040bd61081..285fe7ad490d 100644 --- a/drivers/firmware/arm_sdei.c +++ b/drivers/firmware/arm_sdei.c @@ -1095,3 +1095,22 @@ int sdei_event_handler(struct pt_regs *regs, return err; } NOKPROBE_SYMBOL(sdei_event_handler); + +void sdei_handler_abort(void) +{ + /* + * If the crash happened in an SDEI event handler then we need to + * finish the handler with the firmware so that we can have working + * interrupts in the crash kernel. + */ + if (__this_cpu_read(sdei_active_critical_event)) { + pr_warn("still in SDEI critical event context, attempting to finish handler.\n"); + __sdei_handler_abort(); + __this_cpu_write(sdei_active_critical_event, NULL); + } + if (__this_cpu_read(sdei_active_normal_event)) { + pr_warn("still in SDEI normal event context, attempting to finish handler.\n"); + __sdei_handler_abort(); + __this_cpu_write(sdei_active_normal_event, NULL); + } +} diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 6a9aa97373d3..49b70c70dc69 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -978,7 +978,8 @@ static int cs_dsp_create_control(struct cs_dsp *dsp, ctl->alg_region.alg == alg_region->alg && ctl->alg_region.type == alg_region->type) { if ((!subname && !ctl->subname) || - (subname && !strncmp(ctl->subname, subname, ctl->subname_len))) { + (subname && (ctl->subname_len == subname_len) && + !strncmp(ctl->subname, subname, ctl->subname_len))) { if (!ctl->enabled) ctl->enabled = 1; return 0; diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 16d64a34d1e1..92389a5481ff 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -88,6 +88,7 @@ lib-$(CONFIG_EFI_GENERIC_STUB) += efi-stub.o string.o intrinsics.o systable.o \ lib-$(CONFIG_ARM) += arm32-stub.o lib-$(CONFIG_ARM64) += arm64.o arm64-stub.o smbios.o lib-$(CONFIG_X86) += x86-stub.o +lib-$(CONFIG_X86_64) += x86-5lvl.o lib-$(CONFIG_RISCV) += riscv.o riscv-stub.o lib-$(CONFIG_LOONGARCH) += loongarch.o loongarch-stub.o @@ -146,7 +147,7 @@ STUBCOPY_RELOC-$(CONFIG_ARM64) := R_AARCH64_ABS # For RISC-V, we don't need anything special other than arm64. Keep all the # symbols in .init section and make sure that no absolute symbols references -# doesn't exist. +# exist. STUBCOPY_FLAGS-$(CONFIG_RISCV) += --prefix-alloc-sections=.init \ --prefix-symbols=__efistub_ STUBCOPY_RELOC-$(CONFIG_RISCV) := R_RISCV_HI20 diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index 770b8ecb7398..8c40fc89f5f9 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c @@ -106,7 +106,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, */ status = efi_random_alloc(*reserve_size, min_kimg_align, reserve_addr, phys_seed, - EFI_LOADER_CODE); + EFI_LOADER_CODE, EFI_ALLOC_LIMIT); if (status != EFI_SUCCESS) efi_warn("efi_random_alloc() failed: 0x%lx\n", status); } else { diff --git a/drivers/firmware/efi/libstub/efi-stub-entry.c b/drivers/firmware/efi/libstub/efi-stub-entry.c index cc4dcaea67fa..2f1902e5d407 100644 --- a/drivers/firmware/efi/libstub/efi-stub-entry.c +++ b/drivers/firmware/efi/libstub/efi-stub-entry.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only #include <linux/efi.h> +#include <linux/screen_info.h> + #include <asm/efi.h> #include "efistub.h" diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 732984295295..bfa30625f5d0 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -73,6 +73,8 @@ efi_status_t efi_parse_options(char const *cmdline) efi_loglevel = CONSOLE_LOGLEVEL_QUIET; } else if (!strcmp(param, "noinitrd")) { efi_noinitrd = true; + } else if (IS_ENABLED(CONFIG_X86_64) && !strcmp(param, "no5lvl")) { + efi_no5lvl = true; } else if (!strcmp(param, "efi") && val) { efi_nochunk = parse_option_str(val, "nochunk"); efi_novamap |= parse_option_str(val, "novamap"); diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 6aa38a1bf126..9823f6fb3e01 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -33,6 +33,7 @@ #define EFI_ALLOC_LIMIT ULONG_MAX #endif +extern bool efi_no5lvl; extern bool efi_nochunk; extern bool efi_nokaslr; extern int efi_loglevel; @@ -955,7 +956,7 @@ efi_status_t efi_get_random_bytes(unsigned long size, u8 *out); efi_status_t efi_random_alloc(unsigned long size, unsigned long align, unsigned long *addr, unsigned long random_seed, - int memory_type); + int memory_type, unsigned long alloc_limit); efi_status_t efi_random_get_seed(void); diff --git a/drivers/firmware/efi/libstub/randomalloc.c b/drivers/firmware/efi/libstub/randomalloc.c index 32c7a54923b4..674a064b8f7a 100644 --- a/drivers/firmware/efi/libstub/randomalloc.c +++ b/drivers/firmware/efi/libstub/randomalloc.c @@ -16,7 +16,8 @@ */ static unsigned long get_entry_num_slots(efi_memory_desc_t *md, unsigned long size, - unsigned long align_shift) + unsigned long align_shift, + u64 alloc_limit) { unsigned long align = 1UL << align_shift; u64 first_slot, last_slot, region_end; @@ -29,7 +30,7 @@ static unsigned long get_entry_num_slots(efi_memory_desc_t *md, return 0; region_end = min(md->phys_addr + md->num_pages * EFI_PAGE_SIZE - 1, - (u64)EFI_ALLOC_LIMIT); + alloc_limit); if (region_end < size) return 0; @@ -54,7 +55,8 @@ efi_status_t efi_random_alloc(unsigned long size, unsigned long align, unsigned long *addr, unsigned long random_seed, - int memory_type) + int memory_type, + unsigned long alloc_limit) { unsigned long total_slots = 0, target_slot; unsigned long total_mirrored_slots = 0; @@ -76,7 +78,7 @@ efi_status_t efi_random_alloc(unsigned long size, efi_memory_desc_t *md = (void *)map->map + map_offset; unsigned long slots; - slots = get_entry_num_slots(md, size, ilog2(align)); + slots = get_entry_num_slots(md, size, ilog2(align), alloc_limit); MD_NUM_SLOTS(md) = slots; total_slots += slots; if (md->attribute & EFI_MEMORY_MORE_RELIABLE) diff --git a/drivers/firmware/efi/libstub/screen_info.c b/drivers/firmware/efi/libstub/screen_info.c index 4be1c4d1f922..a51ec201ca3c 100644 --- a/drivers/firmware/efi/libstub/screen_info.c +++ b/drivers/firmware/efi/libstub/screen_info.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 #include <linux/efi.h> +#include <linux/screen_info.h> + #include <asm/efi.h> #include "efistub.h" diff --git a/drivers/firmware/efi/libstub/x86-5lvl.c b/drivers/firmware/efi/libstub/x86-5lvl.c new file mode 100644 index 000000000000..479dd445acdc --- /dev/null +++ b/drivers/firmware/efi/libstub/x86-5lvl.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/efi.h> + +#include <asm/boot.h> +#include <asm/desc.h> +#include <asm/efi.h> + +#include "efistub.h" +#include "x86-stub.h" + +bool efi_no5lvl; + +static void (*la57_toggle)(void *cr3); + +static const struct desc_struct gdt[] = { + [GDT_ENTRY_KERNEL32_CS] = GDT_ENTRY_INIT(0xc09b, 0, 0xfffff), + [GDT_ENTRY_KERNEL_CS] = GDT_ENTRY_INIT(0xa09b, 0, 0xfffff), +}; + +/* + * Enabling (or disabling) 5 level paging is tricky, because it can only be + * done from 32-bit mode with paging disabled. This means not only that the + * code itself must be running from 32-bit addressable physical memory, but + * also that the root page table must be 32-bit addressable, as programming + * a 64-bit value into CR3 when running in 32-bit mode is not supported. + */ +efi_status_t efi_setup_5level_paging(void) +{ + u8 tmpl_size = (u8 *)&trampoline_ljmp_imm_offset - (u8 *)&trampoline_32bit_src; + efi_status_t status; + u8 *la57_code; + + if (!efi_is_64bit()) + return EFI_SUCCESS; + + /* check for 5 level paging support */ + if (native_cpuid_eax(0) < 7 || + !(native_cpuid_ecx(7) & (1 << (X86_FEATURE_LA57 & 31)))) + return EFI_SUCCESS; + + /* allocate some 32-bit addressable memory for code and a page table */ + status = efi_allocate_pages(2 * PAGE_SIZE, (unsigned long *)&la57_code, + U32_MAX); + if (status != EFI_SUCCESS) + return status; + + la57_toggle = memcpy(la57_code, trampoline_32bit_src, tmpl_size); + memset(la57_code + tmpl_size, 0x90, PAGE_SIZE - tmpl_size); + + /* + * To avoid the need to allocate a 32-bit addressable stack, the + * trampoline uses a LJMP instruction to switch back to long mode. + * LJMP takes an absolute destination address, which needs to be + * fixed up at runtime. + */ + *(u32 *)&la57_code[trampoline_ljmp_imm_offset] += (unsigned long)la57_code; + + efi_adjust_memory_range_protection((unsigned long)la57_toggle, PAGE_SIZE); + + return EFI_SUCCESS; +} + +void efi_5level_switch(void) +{ + bool want_la57 = IS_ENABLED(CONFIG_X86_5LEVEL) && !efi_no5lvl; + bool have_la57 = native_read_cr4() & X86_CR4_LA57; + bool need_toggle = want_la57 ^ have_la57; + u64 *pgt = (void *)la57_toggle + PAGE_SIZE; + u64 *cr3 = (u64 *)__native_read_cr3(); + u64 *new_cr3; + + if (!la57_toggle || !need_toggle) + return; + + if (!have_la57) { + /* + * 5 level paging will be enabled, so a root level page needs + * to be allocated from the 32-bit addressable physical region, + * with its first entry referring to the existing hierarchy. + */ + new_cr3 = memset(pgt, 0, PAGE_SIZE); + new_cr3[0] = (u64)cr3 | _PAGE_TABLE_NOENC; + } else { + /* take the new root table pointer from the current entry #0 */ + new_cr3 = (u64 *)(cr3[0] & PAGE_MASK); + + /* copy the new root table if it is not 32-bit addressable */ + if ((u64)new_cr3 > U32_MAX) + new_cr3 = memcpy(pgt, new_cr3, PAGE_SIZE); + } + + native_load_gdt(&(struct desc_ptr){ sizeof(gdt) - 1, (u64)gdt }); + + la57_toggle(new_cr3); +} diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index 220be75a5cdc..2fee52ed335d 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -15,16 +15,16 @@ #include <asm/setup.h> #include <asm/desc.h> #include <asm/boot.h> +#include <asm/kaslr.h> +#include <asm/sev.h> #include "efistub.h" - -/* Maximum physical address for 64-bit kernel with 4-level paging */ -#define MAXMEM_X86_64_4LEVEL (1ull << 46) +#include "x86-stub.h" const efi_system_table_t *efi_system_table; const efi_dxe_services_table_t *efi_dxe_table; -u32 image_offset __section(".data"); static efi_loaded_image_t *image = NULL; +static efi_memory_attribute_protocol_t *memattr; typedef union sev_memory_acceptance_protocol sev_memory_acceptance_protocol_t; union sev_memory_acceptance_protocol { @@ -72,7 +72,7 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom) rom->data.type = SETUP_PCI; rom->data.len = size - sizeof(struct setup_data); rom->data.next = 0; - rom->pcilen = pci->romsize; + rom->pcilen = romsize; *__rom = rom; status = efi_call_proto(pci, pci.read, EfiPciIoWidthUint16, @@ -223,8 +223,8 @@ static void retrieve_apple_device_properties(struct boot_params *boot_params) } } -static void -adjust_memory_range_protection(unsigned long start, unsigned long size) +void efi_adjust_memory_range_protection(unsigned long start, + unsigned long size) { efi_status_t status; efi_gcd_memory_space_desc_t desc; @@ -232,12 +232,18 @@ adjust_memory_range_protection(unsigned long start, unsigned long size) unsigned long rounded_start, rounded_end; unsigned long unprotect_start, unprotect_size; - if (efi_dxe_table == NULL) - return; - rounded_start = rounddown(start, EFI_PAGE_SIZE); rounded_end = roundup(start + size, EFI_PAGE_SIZE); + if (memattr != NULL) { + efi_call_proto(memattr, clear_memory_attributes, rounded_start, + rounded_end - rounded_start, EFI_MEMORY_XP); + return; + } + + if (efi_dxe_table == NULL) + return; + /* * Don't modify memory region attributes, they are * already suitable, to lower the possibility to @@ -278,49 +284,6 @@ adjust_memory_range_protection(unsigned long start, unsigned long size) } } -/* - * Trampoline takes 2 pages and can be loaded in first megabyte of memory - * with its end placed between 128k and 640k where BIOS might start. - * (see arch/x86/boot/compressed/pgtable_64.c) - * - * We cannot find exact trampoline placement since memory map - * can be modified by UEFI, and it can alter the computed address. - */ - -#define TRAMPOLINE_PLACEMENT_BASE ((128 - 8)*1024) -#define TRAMPOLINE_PLACEMENT_SIZE (640*1024 - (128 - 8)*1024) - -void startup_32(struct boot_params *boot_params); - -static void -setup_memory_protection(unsigned long image_base, unsigned long image_size) -{ - /* - * Allow execution of possible trampoline used - * for switching between 4- and 5-level page tables - * and relocated kernel image. - */ - - adjust_memory_range_protection(TRAMPOLINE_PLACEMENT_BASE, - TRAMPOLINE_PLACEMENT_SIZE); - -#ifdef CONFIG_64BIT - if (image_base != (unsigned long)startup_32) - adjust_memory_range_protection(image_base, image_size); -#else - /* - * Clear protection flags on a whole range of possible - * addresses used for KASLR. We don't need to do that - * on x86_64, since KASLR/extraction is performed after - * dedicated identity page tables are built and we only - * need to remove possible protection on relocated image - * itself disregarding further relocations. - */ - adjust_memory_range_protection(LOAD_PHYSICAL_ADDR, - KERNEL_IMAGE_SIZE - LOAD_PHYSICAL_ADDR); -#endif -} - static void setup_unaccepted_memory(void) { efi_guid_t mem_acceptance_proto = OVMF_SEV_MEMORY_ACCEPTANCE_PROTOCOL_GUID; @@ -346,9 +309,7 @@ static void setup_unaccepted_memory(void) static const efi_char16_t apple[] = L"Apple"; -static void setup_quirks(struct boot_params *boot_params, - unsigned long image_base, - unsigned long image_size) +static void setup_quirks(struct boot_params *boot_params) { efi_char16_t *fw_vendor = (efi_char16_t *)(unsigned long) efi_table_attr(efi_system_table, fw_vendor); @@ -357,9 +318,6 @@ static void setup_quirks(struct boot_params *boot_params, if (IS_ENABLED(CONFIG_APPLE_PROPERTIES)) retrieve_apple_device_properties(boot_params); } - - if (IS_ENABLED(CONFIG_EFI_DXE_MEM_ATTRIBUTES)) - setup_memory_protection(image_base, image_size); } /* @@ -512,7 +470,6 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, } image_base = efi_table_attr(image, image_base); - image_offset = (void *)startup_32 - image_base; status = efi_allocate_pages(sizeof(struct boot_params), (unsigned long *)&boot_params, ULONG_MAX); @@ -803,19 +760,96 @@ static efi_status_t exit_boot(struct boot_params *boot_params, void *handle) return EFI_SUCCESS; } +static bool have_unsupported_snp_features(void) +{ + u64 unsupported; + + unsupported = snp_get_unsupported_features(sev_get_status()); + if (unsupported) { + efi_err("Unsupported SEV-SNP features detected: 0x%llx\n", + unsupported); + return true; + } + return false; +} + +static void efi_get_seed(void *seed, int size) +{ + efi_get_random_bytes(size, seed); + + /* + * This only updates seed[0] when running on 32-bit, but in that case, + * seed[1] is not used anyway, as there is no virtual KASLR on 32-bit. + */ + *(unsigned long *)seed ^= kaslr_get_random_long("EFI"); +} + +static void error(char *str) +{ + efi_warn("Decompression failed: %s\n", str); +} + +static efi_status_t efi_decompress_kernel(unsigned long *kernel_entry) +{ + unsigned long virt_addr = LOAD_PHYSICAL_ADDR; + unsigned long addr, alloc_size, entry; + efi_status_t status; + u32 seed[2] = {}; + + /* determine the required size of the allocation */ + alloc_size = ALIGN(max_t(unsigned long, output_len, kernel_total_size), + MIN_KERNEL_ALIGN); + + if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && !efi_nokaslr) { + u64 range = KERNEL_IMAGE_SIZE - LOAD_PHYSICAL_ADDR - kernel_total_size; + + efi_get_seed(seed, sizeof(seed)); + + virt_addr += (range * seed[1]) >> 32; + virt_addr &= ~(CONFIG_PHYSICAL_ALIGN - 1); + } + + status = efi_random_alloc(alloc_size, CONFIG_PHYSICAL_ALIGN, &addr, + seed[0], EFI_LOADER_CODE, + EFI_X86_KERNEL_ALLOC_LIMIT); + if (status != EFI_SUCCESS) + return status; + + entry = decompress_kernel((void *)addr, virt_addr, error); + if (entry == ULONG_MAX) { + efi_free(alloc_size, addr); + return EFI_LOAD_ERROR; + } + + *kernel_entry = addr + entry; + + efi_adjust_memory_range_protection(addr, kernel_total_size); + + return EFI_SUCCESS; +} + +static void __noreturn enter_kernel(unsigned long kernel_addr, + struct boot_params *boot_params) +{ + /* enter decompressed kernel with boot_params pointer in RSI/ESI */ + asm("jmp *%0"::"r"(kernel_addr), "S"(boot_params)); + + unreachable(); +} + /* - * On success, we return the address of startup_32, which has potentially been - * relocated by efi_relocate_kernel. - * On failure, we exit to the firmware via efi_exit instead of returning. + * On success, this routine will jump to the relocated image directly and never + * return. On failure, it will exit to the firmware via efi_exit() instead of + * returning. */ -asmlinkage unsigned long efi_main(efi_handle_t handle, - efi_system_table_t *sys_table_arg, - struct boot_params *boot_params) +void __noreturn efi_stub_entry(efi_handle_t handle, + efi_system_table_t *sys_table_arg, + struct boot_params *boot_params) { - unsigned long bzimage_addr = (unsigned long)startup_32; - unsigned long buffer_start, buffer_end; + efi_guid_t guid = EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID; struct setup_header *hdr = &boot_params->hdr; const struct linux_efi_initrd *initrd = NULL; + unsigned long kernel_entry; efi_status_t status; efi_system_table = sys_table_arg; @@ -823,65 +857,25 @@ asmlinkage unsigned long efi_main(efi_handle_t handle, if (efi_system_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) efi_exit(handle, EFI_INVALID_PARAMETER); - efi_dxe_table = get_efi_config_table(EFI_DXE_SERVICES_TABLE_GUID); - if (efi_dxe_table && - efi_dxe_table->hdr.signature != EFI_DXE_SERVICES_TABLE_SIGNATURE) { - efi_warn("Ignoring DXE services table: invalid signature\n"); - efi_dxe_table = NULL; + if (have_unsupported_snp_features()) + efi_exit(handle, EFI_UNSUPPORTED); + + if (IS_ENABLED(CONFIG_EFI_DXE_MEM_ATTRIBUTES)) { + efi_dxe_table = get_efi_config_table(EFI_DXE_SERVICES_TABLE_GUID); + if (efi_dxe_table && + efi_dxe_table->hdr.signature != EFI_DXE_SERVICES_TABLE_SIGNATURE) { + efi_warn("Ignoring DXE services table: invalid signature\n"); + efi_dxe_table = NULL; + } } - /* - * If the kernel isn't already loaded at a suitable address, - * relocate it. - * - * It must be loaded above LOAD_PHYSICAL_ADDR. - * - * The maximum address for 64-bit is 1 << 46 for 4-level paging. This - * is defined as the macro MAXMEM, but unfortunately that is not a - * compile-time constant if 5-level paging is configured, so we instead - * define our own macro for use here. - * - * For 32-bit, the maximum address is complicated to figure out, for - * now use KERNEL_IMAGE_SIZE, which will be 512MiB, the same as what - * KASLR uses. - * - * Also relocate it if image_offset is zero, i.e. the kernel wasn't - * loaded by LoadImage, but rather by a bootloader that called the - * handover entry. The reason we must always relocate in this case is - * to handle the case of systemd-boot booting a unified kernel image, - * which is a PE executable that contains the bzImage and an initrd as - * COFF sections. The initrd section is placed after the bzImage - * without ensuring that there are at least init_size bytes available - * for the bzImage, and thus the compressed kernel's startup code may - * overwrite the initrd unless it is moved out of the way. - */ + /* grab the memory attributes protocol if it exists */ + efi_bs_call(locate_protocol, &guid, NULL, (void **)&memattr); - buffer_start = ALIGN(bzimage_addr - image_offset, - hdr->kernel_alignment); - buffer_end = buffer_start + hdr->init_size; - - if ((buffer_start < LOAD_PHYSICAL_ADDR) || - (IS_ENABLED(CONFIG_X86_32) && buffer_end > KERNEL_IMAGE_SIZE) || - (IS_ENABLED(CONFIG_X86_64) && buffer_end > MAXMEM_X86_64_4LEVEL) || - (image_offset == 0)) { - extern char _bss[]; - - status = efi_relocate_kernel(&bzimage_addr, - (unsigned long)_bss - bzimage_addr, - hdr->init_size, - hdr->pref_address, - hdr->kernel_alignment, - LOAD_PHYSICAL_ADDR); - if (status != EFI_SUCCESS) { - efi_err("efi_relocate_kernel() failed!\n"); - goto fail; - } - /* - * Now that we've copied the kernel elsewhere, we no longer - * have a set up block before startup_32(), so reset image_offset - * to zero in case it was set earlier. - */ - image_offset = 0; + status = efi_setup_5level_paging(); + if (status != EFI_SUCCESS) { + efi_err("efi_setup_5level_paging() failed!\n"); + goto fail; } #ifdef CONFIG_CMDLINE_BOOL @@ -901,6 +895,12 @@ asmlinkage unsigned long efi_main(efi_handle_t handle, } } + status = efi_decompress_kernel(&kernel_entry); + if (status != EFI_SUCCESS) { + efi_err("Failed to decompress kernel\n"); + goto fail; + } + /* * At this point, an initrd may already have been loaded by the * bootloader and passed via bootparams. We permit an initrd loaded @@ -940,7 +940,7 @@ asmlinkage unsigned long efi_main(efi_handle_t handle, setup_efi_pci(boot_params); - setup_quirks(boot_params, bzimage_addr, buffer_end - buffer_start); + setup_quirks(boot_params); setup_unaccepted_memory(); @@ -950,9 +950,38 @@ asmlinkage unsigned long efi_main(efi_handle_t handle, goto fail; } - return bzimage_addr; + /* + * Call the SEV init code while still running with the firmware's + * GDT/IDT, so #VC exceptions will be handled by EFI. + */ + sev_enable(boot_params); + + efi_5level_switch(); + + enter_kernel(kernel_entry, boot_params); fail: - efi_err("efi_main() failed!\n"); + efi_err("efi_stub_entry() failed!\n"); efi_exit(handle, status); } + +#ifdef CONFIG_EFI_HANDOVER_PROTOCOL +void efi_handover_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg, + struct boot_params *boot_params) +{ + extern char _bss[], _ebss[]; + + memset(_bss, 0, _ebss - _bss); + efi_stub_entry(handle, sys_table_arg, boot_params); +} + +#ifndef CONFIG_EFI_MIXED +extern __alias(efi_handover_entry) +void efi32_stub_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg, + struct boot_params *boot_params); + +extern __alias(efi_handover_entry) +void efi64_stub_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg, + struct boot_params *boot_params); +#endif +#endif diff --git a/drivers/firmware/efi/libstub/x86-stub.h b/drivers/firmware/efi/libstub/x86-stub.h new file mode 100644 index 000000000000..37c5a36b9d8c --- /dev/null +++ b/drivers/firmware/efi/libstub/x86-stub.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <linux/efi.h> + +extern void trampoline_32bit_src(void *, bool); +extern const u16 trampoline_ljmp_imm_offset; + +void efi_adjust_memory_range_protection(unsigned long start, + unsigned long size); + +#ifdef CONFIG_X86_64 +efi_status_t efi_setup_5level_paging(void); +void efi_5level_switch(void); +#else +static inline efi_status_t efi_setup_5level_paging(void) { return EFI_SUCCESS; } +static inline void efi_5level_switch(void) {} +#endif diff --git a/drivers/firmware/efi/libstub/zboot.c b/drivers/firmware/efi/libstub/zboot.c index e5d7fa1f1d8f..bdb17eac0cb4 100644 --- a/drivers/firmware/efi/libstub/zboot.c +++ b/drivers/firmware/efi/libstub/zboot.c @@ -119,7 +119,7 @@ efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab) } status = efi_random_alloc(alloc_size, min_kimg_align, &image_base, - seed, EFI_LOADER_CODE); + seed, EFI_LOADER_CODE, EFI_ALLOC_LIMIT); if (status != EFI_SUCCESS) { efi_err("Failed to allocate memory\n"); goto free_cmdline; diff --git a/drivers/firmware/efi/riscv-runtime.c b/drivers/firmware/efi/riscv-runtime.c index d0daacd2c903..09525fb5c240 100644 --- a/drivers/firmware/efi/riscv-runtime.c +++ b/drivers/firmware/efi/riscv-runtime.c @@ -130,14 +130,25 @@ static int __init riscv_enable_runtime_services(void) } early_initcall(riscv_enable_runtime_services); -void efi_virtmap_load(void) +static void efi_virtmap_load(void) { preempt_disable(); switch_mm(current->active_mm, &efi_mm, NULL); } -void efi_virtmap_unload(void) +static void efi_virtmap_unload(void) { switch_mm(&efi_mm, current->active_mm, NULL); preempt_enable(); } + +void arch_efi_call_virt_setup(void) +{ + sync_kernel_mappings(efi_mm.pgd); + efi_virtmap_load(); +} + +void arch_efi_call_virt_teardown(void) +{ + efi_virtmap_unload(); +} diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c index a400c4312c82..5d56bc40a79d 100644 --- a/drivers/firmware/efi/runtime-wrappers.c +++ b/drivers/firmware/efi/runtime-wrappers.c @@ -40,55 +40,97 @@ * code doesn't get too cluttered: */ #define efi_call_virt(f, args...) \ - efi_call_virt_pointer(efi.runtime, f, args) -#define __efi_call_virt(f, args...) \ - __efi_call_virt_pointer(efi.runtime, f, args) + arch_efi_call_virt(efi.runtime, f, args) + +union efi_rts_args { + struct { + efi_time_t *time; + efi_time_cap_t *capabilities; + } GET_TIME; + + struct { + efi_time_t *time; + } SET_TIME; + + struct { + efi_bool_t *enabled; + efi_bool_t *pending; + efi_time_t *time; + } GET_WAKEUP_TIME; + + struct { + efi_bool_t enable; + efi_time_t *time; + } SET_WAKEUP_TIME; + + struct { + efi_char16_t *name; + efi_guid_t *vendor; + u32 *attr; + unsigned long *data_size; + void *data; + } GET_VARIABLE; + + struct { + unsigned long *name_size; + efi_char16_t *name; + efi_guid_t *vendor; + } GET_NEXT_VARIABLE; + + struct { + efi_char16_t *name; + efi_guid_t *vendor; + u32 attr; + unsigned long data_size; + void *data; + } SET_VARIABLE; + + struct { + u32 attr; + u64 *storage_space; + u64 *remaining_space; + u64 *max_variable_size; + } QUERY_VARIABLE_INFO; + + struct { + u32 *high_count; + } GET_NEXT_HIGH_MONO_COUNT; + + struct { + efi_capsule_header_t **capsules; + unsigned long count; + unsigned long sg_list; + } UPDATE_CAPSULE; + + struct { + efi_capsule_header_t **capsules; + unsigned long count; + u64 *max_size; + int *reset_type; + } QUERY_CAPSULE_CAPS; + + struct { + efi_status_t (__efiapi *acpi_prm_handler)(u64, void *); + u64 param_buffer_addr; + void *context; + } ACPI_PRM_HANDLER; +}; struct efi_runtime_work efi_rts_work; /* - * efi_queue_work: Queue efi_runtime_service() and wait until it's done - * @rts: efi_runtime_service() function identifier - * @rts_arg<1-5>: efi_runtime_service() function arguments + * efi_queue_work: Queue EFI runtime service call and wait for completion + * @_rts: EFI runtime service function identifier + * @_args: Arguments to pass to the EFI runtime service * * Accesses to efi_runtime_services() are serialized by a binary * semaphore (efi_runtime_lock) and caller waits until the work is * finished, hence _only_ one work is queued at a time and the caller * thread waits for completion. */ -#define efi_queue_work(_rts, _arg1, _arg2, _arg3, _arg4, _arg5) \ -({ \ - efi_rts_work.status = EFI_ABORTED; \ - \ - if (!efi_enabled(EFI_RUNTIME_SERVICES)) { \ - pr_warn_once("EFI Runtime Services are disabled!\n"); \ - efi_rts_work.status = EFI_DEVICE_ERROR; \ - goto exit; \ - } \ - \ - init_completion(&efi_rts_work.efi_rts_comp); \ - INIT_WORK(&efi_rts_work.work, efi_call_rts); \ - efi_rts_work.arg1 = _arg1; \ - efi_rts_work.arg2 = _arg2; \ - efi_rts_work.arg3 = _arg3; \ - efi_rts_work.arg4 = _arg4; \ - efi_rts_work.arg5 = _arg5; \ - efi_rts_work.efi_rts_id = _rts; \ - \ - /* \ - * queue_work() returns 0 if work was already on queue, \ - * _ideally_ this should never happen. \ - */ \ - if (queue_work(efi_rts_wq, &efi_rts_work.work)) \ - wait_for_completion(&efi_rts_work.efi_rts_comp); \ - else \ - pr_err("Failed to queue work to efi_rts_wq.\n"); \ - \ - WARN_ON_ONCE(efi_rts_work.status == EFI_ABORTED); \ -exit: \ - efi_rts_work.efi_rts_id = EFI_NONE; \ - efi_rts_work.status; \ -}) +#define efi_queue_work(_rts, _args...) \ + __efi_queue_work(EFI_ ## _rts, \ + &(union efi_rts_args){ ._rts = { _args }}) #ifndef arch_efi_save_flags #define arch_efi_save_flags(state_flags) local_save_flags(state_flags) @@ -103,7 +145,7 @@ unsigned long efi_call_virt_save_flags(void) return flags; } -void efi_call_virt_check_flags(unsigned long flags, const char *call) +void efi_call_virt_check_flags(unsigned long flags, const void *caller) { unsigned long cur_flags, mismatch; @@ -114,8 +156,8 @@ void efi_call_virt_check_flags(unsigned long flags, const char *call) return; add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_NOW_UNRELIABLE); - pr_err_ratelimited(FW_BUG "IRQ flags corrupted (0x%08lx=>0x%08lx) by EFI %s\n", - flags, cur_flags, call); + pr_err_ratelimited(FW_BUG "IRQ flags corrupted (0x%08lx=>0x%08lx) by EFI call from %pS\n", + flags, cur_flags, caller ?: __builtin_return_address(0)); arch_efi_restore_flags(flags); } @@ -170,74 +212,90 @@ extern struct semaphore __efi_uv_runtime_lock __alias(efi_runtime_lock); /* * Calls the appropriate efi_runtime_service() with the appropriate * arguments. - * - * Semantics followed by efi_call_rts() to understand efi_runtime_work: - * 1. If argument was a pointer, recast it from void pointer to original - * pointer type. - * 2. If argument was a value, recast it from void pointer to original - * pointer type and dereference it. */ static void efi_call_rts(struct work_struct *work) { - void *arg1, *arg2, *arg3, *arg4, *arg5; + const union efi_rts_args *args = efi_rts_work.args; efi_status_t status = EFI_NOT_FOUND; + unsigned long flags; - arg1 = efi_rts_work.arg1; - arg2 = efi_rts_work.arg2; - arg3 = efi_rts_work.arg3; - arg4 = efi_rts_work.arg4; - arg5 = efi_rts_work.arg5; + arch_efi_call_virt_setup(); + flags = efi_call_virt_save_flags(); switch (efi_rts_work.efi_rts_id) { case EFI_GET_TIME: - status = efi_call_virt(get_time, (efi_time_t *)arg1, - (efi_time_cap_t *)arg2); + status = efi_call_virt(get_time, + args->GET_TIME.time, + args->GET_TIME.capabilities); break; case EFI_SET_TIME: - status = efi_call_virt(set_time, (efi_time_t *)arg1); + status = efi_call_virt(set_time, + args->SET_TIME.time); break; case EFI_GET_WAKEUP_TIME: - status = efi_call_virt(get_wakeup_time, (efi_bool_t *)arg1, - (efi_bool_t *)arg2, (efi_time_t *)arg3); + status = efi_call_virt(get_wakeup_time, + args->GET_WAKEUP_TIME.enabled, + args->GET_WAKEUP_TIME.pending, + args->GET_WAKEUP_TIME.time); break; case EFI_SET_WAKEUP_TIME: - status = efi_call_virt(set_wakeup_time, *(efi_bool_t *)arg1, - (efi_time_t *)arg2); + status = efi_call_virt(set_wakeup_time, + args->SET_WAKEUP_TIME.enable, + args->SET_WAKEUP_TIME.time); break; case EFI_GET_VARIABLE: - status = efi_call_virt(get_variable, (efi_char16_t *)arg1, - (efi_guid_t *)arg2, (u32 *)arg3, - (unsigned long *)arg4, (void *)arg5); + status = efi_call_virt(get_variable, + args->GET_VARIABLE.name, + args->GET_VARIABLE.vendor, + args->GET_VARIABLE.attr, + args->GET_VARIABLE.data_size, + args->GET_VARIABLE.data); break; case EFI_GET_NEXT_VARIABLE: - status = efi_call_virt(get_next_variable, (unsigned long *)arg1, - (efi_char16_t *)arg2, - (efi_guid_t *)arg3); + status = efi_call_virt(get_next_variable, + args->GET_NEXT_VARIABLE.name_size, + args->GET_NEXT_VARIABLE.name, + args->GET_NEXT_VARIABLE.vendor); break; case EFI_SET_VARIABLE: - status = efi_call_virt(set_variable, (efi_char16_t *)arg1, - (efi_guid_t *)arg2, *(u32 *)arg3, - *(unsigned long *)arg4, (void *)arg5); + status = efi_call_virt(set_variable, + args->SET_VARIABLE.name, + args->SET_VARIABLE.vendor, + args->SET_VARIABLE.attr, + args->SET_VARIABLE.data_size, + args->SET_VARIABLE.data); break; case EFI_QUERY_VARIABLE_INFO: - status = efi_call_virt(query_variable_info, *(u32 *)arg1, - (u64 *)arg2, (u64 *)arg3, (u64 *)arg4); + status = efi_call_virt(query_variable_info, + args->QUERY_VARIABLE_INFO.attr, + args->QUERY_VARIABLE_INFO.storage_space, + args->QUERY_VARIABLE_INFO.remaining_space, + args->QUERY_VARIABLE_INFO.max_variable_size); break; case EFI_GET_NEXT_HIGH_MONO_COUNT: - status = efi_call_virt(get_next_high_mono_count, (u32 *)arg1); + status = efi_call_virt(get_next_high_mono_count, + args->GET_NEXT_HIGH_MONO_COUNT.high_count); break; case EFI_UPDATE_CAPSULE: status = efi_call_virt(update_capsule, - (efi_capsule_header_t **)arg1, - *(unsigned long *)arg2, - *(unsigned long *)arg3); + args->UPDATE_CAPSULE.capsules, + args->UPDATE_CAPSULE.count, + args->UPDATE_CAPSULE.sg_list); break; case EFI_QUERY_CAPSULE_CAPS: status = efi_call_virt(query_capsule_caps, - (efi_capsule_header_t **)arg1, - *(unsigned long *)arg2, (u64 *)arg3, - (int *)arg4); + args->QUERY_CAPSULE_CAPS.capsules, + args->QUERY_CAPSULE_CAPS.count, + args->QUERY_CAPSULE_CAPS.max_size, + args->QUERY_CAPSULE_CAPS.reset_type); break; + case EFI_ACPI_PRM_HANDLER: +#ifdef CONFIG_ACPI_PRMT + status = arch_efi_call_virt(args, ACPI_PRM_HANDLER.acpi_prm_handler, + args->ACPI_PRM_HANDLER.param_buffer_addr, + args->ACPI_PRM_HANDLER.context); + break; +#endif default: /* * Ideally, we should never reach here because a caller of this @@ -246,17 +304,53 @@ static void efi_call_rts(struct work_struct *work) */ pr_err("Requested executing invalid EFI Runtime Service.\n"); } + + efi_call_virt_check_flags(flags, efi_rts_work.caller); + arch_efi_call_virt_teardown(); + efi_rts_work.status = status; complete(&efi_rts_work.efi_rts_comp); } +static efi_status_t __efi_queue_work(enum efi_rts_ids id, + union efi_rts_args *args) +{ + efi_rts_work.efi_rts_id = id; + efi_rts_work.args = args; + efi_rts_work.caller = __builtin_return_address(0); + efi_rts_work.status = EFI_ABORTED; + + if (!efi_enabled(EFI_RUNTIME_SERVICES)) { + pr_warn_once("EFI Runtime Services are disabled!\n"); + efi_rts_work.status = EFI_DEVICE_ERROR; + goto exit; + } + + init_completion(&efi_rts_work.efi_rts_comp); + INIT_WORK(&efi_rts_work.work, efi_call_rts); + + /* + * queue_work() returns 0 if work was already on queue, + * _ideally_ this should never happen. + */ + if (queue_work(efi_rts_wq, &efi_rts_work.work)) + wait_for_completion(&efi_rts_work.efi_rts_comp); + else + pr_err("Failed to queue work to efi_rts_wq.\n"); + + WARN_ON_ONCE(efi_rts_work.status == EFI_ABORTED); +exit: + efi_rts_work.efi_rts_id = EFI_NONE; + return efi_rts_work.status; +} + static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) { efi_status_t status; if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(EFI_GET_TIME, tm, tc, NULL, NULL, NULL); + status = efi_queue_work(GET_TIME, tm, tc); up(&efi_runtime_lock); return status; } @@ -267,7 +361,7 @@ static efi_status_t virt_efi_set_time(efi_time_t *tm) if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(EFI_SET_TIME, tm, NULL, NULL, NULL, NULL); + status = efi_queue_work(SET_TIME, tm); up(&efi_runtime_lock); return status; } @@ -280,8 +374,7 @@ static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(EFI_GET_WAKEUP_TIME, enabled, pending, tm, NULL, - NULL); + status = efi_queue_work(GET_WAKEUP_TIME, enabled, pending, tm); up(&efi_runtime_lock); return status; } @@ -292,8 +385,7 @@ static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(EFI_SET_WAKEUP_TIME, &enabled, tm, NULL, NULL, - NULL); + status = efi_queue_work(SET_WAKEUP_TIME, enabled, tm); up(&efi_runtime_lock); return status; } @@ -308,7 +400,7 @@ static efi_status_t virt_efi_get_variable(efi_char16_t *name, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(EFI_GET_VARIABLE, name, vendor, attr, data_size, + status = efi_queue_work(GET_VARIABLE, name, vendor, attr, data_size, data); up(&efi_runtime_lock); return status; @@ -322,8 +414,7 @@ static efi_status_t virt_efi_get_next_variable(unsigned long *name_size, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(EFI_GET_NEXT_VARIABLE, name_size, name, vendor, - NULL, NULL); + status = efi_queue_work(GET_NEXT_VARIABLE, name_size, name, vendor); up(&efi_runtime_lock); return status; } @@ -338,24 +429,23 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(EFI_SET_VARIABLE, name, vendor, &attr, &data_size, + status = efi_queue_work(SET_VARIABLE, name, vendor, attr, data_size, data); up(&efi_runtime_lock); return status; } static efi_status_t -virt_efi_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor, - u32 attr, unsigned long data_size, - void *data) +virt_efi_set_variable_nb(efi_char16_t *name, efi_guid_t *vendor, u32 attr, + unsigned long data_size, void *data) { efi_status_t status; if (down_trylock(&efi_runtime_lock)) return EFI_NOT_READY; - status = efi_call_virt(set_variable, name, vendor, attr, data_size, - data); + status = efi_call_virt_pointer(efi.runtime, set_variable, name, vendor, + attr, data_size, data); up(&efi_runtime_lock); return status; } @@ -373,17 +463,15 @@ static efi_status_t virt_efi_query_variable_info(u32 attr, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(EFI_QUERY_VARIABLE_INFO, &attr, storage_space, - remaining_space, max_variable_size, NULL); + status = efi_queue_work(QUERY_VARIABLE_INFO, attr, storage_space, + remaining_space, max_variable_size); up(&efi_runtime_lock); return status; } static efi_status_t -virt_efi_query_variable_info_nonblocking(u32 attr, - u64 *storage_space, - u64 *remaining_space, - u64 *max_variable_size) +virt_efi_query_variable_info_nb(u32 attr, u64 *storage_space, + u64 *remaining_space, u64 *max_variable_size) { efi_status_t status; @@ -393,8 +481,9 @@ virt_efi_query_variable_info_nonblocking(u32 attr, if (down_trylock(&efi_runtime_lock)) return EFI_NOT_READY; - status = efi_call_virt(query_variable_info, attr, storage_space, - remaining_space, max_variable_size); + status = efi_call_virt_pointer(efi.runtime, query_variable_info, attr, + storage_space, remaining_space, + max_variable_size); up(&efi_runtime_lock); return status; } @@ -405,8 +494,7 @@ static efi_status_t virt_efi_get_next_high_mono_count(u32 *count) if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(EFI_GET_NEXT_HIGH_MONO_COUNT, count, NULL, NULL, - NULL, NULL); + status = efi_queue_work(GET_NEXT_HIGH_MONO_COUNT, count); up(&efi_runtime_lock); return status; } @@ -421,8 +509,13 @@ static void virt_efi_reset_system(int reset_type, "could not get exclusive access to the firmware\n"); return; } + + arch_efi_call_virt_setup(); efi_rts_work.efi_rts_id = EFI_RESET_SYSTEM; - __efi_call_virt(reset_system, reset_type, status, data_size, data); + arch_efi_call_virt(efi.runtime, reset_system, reset_type, status, + data_size, data); + arch_efi_call_virt_teardown(); + up(&efi_runtime_lock); } @@ -437,8 +530,7 @@ static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(EFI_UPDATE_CAPSULE, capsules, &count, &sg_list, - NULL, NULL); + status = efi_queue_work(UPDATE_CAPSULE, capsules, count, sg_list); up(&efi_runtime_lock); return status; } @@ -455,26 +547,44 @@ static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(EFI_QUERY_CAPSULE_CAPS, capsules, &count, - max_size, reset_type, NULL); + status = efi_queue_work(QUERY_CAPSULE_CAPS, capsules, count, + max_size, reset_type); up(&efi_runtime_lock); return status; } -void efi_native_runtime_setup(void) +void __init efi_native_runtime_setup(void) { - efi.get_time = virt_efi_get_time; - efi.set_time = virt_efi_set_time; - efi.get_wakeup_time = virt_efi_get_wakeup_time; - efi.set_wakeup_time = virt_efi_set_wakeup_time; - efi.get_variable = virt_efi_get_variable; - efi.get_next_variable = virt_efi_get_next_variable; - efi.set_variable = virt_efi_set_variable; - efi.set_variable_nonblocking = virt_efi_set_variable_nonblocking; - efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; - efi.reset_system = virt_efi_reset_system; - efi.query_variable_info = virt_efi_query_variable_info; - efi.query_variable_info_nonblocking = virt_efi_query_variable_info_nonblocking; - efi.update_capsule = virt_efi_update_capsule; - efi.query_capsule_caps = virt_efi_query_capsule_caps; + efi.get_time = virt_efi_get_time; + efi.set_time = virt_efi_set_time; + efi.get_wakeup_time = virt_efi_get_wakeup_time; + efi.set_wakeup_time = virt_efi_set_wakeup_time; + efi.get_variable = virt_efi_get_variable; + efi.get_next_variable = virt_efi_get_next_variable; + efi.set_variable = virt_efi_set_variable; + efi.set_variable_nonblocking = virt_efi_set_variable_nb; + efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; + efi.reset_system = virt_efi_reset_system; + efi.query_variable_info = virt_efi_query_variable_info; + efi.query_variable_info_nonblocking = virt_efi_query_variable_info_nb; + efi.update_capsule = virt_efi_update_capsule; + efi.query_capsule_caps = virt_efi_query_capsule_caps; } + +#ifdef CONFIG_ACPI_PRMT + +efi_status_t +efi_call_acpi_prm_handler(efi_status_t (__efiapi *handler_addr)(u64, void *), + u64 param_buffer_addr, void *context) +{ + efi_status_t status; + + if (down_interruptible(&efi_runtime_lock)) + return EFI_ABORTED; + status = efi_queue_work(ACPI_PRM_HANDLER, handler_addr, + param_buffer_addr, context); + up(&efi_runtime_lock); + return status; +} + +#endif diff --git a/drivers/firmware/google/Kconfig b/drivers/firmware/google/Kconfig index 1bc7cbf2f65d..41b78f5cb735 100644 --- a/drivers/firmware/google/Kconfig +++ b/drivers/firmware/google/Kconfig @@ -59,7 +59,7 @@ config GOOGLE_MEMCONSOLE_X86_LEGACY config GOOGLE_FRAMEBUFFER_COREBOOT tristate "Coreboot Framebuffer" - depends on FB_SIMPLE + depends on FB_SIMPLE || DRM_SIMPLEDRM depends on GOOGLE_COREBOOT_TABLE help This option enables the kernel to search for a framebuffer in diff --git a/drivers/firmware/imx/Makefile b/drivers/firmware/imx/Makefile index b76acbade2a0..8f9f04a513a8 100644 --- a/drivers/firmware/imx/Makefile +++ b/drivers/firmware/imx/Makefile @@ -1,4 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_IMX_DSP) += imx-dsp.o obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o rm.o imx-scu-soc.o -obj-$(CONFIG_IMX_SCU_PD) += scu-pd.o diff --git a/drivers/firmware/imx/imx-dsp.c b/drivers/firmware/imx/imx-dsp.c index a6c06d7476c3..3dba590a2a95 100644 --- a/drivers/firmware/imx/imx-dsp.c +++ b/drivers/firmware/imx/imx-dsp.c @@ -10,7 +10,6 @@ #include <linux/kernel.h> #include <linux/mailbox_client.h> #include <linux/module.h> -#include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/slab.h> diff --git a/drivers/firmware/imx/imx-scu-irq.c b/drivers/firmware/imx/imx-scu-irq.c index d9dcc20945c6..6125cccc9ba7 100644 --- a/drivers/firmware/imx/imx-scu-irq.c +++ b/drivers/firmware/imx/imx-scu-irq.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright 2019 NXP + * Copyright 2019,2023 NXP * * Implementation of the SCU IRQ functions using MU. * @@ -9,12 +9,15 @@ #include <dt-bindings/firmware/imx/rsrc.h> #include <linux/firmware/imx/ipc.h> #include <linux/firmware/imx/sci.h> +#include <linux/kobject.h> #include <linux/mailbox_client.h> +#include <linux/of.h> #include <linux/suspend.h> +#include <linux/sysfs.h> #define IMX_SC_IRQ_FUNC_ENABLE 1 #define IMX_SC_IRQ_FUNC_STATUS 2 -#define IMX_SC_IRQ_NUM_GROUP 4 +#define IMX_SC_IRQ_NUM_GROUP 9 static u32 mu_resource_id; @@ -40,63 +43,102 @@ struct imx_sc_msg_irq_enable { u8 enable; } __packed; +struct scu_wakeup { + u32 mask; + u32 wakeup_src; + bool valid; +}; + +/* Sysfs functions */ +static struct kobject *wakeup_obj; +static ssize_t wakeup_source_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); +static struct kobj_attribute wakeup_source_attr = + __ATTR(wakeup_src, 0660, wakeup_source_show, NULL); + +static struct scu_wakeup scu_irq_wakeup[IMX_SC_IRQ_NUM_GROUP]; + static struct imx_sc_ipc *imx_sc_irq_ipc_handle; static struct work_struct imx_sc_irq_work; -static ATOMIC_NOTIFIER_HEAD(imx_scu_irq_notifier_chain); +static BLOCKING_NOTIFIER_HEAD(imx_scu_irq_notifier_chain); int imx_scu_irq_register_notifier(struct notifier_block *nb) { - return atomic_notifier_chain_register( + return blocking_notifier_chain_register( &imx_scu_irq_notifier_chain, nb); } EXPORT_SYMBOL(imx_scu_irq_register_notifier); int imx_scu_irq_unregister_notifier(struct notifier_block *nb) { - return atomic_notifier_chain_unregister( + return blocking_notifier_chain_unregister( &imx_scu_irq_notifier_chain, nb); } EXPORT_SYMBOL(imx_scu_irq_unregister_notifier); static int imx_scu_irq_notifier_call_chain(unsigned long status, u8 *group) { - return atomic_notifier_call_chain(&imx_scu_irq_notifier_chain, + return blocking_notifier_call_chain(&imx_scu_irq_notifier_chain, status, (void *)group); } static void imx_scu_irq_work_handler(struct work_struct *work) { - struct imx_sc_msg_irq_get_status msg; - struct imx_sc_rpc_msg *hdr = &msg.hdr; u32 irq_status; int ret; u8 i; for (i = 0; i < IMX_SC_IRQ_NUM_GROUP; i++) { - hdr->ver = IMX_SC_RPC_VERSION; - hdr->svc = IMX_SC_RPC_SVC_IRQ; - hdr->func = IMX_SC_IRQ_FUNC_STATUS; - hdr->size = 2; - - msg.data.req.resource = mu_resource_id; - msg.data.req.group = i; + if (scu_irq_wakeup[i].mask) { + scu_irq_wakeup[i].valid = false; + scu_irq_wakeup[i].wakeup_src = 0; + } - ret = imx_scu_call_rpc(imx_sc_irq_ipc_handle, &msg, true); + ret = imx_scu_irq_get_status(i, &irq_status); if (ret) { pr_err("get irq group %d status failed, ret %d\n", i, ret); return; } - irq_status = msg.data.resp.status; if (!irq_status) continue; + if (scu_irq_wakeup[i].mask & irq_status) { + scu_irq_wakeup[i].valid = true; + scu_irq_wakeup[i].wakeup_src = irq_status & scu_irq_wakeup[i].mask; + } else { + scu_irq_wakeup[i].wakeup_src = irq_status; + } pm_system_wakeup(); imx_scu_irq_notifier_call_chain(irq_status, &i); } } +int imx_scu_irq_get_status(u8 group, u32 *irq_status) +{ + struct imx_sc_msg_irq_get_status msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + int ret; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_IRQ; + hdr->func = IMX_SC_IRQ_FUNC_STATUS; + hdr->size = 2; + + msg.data.req.resource = mu_resource_id; + msg.data.req.group = group; + + ret = imx_scu_call_rpc(imx_sc_irq_ipc_handle, &msg, true); + if (ret) + return ret; + + if (irq_status) + *irq_status = msg.data.resp.status; + + return 0; +} +EXPORT_SYMBOL(imx_scu_irq_get_status); + int imx_scu_irq_group_enable(u8 group, u32 mask, u8 enable) { struct imx_sc_msg_irq_enable msg; @@ -121,6 +163,11 @@ int imx_scu_irq_group_enable(u8 group, u32 mask, u8 enable) pr_err("enable irq failed, group %d, mask %d, ret %d\n", group, mask, ret); + if (enable) + scu_irq_wakeup[group].mask |= mask; + else + scu_irq_wakeup[group].mask &= ~mask; + return ret; } EXPORT_SYMBOL(imx_scu_irq_group_enable); @@ -130,6 +177,25 @@ static void imx_scu_irq_callback(struct mbox_client *c, void *msg) schedule_work(&imx_sc_irq_work); } +static ssize_t wakeup_source_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + int i; + + for (i = 0; i < IMX_SC_IRQ_NUM_GROUP; i++) { + if (!scu_irq_wakeup[i].wakeup_src) + continue; + + if (scu_irq_wakeup[i].valid) + sprintf(buf, "Wakeup source group = %d, irq = 0x%x\n", + i, scu_irq_wakeup[i].wakeup_src); + else + sprintf(buf, "Spurious SCU wakeup, group = %d, irq = 0x%x\n", + i, scu_irq_wakeup[i].wakeup_src); + } + + return strlen(buf); +} + int imx_scu_enable_general_irq_channel(struct device *dev) { struct of_phandle_args spec; @@ -169,6 +235,25 @@ int imx_scu_enable_general_irq_channel(struct device *dev) mu_resource_id = IMX_SC_R_MU_0A + i; + /* Create directory under /sysfs/firmware */ + wakeup_obj = kobject_create_and_add("scu_wakeup_source", firmware_kobj); + if (!wakeup_obj) { + ret = -ENOMEM; + goto free_ch; + } + + ret = sysfs_create_file(wakeup_obj, &wakeup_source_attr.attr); + if (ret) { + dev_err(dev, "Cannot create wakeup source src file......\n"); + kobject_put(wakeup_obj); + goto free_ch; + } + + return 0; + +free_ch: + mbox_free_channel(ch); + return ret; } EXPORT_SYMBOL(imx_scu_enable_general_irq_channel); diff --git a/drivers/firmware/imx/imx-scu-soc.c b/drivers/firmware/imx/imx-scu-soc.c index 2f32353de2c9..497192320562 100644 --- a/drivers/firmware/imx/imx-scu-soc.c +++ b/drivers/firmware/imx/imx-scu-soc.c @@ -78,6 +78,22 @@ static int imx_scu_soc_id(void) return msg.data.resp.id; } +static const char *imx_scu_soc_name(u32 id) +{ + switch (id) { + case 0x1: + return "i.MX8QM"; + case 0x2: + return "i.MX8QXP"; + case 0xe: + return "i.MX8DXL"; + default: + break; + } + + return "NULL"; +} + int imx_scu_soc_init(struct device *dev) { struct soc_device_attribute *soc_dev_attr; @@ -113,9 +129,7 @@ int imx_scu_soc_init(struct device *dev) /* format soc_id value passed from SCU firmware */ val = id & 0x1f; - soc_dev_attr->soc_id = devm_kasprintf(dev, GFP_KERNEL, "0x%x", val); - if (!soc_dev_attr->soc_id) - return -ENOMEM; + soc_dev_attr->soc_id = imx_scu_soc_name(val); /* format revision value passed from SCU firmware */ val = (id >> 5) & 0xf; diff --git a/drivers/firmware/imx/imx-scu.c b/drivers/firmware/imx/imx-scu.c index 47db49911e7b..1dd4362ef9a3 100644 --- a/drivers/firmware/imx/imx-scu.c +++ b/drivers/firmware/imx/imx-scu.c @@ -16,11 +16,12 @@ #include <linux/mailbox_client.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/of.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #define SCU_MU_CHAN_NUM 8 -#define MAX_RX_TIMEOUT (msecs_to_jiffies(30)) +#define MAX_RX_TIMEOUT (msecs_to_jiffies(3000)) struct imx_sc_chan { struct imx_sc_ipc *sc_ipc; @@ -353,7 +354,12 @@ static struct platform_driver imx_scu_driver = { }, .probe = imx_scu_probe, }; -builtin_platform_driver(imx_scu_driver); + +static int __init imx_scu_driver_init(void) +{ + return platform_driver_register(&imx_scu_driver); +} +subsys_initcall_sync(imx_scu_driver_init); MODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>"); MODULE_DESCRIPTION("IMX SCU firmware protocol driver"); diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c deleted file mode 100644 index 84b673427073..000000000000 --- a/drivers/firmware/imx/scu-pd.c +++ /dev/null @@ -1,428 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2016 Freescale Semiconductor, Inc. - * Copyright 2017-2018 NXP - * Dong Aisheng <aisheng.dong@nxp.com> - * - * Implementation of the SCU based Power Domains - * - * NOTE: a better implementation suggested by Ulf Hansson is using a - * single global power domain and implement the ->attach|detach_dev() - * callback for the genpd and use the regular of_genpd_add_provider_simple(). - * From within the ->attach_dev(), we could get the OF node for - * the device that is being attached and then parse the power-domain - * cell containing the "resource id" and store that in the per device - * struct generic_pm_domain_data (we have void pointer there for - * storing these kind of things). - * - * Additionally, we need to implement the ->stop() and ->start() - * callbacks of genpd, which is where you "power on/off" devices, - * rather than using the above ->power_on|off() callbacks. - * - * However, there're two known issues: - * 1. The ->attach_dev() of power domain infrastructure still does - * not support multi domains case as the struct device *dev passed - * in is a virtual PD device, it does not help for parsing the real - * device resource id from device tree, so it's unware of which - * real sub power domain of device should be attached. - * - * The framework needs some proper extension to support multi power - * domain cases. - * - * Update: Genpd assigns the ->of_node for the virtual device before it - * invokes ->attach_dev() callback, hence parsing for device resources via - * DT should work fine. - * - * 2. It also breaks most of current drivers as the driver probe sequence - * behavior changed if removing ->power_on|off() callback and use - * ->start() and ->stop() instead. genpd_dev_pm_attach will only power - * up the domain and attach device, but will not call .start() which - * relies on device runtime pm. That means the device power is still - * not up before running driver probe function. For SCU enabled - * platforms, all device drivers accessing registers/clock without power - * domain enabled will trigger a HW access error. That means we need fix - * most drivers probe sequence with proper runtime pm. - * - * Update: Runtime PM support isn't necessary. Instead, this can easily be - * fixed in drivers by adding a call to dev_pm_domain_start() during probe. - * - * In summary, the second part needs to be addressed via minor updates to the - * relevant drivers, before the "single global power domain" model can be used. - * - */ - -#include <dt-bindings/firmware/imx/rsrc.h> -#include <linux/firmware/imx/sci.h> -#include <linux/firmware/imx/svc/rm.h> -#include <linux/io.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/of_platform.h> -#include <linux/platform_device.h> -#include <linux/pm.h> -#include <linux/pm_domain.h> -#include <linux/slab.h> - -/* SCU Power Mode Protocol definition */ -struct imx_sc_msg_req_set_resource_power_mode { - struct imx_sc_rpc_msg hdr; - u16 resource; - u8 mode; -} __packed __aligned(4); - -#define IMX_SCU_PD_NAME_SIZE 20 -struct imx_sc_pm_domain { - struct generic_pm_domain pd; - char name[IMX_SCU_PD_NAME_SIZE]; - u32 rsrc; -}; - -struct imx_sc_pd_range { - char *name; - u32 rsrc; - u8 num; - - /* add domain index */ - bool postfix; - u8 start_from; -}; - -struct imx_sc_pd_soc { - const struct imx_sc_pd_range *pd_ranges; - u8 num_ranges; -}; - -static int imx_con_rsrc; - -static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { - /* LSIO SS */ - { "pwm", IMX_SC_R_PWM_0, 8, true, 0 }, - { "gpio", IMX_SC_R_GPIO_0, 8, true, 0 }, - { "gpt", IMX_SC_R_GPT_0, 5, true, 0 }, - { "kpp", IMX_SC_R_KPP, 1, false, 0 }, - { "fspi", IMX_SC_R_FSPI_0, 2, true, 0 }, - { "mu_a", IMX_SC_R_MU_0A, 14, true, 0 }, - { "mu_b", IMX_SC_R_MU_5B, 9, true, 5 }, - - /* CONN SS */ - { "usb", IMX_SC_R_USB_0, 2, true, 0 }, - { "usb0phy", IMX_SC_R_USB_0_PHY, 1, false, 0 }, - { "usb1phy", IMX_SC_R_USB_1_PHY, 1, false, 0}, - { "usb2", IMX_SC_R_USB_2, 1, false, 0 }, - { "usb2phy", IMX_SC_R_USB_2_PHY, 1, false, 0 }, - { "sdhc", IMX_SC_R_SDHC_0, 3, true, 0 }, - { "enet", IMX_SC_R_ENET_0, 2, true, 0 }, - { "nand", IMX_SC_R_NAND, 1, false, 0 }, - { "mlb", IMX_SC_R_MLB_0, 1, true, 0 }, - - /* AUDIO SS */ - { "audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false, 0 }, - { "audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false, 0 }, - { "audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false, 0 }, - { "audio-clk-1", IMX_SC_R_AUDIO_CLK_1, 1, false, 0 }, - { "dma0-ch", IMX_SC_R_DMA_0_CH0, 16, true, 0 }, - { "dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true, 0 }, - { "dma2-ch", IMX_SC_R_DMA_2_CH0, 5, true, 0 }, - { "asrc0", IMX_SC_R_ASRC_0, 1, false, 0 }, - { "asrc1", IMX_SC_R_ASRC_1, 1, false, 0 }, - { "esai0", IMX_SC_R_ESAI_0, 1, false, 0 }, - { "spdif0", IMX_SC_R_SPDIF_0, 1, false, 0 }, - { "spdif1", IMX_SC_R_SPDIF_1, 1, false, 0 }, - { "sai", IMX_SC_R_SAI_0, 3, true, 0 }, - { "sai3", IMX_SC_R_SAI_3, 1, false, 0 }, - { "sai4", IMX_SC_R_SAI_4, 1, false, 0 }, - { "sai5", IMX_SC_R_SAI_5, 1, false, 0 }, - { "sai6", IMX_SC_R_SAI_6, 1, false, 0 }, - { "sai7", IMX_SC_R_SAI_7, 1, false, 0 }, - { "amix", IMX_SC_R_AMIX, 1, false, 0 }, - { "mqs0", IMX_SC_R_MQS_0, 1, false, 0 }, - { "dsp", IMX_SC_R_DSP, 1, false, 0 }, - { "dsp-ram", IMX_SC_R_DSP_RAM, 1, false, 0 }, - - /* DMA SS */ - { "can", IMX_SC_R_CAN_0, 3, true, 0 }, - { "ftm", IMX_SC_R_FTM_0, 2, true, 0 }, - { "lpi2c", IMX_SC_R_I2C_0, 4, true, 0 }, - { "adc", IMX_SC_R_ADC_0, 2, true, 0 }, - { "lcd", IMX_SC_R_LCD_0, 1, true, 0 }, - { "lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 }, - { "lpuart", IMX_SC_R_UART_0, 4, true, 0 }, - { "lpspi", IMX_SC_R_SPI_0, 4, true, 0 }, - { "irqstr_dsp", IMX_SC_R_IRQSTR_DSP, 1, false, 0 }, - - /* VPU SS */ - { "vpu", IMX_SC_R_VPU, 1, false, 0 }, - { "vpu-pid", IMX_SC_R_VPU_PID0, 8, true, 0 }, - { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, false, 0 }, - { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, false, 0 }, - { "vpu-enc1", IMX_SC_R_VPU_ENC_1, 1, false, 0 }, - { "vpu-mu0", IMX_SC_R_VPU_MU_0, 1, false, 0 }, - { "vpu-mu1", IMX_SC_R_VPU_MU_1, 1, false, 0 }, - { "vpu-mu2", IMX_SC_R_VPU_MU_2, 1, false, 0 }, - - /* GPU SS */ - { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, true, 0 }, - - /* HSIO SS */ - { "pcie-b", IMX_SC_R_PCIE_B, 1, false, 0 }, - { "serdes-1", IMX_SC_R_SERDES_1, 1, false, 0 }, - { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, false, 0 }, - - /* MIPI SS */ - { "mipi0", IMX_SC_R_MIPI_0, 1, false, 0 }, - { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, false, 0 }, - { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, true, 0 }, - - { "mipi1", IMX_SC_R_MIPI_1, 1, false, 0 }, - { "mipi1-pwm0", IMX_SC_R_MIPI_1_PWM_0, 1, false, 0 }, - { "mipi1-i2c", IMX_SC_R_MIPI_1_I2C_0, 2, true, 0 }, - - /* LVDS SS */ - { "lvds0", IMX_SC_R_LVDS_0, 1, false, 0 }, - { "lvds0-pwm", IMX_SC_R_LVDS_0_PWM_0, 1, false, 0 }, - { "lvds0-lpi2c", IMX_SC_R_LVDS_0_I2C_0, 2, true, 0 }, - { "lvds1", IMX_SC_R_LVDS_1, 1, false, 0 }, - { "lvds1-pwm", IMX_SC_R_LVDS_1_PWM_0, 1, false, 0 }, - { "lvds1-lpi2c", IMX_SC_R_LVDS_1_I2C_0, 2, true, 0 }, - - /* DC SS */ - { "dc0", IMX_SC_R_DC_0, 1, false, 0 }, - { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, true, 0 }, - { "dc0-video", IMX_SC_R_DC_0_VIDEO0, 2, true, 0 }, - - /* CM40 SS */ - { "cm40-i2c", IMX_SC_R_M4_0_I2C, 1, false, 0 }, - { "cm40-intmux", IMX_SC_R_M4_0_INTMUX, 1, false, 0 }, - { "cm40-pid", IMX_SC_R_M4_0_PID0, 5, true, 0}, - { "cm40-mu-a1", IMX_SC_R_M4_0_MU_1A, 1, false, 0}, - { "cm40-lpuart", IMX_SC_R_M4_0_UART, 1, false, 0}, - - /* CM41 SS */ - { "cm41-i2c", IMX_SC_R_M4_1_I2C, 1, false, 0 }, - { "cm41-intmux", IMX_SC_R_M4_1_INTMUX, 1, false, 0 }, - { "cm41-pid", IMX_SC_R_M4_1_PID0, 5, true, 0}, - { "cm41-mu-a1", IMX_SC_R_M4_1_MU_1A, 1, false, 0}, - { "cm41-lpuart", IMX_SC_R_M4_1_UART, 1, false, 0}, - - /* IMAGE SS */ - { "img-jpegdec-mp", IMX_SC_R_MJPEG_DEC_MP, 1, false, 0 }, - { "img-jpegdec-s0", IMX_SC_R_MJPEG_DEC_S0, 4, true, 0 }, - { "img-jpegenc-mp", IMX_SC_R_MJPEG_ENC_MP, 1, false, 0 }, - { "img-jpegenc-s0", IMX_SC_R_MJPEG_ENC_S0, 4, true, 0 }, -}; - -static const struct imx_sc_pd_soc imx8qxp_scu_pd = { - .pd_ranges = imx8qxp_scu_pd_ranges, - .num_ranges = ARRAY_SIZE(imx8qxp_scu_pd_ranges), -}; - -static struct imx_sc_ipc *pm_ipc_handle; - -static inline struct imx_sc_pm_domain * -to_imx_sc_pd(struct generic_pm_domain *genpd) -{ - return container_of(genpd, struct imx_sc_pm_domain, pd); -} - -static void imx_sc_pd_get_console_rsrc(void) -{ - struct of_phandle_args specs; - int ret; - - if (!of_stdout) - return; - - ret = of_parse_phandle_with_args(of_stdout, "power-domains", - "#power-domain-cells", - 0, &specs); - if (ret) - return; - - imx_con_rsrc = specs.args[0]; -} - -static int imx_sc_pd_power(struct generic_pm_domain *domain, bool power_on) -{ - struct imx_sc_msg_req_set_resource_power_mode msg; - struct imx_sc_rpc_msg *hdr = &msg.hdr; - struct imx_sc_pm_domain *pd; - int ret; - - pd = to_imx_sc_pd(domain); - - hdr->ver = IMX_SC_RPC_VERSION; - hdr->svc = IMX_SC_RPC_SVC_PM; - hdr->func = IMX_SC_PM_FUNC_SET_RESOURCE_POWER_MODE; - hdr->size = 2; - - msg.resource = pd->rsrc; - msg.mode = power_on ? IMX_SC_PM_PW_MODE_ON : IMX_SC_PM_PW_MODE_LP; - - ret = imx_scu_call_rpc(pm_ipc_handle, &msg, true); - if (ret) - dev_err(&domain->dev, "failed to power %s resource %d ret %d\n", - power_on ? "up" : "off", pd->rsrc, ret); - - return ret; -} - -static int imx_sc_pd_power_on(struct generic_pm_domain *domain) -{ - return imx_sc_pd_power(domain, true); -} - -static int imx_sc_pd_power_off(struct generic_pm_domain *domain) -{ - return imx_sc_pd_power(domain, false); -} - -static struct generic_pm_domain *imx_scu_pd_xlate(struct of_phandle_args *spec, - void *data) -{ - struct generic_pm_domain *domain = ERR_PTR(-ENOENT); - struct genpd_onecell_data *pd_data = data; - unsigned int i; - - for (i = 0; i < pd_data->num_domains; i++) { - struct imx_sc_pm_domain *sc_pd; - - sc_pd = to_imx_sc_pd(pd_data->domains[i]); - if (sc_pd->rsrc == spec->args[0]) { - domain = &sc_pd->pd; - break; - } - } - - return domain; -} - -static struct imx_sc_pm_domain * -imx_scu_add_pm_domain(struct device *dev, int idx, - const struct imx_sc_pd_range *pd_ranges) -{ - struct imx_sc_pm_domain *sc_pd; - bool is_off = true; - int ret; - - if (!imx_sc_rm_is_resource_owned(pm_ipc_handle, pd_ranges->rsrc + idx)) - return NULL; - - sc_pd = devm_kzalloc(dev, sizeof(*sc_pd), GFP_KERNEL); - if (!sc_pd) - return ERR_PTR(-ENOMEM); - - sc_pd->rsrc = pd_ranges->rsrc + idx; - sc_pd->pd.power_off = imx_sc_pd_power_off; - sc_pd->pd.power_on = imx_sc_pd_power_on; - - if (pd_ranges->postfix) - snprintf(sc_pd->name, sizeof(sc_pd->name), - "%s%i", pd_ranges->name, pd_ranges->start_from + idx); - else - snprintf(sc_pd->name, sizeof(sc_pd->name), - "%s", pd_ranges->name); - - sc_pd->pd.name = sc_pd->name; - if (imx_con_rsrc == sc_pd->rsrc) { - sc_pd->pd.flags = GENPD_FLAG_RPM_ALWAYS_ON; - is_off = false; - } - - if (sc_pd->rsrc >= IMX_SC_R_LAST) { - dev_warn(dev, "invalid pd %s rsrc id %d found", - sc_pd->name, sc_pd->rsrc); - - devm_kfree(dev, sc_pd); - return NULL; - } - - ret = pm_genpd_init(&sc_pd->pd, NULL, is_off); - if (ret) { - dev_warn(dev, "failed to init pd %s rsrc id %d", - sc_pd->name, sc_pd->rsrc); - devm_kfree(dev, sc_pd); - return NULL; - } - - return sc_pd; -} - -static int imx_scu_init_pm_domains(struct device *dev, - const struct imx_sc_pd_soc *pd_soc) -{ - const struct imx_sc_pd_range *pd_ranges = pd_soc->pd_ranges; - struct generic_pm_domain **domains; - struct genpd_onecell_data *pd_data; - struct imx_sc_pm_domain *sc_pd; - u32 count = 0; - int i, j; - - for (i = 0; i < pd_soc->num_ranges; i++) - count += pd_ranges[i].num; - - domains = devm_kcalloc(dev, count, sizeof(*domains), GFP_KERNEL); - if (!domains) - return -ENOMEM; - - pd_data = devm_kzalloc(dev, sizeof(*pd_data), GFP_KERNEL); - if (!pd_data) - return -ENOMEM; - - count = 0; - for (i = 0; i < pd_soc->num_ranges; i++) { - for (j = 0; j < pd_ranges[i].num; j++) { - sc_pd = imx_scu_add_pm_domain(dev, j, &pd_ranges[i]); - if (IS_ERR_OR_NULL(sc_pd)) - continue; - - domains[count++] = &sc_pd->pd; - dev_dbg(dev, "added power domain %s\n", sc_pd->pd.name); - } - } - - pd_data->domains = domains; - pd_data->num_domains = count; - pd_data->xlate = imx_scu_pd_xlate; - - of_genpd_add_provider_onecell(dev->of_node, pd_data); - - return 0; -} - -static int imx_sc_pd_probe(struct platform_device *pdev) -{ - const struct imx_sc_pd_soc *pd_soc; - int ret; - - ret = imx_scu_get_handle(&pm_ipc_handle); - if (ret) - return ret; - - pd_soc = of_device_get_match_data(&pdev->dev); - if (!pd_soc) - return -ENODEV; - - imx_sc_pd_get_console_rsrc(); - - return imx_scu_init_pm_domains(&pdev->dev, pd_soc); -} - -static const struct of_device_id imx_sc_pd_match[] = { - { .compatible = "fsl,imx8qxp-scu-pd", &imx8qxp_scu_pd}, - { .compatible = "fsl,scu-pd", &imx8qxp_scu_pd}, - { /* sentinel */ } -}; - -static struct platform_driver imx_sc_pd_driver = { - .driver = { - .name = "imx-scu-pd", - .of_match_table = imx_sc_pd_match, - }, - .probe = imx_sc_pd_probe, -}; -builtin_platform_driver(imx_sc_pd_driver); - -MODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>"); -MODULE_DESCRIPTION("IMX SCU Power Domain driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/firmware/meson/meson_sm.c b/drivers/firmware/meson/meson_sm.c index 798bcdb05d84..9a2656d73600 100644 --- a/drivers/firmware/meson/meson_sm.c +++ b/drivers/firmware/meson/meson_sm.c @@ -292,6 +292,8 @@ static int __init meson_sm_probe(struct platform_device *pdev) return -ENOMEM; chip = of_match_device(meson_sm_ids, dev)->data; + if (!chip) + return -EINVAL; if (chip->cmd_shmem_in_base) { fw->sm_shmem_in_base = meson_sm_map_shmem(chip->cmd_shmem_in_base, diff --git a/drivers/firmware/mtk-adsp-ipc.c b/drivers/firmware/mtk-adsp-ipc.c index 3c071f814455..85e94ddc7204 100644 --- a/drivers/firmware/mtk-adsp-ipc.c +++ b/drivers/firmware/mtk-adsp-ipc.c @@ -8,7 +8,6 @@ #include <linux/kernel.h> #include <linux/mailbox_client.h> #include <linux/module.h> -#include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/slab.h> diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index fde33acd46b7..06fe8aca870d 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -26,10 +26,6 @@ static bool download_mode = IS_ENABLED(CONFIG_QCOM_SCM_DOWNLOAD_MODE_DEFAULT); module_param(download_mode, bool, 0); -#define SCM_HAS_CORE_CLK BIT(0) -#define SCM_HAS_IFACE_CLK BIT(1) -#define SCM_HAS_BUS_CLK BIT(2) - struct qcom_scm { struct device *dev; struct clk *core_clk; @@ -351,7 +347,7 @@ int qcom_scm_set_warm_boot_addr(void *entry) return qcom_scm_set_boot_addr(entry, qcom_scm_cpu_warm_bits); return 0; } -EXPORT_SYMBOL(qcom_scm_set_warm_boot_addr); +EXPORT_SYMBOL_GPL(qcom_scm_set_warm_boot_addr); /** * qcom_scm_set_cold_boot_addr() - Set the cold boot address for all cpus @@ -364,7 +360,7 @@ int qcom_scm_set_cold_boot_addr(void *entry) return qcom_scm_set_boot_addr(entry, qcom_scm_cpu_cold_bits); return 0; } -EXPORT_SYMBOL(qcom_scm_set_cold_boot_addr); +EXPORT_SYMBOL_GPL(qcom_scm_set_cold_boot_addr); /** * qcom_scm_cpu_power_down() - Power down the cpu @@ -386,7 +382,7 @@ void qcom_scm_cpu_power_down(u32 flags) qcom_scm_call_atomic(__scm ? __scm->dev : NULL, &desc, NULL); } -EXPORT_SYMBOL(qcom_scm_cpu_power_down); +EXPORT_SYMBOL_GPL(qcom_scm_cpu_power_down); int qcom_scm_set_remote_state(u32 state, u32 id) { @@ -405,7 +401,7 @@ int qcom_scm_set_remote_state(u32 state, u32 id) return ret ? : res.result[0]; } -EXPORT_SYMBOL(qcom_scm_set_remote_state); +EXPORT_SYMBOL_GPL(qcom_scm_set_remote_state); static int __qcom_scm_set_dload_mode(struct device *dev, bool enable) { @@ -515,7 +511,7 @@ out: return ret ? : res.result[0]; } -EXPORT_SYMBOL(qcom_scm_pas_init_image); +EXPORT_SYMBOL_GPL(qcom_scm_pas_init_image); /** * qcom_scm_pas_metadata_release() - release metadata context @@ -532,7 +528,7 @@ void qcom_scm_pas_metadata_release(struct qcom_scm_pas_metadata *ctx) ctx->phys = 0; ctx->size = 0; } -EXPORT_SYMBOL(qcom_scm_pas_metadata_release); +EXPORT_SYMBOL_GPL(qcom_scm_pas_metadata_release); /** * qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral @@ -571,7 +567,7 @@ int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size) return ret ? : res.result[0]; } -EXPORT_SYMBOL(qcom_scm_pas_mem_setup); +EXPORT_SYMBOL_GPL(qcom_scm_pas_mem_setup); /** * qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware @@ -606,7 +602,7 @@ int qcom_scm_pas_auth_and_reset(u32 peripheral) return ret ? : res.result[0]; } -EXPORT_SYMBOL(qcom_scm_pas_auth_and_reset); +EXPORT_SYMBOL_GPL(qcom_scm_pas_auth_and_reset); /** * qcom_scm_pas_shutdown() - Shut down the remote processor @@ -641,7 +637,7 @@ int qcom_scm_pas_shutdown(u32 peripheral) return ret ? : res.result[0]; } -EXPORT_SYMBOL(qcom_scm_pas_shutdown); +EXPORT_SYMBOL_GPL(qcom_scm_pas_shutdown); /** * qcom_scm_pas_supported() - Check if the peripheral authentication service is @@ -670,7 +666,7 @@ bool qcom_scm_pas_supported(u32 peripheral) return ret ? false : !!res.result[0]; } -EXPORT_SYMBOL(qcom_scm_pas_supported); +EXPORT_SYMBOL_GPL(qcom_scm_pas_supported); static int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) { @@ -732,7 +728,7 @@ int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val) return ret < 0 ? ret : 0; } -EXPORT_SYMBOL(qcom_scm_io_readl); +EXPORT_SYMBOL_GPL(qcom_scm_io_readl); int qcom_scm_io_writel(phys_addr_t addr, unsigned int val) { @@ -747,7 +743,7 @@ int qcom_scm_io_writel(phys_addr_t addr, unsigned int val) return qcom_scm_call_atomic(__scm->dev, &desc, NULL); } -EXPORT_SYMBOL(qcom_scm_io_writel); +EXPORT_SYMBOL_GPL(qcom_scm_io_writel); /** * qcom_scm_restore_sec_cfg_available() - Check if secure environment @@ -760,7 +756,7 @@ bool qcom_scm_restore_sec_cfg_available(void) return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_MP, QCOM_SCM_MP_RESTORE_SEC_CFG); } -EXPORT_SYMBOL(qcom_scm_restore_sec_cfg_available); +EXPORT_SYMBOL_GPL(qcom_scm_restore_sec_cfg_available); int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare) { @@ -779,7 +775,7 @@ int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare) return ret ? : res.result[0]; } -EXPORT_SYMBOL(qcom_scm_restore_sec_cfg); +EXPORT_SYMBOL_GPL(qcom_scm_restore_sec_cfg); int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size) { @@ -800,7 +796,7 @@ int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size) return ret ? : res.result[1]; } -EXPORT_SYMBOL(qcom_scm_iommu_secure_ptbl_size); +EXPORT_SYMBOL_GPL(qcom_scm_iommu_secure_ptbl_size); int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare) { @@ -824,7 +820,7 @@ int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare) return ret; } -EXPORT_SYMBOL(qcom_scm_iommu_secure_ptbl_init); +EXPORT_SYMBOL_GPL(qcom_scm_iommu_secure_ptbl_init); int qcom_scm_iommu_set_cp_pool_size(u32 spare, u32 size) { @@ -839,7 +835,7 @@ int qcom_scm_iommu_set_cp_pool_size(u32 spare, u32 size) return qcom_scm_call(__scm->dev, &desc, NULL); } -EXPORT_SYMBOL(qcom_scm_iommu_set_cp_pool_size); +EXPORT_SYMBOL_GPL(qcom_scm_iommu_set_cp_pool_size); int qcom_scm_mem_protect_video_var(u32 cp_start, u32 cp_size, u32 cp_nonpixel_start, @@ -863,7 +859,7 @@ int qcom_scm_mem_protect_video_var(u32 cp_start, u32 cp_size, return ret ? : res.result[0]; } -EXPORT_SYMBOL(qcom_scm_mem_protect_video_var); +EXPORT_SYMBOL_GPL(qcom_scm_mem_protect_video_var); static int __qcom_scm_assign_mem(struct device *dev, phys_addr_t mem_region, size_t mem_sz, phys_addr_t src, size_t src_sz, @@ -972,7 +968,7 @@ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, *srcvm = next_vm; return 0; } -EXPORT_SYMBOL(qcom_scm_assign_mem); +EXPORT_SYMBOL_GPL(qcom_scm_assign_mem); /** * qcom_scm_ocmem_lock_available() - is OCMEM lock/unlock interface available @@ -982,7 +978,7 @@ bool qcom_scm_ocmem_lock_available(void) return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_OCMEM, QCOM_SCM_OCMEM_LOCK_CMD); } -EXPORT_SYMBOL(qcom_scm_ocmem_lock_available); +EXPORT_SYMBOL_GPL(qcom_scm_ocmem_lock_available); /** * qcom_scm_ocmem_lock() - call OCMEM lock interface to assign an OCMEM @@ -1008,7 +1004,7 @@ int qcom_scm_ocmem_lock(enum qcom_scm_ocmem_client id, u32 offset, u32 size, return qcom_scm_call(__scm->dev, &desc, NULL); } -EXPORT_SYMBOL(qcom_scm_ocmem_lock); +EXPORT_SYMBOL_GPL(qcom_scm_ocmem_lock); /** * qcom_scm_ocmem_unlock() - call OCMEM unlock interface to release an OCMEM @@ -1031,7 +1027,7 @@ int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset, u32 size) return qcom_scm_call(__scm->dev, &desc, NULL); } -EXPORT_SYMBOL(qcom_scm_ocmem_unlock); +EXPORT_SYMBOL_GPL(qcom_scm_ocmem_unlock); /** * qcom_scm_ice_available() - Is the ICE key programming interface available? @@ -1046,7 +1042,7 @@ bool qcom_scm_ice_available(void) __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_ES, QCOM_SCM_ES_CONFIG_SET_ICE_KEY); } -EXPORT_SYMBOL(qcom_scm_ice_available); +EXPORT_SYMBOL_GPL(qcom_scm_ice_available); /** * qcom_scm_ice_invalidate_key() - Invalidate an inline encryption key @@ -1072,7 +1068,7 @@ int qcom_scm_ice_invalidate_key(u32 index) return qcom_scm_call(__scm->dev, &desc, NULL); } -EXPORT_SYMBOL(qcom_scm_ice_invalidate_key); +EXPORT_SYMBOL_GPL(qcom_scm_ice_invalidate_key); /** * qcom_scm_ice_set_key() - Set an inline encryption key @@ -1138,7 +1134,7 @@ int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size, dma_free_coherent(__scm->dev, key_size, keybuf, key_phys); return ret; } -EXPORT_SYMBOL(qcom_scm_ice_set_key); +EXPORT_SYMBOL_GPL(qcom_scm_ice_set_key); /** * qcom_scm_hdcp_available() - Check if secure environment supports HDCP. @@ -1160,7 +1156,7 @@ bool qcom_scm_hdcp_available(void) return avail; } -EXPORT_SYMBOL(qcom_scm_hdcp_available); +EXPORT_SYMBOL_GPL(qcom_scm_hdcp_available); /** * qcom_scm_hdcp_req() - Send HDCP request. @@ -1207,7 +1203,7 @@ int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) return ret; } -EXPORT_SYMBOL(qcom_scm_hdcp_req); +EXPORT_SYMBOL_GPL(qcom_scm_hdcp_req); int qcom_scm_iommu_set_pt_format(u32 sec_id, u32 ctx_num, u32 pt_fmt) { @@ -1223,7 +1219,7 @@ int qcom_scm_iommu_set_pt_format(u32 sec_id, u32 ctx_num, u32 pt_fmt) return qcom_scm_call(__scm->dev, &desc, NULL); } -EXPORT_SYMBOL(qcom_scm_iommu_set_pt_format); +EXPORT_SYMBOL_GPL(qcom_scm_iommu_set_pt_format); int qcom_scm_qsmmu500_wait_safe_toggle(bool en) { @@ -1239,13 +1235,13 @@ int qcom_scm_qsmmu500_wait_safe_toggle(bool en) return qcom_scm_call_atomic(__scm->dev, &desc, NULL); } -EXPORT_SYMBOL(qcom_scm_qsmmu500_wait_safe_toggle); +EXPORT_SYMBOL_GPL(qcom_scm_qsmmu500_wait_safe_toggle); bool qcom_scm_lmh_dcvsh_available(void) { return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_LMH, QCOM_SCM_LMH_LIMIT_DCVSH); } -EXPORT_SYMBOL(qcom_scm_lmh_dcvsh_available); +EXPORT_SYMBOL_GPL(qcom_scm_lmh_dcvsh_available); int qcom_scm_lmh_profile_change(u32 profile_id) { @@ -1259,7 +1255,7 @@ int qcom_scm_lmh_profile_change(u32 profile_id) return qcom_scm_call(__scm->dev, &desc, NULL); } -EXPORT_SYMBOL(qcom_scm_lmh_profile_change); +EXPORT_SYMBOL_GPL(qcom_scm_lmh_profile_change); int qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val, u64 limit_node, u32 node_id, u64 version) @@ -1297,7 +1293,7 @@ int qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val, dma_free_coherent(__scm->dev, payload_size, payload_buf, payload_phys); return ret; } -EXPORT_SYMBOL(qcom_scm_lmh_dcvsh); +EXPORT_SYMBOL_GPL(qcom_scm_lmh_dcvsh); static int qcom_scm_find_dload_address(struct device *dev, u64 *addr) { @@ -1332,7 +1328,7 @@ bool qcom_scm_is_available(void) { return !!__scm; } -EXPORT_SYMBOL(qcom_scm_is_available); +EXPORT_SYMBOL_GPL(qcom_scm_is_available); static int qcom_scm_assert_valid_wq_ctx(u32 wq_ctx) { @@ -1405,7 +1401,6 @@ out: static int qcom_scm_probe(struct platform_device *pdev) { struct qcom_scm *scm; - unsigned long clks; int irq, ret; scm = devm_kzalloc(&pdev->dev, sizeof(*scm), GFP_KERNEL); @@ -1418,51 +1413,22 @@ static int qcom_scm_probe(struct platform_device *pdev) mutex_init(&scm->scm_bw_lock); - clks = (unsigned long)of_device_get_match_data(&pdev->dev); - scm->path = devm_of_icc_get(&pdev->dev, NULL); if (IS_ERR(scm->path)) return dev_err_probe(&pdev->dev, PTR_ERR(scm->path), "failed to acquire interconnect path\n"); - scm->core_clk = devm_clk_get(&pdev->dev, "core"); - if (IS_ERR(scm->core_clk)) { - if (PTR_ERR(scm->core_clk) == -EPROBE_DEFER) - return PTR_ERR(scm->core_clk); - - if (clks & SCM_HAS_CORE_CLK) { - dev_err(&pdev->dev, "failed to acquire core clk\n"); - return PTR_ERR(scm->core_clk); - } - - scm->core_clk = NULL; - } - - scm->iface_clk = devm_clk_get(&pdev->dev, "iface"); - if (IS_ERR(scm->iface_clk)) { - if (PTR_ERR(scm->iface_clk) == -EPROBE_DEFER) - return PTR_ERR(scm->iface_clk); - - if (clks & SCM_HAS_IFACE_CLK) { - dev_err(&pdev->dev, "failed to acquire iface clk\n"); - return PTR_ERR(scm->iface_clk); - } - - scm->iface_clk = NULL; - } + scm->core_clk = devm_clk_get_optional(&pdev->dev, "core"); + if (IS_ERR(scm->core_clk)) + return PTR_ERR(scm->core_clk); - scm->bus_clk = devm_clk_get(&pdev->dev, "bus"); - if (IS_ERR(scm->bus_clk)) { - if (PTR_ERR(scm->bus_clk) == -EPROBE_DEFER) - return PTR_ERR(scm->bus_clk); + scm->iface_clk = devm_clk_get_optional(&pdev->dev, "iface"); + if (IS_ERR(scm->iface_clk)) + return PTR_ERR(scm->iface_clk); - if (clks & SCM_HAS_BUS_CLK) { - dev_err(&pdev->dev, "failed to acquire bus clk\n"); - return PTR_ERR(scm->bus_clk); - } - - scm->bus_clk = NULL; - } + scm->bus_clk = devm_clk_get_optional(&pdev->dev, "bus"); + if (IS_ERR(scm->bus_clk)) + return PTR_ERR(scm->bus_clk); scm->reset.ops = &qcom_scm_pas_reset_ops; scm->reset.nr_resets = 1; @@ -1512,39 +1478,15 @@ static void qcom_scm_shutdown(struct platform_device *pdev) } static const struct of_device_id qcom_scm_dt_match[] = { - { .compatible = "qcom,scm-apq8064", - /* FIXME: This should have .data = (void *) SCM_HAS_CORE_CLK */ - }, - { .compatible = "qcom,scm-apq8084", .data = (void *)(SCM_HAS_CORE_CLK | - SCM_HAS_IFACE_CLK | - SCM_HAS_BUS_CLK) - }, + { .compatible = "qcom,scm" }, + + /* Legacy entries kept for backwards compatibility */ + { .compatible = "qcom,scm-apq8064" }, + { .compatible = "qcom,scm-apq8084" }, { .compatible = "qcom,scm-ipq4019" }, - { .compatible = "qcom,scm-mdm9607", .data = (void *)(SCM_HAS_CORE_CLK | - SCM_HAS_IFACE_CLK | - SCM_HAS_BUS_CLK) }, - { .compatible = "qcom,scm-msm8660", .data = (void *) SCM_HAS_CORE_CLK }, - { .compatible = "qcom,scm-msm8960", .data = (void *) SCM_HAS_CORE_CLK }, - { .compatible = "qcom,scm-msm8916", .data = (void *)(SCM_HAS_CORE_CLK | - SCM_HAS_IFACE_CLK | - SCM_HAS_BUS_CLK) - }, - { .compatible = "qcom,scm-msm8953", .data = (void *)(SCM_HAS_CORE_CLK | - SCM_HAS_IFACE_CLK | - SCM_HAS_BUS_CLK) - }, - { .compatible = "qcom,scm-msm8974", .data = (void *)(SCM_HAS_CORE_CLK | - SCM_HAS_IFACE_CLK | - SCM_HAS_BUS_CLK) - }, - { .compatible = "qcom,scm-msm8976", .data = (void *)(SCM_HAS_CORE_CLK | - SCM_HAS_IFACE_CLK | - SCM_HAS_BUS_CLK) - }, - { .compatible = "qcom,scm-msm8994" }, + { .compatible = "qcom,scm-msm8953" }, + { .compatible = "qcom,scm-msm8974" }, { .compatible = "qcom,scm-msm8996" }, - { .compatible = "qcom,scm-sm6375", .data = (void *)SCM_HAS_CORE_CLK }, - { .compatible = "qcom,scm" }, {} }; MODULE_DEVICE_TABLE(of, qcom_scm_dt_match); diff --git a/drivers/firmware/raspberrypi.c b/drivers/firmware/raspberrypi.c index c3bc29e0a488..f66efaa5196d 100644 --- a/drivers/firmware/raspberrypi.c +++ b/drivers/firmware/raspberrypi.c @@ -10,6 +10,7 @@ #include <linux/kref.h> #include <linux/mailbox_client.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/slab.h> diff --git a/drivers/firmware/scpi_pm_domain.c b/drivers/firmware/scpi_pm_domain.c index 800673910b51..2231e6dd2070 100644 --- a/drivers/firmware/scpi_pm_domain.c +++ b/drivers/firmware/scpi_pm_domain.c @@ -8,7 +8,8 @@ #include <linux/err.h> #include <linux/io.h> #include <linux/module.h> -#include <linux/of_platform.h> +#include <linux/of.h> +#include <linux/platform_device.h> #include <linux/pm_domain.h> #include <linux/scpi_protocol.h> diff --git a/drivers/firmware/stratix10-rsu.c b/drivers/firmware/stratix10-rsu.c index e51c95f8d445..4f7a7abada48 100644 --- a/drivers/firmware/stratix10-rsu.c +++ b/drivers/firmware/stratix10-rsu.c @@ -10,7 +10,6 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/of.h> -#include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/firmware/intel/stratix10-svc-client.h> #include <linux/string.h> @@ -34,6 +33,10 @@ #define INVALID_RETRY_COUNTER 0xFF #define INVALID_DCMF_VERSION 0xFF #define INVALID_DCMF_STATUS 0xFFFFFFFF +#define INVALID_SPT_ADDRESS 0x0 + +#define RSU_GET_SPT_CMD 0x5A +#define RSU_GET_SPT_RESP_LEN (4 * sizeof(unsigned int)) typedef void (*rsu_callback)(struct stratix10_svc_client *client, struct stratix10_svc_cb_data *data); @@ -59,6 +62,9 @@ typedef void (*rsu_callback)(struct stratix10_svc_client *client, * @dcmf_status.dcmf3: dcmf3 status * @retry_counter: the current image's retry counter * @max_retry: the preset max retry value + * @spt0_address: address of spt0 + * @spt1_address: address of spt1 + * @get_spt_response_buf: response from sdm for get_spt command */ struct stratix10_rsu_priv { struct stratix10_svc_chan *chan; @@ -90,6 +96,11 @@ struct stratix10_rsu_priv { unsigned int retry_counter; unsigned int max_retry; + + unsigned long spt0_address; + unsigned long spt1_address; + + unsigned int *get_spt_response_buf; }; /** @@ -259,6 +270,36 @@ static void rsu_dcmf_status_callback(struct stratix10_svc_client *client, complete(&priv->completion); } +static void rsu_get_spt_callback(struct stratix10_svc_client *client, + struct stratix10_svc_cb_data *data) +{ + struct stratix10_rsu_priv *priv = client->priv; + unsigned long *mbox_err = (unsigned long *)data->kaddr1; + unsigned long *resp_len = (unsigned long *)data->kaddr2; + + if (data->status != BIT(SVC_STATUS_OK) || (*mbox_err) || + (*resp_len != RSU_GET_SPT_RESP_LEN)) + goto error; + + priv->spt0_address = priv->get_spt_response_buf[0]; + priv->spt0_address <<= 32; + priv->spt0_address |= priv->get_spt_response_buf[1]; + + priv->spt1_address = priv->get_spt_response_buf[2]; + priv->spt1_address <<= 32; + priv->spt1_address |= priv->get_spt_response_buf[3]; + + goto complete; + +error: + dev_err(client->dev, "failed to get SPTs\n"); + +complete: + stratix10_svc_free_memory(priv->chan, priv->get_spt_response_buf); + priv->get_spt_response_buf = NULL; + complete(&priv->completion); +} + /** * rsu_send_msg() - send a message to Intel service layer * @priv: pointer to rsu private data @@ -288,6 +329,14 @@ static int rsu_send_msg(struct stratix10_rsu_priv *priv, if (arg) msg.arg[0] = arg; + if (command == COMMAND_MBOX_SEND_CMD) { + msg.arg[1] = 0; + msg.payload = NULL; + msg.payload_length = 0; + msg.payload_output = priv->get_spt_response_buf; + msg.payload_length_output = RSU_GET_SPT_RESP_LEN; + } + ret = stratix10_svc_send(priv->chan, &msg); if (ret < 0) goto status_done; @@ -572,6 +621,34 @@ static ssize_t notify_store(struct device *dev, return count; } +static ssize_t spt0_address_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); + + if (!priv) + return -ENODEV; + + if (priv->spt0_address == INVALID_SPT_ADDRESS) + return -EIO; + + return scnprintf(buf, PAGE_SIZE, "0x%08lx\n", priv->spt0_address); +} + +static ssize_t spt1_address_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); + + if (!priv) + return -ENODEV; + + if (priv->spt1_address == INVALID_SPT_ADDRESS) + return -EIO; + + return scnprintf(buf, PAGE_SIZE, "0x%08lx\n", priv->spt1_address); +} + static DEVICE_ATTR_RO(current_image); static DEVICE_ATTR_RO(fail_image); static DEVICE_ATTR_RO(state); @@ -590,6 +667,8 @@ static DEVICE_ATTR_RO(dcmf2_status); static DEVICE_ATTR_RO(dcmf3_status); static DEVICE_ATTR_WO(reboot_image); static DEVICE_ATTR_WO(notify); +static DEVICE_ATTR_RO(spt0_address); +static DEVICE_ATTR_RO(spt1_address); static struct attribute *rsu_attrs[] = { &dev_attr_current_image.attr, @@ -610,6 +689,8 @@ static struct attribute *rsu_attrs[] = { &dev_attr_dcmf3_status.attr, &dev_attr_reboot_image.attr, &dev_attr_notify.attr, + &dev_attr_spt0_address.attr, + &dev_attr_spt1_address.attr, NULL }; @@ -639,11 +720,13 @@ static int stratix10_rsu_probe(struct platform_device *pdev) priv->dcmf_version.dcmf1 = INVALID_DCMF_VERSION; priv->dcmf_version.dcmf2 = INVALID_DCMF_VERSION; priv->dcmf_version.dcmf3 = INVALID_DCMF_VERSION; - priv->max_retry = INVALID_RETRY_COUNTER; priv->dcmf_status.dcmf0 = INVALID_DCMF_STATUS; priv->dcmf_status.dcmf1 = INVALID_DCMF_STATUS; priv->dcmf_status.dcmf2 = INVALID_DCMF_STATUS; priv->dcmf_status.dcmf3 = INVALID_DCMF_STATUS; + priv->max_retry = INVALID_RETRY_COUNTER; + priv->spt0_address = INVALID_SPT_ADDRESS; + priv->spt1_address = INVALID_SPT_ADDRESS; mutex_init(&priv->lock); priv->chan = stratix10_svc_request_channel_byname(&priv->client, @@ -693,6 +776,20 @@ static int stratix10_rsu_probe(struct platform_device *pdev) stratix10_svc_free_channel(priv->chan); } + priv->get_spt_response_buf = + stratix10_svc_allocate_memory(priv->chan, RSU_GET_SPT_RESP_LEN); + + if (IS_ERR(priv->get_spt_response_buf)) { + dev_err(dev, "failed to allocate get spt buffer\n"); + } else { + ret = rsu_send_msg(priv, COMMAND_MBOX_SEND_CMD, + RSU_GET_SPT_CMD, rsu_get_spt_callback); + if (ret) { + dev_err(dev, "Error, getting SPT table %i\n", ret); + stratix10_svc_free_channel(priv->chan); + } + } + return ret; } diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index 2d674126160f..c693da60e9a9 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -37,6 +37,7 @@ #define SVC_NUM_CHANNEL 3 #define FPGA_CONFIG_DATA_CLAIM_TIMEOUT_MS 200 #define FPGA_CONFIG_STATUS_TIMEOUT_SEC 30 +#define BYTE_TO_WORD_SIZE 4 /* stratix10 service layer clients */ #define STRATIX10_RSU "stratix10-rsu" @@ -361,6 +362,13 @@ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data, cb_data->kaddr2 = svc_pa_to_va(res.a2); cb_data->kaddr3 = &res.a3; break; + case COMMAND_MBOX_SEND_CMD: + cb_data->status = BIT(SVC_STATUS_OK); + cb_data->kaddr1 = &res.a1; + /* SDM return size in u8. Convert size to u32 word */ + res.a2 = res.a2 * BYTE_TO_WORD_SIZE; + cb_data->kaddr2 = &res.a2; + break; default: pr_warn("it shouldn't happen\n"); break; @@ -534,6 +542,15 @@ static int svc_normal_to_secure_thread(void *data) a1 = 0; a2 = 0; break; + case COMMAND_MBOX_SEND_CMD: + a0 = INTEL_SIP_SMC_MBOX_SEND_CMD; + a1 = pdata->arg[0]; + a2 = (unsigned long)pdata->paddr; + a3 = (unsigned long)pdata->size / BYTE_TO_WORD_SIZE; + a4 = pdata->arg[1]; + a5 = (unsigned long)pdata->paddr_output; + a6 = (unsigned long)pdata->size_output / BYTE_TO_WORD_SIZE; + break; default: pr_warn("it shouldn't happen\n"); break; @@ -597,6 +614,7 @@ static int svc_normal_to_secure_thread(void *data) case COMMAND_FCS_DATA_ENCRYPTION: case COMMAND_FCS_DATA_DECRYPTION: case COMMAND_FCS_RANDOM_NUMBER_GEN: + case COMMAND_MBOX_SEND_CMD: cbdata->status = BIT(SVC_STATUS_INVALID_PARAM); cbdata->kaddr1 = NULL; cbdata->kaddr2 = NULL; @@ -756,7 +774,7 @@ svc_create_memory_pool(struct platform_device *pdev, paddr = begin; size = end - begin; va = devm_memremap(dev, paddr, size, MEMREMAP_WC); - if (!va) { + if (IS_ERR(va)) { dev_err(dev, "fail to remap shared memory\n"); return ERR_PTR(-EINVAL); } diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c index 17bd3590aaa2..51d062e0c3f1 100644 --- a/drivers/firmware/tegra/bpmp.c +++ b/drivers/firmware/tegra/bpmp.c @@ -8,8 +8,7 @@ #include <linux/mailbox_client.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_address.h> -#include <linux/of_device.h> +#include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/pm.h> #include <linux/semaphore.h> diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 039d92a595ec..26a37f47f4ca 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -97,7 +97,6 @@ struct ti_sci_desc { * @node: list head * @host_id: Host ID * @users: Number of users of this instance - * @is_suspending: Flag set to indicate in suspend path. */ struct ti_sci_info { struct device *dev; @@ -116,7 +115,6 @@ struct ti_sci_info { u8 host_id; /* protected by ti_sci_list_mutex */ int users; - bool is_suspending; }; #define cl_to_ti_sci_info(c) container_of(c, struct ti_sci_info, cl) @@ -418,14 +416,14 @@ static inline int ti_sci_do_xfer(struct ti_sci_info *info, ret = 0; - if (!info->is_suspending) { + if (system_state <= SYSTEM_RUNNING) { /* And we wait for the response. */ timeout = msecs_to_jiffies(info->desc->max_rx_timeout_ms); if (!wait_for_completion_timeout(&xfer->done, timeout)) ret = -ETIMEDOUT; } else { /* - * If we are suspending, we cannot use wait_for_completion_timeout + * If we are !running, we cannot use wait_for_completion_timeout * during noirq phase, so we must manually poll the completion. */ ret = read_poll_timeout_atomic(try_wait_for_completion, done_state, @@ -1978,8 +1976,6 @@ static int ti_sci_free_irq(const struct ti_sci_handle *handle, u32 valid_params, * @src_index: IRQ source index within the source device * @dst_id: Device ID of the IRQ destination * @dst_host_irq: IRQ number of the destination device - * @vint_irq: Boolean specifying if this interrupt belongs to - * Interrupt Aggregator. * * Return: 0 if all went fine, else return appropriate error. */ @@ -2026,8 +2022,6 @@ static int ti_sci_cmd_set_event_map(const struct ti_sci_handle *handle, * @src_index: IRQ source index within the source device * @dst_id: Device ID of the IRQ destination * @dst_host_irq: IRQ number of the destination device - * @vint_irq: Boolean specifying if this interrupt belongs to - * Interrupt Aggregator. * * Return: 0 if all went fine, else return appropriate error. */ @@ -2620,6 +2614,7 @@ fail: * configuration flags * @handle: Pointer to TI SCI handle * @proc_id: Processor ID this request is for + * @bootvector: Processor Boot vector (start address) * @config_flags_set: Configuration flags to be set * @config_flags_clear: Configuration flags to be cleared. * @@ -2736,9 +2731,13 @@ fail: } /** - * ti_sci_cmd_get_boot_status() - Command to get the processor boot status + * ti_sci_cmd_proc_get_status() - Command to get the processor boot status * @handle: Pointer to TI SCI handle * @proc_id: Processor ID this request is for + * @bv: Processor Boot vector (start address) + * @cfg_flags: Processor specific configuration flags + * @ctrl_flags: Processor specific control flags + * @sts_flags: Processor specific status flags * * Return: 0 if all went well, else returns appropriate error value. */ @@ -3256,7 +3255,7 @@ EXPORT_SYMBOL_GPL(devm_ti_sci_get_of_resource); * @handle: TISCI handle * @dev: Device pointer to which the resource is assigned * @dev_id: TISCI device id to which the resource is assigned - * @suub_type: TISCI resource subytpe representing the resource. + * @sub_type: TISCI resource subytpe representing the resource. * * Return: Pointer to ti_sci_resource if all went well else appropriate * error pointer. @@ -3281,35 +3280,6 @@ static int tisci_reboot_handler(struct notifier_block *nb, unsigned long mode, return NOTIFY_BAD; } -static void ti_sci_set_is_suspending(struct ti_sci_info *info, bool is_suspending) -{ - info->is_suspending = is_suspending; -} - -static int ti_sci_suspend(struct device *dev) -{ - struct ti_sci_info *info = dev_get_drvdata(dev); - /* - * We must switch operation to polled mode now as drivers and the genpd - * layer may make late TI SCI calls to change clock and device states - * from the noirq phase of suspend. - */ - ti_sci_set_is_suspending(info, true); - - return 0; -} - -static int ti_sci_resume(struct device *dev) -{ - struct ti_sci_info *info = dev_get_drvdata(dev); - - ti_sci_set_is_suspending(info, false); - - return 0; -} - -static DEFINE_SIMPLE_DEV_PM_OPS(ti_sci_pm_ops, ti_sci_suspend, ti_sci_resume); - /* Description for K2G */ static const struct ti_sci_desc ti_sci_pmmc_k2g_desc = { .default_host_id = 2, @@ -3516,7 +3486,6 @@ static struct platform_driver ti_sci_driver = { .driver = { .name = "ti-sci", .of_match_table = of_match_ptr(ti_sci_of_match), - .pm = &ti_sci_pm_ops, }, }; module_platform_driver(ti_sci_driver); diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index f8c4eb2b43f8..4cc1ac7f76ed 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -18,6 +18,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/of_platform.h> +#include <linux/platform_device.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/hashtable.h> @@ -339,6 +340,8 @@ int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 arg0, u32 arg1, static u32 pm_api_version; static u32 pm_tz_version; +static u32 pm_family_code; +static u32 pm_sub_family_code; int zynqmp_pm_register_sgi(u32 sgi_num, u32 reset) { @@ -405,6 +408,39 @@ int zynqmp_pm_get_chipid(u32 *idcode, u32 *version) EXPORT_SYMBOL_GPL(zynqmp_pm_get_chipid); /** + * zynqmp_pm_get_family_info() - Get family info of platform + * @family: Returned family code value + * @subfamily: Returned sub-family code value + * + * Return: Returns status, either success or error+reason + */ +static int zynqmp_pm_get_family_info(u32 *family, u32 *subfamily) +{ + u32 ret_payload[PAYLOAD_ARG_CNT]; + u32 idcode; + int ret; + + /* Check is family or sub-family code already received */ + if (pm_family_code && pm_sub_family_code) { + *family = pm_family_code; + *subfamily = pm_sub_family_code; + return 0; + } + + ret = zynqmp_pm_invoke_fn(PM_GET_CHIPID, 0, 0, 0, 0, ret_payload); + if (ret < 0) + return ret; + + idcode = ret_payload[1]; + pm_family_code = FIELD_GET(FAMILY_CODE_MASK, idcode); + pm_sub_family_code = FIELD_GET(SUB_FAMILY_CODE_MASK, idcode); + *family = pm_family_code; + *subfamily = pm_sub_family_code; + + return 0; +} + +/** * zynqmp_pm_get_trustzone_version() - Get secure trustzone firmware version * @version: Returned version value * @@ -1121,6 +1157,15 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_get_config); int zynqmp_pm_pinctrl_set_config(const u32 pin, const u32 param, u32 value) { + int ret; + + if (pm_family_code == ZYNQMP_FAMILY_CODE && + param == PM_PINCTRL_CONFIG_TRI_STATE) { + ret = zynqmp_pm_feature(PM_PINCTRL_CONFIG_PARAM_SET); + if (ret < PM_PINCTRL_PARAM_SET_VERSION) + return -EOPNOTSUPP; + } + return zynqmp_pm_invoke_fn(PM_PINCTRL_CONFIG_PARAM_SET, pin, param, value, 0, NULL); } @@ -1919,6 +1964,11 @@ static int zynqmp_firmware_probe(struct platform_device *pdev) pr_info("%s Platform Management API v%d.%d\n", __func__, pm_api_version >> 16, pm_api_version & 0xFFFF); + /* Get the Family code and sub family code of platform */ + ret = zynqmp_pm_get_family_info(&pm_family_code, &pm_sub_family_code); + if (ret < 0) + return ret; + /* Check trustzone version number */ ret = zynqmp_pm_get_trustzone_version(&pm_tz_version); if (ret) |