Commit 4be3f47e authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

Merge tag 'scmi-updates-5.13' of...

Merge tag 'scmi-updates-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux into arm/drivers

ARM SCMI updates for v5.13

The major and big addition this time is to support modularisation of
individual SCMI protocols thus enabling to add support for vendors'
custom SCMI protocol. This changes the interface provided by the SCMI
driver to all the users of SCMI and hence involved changes in various
other subsystem SCMI drivers. The change has been split with a bit of
transient code to preserve bisectability and avoiding one big patch bomb
changing all the users.

This also includes SCMI IIO driver(pulled from IIO tree) and support for
per-cpu DVFS.

* tag 'scmi-updates-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux: (41 commits)
  firmware: arm_scmi: Add dynamic scmi devices creation
  firmware: arm_scmi: Add protocol modularization support
  firmware: arm_scmi: Rename non devres notify_ops
  firmware: arm_scmi: Make notify_priv really private
  firmware: arm_scmi: Cleanup events registration transient code
  firmware: arm_scmi: Cleanup unused core transfer helper wrappers
  firmware: arm_scmi: Cleanup legacy protocol init code
  firmware: arm_scmi: Make references to handle const
  firmware: arm_scmi: Remove legacy scmi_voltage_ops protocol interface
  regulator: scmi: Port driver to the new scmi_voltage_proto_ops interface
  firmware: arm_scmi: Port voltage protocol to new protocols interface
  firmware: arm_scmi: Port systempower protocol to new protocols interface
  firmware: arm_scmi: Remove legacy scmi_sensor_ops protocol interface
  iio/scmi: Port driver to the new scmi_sensor_proto_ops interface
  hwmon: (scmi) port driver to the new scmi_sensor_proto_ops interface
  firmware: arm_scmi: Port sensor protocol to new protocols interface
  firmware: arm_scmi: Remove legacy scmi_reset_ops protocol interface
  reset: reset-scmi: Port driver to the new scmi_reset_proto_ops interface
  firmware: arm_scmi: Port reset protocol to new protocols interface
  firmware: arm_scmi: Remove legacy scmi_clk_ops protocol interface
  ...

Link: https://lore.kernel.org/r/20210331100657.ilu63i4swnr3zp4e@bogus


Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents ba87f200 d4f9dddd
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -8692,6 +8692,12 @@ S: Maintained
F:	Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.txt
F:	drivers/iio/multiplexer/iio-mux.c
IIO SCMI BASED DRIVER
M:	Jyoti Bhayana <jbhayana@google.com>
L:	linux-iio@vger.kernel.org
S:	Maintained
F:	drivers/iio/common/scmi_sensors/scmi_iio.c
IIO SUBSYSTEM AND DRIVERS
M:	Jonathan Cameron <jic23@kernel.org>
R:	Lars-Peter Clausen <lars@metafoo.de>
+18 −10
Original line number Diff line number Diff line
@@ -2,7 +2,7 @@
/*
 * System Control and Power Interface (SCMI) Protocol based clock driver
 *
 * Copyright (C) 2018 ARM Ltd.
 * Copyright (C) 2018-2021 ARM Ltd.
 */

#include <linux/clk-provider.h>
@@ -13,11 +13,13 @@
#include <linux/scmi_protocol.h>
#include <asm/div64.h>

static const struct scmi_clk_proto_ops *scmi_proto_clk_ops;

struct scmi_clk {
	u32 id;
	struct clk_hw hw;
	const struct scmi_clock_info *info;
	const struct scmi_handle *handle;
	const struct scmi_protocol_handle *ph;
};

