Unverified Commit 21cbc105 authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

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

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

Arm SCMI updates for v6.10

1. Basic support for SCMI v3.2 pincontrol protocol

   SCMI v3.2 introduces pincontrol protocol which is intended for
   controlling pins and their configuration. The pin control protocol
   provides commands to:
   - List the pins, groups of pins, available functions, and their
     association with each other.
   - Set the parameter configuration and multiplexing of the pins or
     groups of pins
   - Optionally request exclusive access to a pin or group of pins.
   - Optionally configure the permissions of an agent to access a pin
     or group of pins.

  These changes adds basic support for the same in the SCMI core layer
  and an implementation of the generic scmi-pinctrl driver with associated
  DT bindings.

2. Framework support for multiple vendors custom protocols

   With the fixed space for vendor protocols, the possibility of having
   multiple vendors implementing distinct SCMI vendor protocols with
   the same overlapping protocol number is very high and with the need
   to support them all in a single kernel image or a module is also high.

   In order to implement the same we assume:
   - vendor protocols has to be tagged at build time with a vendor_id
   - vendor protocols could also optionally be tagged at build time with
     sub_vendor_id and implementation version

  At the initialisation all the built vendor protocols are registered
  with the SCMI core using a key derived from the above tags

3. Logging and tracing improvements

   This includes using dev_err_probe() to bail out from probe, adding
   message dump traces for bad and unexpected replies and widening of
   the tag buffer in trace_scmi_dump_msg to allow diverse tag names

4. Miscellaneous updates or improvements

   This includes adding the accessor function get_max_msg_size() used
   in pinctl protocol, updation of dt-bindings examples for protocol@13
   to promote new bindings and simplification of scmi_devm_notifier_unregister

* tag 'scmi-updates-6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux:
  pinctrl: Implementation of the generic scmi-pinctrl driver
  firmware: arm_scmi: Add basic support for SCMI v3.2 pincontrol protocol
  dt-bindings: firmware: Support SCMI pinctrl protocol
  firmware: arm_scmi: Introduce get_max_msg_size() helper/accessor
  firmware: arm_scmi: Add support for multiple vendors custom protocols
  dt-bindings: firmware: arm,scmi: Update examples for protocol@13
  firmware: arm_scmi: Avoid non-constant printk format strings
  firmware: arm_scmi: Use dev_err_probe to bail out
  firmware: arm_scmi: Simplify scmi_devm_notifier_unregister
  firmware: arm_scmi: Add message dump traces for bad and unexpected replies
  firmware: arm_scmi: Add helper to trace bad messages
  include: trace: Widen the tag buffer in trace_scmi_dump_msg
  firmware: arm_scmi: Log the perf domain names in the error paths

Link: https://lore.kernel.org/r/20240426105031.1526987-1-sudeep.holla@arm.com


Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents 40b561e5 eb524cb6
Loading
Loading
Loading
Loading
+52 −2
Original line number Diff line number Diff line
@@ -247,6 +247,37 @@ properties:
      reg:
        const: 0x18

  protocol@19:
    type: object
    allOf:
      - $ref: '#/$defs/protocol-node'
      - $ref: /schemas/pinctrl/pinctrl.yaml

    unevaluatedProperties: false

    properties:
      reg:
        const: 0x19

    patternProperties:
      '-pins$':
        type: object
        allOf:
          - $ref: /schemas/pinctrl/pincfg-node.yaml#
          - $ref: /schemas/pinctrl/pinmux-node.yaml#
        unevaluatedProperties: false

        description:
          A pin multiplexing sub-node describes how to configure a
          set of pins in some desired function.
          A single sub-node may define several pin configurations.
          This sub-node is using the default pinctrl bindings to configure
          pin multiplexing and using SCMI protocol to apply a specified
          configuration.

    required:
      - reg

additionalProperties: false

$defs:
@@ -355,7 +386,7 @@ examples:

            scmi_dvfs: protocol@13 {
                reg = <0x13>;
                #clock-cells = <1>;
                #power-domain-cells = <1>;

                mboxes = <&mhuB 1 0>,
                         <&mhuB 1 1>;
@@ -401,6 +432,25 @@ examples:
            scmi_powercap: protocol@18 {
                reg = <0x18>;
            };

            scmi_pinctrl: protocol@19 {
                reg = <0x19>;

                i2c2-pins {
                    groups = "g_i2c2_a", "g_i2c2_b";
                    function = "f_i2c2";
                };

                mdio-pins {
                    groups = "g_avb_mdio";
                    drive-strength = <24>;
                };

                keys_pins: keys-pins {
                    pins = "gpio_5_17", "gpio_5_20", "gpio_5_22", "gpio_2_1";
                    bias-pull-up;
                };
            };
        };
    };

