@@ -28,8 +28,11 @@ static unsigned long scmi_clk_recalc_rate(struct clk_hw *hw,
int ret;
u64 rate;
struct scmi_clk *clk = to_scmi_clk(hw);
+ const struct scmi_clk_ops *clk_ops;
- ret = clk->handle->clk_ops->rate_get(clk->handle, clk->id, &rate);
+ clk_ops = scmi_protocol_get_ops(clk->handle, SCMI_PROTOCOL_CLOCK);
+
+ ret = clk_ops->rate_get(clk->handle, clk->id, &rate);
if (ret)
return 0;
return rate;
@@ -68,22 +71,31 @@ static int scmi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct scmi_clk *clk = to_scmi_clk(hw);
+ const struct scmi_clk_ops *clk_ops;
+
+ clk_ops = scmi_protocol_get_ops(clk->handle, SCMI_PROTOCOL_CLOCK);
- return clk->handle->clk_ops->rate_set(clk->handle, clk->id, 0, rate);
+ return clk_ops->rate_set(clk->handle, clk->id, 0, rate);
}
static int scmi_clk_enable(struct clk_hw *hw)
{
struct scmi_clk *clk = to_scmi_clk(hw);
+ const struct scmi_clk_ops *clk_ops;
+
+ clk_ops = scmi_protocol_get_ops(clk->handle, SCMI_PROTOCOL_CLOCK);
- return clk->handle->clk_ops->enable(clk->handle, clk->id);
+ return clk_ops->enable(clk->handle, clk->id);
}
static void scmi_clk_disable(struct clk_hw *hw)
{
struct scmi_clk *clk = to_scmi_clk(hw);
+ const struct scmi_clk_ops *clk_ops;
- clk->handle->clk_ops->disable(clk->handle, clk->id);
+ clk_ops = scmi_protocol_get_ops(clk->handle, SCMI_PROTOCOL_CLOCK);
+
+ clk_ops->disable(clk->handle, clk->id);
}
static const struct clk_ops scmi_clk_ops = {
@@ -126,11 +138,14 @@ static int scmi_clocks_probe(struct scmi_device *sdev)
struct device *dev = &sdev->dev;
struct device_node *np = dev->of_node;
const struct scmi_handle *handle = sdev->handle;
+ const struct scmi_clk_ops *clk_ops;
+
+ clk_ops = scmi_protocol_get_ops(handle, SCMI_PROTOCOL_CLOCK);
- if (!handle || !handle->clk_ops)
+ if (!handle || !clk_ops)
return -ENODEV;
- count = handle->clk_ops->count_get(handle);
+ count = clk_ops->count_get(handle);
if (count < 0) {
dev_err(dev, "%pOFn: invalid clock output count\n", np);
return -EINVAL;
@@ -151,7 +166,7 @@ static int scmi_clocks_probe(struct scmi_device *sdev)
if (!sclk)
return -ENOMEM;
- sclk->info = handle->clk_ops->info_get(handle, idx);
+ sclk->info = clk_ops->info_get(handle, idx);
if (!sclk->info) {
dev_dbg(dev, "invalid clock info for idx %d\n", idx);
continue;
@@ -29,11 +29,13 @@ static const struct scmi_handle *handle;
static unsigned int scmi_cpufreq_get_rate(unsigned int cpu)
{
struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu);
- struct scmi_perf_ops *perf_ops = handle->perf_ops;
struct scmi_data *priv = policy->driver_data;
+ const struct scmi_perf_ops *perf_ops;
unsigned long rate;
int ret;
+ perf_ops = scmi_protocol_get_ops(handle, SCMI_PROTOCOL_PERF);
+
ret = perf_ops->freq_get(handle, priv->domain_id, &rate, false);
if (ret)
return 0;
@@ -50,9 +52,11 @@ scmi_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int index)
{
int ret;
struct scmi_data *priv = policy->driver_data;
- struct scmi_perf_ops *perf_ops = handle->perf_ops;
+ const struct scmi_perf_ops *perf_ops;
u64 freq = policy->freq_table[index].frequency;
+ perf_ops = scmi_protocol_get_ops(handle, SCMI_PROTOCOL_PERF);
+
ret = perf_ops->freq_set(handle, priv->domain_id, freq * 1000, false);
if (!ret)
arch_set_freq_scale(policy->related_cpus, freq,
@@ -64,7 +68,9 @@ static unsigned int scmi_cpufreq_fast_switch(struct cpufreq_policy *policy,
unsigned int target_freq)
{
struct scmi_data *priv = policy->driver_data;
- struct scmi_perf_ops *perf_ops = handle->perf_ops;
+ const struct scmi_perf_ops *perf_ops;
+
+ perf_ops = scmi_protocol_get_ops(handle, SCMI_PROTOCOL_PERF);
if (!perf_ops->freq_set(handle, priv->domain_id,
target_freq * 1000, true)) {
@@ -79,10 +85,13 @@ static unsigned int scmi_cpufreq_fast_switch(struct cpufreq_policy *policy,
static int
scmi_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask)
{
+ const struct scmi_perf_ops *perf_ops;
int cpu, domain, tdomain;
struct device *tcpu_dev;
- domain = handle->perf_ops->device_domain_id(cpu_dev);
+ perf_ops = scmi_protocol_get_ops(handle, SCMI_PROTOCOL_PERF);
+
+ domain = perf_ops->device_domain_id(cpu_dev);
if (domain < 0)
return domain;
@@ -94,7 +103,7 @@ scmi_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask)
if (!tcpu_dev)
continue;
- tdomain = handle->perf_ops->device_domain_id(tcpu_dev);
+ tdomain = perf_ops->device_domain_id(tcpu_dev);
if (tdomain == domain)
cpumask_set_cpu(cpu, cpumask);
}
@@ -106,6 +115,7 @@ static int __maybe_unused
scmi_get_cpu_power(unsigned long *power, unsigned long *KHz, int cpu)
{
struct device *cpu_dev = get_cpu_device(cpu);
+ const struct scmi_perf_ops *perf_ops;
unsigned long Hz;
int ret, domain;
@@ -114,13 +124,15 @@ scmi_get_cpu_power(unsigned long *power, unsigned long *KHz, int cpu)
return -ENODEV;
}
- domain = handle->perf_ops->device_domain_id(cpu_dev);
+ perf_ops = scmi_protocol_get_ops(handle, SCMI_PROTOCOL_PERF);
+
+ domain = perf_ops->device_domain_id(cpu_dev);
if (domain < 0)
return domain;
/* Get the power cost of the performance domain. */
Hz = *KHz * 1000;
- ret = handle->perf_ops->est_power_get(handle, domain, &Hz, power);
+ ret = perf_ops->est_power_get(handle, domain, &Hz, power);
if (ret)
return ret;
@@ -138,6 +150,9 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
struct scmi_data *priv;
struct cpufreq_frequency_table *freq_table;
struct em_data_callback em_cb = EM_DATA_CB(scmi_get_cpu_power);
+ const struct scmi_perf_ops *perf_ops;
+
+ perf_ops = scmi_protocol_get_ops(handle, SCMI_PROTOCOL_PERF);
cpu_dev = get_cpu_device(policy->cpu);
if (!cpu_dev) {
@@ -145,7 +160,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
return -ENODEV;
}
- ret = handle->perf_ops->device_opps_add(handle, cpu_dev);
+ ret = perf_ops->device_opps_add(handle, cpu_dev);
if (ret) {
dev_warn(cpu_dev, "failed to add opps to the device\n");
return ret;
@@ -184,7 +199,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
}
priv->cpu_dev = cpu_dev;
- priv->domain_id = handle->perf_ops->device_domain_id(cpu_dev);
+ priv->domain_id = perf_ops->device_domain_id(cpu_dev);
policy->driver_data = priv;
policy->freq_table = freq_table;
@@ -192,7 +207,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
/* SCMI allows DVFS request for any domain from any CPU */
policy->dvfs_possible_from_any_cpu = true;
- latency = handle->perf_ops->transition_latency_get(handle, cpu_dev);
+ latency = perf_ops->transition_latency_get(handle, cpu_dev);
if (!latency)
latency = CPUFREQ_ETERNAL;
@@ -243,7 +258,7 @@ static int scmi_cpufreq_probe(struct scmi_device *sdev)
handle = sdev->handle;
- if (!handle || !handle->perf_ops)
+ if (!handle || !scmi_protocol_get_ops(handle, SCMI_PROTOCOL_PERF))
return -ENODEV;
ret = cpufreq_register_driver(&scmi_cpufreq_driver);
@@ -47,6 +47,16 @@ static int scmi_dev_match(struct device *dev, struct device_driver *drv)
return 0;
}
+void scmi_protocol_register_ops(struct scmi_handle *handle, u8 protocol_id,
+ const void *ops, void *priv)
+{
+ spin_lock(&protocol_lock);
+ handle->protocols[protocol_id].ops = ops;
+ handle->protocols[protocol_id].priv = priv;
+ spin_unlock(&protocol_lock);
+}
+EXPORT_SYMBOL_GPL(scmi_protocol_register_ops);
+
static int scmi_protocol_init(int protocol_id, struct scmi_handle *handle)
{
scmi_prot_init_fn_t fn = idr_find(&scmi_protocols, protocol_id);
@@ -273,7 +273,8 @@ static int scmi_clock_disable(const struct scmi_handle *handle, u32 clk_id)
static int scmi_clock_count_get(const struct scmi_handle *handle)
{
- struct clock_info *ci = handle->clk_priv;
+ struct clock_info *ci = scmi_protocol_get_priv(handle,
+ SCMI_PROTOCOL_CLOCK);
return ci->num_clocks;
}
@@ -281,7 +282,8 @@ static int scmi_clock_count_get(const struct scmi_handle *handle)
static const struct scmi_clock_info *
scmi_clock_info_get(const struct scmi_handle *handle, u32 clk_id)
{
- struct clock_info *ci = handle->clk_priv;
+ struct clock_info *ci = scmi_protocol_get_priv(handle,
+ SCMI_PROTOCOL_CLOCK);
struct scmi_clock_info *clk = ci->clk + clk_id;
if (!clk->name[0])
@@ -329,8 +331,8 @@ static int scmi_clock_protocol_init(struct scmi_handle *handle)
scmi_clock_describe_rates_get(handle, clkid, clk);
}
- handle->clk_ops = &clk_ops;
- handle->clk_priv = cinfo;
+ scmi_protocol_register_ops(handle, SCMI_PROTOCOL_CLOCK,
+ &clk_ops, cinfo);
return 0;
}
@@ -362,7 +362,9 @@ static int scmi_dvfs_device_opps_add(const struct scmi_handle *handle,
unsigned long freq;
struct scmi_opp *opp;
struct perf_dom_info *dom;
- struct scmi_perf_info *pi = handle->perf_priv;
+ struct scmi_perf_info *pi;
+
+ pi = scmi_protocol_get_priv(handle, SCMI_PROTOCOL_PERF);
domain = scmi_dev_domain_id(dev);
if (domain < 0)
@@ -391,9 +393,11 @@ static int scmi_dvfs_transition_latency_get(const struct scmi_handle *handle,
struct device *dev)
{
struct perf_dom_info *dom;
- struct scmi_perf_info *pi = handle->perf_priv;
+ struct scmi_perf_info *pi;
int domain = scmi_dev_domain_id(dev);
+ pi = scmi_protocol_get_priv(handle, SCMI_PROTOCOL_PERF);
+
if (domain < 0)
return domain;
@@ -405,7 +409,8 @@ static int scmi_dvfs_transition_latency_get(const struct scmi_handle *handle,
static int scmi_dvfs_freq_set(const struct scmi_handle *handle, u32 domain,
unsigned long freq, bool poll)
{
- struct scmi_perf_info *pi = handle->perf_priv;
+ struct scmi_perf_info *pi = scmi_protocol_get_priv(handle,
+ SCMI_PROTOCOL_PERF);
struct perf_dom_info *dom = pi->dom_info + domain;
return scmi_perf_level_set(handle, domain, freq / dom->mult_factor,
@@ -417,7 +422,8 @@ static int scmi_dvfs_freq_get(const struct scmi_handle *handle, u32 domain,
{
int ret;
u32 level;
- struct scmi_perf_info *pi = handle->perf_priv;
+ struct scmi_perf_info *pi = scmi_protocol_get_priv(handle,
+ SCMI_PROTOCOL_PERF);
struct perf_dom_info *dom = pi->dom_info + domain;
ret = scmi_perf_level_get(handle, domain, &level, poll);
@@ -430,12 +436,14 @@ static int scmi_dvfs_freq_get(const struct scmi_handle *handle, u32 domain,
static int scmi_dvfs_est_power_get(const struct scmi_handle *handle, u32 domain,
unsigned long *freq, unsigned long *power)
{
- struct scmi_perf_info *pi = handle->perf_priv;
+ struct scmi_perf_info *pi;
struct perf_dom_info *dom;
unsigned long opp_freq;
int idx, ret = -EINVAL;
struct scmi_opp *opp;
+ pi = scmi_protocol_get_priv(handle, SCMI_PROTOCOL_PERF);
+
dom = pi->dom_info + domain;
if (!dom)
return -EIO;
@@ -496,8 +504,8 @@ static int scmi_perf_protocol_init(struct scmi_handle *handle)
scmi_perf_describe_levels_get(handle, domain, dom);
}
- handle->perf_ops = &perf_ops;
- handle->perf_priv = pinfo;
+ scmi_protocol_register_ops(handle, SCMI_PROTOCOL_PERF,
+ &perf_ops, pinfo);
return 0;
}
@@ -159,14 +159,17 @@ scmi_power_state_get(const struct scmi_handle *handle, u32 domain, u32 *state)
static int scmi_power_num_domains_get(const struct scmi_handle *handle)
{
- struct scmi_power_info *pi = handle->power_priv;
+ struct scmi_power_info *pi;
+
+ pi = scmi_protocol_get_priv(handle, SCMI_PROTOCOL_POWER);
return pi->num_domains;
}
static char *scmi_power_name_get(const struct scmi_handle *handle, u32 domain)
{
- struct scmi_power_info *pi = handle->power_priv;
+ struct scmi_power_info *pi = scmi_protocol_get_priv(handle,
+ SCMI_PROTOCOL_POWER);
struct power_dom_info *dom = pi->dom_info + domain;
return dom->name;
@@ -207,8 +210,8 @@ static int scmi_power_protocol_init(struct scmi_handle *handle)
scmi_power_domain_attributes_get(handle, domain, dom);
}
- handle->power_ops = &power_ops;
- handle->power_priv = pinfo;
+ scmi_protocol_register_ops(handle, SCMI_PROTOCOL_POWER,
+ &power_ops, pinfo);
return 0;
}
@@ -25,7 +25,9 @@ static int scmi_pd_power(struct generic_pm_domain *domain, bool power_on)
int ret;
u32 state, ret_state;
struct scmi_pm_domain *pd = to_scmi_pd(domain);
- const struct scmi_power_ops *ops = pd->handle->power_ops;
+ const struct scmi_power_ops *ops;
+
+ ops = scmi_protocol_get_ops(pd->handle, SCMI_PROTOCOL_POWER);
if (power_on)
state = SCMI_POWER_STATE_GENERIC_ON;
@@ -60,11 +62,13 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
struct genpd_onecell_data *scmi_pd_data;
struct generic_pm_domain **domains;
const struct scmi_handle *handle = sdev->handle;
+ const struct scmi_power_ops *power_ops;
- if (!handle || !handle->power_ops)
+ power_ops = scmi_protocol_get_ops(handle, SCMI_PROTOCOL_POWER);
+ if (!handle || !power_ops)
return -ENODEV;
- num_domains = handle->power_ops->num_domains_get(handle);
+ num_domains = power_ops->num_domains_get(handle);
if (num_domains < 0) {
dev_err(dev, "number of domains not found\n");
return num_domains;
@@ -89,12 +93,12 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
scmi_pd->domain = i;
scmi_pd->handle = handle;
- scmi_pd->name = handle->power_ops->name_get(handle, i);
+ scmi_pd->name = power_ops->name_get(handle, i);
scmi_pd->genpd.name = scmi_pd->name;
scmi_pd->genpd.power_off = scmi_pd_power_off;
scmi_pd->genpd.power_on = scmi_pd_power_on;
- if (handle->power_ops->state_get(handle, i, &state)) {
+ if (power_ops->state_get(handle, i, &state)) {
dev_warn(dev, "failed to get state for domain %d\n", i);
continue;
}
@@ -234,14 +234,16 @@ static int scmi_sensor_reading_get(const struct scmi_handle *handle,
static const struct scmi_sensor_info *
scmi_sensor_info_get(const struct scmi_handle *handle, u32 sensor_id)
{
- struct sensors_info *si = handle->sensor_priv;
+ struct sensors_info *si = scmi_protocol_get_priv(handle,
+ SCMI_PROTOCOL_SENSOR);
return si->sensors + sensor_id;
}
static int scmi_sensor_count_get(const struct scmi_handle *handle)
{
- struct sensors_info *si = handle->sensor_priv;
+ struct sensors_info *si = scmi_protocol_get_priv(handle,
+ SCMI_PROTOCOL_SENSOR);
return si->num_sensors;
}
@@ -277,8 +279,8 @@ static int scmi_sensors_protocol_init(struct scmi_handle *handle)
scmi_sensor_description_get(handle, sinfo);
- handle->sensor_ops = &sensor_ops;
- handle->sensor_priv = sinfo;
+ scmi_protocol_register_ops(handle, SCMI_PROTOCOL_SENSOR, &sensor_ops,
+ sinfo);
return 0;
}
@@ -26,9 +26,12 @@ static int scmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
const struct scmi_sensor_info *sensor;
struct scmi_sensors *scmi_sensors = dev_get_drvdata(dev);
const struct scmi_handle *h = scmi_sensors->handle;
+ const struct scmi_sensor_ops *sensor_ops;
+
+ sensor_ops = scmi_protocol_get_ops(h, SCMI_PROTOCOL_SENSOR);
sensor = *(scmi_sensors->info[type] + channel);
- ret = h->sensor_ops->reading_get(h, sensor->id, false, &value);
+ ret = sensor_ops->reading_get(h, sensor->id, false, &value);
if (!ret)
*val = value;
@@ -121,11 +124,14 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
struct hwmon_channel_info *scmi_hwmon_chan;
const struct hwmon_channel_info **ptr_scmi_ci;
const struct scmi_handle *handle = sdev->handle;
+ const struct scmi_sensor_ops *sensor_ops;
+
+ sensor_ops = scmi_protocol_get_ops(handle, SCMI_PROTOCOL_SENSOR);
- if (!handle || !handle->sensor_ops)
+ if (!handle || !sensor_ops)
return -ENODEV;
- nr_sensors = handle->sensor_ops->count_get(handle);
+ nr_sensors = sensor_ops->count_get(handle);
if (!nr_sensors)
return -EIO;
@@ -136,7 +142,7 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
scmi_sensors->handle = handle;
for (i = 0; i < nr_sensors; i++) {
- sensor = handle->sensor_ops->info_get(handle, i);
+ sensor = sensor_ops->info_get(handle, i);
if (!sensor)
return -EINVAL;
@@ -186,7 +192,7 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
}
for (i = nr_sensors - 1; i >= 0 ; i--) {
- sensor = handle->sensor_ops->info_get(handle, i);
+ sensor = sensor_ops->info_get(handle, i);
if (!sensor)
continue;
@@ -205,17 +205,24 @@ struct scmi_sensor_ops {
struct scmi_handle {
struct device *dev;
struct scmi_revision_info *version;
- struct scmi_perf_ops *perf_ops;
- struct scmi_clk_ops *clk_ops;
- struct scmi_power_ops *power_ops;
- struct scmi_sensor_ops *sensor_ops;
- /* for protocol internal use */
- void *perf_priv;
- void *clk_priv;
- void *power_priv;
- void *sensor_priv;
+ struct scmi_handle_protocols {
+ const void *ops;
+ void *priv;
+ } protocols[255];
};
+static inline const void *scmi_protocol_get_ops(const struct scmi_handle *h,
+ u8 protocol_id)
+{
+ return h->protocols[protocol_id].ops;
+}
+
+static inline void *scmi_protocol_get_priv(const struct scmi_handle *h,
+ u8 protocol_id)
+{
+ return h->protocols[protocol_id].priv;
+}
+
enum scmi_std_protocol {
SCMI_PROTOCOL_BASE = 0x10,
SCMI_PROTOCOL_POWER = 0x11,
@@ -286,4 +293,6 @@ static inline void scmi_driver_unregister(struct scmi_driver *driver) {}
typedef int (*scmi_prot_init_fn_t)(struct scmi_handle *);
int scmi_protocol_register(int protocol_id, scmi_prot_init_fn_t fn);
+void scmi_protocol_register_ops(struct scmi_handle *handle, u8 protocol_id,
+ const void *ops, void *priv);
void scmi_protocol_unregister(int protocol_id);
The SCMI specific allows implementors to define their custom protocols in the 0x80-0xFF space. The current scmi_handle structure requires us to extend the structure with a set of operations and associated private data in a way that is not quite scaling well. Create a 255 bytes structure that contains an opaque pointer to a set of operations and private data and create two helper functions to retrieve those based on the protocol identifier. Several options were considered, like using a linked list but since we could be performance sensitive in some paths, using an array was faster and simpler. Convert all call sites to use either scmi_protocol_get_ops() or scmi_protocol_get_info(). Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> --- Hi Sudeep, There could be better ways to deal with that, another could be to avoid passing the scmi_handle around, but instead pass a scmi_dev instance which already has a set of operations specific to the protocols already set and provides storage for private data as well. Let me know if you find this appropriate either way. drivers/clk/clk-scmi.c | 29 +++++++++++++---- drivers/cpufreq/scmi-cpufreq.c | 37 +++++++++++++++------- drivers/firmware/arm_scmi/bus.c | 10 ++++++ drivers/firmware/arm_scmi/clock.c | 10 +++--- drivers/firmware/arm_scmi/perf.c | 22 +++++++++---- drivers/firmware/arm_scmi/power.c | 11 ++++--- drivers/firmware/arm_scmi/scmi_pm_domain.c | 14 +++++--- drivers/firmware/arm_scmi/sensors.c | 10 +++--- drivers/hwmon/scmi-hwmon.c | 16 +++++++--- include/linux/scmi_protocol.h | 27 ++++++++++------ 10 files changed, 130 insertions(+), 56 deletions(-)