#define to_scmi_clk(clk) container_of(clk, struct scmi_clk, hw)
@@ -29,7 +31,7 @@ static unsigned long scmi_clk_recalc_rate(struct clk_hw *hw,
	u64 rate;
	struct scmi_clk *clk = to_scmi_clk(hw);

	ret = clk->handle->clk_ops->rate_get(clk->handle, clk->id, &rate);
	ret = scmi_proto_clk_ops->rate_get(clk->ph, clk->id, &rate);
	if (ret)
		return 0;
	return rate;
@@ -69,21 +71,21 @@ static int scmi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
{
	struct scmi_clk *clk = to_scmi_clk(hw);

	return clk->handle->clk_ops->rate_set(clk->handle, clk->id, rate);
	return scmi_proto_clk_ops->rate_set(clk->ph, clk->id, rate);
}

static int scmi_clk_enable(struct clk_hw *hw)
{
	struct scmi_clk *clk = to_scmi_clk(hw);

	return clk->handle->clk_ops->enable(clk->handle, clk->id);
	return scmi_proto_clk_ops->enable(clk->ph, clk->id);
}

static void scmi_clk_disable(struct clk_hw *hw)
{
	struct scmi_clk *clk = to_scmi_clk(hw);

	clk->handle->clk_ops->disable(clk->handle, clk->id);
	scmi_proto_clk_ops->disable(clk->ph, clk->id);
}

static const struct clk_ops scmi_clk_ops = {
@@ -142,11 +144,17 @@ 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;
	struct scmi_protocol_handle *ph;

	if (!handle || !handle->clk_ops)
	if (!handle)
		return -ENODEV;

	count = handle->clk_ops->count_get(handle);
	scmi_proto_clk_ops =
		handle->devm_protocol_get(sdev, SCMI_PROTOCOL_CLOCK, &ph);
	if (IS_ERR(scmi_proto_clk_ops))
		return PTR_ERR(scmi_proto_clk_ops);

	count = scmi_proto_clk_ops->count_get(ph);
	if (count < 0) {
		dev_err(dev, "%pOFn: invalid clock output count\n", np);
		return -EINVAL;
@@ -167,14 +175,14 @@ static int scmi_clocks_probe(struct scmi_device *sdev)
		if (!sclk)
			return -ENOMEM;

		sclk->info = handle->clk_ops->info_get(handle, idx);
		sclk->info = scmi_proto_clk_ops->info_get(ph, idx);
		if (!sclk->info) {
			dev_dbg(dev, "invalid clock info for idx %d\n", idx);
			continue;
		}

		sclk->id = idx;
		sclk->handle = handle;
		sclk->ph = ph;

		err = scmi_clk_ops_init(dev, sclk);
		if (err) {
+73 −34
Original line number Diff line number Diff line
@@ -2,7 +2,7 @@
/*
 * System Control and Power Interface (SCMI) based CPUFreq Interface driver
 *
 * Copyright (C) 2018 ARM Ltd.
 * Copyright (C) 2018-2021 ARM Ltd.
 * Sudeep Holla <sudeep.holla@arm.com>
 */

@@ -25,17 +25,17 @@ struct scmi_data {
	struct device *cpu_dev;
};

static const struct scmi_handle *handle;
static struct scmi_protocol_handle *ph;
static const struct scmi_perf_proto_ops *perf_ops;

static unsigned int scmi_cpufreq_get_rate(unsigned int cpu)
{
	struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu);
	const struct scmi_perf_ops *perf_ops = handle->perf_ops;
	struct scmi_data *priv = policy->driver_data;
	unsigned long rate;
	int ret;

	ret = perf_ops->freq_get(handle, priv->domain_id, &rate, false);
	ret = perf_ops->freq_get(ph, priv->domain_id, &rate, false);
	if (ret)
		return 0;
	return rate / 1000;
@@ -50,19 +50,17 @@ static int
scmi_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int index)
{
	struct scmi_data *priv = policy->driver_data;
	const struct scmi_perf_ops *perf_ops = handle->perf_ops;
	u64 freq = policy->freq_table[index].frequency;

	return perf_ops->freq_set(handle, priv->domain_id, freq * 1000, false);
	return perf_ops->freq_set(ph, priv->domain_id, freq * 1000, false);
}

static unsigned int scmi_cpufreq_fast_switch(struct cpufreq_policy *policy,
					     unsigned int target_freq)
{
	struct scmi_data *priv = policy->driver_data;
	const struct scmi_perf_ops *perf_ops = handle->perf_ops;

	if (!perf_ops->freq_set(handle, priv->domain_id,
	if (!perf_ops->freq_set(ph, priv->domain_id,
				target_freq * 1000, true))
		return target_freq;

@@ -75,7 +73,7 @@ scmi_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask)
	int cpu, domain, tdomain;
	struct device *tcpu_dev;

	domain = handle->perf_ops->device_domain_id(cpu_dev);
	domain = perf_ops->device_domain_id(cpu_dev);
	if (domain < 0)
		return domain;

@@ -87,7 +85,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);
	}
@@ -102,13 +100,13 @@ scmi_get_cpu_power(unsigned long *power, unsigned long *KHz,
	unsigned long Hz;
	int ret, domain;

	domain = handle->perf_ops->device_domain_id(cpu_dev);
	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(ph, domain, &Hz, power);
	if (ret)
		return ret;

@@ -126,6 +124,7 @@ 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);
	cpumask_var_t opp_shared_cpus;
	bool power_scale_mw;

	cpu_dev = get_cpu_device(policy->cpu);
@@ -134,32 +133,66 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
		return -ENODEV;
	}

	ret = handle->perf_ops->device_opps_add(handle, cpu_dev);
	if (ret) {
		dev_warn(cpu_dev, "failed to add opps to the device\n");
		return ret;
	}
	if (!zalloc_cpumask_var(&opp_shared_cpus, GFP_KERNEL))
		ret = -ENOMEM;

	/* Obtain CPUs that share SCMI performance controls */
	ret = scmi_get_sharing_cpus(cpu_dev, policy->cpus);
	if (ret) {
		dev_warn(cpu_dev, "failed to get sharing cpumask\n");
		return ret;
		goto out_free_cpumask;
	}

	/*
	 * Obtain CPUs that share performance levels.
	 * The OPP 'sharing cpus' info may come from DT through an empty opp
	 * table and opp-shared.
	 */
	ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, opp_shared_cpus);
	if (ret || !cpumask_weight(opp_shared_cpus)) {
		/*
		 * Either opp-table is not set or no opp-shared was found.
		 * Use the CPU mask from SCMI to designate CPUs sharing an OPP
		 * table.
		 */
		cpumask_copy(opp_shared_cpus, policy->cpus);
	}

	ret = dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus);
	 /*
	  * A previous CPU may have marked OPPs as shared for a few CPUs, based on
	  * what OPP core provided. If the current CPU is part of those few, then
	  * there is no need to add OPPs again.
	  */
	nr_opp = dev_pm_opp_get_opp_count(cpu_dev);
	if (nr_opp <= 0) {
		ret = perf_ops->device_opps_add(ph, cpu_dev);
		if (ret) {
		dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n",
			__func__, ret);
		return ret;
			dev_warn(cpu_dev, "failed to add opps to the device\n");
			goto out_free_cpumask;
		}

		nr_opp = dev_pm_opp_get_opp_count(cpu_dev);
		if (nr_opp <= 0) {
		dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n");
		ret = -EPROBE_DEFER;
			dev_err(cpu_dev, "%s: No OPPs for this device: %d\n",
				__func__, ret);

			ret = -ENODEV;
			goto out_free_opp;
		}

		ret = dev_pm_opp_set_sharing_cpus(cpu_dev, opp_shared_cpus);
		if (ret) {
			dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n",
				__func__, ret);

			goto out_free_opp;
		}

		power_scale_mw = perf_ops->power_scale_mw_get(ph);
		em_dev_register_perf_domain(cpu_dev, nr_opp, &em_cb,
					    opp_shared_cpus, power_scale_mw);
	}

	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
	if (!priv) {
		ret = -ENOMEM;
@@ -173,7 +206,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;
@@ -181,26 +214,27 @@ 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(ph, cpu_dev);
	if (!latency)
		latency = CPUFREQ_ETERNAL;

	policy->cpuinfo.transition_latency = latency;

	policy->fast_switch_possible =
		handle->perf_ops->fast_switch_possible(handle, cpu_dev);

	power_scale_mw = handle->perf_ops->power_scale_mw_get(handle);
	em_dev_register_perf_domain(cpu_dev, nr_opp, &em_cb, policy->cpus,
				    power_scale_mw);
		perf_ops->fast_switch_possible(ph, cpu_dev);

	free_cpumask_var(opp_shared_cpus);
	return 0;

out_free_priv:
	kfree(priv);

out_free_opp:
	dev_pm_opp_remove_all_dynamic(cpu_dev);

out_free_cpumask:
	free_cpumask_var(opp_shared_cpus);

	return ret;
}