@@ -468,7 +518,7 @@ examples:
                reg = <0x13>;
                linaro,optee-channel-id = <1>;
                shmem = <&cpu_optee_lpri0>;
                #clock-cells = <1>;
                #power-domain-cells = <1>;
            };

            scmi_clk0: protocol@14 {
+1 −0
Original line number Diff line number Diff line
@@ -21522,6 +21522,7 @@ F: drivers/cpufreq/sc[mp]i-cpufreq.c
F:	drivers/firmware/arm_scmi/
F:	drivers/firmware/arm_scpi.c
F:	drivers/hwmon/scmi-hwmon.c
F:	drivers/pinctrl/pinctrl-scmi.c
F:	drivers/pmdomain/arm/
F:	drivers/powercap/arm_scmi_powercap.c
F:	drivers/regulator/scmi-regulator.c
+2 −1
Original line number Diff line number Diff line
@@ -10,7 +10,8 @@ scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_SMC) += smc.o
scmi-transport-$(CONFIG_ARM_SCMI_HAVE_MSG) += msg.o
scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_VIRTIO) += virtio.o
scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_OPTEE) += optee.o
scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o voltage.o powercap.o
scmi-protocols-y := base.o clock.o perf.o power.o reset.o sensors.o system.o voltage.o powercap.o
scmi-protocols-y += pinctrl.o
scmi-module-objs := $(scmi-driver-y) $(scmi-protocols-y) $(scmi-transport-y)

obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-core.o
+11 −0
Original line number Diff line number Diff line
@@ -301,6 +301,17 @@ extern const struct scmi_desc scmi_optee_desc;

void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr, void *priv);

enum scmi_bad_msg {
	MSG_UNEXPECTED = -1,
	MSG_INVALID = -2,
	MSG_UNKNOWN = -3,
	MSG_NOMEM = -4,
	MSG_MBOX_SPURIOUS = -5,
};

void scmi_bad_message_trace(struct scmi_chan_info *cinfo, u32 msg_hdr,
			    enum scmi_bad_msg err);

/* shmem related declarations */
struct scmi_shared_mem;

+239 −30
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@
#include <linux/processor.h>
#include <linux/refcount.h>
#include <linux/slab.h>
#include <linux/xarray.h>

#include "common.h"
#include "notify.h"
@@ -44,8 +45,7 @@

static DEFINE_IDA(scmi_id);

static DEFINE_IDR(scmi_protocols);
static DEFINE_SPINLOCK(protocol_lock);
static DEFINE_XARRAY(scmi_protocols);

/* List of all SCMI devices active in system */
static LIST_HEAD(scmi_list);
@@ -194,11 +194,94 @@ struct scmi_info {
#define bus_nb_to_scmi_info(nb)	container_of(nb, struct scmi_info, bus_nb)
#define req_nb_to_scmi_info(nb)	container_of(nb, struct scmi_info, dev_req_nb)

static const struct scmi_protocol *scmi_protocol_get(int protocol_id)
static unsigned long
scmi_vendor_protocol_signature(unsigned int protocol_id, char *vendor_id,
			       char *sub_vendor_id, u32 impl_ver)
{
	const struct scmi_protocol *proto;
	char *signature, *p;
	unsigned long hash = 0;

	/* vendor_id/sub_vendor_id guaranteed <= SCMI_SHORT_NAME_MAX_SIZE */
	signature = kasprintf(GFP_KERNEL, "%02X|%s|%s|0x%08X", protocol_id,
			      vendor_id ?: "", sub_vendor_id ?: "", impl_ver);
	if (!signature)
		return 0;

	p = signature;
	while (*p)
		hash = partial_name_hash(tolower(*p++), hash);
	hash = end_name_hash(hash);

	kfree(signature);

	return hash;
}

static unsigned long
scmi_protocol_key_calculate(int protocol_id, char *vendor_id,
			    char *sub_vendor_id, u32 impl_ver)
{
	if (protocol_id < SCMI_PROTOCOL_VENDOR_BASE)
		return protocol_id;
	else
		return scmi_vendor_protocol_signature(protocol_id, vendor_id,
						      sub_vendor_id, impl_ver);
}

static const struct scmi_protocol *
__scmi_vendor_protocol_lookup(int protocol_id, char *vendor_id,
			      char *sub_vendor_id, u32 impl_ver)
{
	unsigned long key;
	struct scmi_protocol *proto = NULL;

	key = scmi_protocol_key_calculate(protocol_id, vendor_id,
					  sub_vendor_id, impl_ver);
	if (key)
		proto = xa_load(&scmi_protocols, key);

	return proto;
}

static const struct scmi_protocol *
scmi_vendor_protocol_lookup(int protocol_id, char *vendor_id,
			    char *sub_vendor_id, u32 impl_ver)
{
	const struct scmi_protocol *proto = NULL;

	/* Searching for closest match ...*/
	proto = __scmi_vendor_protocol_lookup(protocol_id, vendor_id,
					      sub_vendor_id, impl_ver);
	if (proto)
		return proto;

	/* Any match just on vendor/sub_vendor ? */
	if (impl_ver) {
		proto = __scmi_vendor_protocol_lookup(protocol_id, vendor_id,
						      sub_vendor_id, 0);
		if (proto)
			return proto;
	}

	/* Any match just on the vendor ? */
	if (sub_vendor_id)
		proto = __scmi_vendor_protocol_lookup(protocol_id, vendor_id,
						      NULL, 0);
	return proto;
}

	proto = idr_find(&scmi_protocols, protocol_id);
static const struct scmi_protocol *
scmi_protocol_get(int protocol_id, struct scmi_revision_info *version)
{
	const struct scmi_protocol *proto = NULL;

	if (protocol_id < SCMI_PROTOCOL_VENDOR_BASE)
		proto = xa_load(&scmi_protocols, protocol_id);
	else
		proto = scmi_vendor_protocol_lookup(protocol_id,
						    version->vendor_id,
						    version->sub_vendor_id,
						    version->impl_ver);
	if (!proto || !try_module_get(proto->owner)) {
		pr_warn("SCMI Protocol 0x%x not found!\n", protocol_id);
		return NULL;
@@ -206,21 +289,46 @@ static const struct scmi_protocol *scmi_protocol_get(int protocol_id)

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

	if (protocol_id >= SCMI_PROTOCOL_VENDOR_BASE)
		pr_info("Loaded SCMI Vendor Protocol 0x%x - %s %s %X\n",
			protocol_id, proto->vendor_id ?: "",
			proto->sub_vendor_id ?: "", proto->impl_ver);

	return proto;
}

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

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

static int scmi_vendor_protocol_check(const struct scmi_protocol *proto)
{
	if (!proto->vendor_id) {
		pr_err("missing vendor_id for protocol 0x%x\n", proto->id);
		return -EINVAL;
	}

	if (strlen(proto->vendor_id) >= SCMI_SHORT_NAME_MAX_SIZE) {
		pr_err("malformed vendor_id for protocol 0x%x\n", proto->id);
		return -EINVAL;
	}

	if (proto->sub_vendor_id &&
	    strlen(proto->sub_vendor_id) >= SCMI_SHORT_NAME_MAX_SIZE) {
		pr_err("malformed sub_vendor_id for protocol 0x%x\n",
		       proto->id);
		return -EINVAL;
	}

	return 0;
}

int scmi_protocol_register(const struct scmi_protocol *proto)
{
	int ret;
	unsigned long key;

	if (!proto) {
		pr_err("invalid protocol\n");
@@ -232,12 +340,23 @@ int scmi_protocol_register(const struct scmi_protocol *proto)
		return -EINVAL;
	}

	spin_lock(&protocol_lock);
	ret = idr_alloc(&scmi_protocols, (void *)proto,
			proto->id, proto->id + 1, GFP_ATOMIC);
	spin_unlock(&protocol_lock);
	if (ret != proto->id) {
		pr_err("unable to allocate SCMI idr slot for 0x%x - err %d\n",
	if (proto->id >= SCMI_PROTOCOL_VENDOR_BASE &&
	    scmi_vendor_protocol_check(proto))
		return -EINVAL;

	/*
	 * Calculate a protocol key to register this protocol with the core;
	 * key value 0 is considered invalid.
	 */
	key = scmi_protocol_key_calculate(proto->id, proto->vendor_id,
					  proto->sub_vendor_id,
					  proto->impl_ver);
	if (!key)
		return -EINVAL;

	ret = xa_insert(&scmi_protocols, key, (void *)proto, GFP_KERNEL);
	if (ret) {
		pr_err("unable to allocate SCMI protocol slot for 0x%x - err %d\n",
		       proto->id, ret);
		return ret;
	}
@@ -250,9 +369,15 @@ EXPORT_SYMBOL_GPL(scmi_protocol_register);

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

	key = scmi_protocol_key_calculate(proto->id, proto->vendor_id,
					  proto->sub_vendor_id,
					  proto->impl_ver);
	if (!key)
		return;

	xa_erase(&scmi_protocols, key);

	pr_debug("Unregistered SCMI Protocol 0x%x\n", proto->id);
}
@@ -696,6 +821,45 @@ scmi_xfer_lookup_unlocked(struct scmi_xfers_info *minfo, u16 xfer_id)
	return xfer ?: ERR_PTR(-EINVAL);
}

/**
 * scmi_bad_message_trace  - A helper to trace weird messages
 *
 * @cinfo: A reference to the channel descriptor on which the message was
 *	   received
 * @msg_hdr: Message header to track
 * @err: A specific error code used as a status value in traces.
 *
 * This helper can be used to trace any kind of weird, incomplete, unexpected,
 * timed-out message that arrives and as such, can be traced only referring to
 * the header content, since the payload is missing/unreliable.
 */
void scmi_bad_message_trace(struct scmi_chan_info *cinfo, u32 msg_hdr,
			    enum scmi_bad_msg err)
{
	char *tag;
	struct scmi_info *info = handle_to_scmi_info(cinfo->handle);

	switch (MSG_XTRACT_TYPE(msg_hdr)) {
	case MSG_TYPE_COMMAND:
		tag = "!RESP";
		break;
	case MSG_TYPE_DELAYED_RESP:
		tag = "!DLYD";
		break;
	case MSG_TYPE_NOTIFICATION:
		tag = "!NOTI";
		break;
	default:
		tag = "!UNKN";
		break;
	}

	trace_scmi_msg_dump(info->id, cinfo->id,
			    MSG_XTRACT_PROT_ID(msg_hdr),
			    MSG_XTRACT_ID(msg_hdr), tag,
			    MSG_XTRACT_TOKEN(msg_hdr), err, NULL, 0);
}

/**
 * scmi_msg_response_validate  - Validate message type against state of related
 * xfer
@@ -822,6 +986,9 @@ scmi_xfer_command_acquire(struct scmi_chan_info *cinfo, u32 msg_hdr)
			"Message for %d type %d is not expected!\n",
			xfer_id, msg_type);
		spin_unlock_irqrestore(&minfo->xfer_lock, flags);

		scmi_bad_message_trace(cinfo, msg_hdr, MSG_UNEXPECTED);

		return xfer;
	}
	refcount_inc(&xfer->users);
@@ -846,6 +1013,9 @@ scmi_xfer_command_acquire(struct scmi_chan_info *cinfo, u32 msg_hdr)
		dev_err(cinfo->dev,
			"Invalid message type:%d for %d - HDR:0x%X  state:%d\n",
			msg_type, xfer_id, msg_hdr, xfer->state);

		scmi_bad_message_trace(cinfo, msg_hdr, MSG_INVALID);

		/* On error the refcount incremented above has to be dropped */
		__scmi_xfer_put(minfo, xfer);
		xfer = ERR_PTR(-EINVAL);
@@ -882,6 +1052,9 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo,
	if (IS_ERR(xfer)) {
		dev_err(dev, "failed to get free message slot (%ld)\n",
			PTR_ERR(xfer));

		scmi_bad_message_trace(cinfo, msg_hdr, MSG_NOMEM);

		scmi_clear_channel(info, cinfo);
		return;
	}
@@ -1001,6 +1174,7 @@ void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr, void *priv)
		break;
	default:
		WARN_ONCE(1, "received unknown msg_type:%d\n", msg_type);
		scmi_bad_message_trace(cinfo, msg_hdr, MSG_UNKNOWN);
		break;
	}
}
@@ -1488,6 +1662,20 @@ static int scmi_common_extended_name_get(const struct scmi_protocol_handle *ph,
	return ret;
}