@@ -233,12 +267,17 @@ static int scmi_cpufreq_probe(struct scmi_device *sdev)
{
	int ret;
	struct device *dev = &sdev->dev;
	const struct scmi_handle *handle;

	handle = sdev->handle;

	if (!handle || !handle->perf_ops)
	if (!handle)
		return -ENODEV;

	perf_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_PERF, &ph);
	if (IS_ERR(perf_ops))
		return PTR_ERR(perf_ops);

#ifdef CONFIG_COMMON_CLK
	/* dummy clock provider as needed by OPP if clocks property is used */
	if (of_find_property(dev->of_node, "#clock-cells", NULL))
+79 −63
Original line number Diff line number Diff line
@@ -2,11 +2,12 @@
/*
 * System Control and Management Interface (SCMI) Base Protocol
 *
 * Copyright (C) 2018 ARM Ltd.
 * Copyright (C) 2018-2021 ARM Ltd.
 */

#define pr_fmt(fmt) "SCMI Notifications BASE - " fmt

#include <linux/module.h>
#include <linux/scmi_protocol.h>

#include "common.h"
@@ -50,30 +51,30 @@ struct scmi_base_error_notify_payld {
 * scmi_base_attributes_get() - gets the implementation details
 *	that are associated with the base protocol.
 *
 * @handle: SCMI entity handle
 * @ph: SCMI protocol handle
 *
 * Return: 0 on success, else appropriate SCMI error.
 */
static int scmi_base_attributes_get(const struct scmi_handle *handle)
static int scmi_base_attributes_get(const struct scmi_protocol_handle *ph)
{
	int ret;
	struct scmi_xfer *t;
	struct scmi_msg_resp_base_attributes *attr_info;
	struct scmi_revision_info *rev = handle->version;
	struct scmi_revision_info *rev = ph->get_priv(ph);

	ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
				 SCMI_PROTOCOL_BASE, 0, sizeof(*attr_info), &t);
	ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES,
				      0, sizeof(*attr_info), &t);
	if (ret)
		return ret;

	ret = scmi_do_xfer(handle, t);
	ret = ph->xops->do_xfer(ph, t);
	if (!ret) {
		attr_info = t->rx.buf;
		rev->num_protocols = attr_info->num_protocols;
		rev->num_agents = attr_info->num_agents;
	}

	scmi_xfer_put(handle, t);
	ph->xops->xfer_put(ph, t);

	return ret;
}
@@ -81,19 +82,20 @@ static int scmi_base_attributes_get(const struct scmi_handle *handle)
/**
 * scmi_base_vendor_id_get() - gets vendor/subvendor identifier ASCII string.
 *
 * @handle: SCMI entity handle
 * @ph: SCMI protocol handle
 * @sub_vendor: specify true if sub-vendor ID is needed
 *
 * Return: 0 on success, else appropriate SCMI error.
 */