/**
 * scmi_common_get_max_msg_size  - Get maximum message size
 * @ph: A protocol handle reference.
 *
 * Return: Maximum message size for the current protocol.
 */
static int scmi_common_get_max_msg_size(const struct scmi_protocol_handle *ph)
{
	const struct scmi_protocol_instance *pi = ph_to_pi(ph);
	struct scmi_info *info = handle_to_scmi_info(pi->handle);

	return info->desc->max_msg_size;
}

/**
 * struct scmi_iterator  - Iterator descriptor
 * @msg: A reference to the message TX buffer; filled by @prepare_message with
@@ -1799,6 +1987,7 @@ static int scmi_protocol_msg_check(const struct scmi_protocol_handle *ph,

static const struct scmi_proto_helpers_ops helpers_ops = {
	.extended_name_get = scmi_common_extended_name_get,
	.get_max_msg_size = scmi_common_get_max_msg_size,
	.iter_response_init = scmi_iterator_init,
	.iter_response_run = scmi_iterator_run,
	.protocol_msg_check = scmi_protocol_msg_check,
@@ -1891,7 +2080,7 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
	/* Protocol specific devres group */
	gid = devres_open_group(handle->dev, NULL, GFP_KERNEL);
	if (!gid) {
		scmi_protocol_put(proto->id);
		scmi_protocol_put(proto);
		goto out;
	}