static int
scmi_base_vendor_id_get(const struct scmi_handle *handle, bool sub_vendor)
scmi_base_vendor_id_get(const struct scmi_protocol_handle *ph, bool sub_vendor)
{
	u8 cmd;
	int ret, size;
	char *vendor_id;
	struct scmi_xfer *t;
	struct scmi_revision_info *rev = handle->version;
	struct scmi_revision_info *rev = ph->get_priv(ph);


	if (sub_vendor) {
		cmd = BASE_DISCOVER_SUB_VENDOR;
@@ -105,15 +107,15 @@ scmi_base_vendor_id_get(const struct scmi_handle *handle, bool sub_vendor)
		size = ARRAY_SIZE(rev->vendor_id);
	}

	ret = scmi_xfer_get_init(handle, cmd, SCMI_PROTOCOL_BASE, 0, size, &t);
	ret = ph->xops->xfer_get_init(ph, cmd, 0, size, &t);
	if (ret)
		return ret;

	ret = scmi_do_xfer(handle, t);
	ret = ph->xops->do_xfer(ph, t);
	if (!ret)
		memcpy(vendor_id, t->rx.buf, size);

	scmi_xfer_put(handle, t);
	ph->xops->xfer_put(ph, t);

	return ret;
}
@@ -123,30 +125,30 @@ scmi_base_vendor_id_get(const struct scmi_handle *handle, bool sub_vendor)
 *	implementation 32-bit version. The format of the version number is
 *	vendor-specific
 *
 * @handle: SCMI entity handle
 * @ph: SCMI protocol handle
 *
 * Return: 0 on success, else appropriate SCMI error.
 */
static int
scmi_base_implementation_version_get(const struct scmi_handle *handle)
scmi_base_implementation_version_get(const struct scmi_protocol_handle *ph)
{
	int ret;
	__le32 *impl_ver;
	struct scmi_xfer *t;
	struct scmi_revision_info *rev = handle->version;
	struct scmi_revision_info *rev = ph->get_priv(ph);

	ret = scmi_xfer_get_init(handle, BASE_DISCOVER_IMPLEMENT_VERSION,
				 SCMI_PROTOCOL_BASE, 0, sizeof(*impl_ver), &t);
	ret = ph->xops->xfer_get_init(ph, BASE_DISCOVER_IMPLEMENT_VERSION,
				      0, sizeof(*impl_ver), &t);
	if (ret)
		return ret;

	ret = scmi_do_xfer(handle, t);
	ret = ph->xops->do_xfer(ph, t);
	if (!ret) {
		impl_ver = t->rx.buf;
		rev->impl_ver = le32_to_cpu(*impl_ver);
	}

	scmi_xfer_put(handle, t);
	ph->xops->xfer_put(ph, t);

	return ret;
}
@@ -155,12 +157,13 @@ scmi_base_implementation_version_get(const struct scmi_handle *handle)
 * scmi_base_implementation_list_get() - gets the list of protocols it is
 *	OSPM is allowed to access
 *
 * @handle: SCMI entity handle
 * @ph: SCMI protocol handle
 * @protocols_imp: pointer to hold the list of protocol identifiers
 *
 * Return: 0 on success, else appropriate SCMI error.
 */