@@ -1955,7 +2144,7 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,

clean:
	/* Take care to put the protocol module's owner before releasing all */
	scmi_protocol_put(proto->id);
	scmi_protocol_put(proto);
	devres_release_group(handle->dev, gid);
out:
	return ERR_PTR(ret);
@@ -1989,7 +2178,7 @@ scmi_get_protocol_instance(const struct scmi_handle *handle, u8 protocol_id)
		const struct scmi_protocol *proto;

		/* Fails if protocol not registered on bus */
		proto = scmi_protocol_get(protocol_id);
		proto = scmi_protocol_get(protocol_id, &info->version);
		if (proto)
			pi = scmi_alloc_init_protocol_instance(info, proto);
		else
@@ -2044,7 +2233,7 @@ void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id)

		idr_remove(&info->protocols, protocol_id);

		scmi_protocol_put(protocol_id);
		scmi_protocol_put(pi->proto);

		devres_release_group(handle->dev, gid);
		dev_dbg(handle->dev, "De-Initialized protocol: 0x%X\n",
@@ -2491,6 +2680,10 @@ scmi_txrx_setup(struct scmi_info *info, struct device_node *of_node,
			ret = 0;
	}

	if (ret)
		dev_err(info->dev,
			"failed to setup channel for protocol:0x%X\n", prot_id);

	return ret;
}

@@ -2760,6 +2953,7 @@ static int scmi_debugfs_raw_mode_setup(struct scmi_info *info)
static int scmi_probe(struct platform_device *pdev)
{
	int ret;
	char *err_str = "probe failure\n";
	struct scmi_handle *handle;
	const struct scmi_desc *desc;
	struct scmi_info *info;
@@ -2810,27 +3004,37 @@ static int scmi_probe(struct platform_device *pdev)

	if (desc->ops->link_supplier) {
		ret = desc->ops->link_supplier(dev);
		if (ret)
		if (ret) {
			err_str = "transport not ready\n";
			goto clear_ida;
		}
	}

	/* Setup all channels described in the DT at first */
	ret = scmi_channels_setup(info);
	if (ret)
	if (ret) {
		err_str = "failed to setup channels\n";
		goto clear_ida;
	}

	ret = bus_register_notifier(&scmi_bus_type, &info->bus_nb);
	if (ret)
	if (ret) {
		err_str = "failed to register bus notifier\n";
		goto clear_txrx_setup;
	}

	ret = blocking_notifier_chain_register(&scmi_requested_devices_nh,
					       &info->dev_req_nb);
	if (ret)
	if (ret) {
		err_str = "failed to register device notifier\n";
		goto clear_bus_notifier;
	}

	ret = scmi_xfer_info_init(info);
	if (ret)
	if (ret) {
		err_str = "failed to init xfers pool\n";
		goto clear_dev_req_notifier;
	}

	if (scmi_top_dentry) {
		info->dbg = scmi_debugfs_common_setup(info);
@@ -2867,9 +3071,11 @@ static int scmi_probe(struct platform_device *pdev)
	 */
	ret = scmi_protocol_acquire(handle, SCMI_PROTOCOL_BASE);
	if (ret) {
		dev_err(dev, "unable to communicate with SCMI\n");
		if (coex)
		err_str = "unable to communicate with SCMI\n";
		if (coex) {
			dev_err(dev, "%s", err_str);
			return 0;
		}
		goto notification_exit;
	}

@@ -2923,7 +3129,8 @@ static int scmi_probe(struct platform_device *pdev)
	scmi_cleanup_txrx_channels(info);
clear_ida:
	ida_free(&scmi_id, info->id);
	return ret;

	return dev_err_probe(dev, ret, "%s", err_str);
}

static void scmi_remove(struct platform_device *pdev)
@@ -3127,6 +3334,7 @@ static int __init scmi_driver_init(void)
	scmi_voltage_register();
	scmi_system_register();
	scmi_powercap_register();
	scmi_pinctrl_register();

	return platform_driver_register(&scmi_driver);
}
@@ -3144,6 +3352,7 @@ static void __exit scmi_driver_exit(void)
	scmi_voltage_unregister();
	scmi_system_unregister();
	scmi_powercap_unregister();
	scmi_pinctrl_unregister();

	scmi_transports_exit();

Loading