static int scmi_base_implementation_list_get(const struct scmi_handle *handle,
static int
scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
				  u8 *protocols_imp)
{
	u8 *list;
@@ -168,10 +171,10 @@ static int scmi_base_implementation_list_get(const struct scmi_handle *handle,
	struct scmi_xfer *t;
	__le32 *num_skip, *num_ret;
	u32 tot_num_ret = 0, loop_num_ret;
	struct device *dev = handle->dev;
	struct device *dev = ph->dev;

	ret = scmi_xfer_get_init(handle, BASE_DISCOVER_LIST_PROTOCOLS,
				 SCMI_PROTOCOL_BASE, sizeof(*num_skip), 0, &t);
	ret = ph->xops->xfer_get_init(ph, BASE_DISCOVER_LIST_PROTOCOLS,
				      sizeof(*num_skip), 0, &t);
	if (ret)
		return ret;

@@ -183,7 +186,7 @@ static int scmi_base_implementation_list_get(const struct scmi_handle *handle,
		/* Set the number of protocols to be skipped/already read */
		*num_skip = cpu_to_le32(tot_num_ret);

		ret = scmi_do_xfer(handle, t);
		ret = ph->xops->do_xfer(ph, t);
		if (ret)
			break;

@@ -198,10 +201,10 @@ static int scmi_base_implementation_list_get(const struct scmi_handle *handle,

		tot_num_ret += loop_num_ret;

		scmi_reset_rx_to_maxsz(handle, t);
		ph->xops->reset_rx_to_maxsz(ph, t);
	} while (loop_num_ret);

	scmi_xfer_put(handle, t);
	ph->xops->xfer_put(ph, t);

	return ret;
}
@@ -209,7 +212,7 @@ static int scmi_base_implementation_list_get(const struct scmi_handle *handle,
/**
 * scmi_base_discover_agent_get() - discover the name of an agent
 *
 * @handle: SCMI entity handle
 * @ph: SCMI protocol handle
 * @id: Agent identifier
 * @name: Agent identifier ASCII string
 *
@@ -218,63 +221,63 @@ static int scmi_base_implementation_list_get(const struct scmi_handle *handle,
 *
 * Return: 0 on success, else appropriate SCMI error.
 */
static int scmi_base_discover_agent_get(const struct scmi_handle *handle,
static int scmi_base_discover_agent_get(const struct scmi_protocol_handle *ph,
					int id, char *name)
{
	int ret;
	struct scmi_xfer *t;

	ret = scmi_xfer_get_init(handle, BASE_DISCOVER_AGENT,
				 SCMI_PROTOCOL_BASE, sizeof(__le32),
				 SCMI_MAX_STR_SIZE, &t);
	ret = ph->xops->xfer_get_init(ph, BASE_DISCOVER_AGENT,
				      sizeof(__le32), SCMI_MAX_STR_SIZE, &t);
	if (ret)
		return ret;

	put_unaligned_le32(id, t->tx.buf);

	ret = scmi_do_xfer(handle, t);
	ret = ph->xops->do_xfer(ph, t);
	if (!ret)
		strlcpy(name, t->rx.buf, SCMI_MAX_STR_SIZE);

	scmi_xfer_put(handle, t);
	ph->xops->xfer_put(ph, t);

	return ret;
}

static int scmi_base_error_notify(const struct scmi_handle *handle, bool enable)
static int scmi_base_error_notify(const struct scmi_protocol_handle *ph,
				  bool enable)
{
	int ret;
	u32 evt_cntl = enable ? BASE_TP_NOTIFY_ALL : 0;
	struct scmi_xfer *t;
	struct scmi_msg_base_error_notify *cfg;

	ret = scmi_xfer_get_init(handle, BASE_NOTIFY_ERRORS,
				 SCMI_PROTOCOL_BASE, sizeof(*cfg), 0, &t);
	ret = ph->xops->xfer_get_init(ph, BASE_NOTIFY_ERRORS,
				      sizeof(*cfg), 0, &t);
	if (ret)
		return ret;

	cfg = t->tx.buf;
	cfg->event_control = cpu_to_le32(evt_cntl);

	ret = scmi_do_xfer(handle, t);
	ret = ph->xops->do_xfer(ph, t);

	scmi_xfer_put(handle, t);
	ph->xops->xfer_put(ph, t);
	return ret;
}

static int scmi_base_set_notify_enabled(const struct scmi_handle *handle,
static int scmi_base_set_notify_enabled(const struct scmi_protocol_handle *ph,
					u8 evt_id, u32 src_id, bool enable)
{
	int ret;

	ret = scmi_base_error_notify(handle, enable);
	ret = scmi_base_error_notify(ph, enable);
	if (ret)
		pr_debug("FAIL_ENABLED - evt[%X] ret:%d\n", evt_id, ret);

	return ret;
}

static void *scmi_base_fill_custom_report(const struct scmi_handle *handle,
static void *scmi_base_fill_custom_report(const struct scmi_protocol_handle *ph,
					  u8 evt_id, ktime_t timestamp,
					  const void *payld, size_t payld_sz,
					  void *report, u32 *src_id)
@@ -318,17 +321,24 @@ static const struct scmi_event_ops base_event_ops = {
	.fill_custom_report = scmi_base_fill_custom_report,
};

int scmi_base_protocol_init(struct scmi_handle *h)
static const struct scmi_protocol_events base_protocol_events = {
	.queue_sz = 4 * SCMI_PROTO_QUEUE_SZ,
	.ops = &base_event_ops,
	.evts = base_events,
	.num_events = ARRAY_SIZE(base_events),
	.num_sources = SCMI_BASE_NUM_SOURCES,
};

static int scmi_base_protocol_init(const struct scmi_protocol_handle *ph)
{
	int id, ret;
	u8 *prot_imp;
	u32 version;
	char name[SCMI_MAX_STR_SIZE];
	const struct scmi_handle *handle = h;
	struct device *dev = handle->dev;
	struct scmi_revision_info *rev = handle->version;
	struct device *dev = ph->dev;
	struct scmi_revision_info *rev = scmi_revision_area_get(ph);

	ret = scmi_version_get(handle, SCMI_PROTOCOL_BASE, &version);
	ret = ph->xops->version_get(ph, &version);
	if (ret)
		return ret;

@@ -338,13 +348,15 @@ int scmi_base_protocol_init(struct scmi_handle *h)

	rev->major_ver = PROTOCOL_REV_MAJOR(version),
	rev->minor_ver = PROTOCOL_REV_MINOR(version);
	ph->set_priv(ph, rev);

	scmi_base_attributes_get(ph);
	scmi_base_vendor_id_get(ph, false);
	scmi_base_vendor_id_get(ph, true);
	scmi_base_implementation_version_get(ph);
	scmi_base_implementation_list_get(ph, prot_imp);

	scmi_base_attributes_get(handle);
	scmi_base_vendor_id_get(handle, false);
	scmi_base_vendor_id_get(handle, true);
	scmi_base_implementation_version_get(handle);
	scmi_base_implementation_list_get(handle, prot_imp);
	scmi_setup_protocol_implemented(handle, prot_imp);
	scmi_setup_protocol_implemented(ph, prot_imp);

	dev_info(dev, "SCMI Protocol v%d.%d '%s:%s' Firmware version 0x%x\n",
		 rev->major_ver, rev->minor_ver, rev->vendor_id,
@@ -352,16 +364,20 @@ int scmi_base_protocol_init(struct scmi_handle *h)
	dev_dbg(dev, "Found %d protocol(s) %d agent(s)\n", rev->num_protocols,
		rev->num_agents);

	scmi_register_protocol_events(handle, SCMI_PROTOCOL_BASE,
				      (4 * SCMI_PROTO_QUEUE_SZ),
				      &base_event_ops, base_events,
				      ARRAY_SIZE(base_events),
				      SCMI_BASE_NUM_SOURCES);

	for (id = 0; id < rev->num_agents; id++) {
		scmi_base_discover_agent_get(handle, id, name);
		scmi_base_discover_agent_get(ph, id, name);
		dev_dbg(dev, "Agent %d: %s\n", id, name);
	}

	return 0;
}

static const struct scmi_protocol scmi_base = {
	.id = SCMI_PROTOCOL_BASE,
	.owner = NULL,
	.instance_init = &scmi_base_protocol_init,
	.ops = NULL,
	.events = &base_protocol_events,
};

DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(base, scmi_base)
+75 −25
Original line number Diff line number Diff line
@@ -2,7 +2,7 @@
/*
 * System Control and Management Interface (SCMI) Message Protocol bus layer
 *
 * Copyright (C) 2018 ARM Ltd.
 * Copyright (C) 2018-2021 ARM Ltd.
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -51,18 +51,53 @@ static int scmi_dev_match(struct device *dev, struct device_driver *drv)
	return 0;
}

static int scmi_protocol_init(int protocol_id, struct scmi_handle *handle)
static int scmi_match_by_id_table(struct device *dev, void *data)
{
	scmi_prot_init_fn_t fn = idr_find(&scmi_protocols, protocol_id);
	struct scmi_device *sdev = to_scmi_dev(dev);
	struct scmi_device_id *id_table = data;

	if (unlikely(!fn))
		return -EINVAL;
	return fn(handle);
	return sdev->protocol_id == id_table->protocol_id &&
		!strcmp(sdev->name, id_table->name);
}

static int scmi_protocol_dummy_init(struct scmi_handle *handle)
struct scmi_device *scmi_child_dev_find(struct device *parent,
					int prot_id, const char *name)
{
	return 0;
	struct scmi_device_id id_table;
	struct device *dev;

	id_table.protocol_id = prot_id;
	id_table.name = name;

	dev = device_find_child(parent, &id_table, scmi_match_by_id_table);
	if (!dev)
		return NULL;

	return to_scmi_dev(dev);
}

const struct scmi_protocol *scmi_protocol_get(int protocol_id)
{
	const struct scmi_protocol *proto;

	proto = idr_find(&scmi_protocols, protocol_id);
	if (!proto || !try_module_get(proto->owner)) {
		pr_warn("SCMI Protocol 0x%x not found!\n", protocol_id);
		return NULL;
	}

	pr_debug("Found SCMI Protocol 0x%x\n", protocol_id);

	return proto;
}

void scmi_protocol_put(int protocol_id)
{
	const struct scmi_protocol *proto;

	proto = idr_find(&scmi_protocols, protocol_id);
	if (proto)
		module_put(proto->owner);
}

static int scmi_dev_probe(struct device *dev)
@@ -70,7 +105,6 @@ static int scmi_dev_probe(struct device *dev)
	struct scmi_driver *scmi_drv = to_scmi_driver(dev->driver);
	struct scmi_device *scmi_dev = to_scmi_dev(dev);
	const struct scmi_device_id *id;
	int ret;

	id = scmi_dev_match_id(scmi_dev, scmi_drv);
	if (!id)
@@ -79,14 +113,6 @@ static int scmi_dev_probe(struct device *dev)
	if (!scmi_dev->handle)
		return -EPROBE_DEFER;

	ret = scmi_protocol_init(scmi_dev->protocol_id, scmi_dev->handle);
	if (ret)
		return ret;

	/* Skip protocol initialisation for additional devices */
	idr_replace(&scmi_protocols, &scmi_protocol_dummy_init,
		    scmi_dev->protocol_id);

	return scmi_drv->probe(scmi_dev);
}

@@ -113,6 +139,10 @@ int scmi_driver_register(struct scmi_driver *driver, struct module *owner,
{
	int retval;

	retval = scmi_protocol_device_request(driver->id_table);
	if (retval)
		return retval;

	driver->driver.bus = &scmi_bus_type;
	driver->driver.name = driver->name;
	driver->driver.owner = owner;
@@ -129,6 +159,7 @@ EXPORT_SYMBOL_GPL(scmi_driver_register);
void scmi_driver_unregister(struct scmi_driver *driver)
{
	driver_unregister(&driver->driver);
	scmi_protocol_device_unrequest(driver->id_table);
}
EXPORT_SYMBOL_GPL(scmi_driver_unregister);

@@ -194,26 +225,45 @@ void scmi_set_handle(struct scmi_device *scmi_dev)
	scmi_dev->handle = scmi_handle_get(&scmi_dev->dev);
}

int scmi_protocol_register(int protocol_id, scmi_prot_init_fn_t fn)
int scmi_protocol_register(const struct scmi_protocol *proto)
{
	int ret;

	if (!proto) {
		pr_err("invalid protocol\n");
		return -EINVAL;
	}

	if (!proto->instance_init) {
		pr_err("missing init for protocol 0x%x\n", proto->id);
		return -EINVAL;
	}

	spin_lock(&protocol_lock);
	ret = idr_alloc(&scmi_protocols, fn, protocol_id, protocol_id + 1,
			GFP_ATOMIC);
	ret = idr_alloc(&scmi_protocols, (void *)proto,
			proto->id, proto->id + 1, GFP_ATOMIC);
	spin_unlock(&protocol_lock);
	if (ret != protocol_id)
		pr_err("unable to allocate SCMI idr slot, err %d\n", ret);

	if (ret != proto->id) {
		pr_err("unable to allocate SCMI idr slot for 0x%x - err %d\n",
		       proto->id, ret);
		return ret;
	}

	pr_debug("Registered SCMI Protocol 0x%x\n", proto->id);

	return 0;
}
EXPORT_SYMBOL_GPL(scmi_protocol_register);

void scmi_protocol_unregister(int protocol_id)
void scmi_protocol_unregister(const struct scmi_protocol *proto)
{
	spin_lock(&protocol_lock);
	idr_remove(&scmi_protocols, protocol_id);
	idr_remove(&scmi_protocols, proto->id);
	spin_unlock(&protocol_lock);

	pr_debug("Unregistered SCMI Protocol 0x%x\n", proto->id);

	return;
}
EXPORT_SYMBOL_GPL(scmi_protocol_unregister);

Loading