diff mbox series

[2/5] firmware: arm_scmi: imx: Add i.MX95 CPU Protocol

Message ID 20250121-imx-lmm-cpu-v1-2-0eab7e073e4e@nxp.com (mailing list archive)
State New
Headers show
Series firmware: scmi/imx: Add i.MX95 LMM/CPU Protocol | expand

Commit Message

Peng Fan (OSS) Jan. 21, 2025, 3:08 p.m. UTC
From: Peng Fan <peng.fan@nxp.com>

This protocol allows an agent to start, stop a CPU or set reset vector. It
is used to manage auxiliary CPUs in an LM (e.g. additional cores in an AP
cluster).

Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 drivers/firmware/arm_scmi/vendors/imx/Kconfig      |  13 +-
 drivers/firmware/arm_scmi/vendors/imx/Makefile     |   1 +
 drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c | 283 +++++++++++++++++++++
 include/linux/scmi_imx_protocol.h                  |  10 +
 4 files changed, 306 insertions(+), 1 deletion(-)

Comments

Dan Carpenter Jan. 22, 2025, 8:48 a.m. UTC | #1
On Tue, Jan 21, 2025 at 11:08:12PM +0800, Peng Fan (OSS) wrote:
> From: Peng Fan <peng.fan@nxp.com>
> 
> This protocol allows an agent to start, stop a CPU or set reset vector. It
> is used to manage auxiliary CPUs in an LM (e.g. additional cores in an AP
> cluster).
> 
> Signed-off-by: Peng Fan <peng.fan@nxp.com>
> ---
>  drivers/firmware/arm_scmi/vendors/imx/Kconfig      |  13 +-
>  drivers/firmware/arm_scmi/vendors/imx/Makefile     |   1 +
>  drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c | 283 +++++++++++++++++++++
>  include/linux/scmi_imx_protocol.h                  |  10 +
>  4 files changed, 306 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/firmware/arm_scmi/vendors/imx/Kconfig b/drivers/firmware/arm_scmi/vendors/imx/Kconfig
> index 1a936fc87d2350e2a21bccd45dfbeebfa3b90286..9070522510e4d3f3d7276a7581f8676006d20f90 100644
> --- a/drivers/firmware/arm_scmi/vendors/imx/Kconfig
> +++ b/drivers/firmware/arm_scmi/vendors/imx/Kconfig
> @@ -12,6 +12,17 @@ config IMX_SCMI_BBM_EXT
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called imx-sm-bbm.
>  
> +config IMX_SCMI_CPU_EXT
> +	tristate "i.MX SCMI CPU EXTENSION"
> +	depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
> +	default y if ARCH_MXC
> +	help
> +	  This enables i.MX System CPU Protocol to manage cpu
> +	  start, stop and etc.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called imx-sm-cpu.
> +
>  config IMX_SCMI_LMM_EXT
>  	tristate "i.MX SCMI LMM EXTENSION"
>  	depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
> @@ -21,7 +32,7 @@ config IMX_SCMI_LMM_EXT
>  	  manage Logical Machines boot, shutdown and etc.
>  
>  	  To compile this driver as a module, choose M here: the
> -	  module will be called imx-sm-lmm.
> +	  module will be called imx-sm-cpu.
>  

It's supposed to be called imx-sm-lmm.

>  config IMX_SCMI_MISC_EXT
>  	tristate "i.MX SCMI MISC EXTENSION"
> diff --git a/drivers/firmware/arm_scmi/vendors/imx/Makefile b/drivers/firmware/arm_scmi/vendors/imx/Makefile
> index f39a99ccaf9af757475e8b112d224669444d7ddc..e3a5ea46345c89da1afae25e55698044672b7c28 100644
> --- a/drivers/firmware/arm_scmi/vendors/imx/Makefile
> +++ b/drivers/firmware/arm_scmi/vendors/imx/Makefile
> @@ -1,4 +1,5 @@
>  # SPDX-License-Identifier: GPL-2.0-only
>  obj-$(CONFIG_IMX_SCMI_BBM_EXT) += imx-sm-bbm.o
> +obj-$(CONFIG_IMX_SCMI_CPU_EXT) += imx-sm-cpu.o
>  obj-$(CONFIG_IMX_SCMI_LMM_EXT) += imx-sm-lmm.o
>  obj-$(CONFIG_IMX_SCMI_MISC_EXT) += imx-sm-misc.o
> diff --git a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..e3f294c2cb69a5b5a916d55984f4a63539937d02
> --- /dev/null
> +++ b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c
> @@ -0,0 +1,283 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * System control and Management Interface (SCMI) NXP CPU Protocol
> + *
> + * Copyright 2025 NXP
> + */
> +
> +#include <linux/bits.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/scmi_protocol.h>
> +#include <linux/scmi_imx_protocol.h>
> +
> +#include "../../protocols.h"
> +#include "../../notify.h"
> +
> +#define SCMI_PROTOCOL_SUPPORTED_VERSION		0x10000
> +
> +enum scmi_imx_cpu_protocol_cmd {
> +	SCMI_IMX_CPU_ATTRIBUTES	= 0x3,
> +	SCMI_IMX_CPU_START = 0x4,
> +	SCMI_IMX_CPU_STOP = 0x5,
> +	SCMI_IMX_CPU_RESET_VECTOR_SET = 0x6,
> +	SCMI_IMX_CPU_INFO_GET = 0xC,
> +};
> +
> +struct scmi_imx_cpu_info {
> +	u32 nr_cpu;
> +};
> +
> +#define SCMI_IMX_CPU_PROTO_ATTR_NUM_CPUS(x)  ((x) & 0xFFFF)
> +struct scmi_msg_imx_cpu_protocol_attributes {
> +	__le32 attributes;
> +};
> +
> +struct scmi_msg_imx_cpu_attributes_out {
> +	__le32 attributes;
> +#define	CPU_MAX_NAME	16
> +	u8 name[CPU_MAX_NAME];

char is always unsigned in the kernel these days but strings should
still always be char.  Same thing in patch 1, there were a couple u8
names.

> +};
> +
> +struct scmi_imx_cpu_reset_vector_set_in {
> +	__le32 cpuid;
> +#define	CPU_VEC_FLAGS_RESUME	BIT(31)
> +#define	CPU_VEC_FLAGS_START	BIT(30)
> +#define	CPU_VEC_FLAGS_BOOT	BIT(29)
> +	__le32 flags;
> +	__le32 resetvectorlow;
> +	__le32 resetvectorhigh;
> +};
> +
> +struct scmi_imx_cpu_info_get_out {
> +#define	CPU_RUN_MODE_START	0
> +#define	CPU_RUN_MODE_HOLD	1
> +#define	CPU_RUN_MODE_STOP	2
> +#define	CPU_RUN_MODE_SLEEP	3
> +	__le32 runmode;
> +	__le32 sleepmode;
> +	__le32 resetvectorlow;
> +	__le32 resetvectorhigh;
> +};
> +
> +static int scmi_imx_cpu_validate_cpuid(const struct scmi_protocol_handle *ph,
> +				       u32 cpuid)
> +{
> +	struct scmi_imx_cpu_info *info = ph->get_priv(ph);
> +
> +	if (cpuid >= info->nr_cpu)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static int scmi_imx_cpu_start(const struct scmi_protocol_handle *ph, u32 cpuid)
> +{
> +	struct scmi_xfer *t;
> +	int ret;
> +
> +	ret = scmi_imx_cpu_validate_cpuid(ph, cpuid);
> +	if (ret)
> +		return ret;
> +
> +	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_CPU_START, sizeof(u32),
> +				      0, &t);
> +	if (ret)
> +		return ret;
> +
> +	put_unaligned_le32(cpuid, t->tx.buf);
> +	ret = ph->xops->do_xfer(ph, t);
> +
> +	ph->xops->xfer_put(ph, t);
> +
> +	return ret;
> +}
> +
> +static int scmi_imx_cpu_stop(const struct scmi_protocol_handle *ph, u32 cpuid)
> +{
> +	struct scmi_xfer *t;
> +	int ret;
> +
> +	ret = scmi_imx_cpu_validate_cpuid(ph, cpuid);
> +	if (ret)
> +		return ret;
> +
> +	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_CPU_STOP, sizeof(u32),
> +				      0, &t);
> +	if (ret)
> +		return ret;
> +
> +	put_unaligned_le32(cpuid, t->tx.buf);
> +	ret = ph->xops->do_xfer(ph, t);
> +
> +	ph->xops->xfer_put(ph, t);
> +
> +	return ret;
> +}
> +
> +static int scmi_imx_cpu_reset_vector_set(const struct scmi_protocol_handle *ph,
> +					 u32 cpuid, u64 vector, bool start,
> +					 bool boot, bool resume)
> +{
> +	struct scmi_imx_cpu_reset_vector_set_in *in;
> +	struct scmi_xfer *t;
> +	int ret;
> +
> +	ret = scmi_imx_cpu_validate_cpuid(ph, cpuid);
> +	if (ret)
> +		return ret;
> +
> +	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_CPU_RESET_VECTOR_SET, sizeof(*in),
> +				      0, &t);
> +	if (ret)
> +		return ret;
> +
> +	in = t->tx.buf;
> +	in->cpuid = cpu_to_le32(cpuid);
> +	in->flags = start ? CPU_VEC_FLAGS_START : 0;
> +	in->flags |= boot ? CPU_VEC_FLAGS_BOOT : 0;
> +	in->flags |= resume ? CPU_VEC_FLAGS_BOOT : 0;

s/CPU_VEC_FLAGS_BOOT/CPU_VEC_FLAGS_RESUME/

> +	in->resetvectorlow = cpu_to_le32(lower_32_bits(vector));
> +	in->resetvectorhigh = cpu_to_le32(upper_32_bits(vector));
> +	ret = ph->xops->do_xfer(ph, t);
> +
> +	ph->xops->xfer_put(ph, t);
> +
> +	return ret;
> +}
> +
> +static int scmi_imx_cpu_started(const struct scmi_protocol_handle *ph, u32 cpuid,
> +				bool *started)
> +{
> +	struct scmi_imx_cpu_info_get_out *out;
> +	struct scmi_xfer *t;
> +	int ret;
> +
> +	*started = false;
> +	ret = scmi_imx_cpu_validate_cpuid(ph, cpuid);
> +	if (ret)
> +		return ret;
> +
> +	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_CPU_INFO_GET, sizeof(u32),
> +				      0, &t);
> +	if (ret)
> +		return ret;
> +
> +	put_unaligned_le32(cpuid, t->tx.buf);
> +	ret = ph->xops->do_xfer(ph, t);
> +	if (!ret) {
> +		out = t->rx.buf;
> +		if ((out->runmode == CPU_RUN_MODE_START) ||
> +		    (out->runmode == CPU_RUN_MODE_SLEEP))
> +			*started = true;
> +	}
> +
> +	ph->xops->xfer_put(ph, t);
> +
> +	return ret;
> +}
> +
> +static const struct scmi_imx_cpu_proto_ops scmi_imx_cpu_proto_ops = {
> +	.cpu_reset_vector_set = scmi_imx_cpu_reset_vector_set,
> +	.cpu_start = scmi_imx_cpu_start,
> +	.cpu_started = scmi_imx_cpu_started,
> +	.cpu_stop = scmi_imx_cpu_stop,
> +};
> +
> +static int scmi_imx_cpu_protocol_attributes_get(const struct scmi_protocol_handle *ph,
> +						struct scmi_imx_cpu_info *info)
> +{
> +	struct scmi_msg_imx_cpu_protocol_attributes *attr;
> +	struct scmi_xfer *t;
> +	int ret;
> +
> +	ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0,
> +				      sizeof(*attr), &t);
> +	if (ret)
> +		return ret;
> +
> +	attr = t->rx.buf;
> +
> +	ret = ph->xops->do_xfer(ph, t);
> +	if (!ret) {
> +		info->nr_cpu = SCMI_IMX_CPU_PROTO_ATTR_NUM_CPUS(attr->attributes);
> +		dev_info(ph->dev, "i.MX SM CPU: %d cpus\n",
> +			 info->nr_cpu);

This can fit in 80 characters:

		dev_info(ph->dev, "i.MX SM CPU: %d cpus\n", info->nr_cpu);

> +	}
> +
> +	ph->xops->xfer_put(ph, t);
> +
> +	return ret;
> +}
> +
> +static int scmi_imx_cpu_attributes_get(const struct scmi_protocol_handle *ph,
> +				       u32 cpuid, struct scmi_imx_cpu_info *info)

No need to pass the "info" pointer.

> +{
> +	struct scmi_msg_imx_cpu_attributes_out *out;
> +	struct scmi_xfer *t;
> +	int ret;
> +
> +	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_CPU_ATTRIBUTES, sizeof(u32), 0, &t);
> +	if (ret)
> +		return ret;
> +
> +	put_unaligned_le32(cpuid, t->tx.buf);
> +	ret = ph->xops->do_xfer(ph, t);
> +	if (!ret) {
> +		out = t->rx.buf;
> +		dev_info(ph->dev, "i.MX CPU: name: %s\n", out->name);
> +	} else {
> +		dev_err(ph->dev, "i.MX cpu: Failed to get info of cpu(%u)\n", cpuid);
> +	}
> +
> +	ph->xops->xfer_put(ph, t);
> +
> +	return ret;
> +}
> +
> +

Multiple blank lines (checkpatch.pl --strict).

> +static int scmi_imx_cpu_protocol_init(const struct scmi_protocol_handle *ph)
> +{

regards,
dan carpenter
Cristian Marussi Jan. 22, 2025, 12:22 p.m. UTC | #2
On Wed, Jan 22, 2025 at 11:48:52AM +0300, Dan Carpenter wrote:
> On Tue, Jan 21, 2025 at 11:08:12PM +0800, Peng Fan (OSS) wrote:
> > From: Peng Fan <peng.fan@nxp.com>
> > 
> > This protocol allows an agent to start, stop a CPU or set reset vector. It
> > is used to manage auxiliary CPUs in an LM (e.g. additional cores in an AP
> > cluster).
> > 

Hi,

just a quick one down below.

> > Signed-off-by: Peng Fan <peng.fan@nxp.com>
> > ---
> >  drivers/firmware/arm_scmi/vendors/imx/Kconfig      |  13 +-
> >  drivers/firmware/arm_scmi/vendors/imx/Makefile     |   1 +
> >  drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c | 283 +++++++++++++++++++++
> >  include/linux/scmi_imx_protocol.h                  |  10 +
> >  4 files changed, 306 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/firmware/arm_scmi/vendors/imx/Kconfig b/drivers/firmware/arm_scmi/vendors/imx/Kconfig
> > index 1a936fc87d2350e2a21bccd45dfbeebfa3b90286..9070522510e4d3f3d7276a7581f8676006d20f90 100644
> > --- a/drivers/firmware/arm_scmi/vendors/imx/Kconfig
> > +++ b/drivers/firmware/arm_scmi/vendors/imx/Kconfig
> > @@ -12,6 +12,17 @@ config IMX_SCMI_BBM_EXT
> >  	  To compile this driver as a module, choose M here: the
> >  	  module will be called imx-sm-bbm.
> >  
> > +config IMX_SCMI_CPU_EXT
> > +	tristate "i.MX SCMI CPU EXTENSION"
> > +	depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
> > +	default y if ARCH_MXC
> > +	help
> > +	  This enables i.MX System CPU Protocol to manage cpu
> > +	  start, stop and etc.
> > +
> > +	  To compile this driver as a module, choose M here: the
> > +	  module will be called imx-sm-cpu.
> > +
> >  config IMX_SCMI_LMM_EXT
> >  	tristate "i.MX SCMI LMM EXTENSION"
> >  	depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
> > @@ -21,7 +32,7 @@ config IMX_SCMI_LMM_EXT
> >  	  manage Logical Machines boot, shutdown and etc.
> >  
> >  	  To compile this driver as a module, choose M here: the
> > -	  module will be called imx-sm-lmm.
> > +	  module will be called imx-sm-cpu.
> >  
> 
> It's supposed to be called imx-sm-lmm.
> 
> >  config IMX_SCMI_MISC_EXT
> >  	tristate "i.MX SCMI MISC EXTENSION"
> > diff --git a/drivers/firmware/arm_scmi/vendors/imx/Makefile b/drivers/firmware/arm_scmi/vendors/imx/Makefile
> > index f39a99ccaf9af757475e8b112d224669444d7ddc..e3a5ea46345c89da1afae25e55698044672b7c28 100644
> > --- a/drivers/firmware/arm_scmi/vendors/imx/Makefile
> > +++ b/drivers/firmware/arm_scmi/vendors/imx/Makefile
> > @@ -1,4 +1,5 @@
> >  # SPDX-License-Identifier: GPL-2.0-only
> >  obj-$(CONFIG_IMX_SCMI_BBM_EXT) += imx-sm-bbm.o
> > +obj-$(CONFIG_IMX_SCMI_CPU_EXT) += imx-sm-cpu.o
> >  obj-$(CONFIG_IMX_SCMI_LMM_EXT) += imx-sm-lmm.o
> >  obj-$(CONFIG_IMX_SCMI_MISC_EXT) += imx-sm-misc.o
> > diff --git a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c
> > new file mode 100644
> > index 0000000000000000000000000000000000000000..e3f294c2cb69a5b5a916d55984f4a63539937d02
> > --- /dev/null
> > +++ b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c
> > @@ -0,0 +1,283 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * System control and Management Interface (SCMI) NXP CPU Protocol
> > + *
> > + * Copyright 2025 NXP
> > + */
> > +
> > +#include <linux/bits.h>
> > +#include <linux/io.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/scmi_protocol.h>
> > +#include <linux/scmi_imx_protocol.h>
> > +
> > +#include "../../protocols.h"
> > +#include "../../notify.h"
> > +
> > +#define SCMI_PROTOCOL_SUPPORTED_VERSION		0x10000
> > +
> > +enum scmi_imx_cpu_protocol_cmd {
> > +	SCMI_IMX_CPU_ATTRIBUTES	= 0x3,
> > +	SCMI_IMX_CPU_START = 0x4,
> > +	SCMI_IMX_CPU_STOP = 0x5,
> > +	SCMI_IMX_CPU_RESET_VECTOR_SET = 0x6,
> > +	SCMI_IMX_CPU_INFO_GET = 0xC,
> > +};
> > +
> > +struct scmi_imx_cpu_info {
> > +	u32 nr_cpu;
> > +};
> > +
> > +#define SCMI_IMX_CPU_PROTO_ATTR_NUM_CPUS(x)  ((x) & 0xFFFF)
> > +struct scmi_msg_imx_cpu_protocol_attributes {
> > +	__le32 attributes;
> > +};
> > +
> > +struct scmi_msg_imx_cpu_attributes_out {
> > +	__le32 attributes;
> > +#define	CPU_MAX_NAME	16
> > +	u8 name[CPU_MAX_NAME];
> 
> char is always unsigned in the kernel these days but strings should
> still always be char.  Same thing in patch 1, there were a couple u8
> names.
> 

While it is certainly true that char is the way to go for strings and, as
such, it is used elsewhere to hold the resource names across all SCMI
protocols, in this context it is a field of structure representing
exactly the layout of message reply coming from the server, and defined
in the SCMI spec as a uint8 array, so, we have generally preferred to
used u8 to represent such fixed size array all across the SCMI stack
protocols implementation....

.... not saying that it is necessarily completelt right, but that is the
reason we are guilty :D

Thanks,
Cristian
Dan Carpenter Jan. 22, 2025, 12:41 p.m. UTC | #3
On Wed, Jan 22, 2025 at 12:22:18PM +0000, Cristian Marussi wrote:
> > > +struct scmi_msg_imx_cpu_attributes_out {
> > > +	__le32 attributes;
> > > +#define	CPU_MAX_NAME	16
> > > +	u8 name[CPU_MAX_NAME];
> > 
> > char is always unsigned in the kernel these days but strings should
> > still always be char.  Same thing in patch 1, there were a couple u8
> > names.
> > 
> 
> While it is certainly true that char is the way to go for strings and, as
> such, it is used elsewhere to hold the resource names across all SCMI
> protocols, in this context it is a field of structure representing
> exactly the layout of message reply coming from the server, and defined
> in the SCMI spec as a uint8 array, so, we have generally preferred to
> used u8 to represent such fixed size array all across the SCMI stack
> protocols implementation....
> 
> .... not saying that it is necessarily completelt right, but that is the
> reason we are guilty :D

Fine.  I don't have intense emotions about this.

It does slightly bother me when we assume that the SCMI server NUL
terminates these when we do things like:

	dev_info(ph->dev, "i.MX CPU: name: %s\n", out->name);

But from a practical perspective we have to trust the SCMI server.

regards,
dan carpenter
Cristian Marussi Jan. 22, 2025, 12:55 p.m. UTC | #4
On Wed, Jan 22, 2025 at 03:41:41PM +0300, Dan Carpenter wrote:
> On Wed, Jan 22, 2025 at 12:22:18PM +0000, Cristian Marussi wrote:
> > > > +struct scmi_msg_imx_cpu_attributes_out {
> > > > +	__le32 attributes;
> > > > +#define	CPU_MAX_NAME	16
> > > > +	u8 name[CPU_MAX_NAME];
> > > 
> > > char is always unsigned in the kernel these days but strings should
> > > still always be char.  Same thing in patch 1, there were a couple u8
> > > names.
> > > 
> > 

Hi Dan,

> > While it is certainly true that char is the way to go for strings and, as
> > such, it is used elsewhere to hold the resource names across all SCMI
> > protocols, in this context it is a field of structure representing
> > exactly the layout of message reply coming from the server, and defined
> > in the SCMI spec as a uint8 array, so, we have generally preferred to
> > used u8 to represent such fixed size array all across the SCMI stack
> > protocols implementation....
> > 
> > .... not saying that it is necessarily completelt right, but that is the
> > reason we are guilty :D
> 
> Fine.  I don't have intense emotions about this.
> 
> It does slightly bother me when we assume that the SCMI server NUL
> terminates these when we do things like:
> 
> 	dev_info(ph->dev, "i.MX CPU: name: %s\n", out->name);
> 

Hang on...I have not really done a proper review still on this series...
...and this printout above straight out of the message payload seems very
wrong to me too..

> But from a practical perspective we have to trust the SCMI server.
>

....nope we should NEVER trust the server...and instead assume it can
kill us (kernel) all the time :P

...despite what the spec says, we tend to assume tha the server can be
maliciously wrong (or just crappy), so in other protocols where we do used
an u8[] to describe the resource name field in a message, we have also always
(hopefully :D) taken care to use it ONLY after having processed that field like...

   strscpy(dom_info->name, attr->name, SCMI_SHORT_NAME_MAX_SIZE);

...to remove any possible bad outcome from a misbehaving SCMI fw server.

Thanks,
Cristian
Sudeep Holla Jan. 22, 2025, 1:59 p.m. UTC | #5
On Wed, Jan 22, 2025 at 03:41:41PM +0300, Dan Carpenter wrote:
> On Wed, Jan 22, 2025 at 12:22:18PM +0000, Cristian Marussi wrote:
> > > > +struct scmi_msg_imx_cpu_attributes_out {
> > > > +	__le32 attributes;
> > > > +#define	CPU_MAX_NAME	16
> > > > +	u8 name[CPU_MAX_NAME];
> > >
> > > char is always unsigned in the kernel these days but strings should
> > > still always be char.  Same thing in patch 1, there were a couple u8
> > > names.
> > >
> >
> > While it is certainly true that char is the way to go for strings and, as
> > such, it is used elsewhere to hold the resource names across all SCMI
> > protocols, in this context it is a field of structure representing
> > exactly the layout of message reply coming from the server, and defined
> > in the SCMI spec as a uint8 array, so, we have generally preferred to
> > used u8 to represent such fixed size array all across the SCMI stack
> > protocols implementation....
> >
> > .... not saying that it is necessarily completelt right, but that is the
> > reason we are guilty :D
>
> Fine.  I don't have intense emotions about this.
>
> It does slightly bother me when we assume that the SCMI server NUL
> terminates these when we do things like:
>
> 	dev_info(ph->dev, "i.MX CPU: name: %s\n", out->name);
>

I was about to reply earlier and got distracted. This makes it even better.
I agree with Cristian that we have defined it as u8 when referring to the
payload from the SCMI platform/server. But it should be used only to extract
information from the response payload and copied to OSPM's own buffer before
accessing it like the above dev_info example.

In short, I agree with you Dan that it shouldn't be used in OS like above.
I haven't looked at the implementation patches(just looked at the doc 3/5),
but if they need such logging, they need to copy it to some buffer in the
kernel.

--
Regards,
Sudeep
kernel test robot Jan. 24, 2025, 4:11 a.m. UTC | #6
Hi Peng,

kernel test robot noticed the following build warnings:

[auto build test WARNING on 0907e7fb35756464aa34c35d6abb02998418164b]

url:    https://github.com/intel-lab-lkp/linux/commits/Peng-Fan-OSS/firmware-arm_scmi-imx-Add-i-MX95-LMM-protocol/20250121-231254
base:   0907e7fb35756464aa34c35d6abb02998418164b
patch link:    https://lore.kernel.org/r/20250121-imx-lmm-cpu-v1-2-0eab7e073e4e%40nxp.com
patch subject: [PATCH 2/5] firmware: arm_scmi: imx: Add i.MX95 CPU Protocol
config: arm64-randconfig-r122-20250124 (https://download.01.org/0day-ci/archive/20250124/202501241148.0CI9mBP5-lkp@intel.com/config)
compiler: aarch64-linux-gcc (GCC) 14.2.0
reproduce: (https://download.01.org/0day-ci/archive/20250124/202501241148.0CI9mBP5-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202501241148.0CI9mBP5-lkp@intel.com/

sparse warnings: (new ones prefixed by >>)
>> drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c:139:19: sparse: sparse: incorrect type in assignment (different base types) @@     expected restricted __le32 [usertype] flags @@     got unsigned long @@
   drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c:139:19: sparse:     expected restricted __le32 [usertype] flags
   drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c:139:19: sparse:     got unsigned long
>> drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c:140:19: sparse: sparse: invalid assignment: |=
   drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c:140:19: sparse:    left side has type restricted __le32
   drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c:140:19: sparse:    right side has type unsigned long
   drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c:141:19: sparse: sparse: invalid assignment: |=
   drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c:141:19: sparse:    left side has type restricted __le32
   drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c:141:19: sparse:    right side has type unsigned long
>> drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c:173:25: sparse: sparse: restricted __le32 degrades to integer
   drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c:205:32: sparse: sparse: restricted __le32 degrades to integer

vim +139 drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c

   119	
   120	static int scmi_imx_cpu_reset_vector_set(const struct scmi_protocol_handle *ph,
   121						 u32 cpuid, u64 vector, bool start,
   122						 bool boot, bool resume)
   123	{
   124		struct scmi_imx_cpu_reset_vector_set_in *in;
   125		struct scmi_xfer *t;
   126		int ret;
   127	
   128		ret = scmi_imx_cpu_validate_cpuid(ph, cpuid);
   129		if (ret)
   130			return ret;
   131	
   132		ret = ph->xops->xfer_get_init(ph, SCMI_IMX_CPU_RESET_VECTOR_SET, sizeof(*in),
   133					      0, &t);
   134		if (ret)
   135			return ret;
   136	
   137		in = t->tx.buf;
   138		in->cpuid = cpu_to_le32(cpuid);
 > 139		in->flags = start ? CPU_VEC_FLAGS_START : 0;
 > 140		in->flags |= boot ? CPU_VEC_FLAGS_BOOT : 0;
   141		in->flags |= resume ? CPU_VEC_FLAGS_BOOT : 0;
   142		in->resetvectorlow = cpu_to_le32(lower_32_bits(vector));
   143		in->resetvectorhigh = cpu_to_le32(upper_32_bits(vector));
   144		ret = ph->xops->do_xfer(ph, t);
   145	
   146		ph->xops->xfer_put(ph, t);
   147	
   148		return ret;
   149	}
   150	
   151	static int scmi_imx_cpu_started(const struct scmi_protocol_handle *ph, u32 cpuid,
   152					bool *started)
   153	{
   154		struct scmi_imx_cpu_info_get_out *out;
   155		struct scmi_xfer *t;
   156		int ret;
   157	
   158		*started = false;
   159		ret = scmi_imx_cpu_validate_cpuid(ph, cpuid);
   160		if (ret)
   161			return ret;
   162	
   163		ret = ph->xops->xfer_get_init(ph, SCMI_IMX_CPU_INFO_GET, sizeof(u32),
   164					      0, &t);
   165		if (ret)
   166			return ret;
   167	
   168		put_unaligned_le32(cpuid, t->tx.buf);
   169		ret = ph->xops->do_xfer(ph, t);
   170		if (!ret) {
   171			out = t->rx.buf;
   172			if ((out->runmode == CPU_RUN_MODE_START) ||
 > 173			    (out->runmode == CPU_RUN_MODE_SLEEP))
   174				*started = true;
   175		}
   176	
   177		ph->xops->xfer_put(ph, t);
   178	
   179		return ret;
   180	}
   181
diff mbox series

Patch

diff --git a/drivers/firmware/arm_scmi/vendors/imx/Kconfig b/drivers/firmware/arm_scmi/vendors/imx/Kconfig
index 1a936fc87d2350e2a21bccd45dfbeebfa3b90286..9070522510e4d3f3d7276a7581f8676006d20f90 100644
--- a/drivers/firmware/arm_scmi/vendors/imx/Kconfig
+++ b/drivers/firmware/arm_scmi/vendors/imx/Kconfig
@@ -12,6 +12,17 @@  config IMX_SCMI_BBM_EXT
 	  To compile this driver as a module, choose M here: the
 	  module will be called imx-sm-bbm.
 
+config IMX_SCMI_CPU_EXT
+	tristate "i.MX SCMI CPU EXTENSION"
+	depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
+	default y if ARCH_MXC
+	help
+	  This enables i.MX System CPU Protocol to manage cpu
+	  start, stop and etc.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called imx-sm-cpu.
+
 config IMX_SCMI_LMM_EXT
 	tristate "i.MX SCMI LMM EXTENSION"
 	depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
@@ -21,7 +32,7 @@  config IMX_SCMI_LMM_EXT
 	  manage Logical Machines boot, shutdown and etc.
 
 	  To compile this driver as a module, choose M here: the
-	  module will be called imx-sm-lmm.
+	  module will be called imx-sm-cpu.
 
 config IMX_SCMI_MISC_EXT
 	tristate "i.MX SCMI MISC EXTENSION"
diff --git a/drivers/firmware/arm_scmi/vendors/imx/Makefile b/drivers/firmware/arm_scmi/vendors/imx/Makefile
index f39a99ccaf9af757475e8b112d224669444d7ddc..e3a5ea46345c89da1afae25e55698044672b7c28 100644
--- a/drivers/firmware/arm_scmi/vendors/imx/Makefile
+++ b/drivers/firmware/arm_scmi/vendors/imx/Makefile
@@ -1,4 +1,5 @@ 
 # SPDX-License-Identifier: GPL-2.0-only
 obj-$(CONFIG_IMX_SCMI_BBM_EXT) += imx-sm-bbm.o
+obj-$(CONFIG_IMX_SCMI_CPU_EXT) += imx-sm-cpu.o
 obj-$(CONFIG_IMX_SCMI_LMM_EXT) += imx-sm-lmm.o
 obj-$(CONFIG_IMX_SCMI_MISC_EXT) += imx-sm-misc.o
diff --git a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c
new file mode 100644
index 0000000000000000000000000000000000000000..e3f294c2cb69a5b5a916d55984f4a63539937d02
--- /dev/null
+++ b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c
@@ -0,0 +1,283 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System control and Management Interface (SCMI) NXP CPU Protocol
+ *
+ * Copyright 2025 NXP
+ */
+
+#include <linux/bits.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/scmi_protocol.h>
+#include <linux/scmi_imx_protocol.h>
+
+#include "../../protocols.h"
+#include "../../notify.h"
+
+#define SCMI_PROTOCOL_SUPPORTED_VERSION		0x10000
+
+enum scmi_imx_cpu_protocol_cmd {
+	SCMI_IMX_CPU_ATTRIBUTES	= 0x3,
+	SCMI_IMX_CPU_START = 0x4,
+	SCMI_IMX_CPU_STOP = 0x5,
+	SCMI_IMX_CPU_RESET_VECTOR_SET = 0x6,
+	SCMI_IMX_CPU_INFO_GET = 0xC,
+};
+
+struct scmi_imx_cpu_info {
+	u32 nr_cpu;
+};
+
+#define SCMI_IMX_CPU_PROTO_ATTR_NUM_CPUS(x)  ((x) & 0xFFFF)
+struct scmi_msg_imx_cpu_protocol_attributes {
+	__le32 attributes;
+};
+
+struct scmi_msg_imx_cpu_attributes_out {
+	__le32 attributes;
+#define	CPU_MAX_NAME	16
+	u8 name[CPU_MAX_NAME];
+};
+
+struct scmi_imx_cpu_reset_vector_set_in {
+	__le32 cpuid;
+#define	CPU_VEC_FLAGS_RESUME	BIT(31)
+#define	CPU_VEC_FLAGS_START	BIT(30)
+#define	CPU_VEC_FLAGS_BOOT	BIT(29)
+	__le32 flags;
+	__le32 resetvectorlow;
+	__le32 resetvectorhigh;
+};
+
+struct scmi_imx_cpu_info_get_out {
+#define	CPU_RUN_MODE_START	0
+#define	CPU_RUN_MODE_HOLD	1
+#define	CPU_RUN_MODE_STOP	2
+#define	CPU_RUN_MODE_SLEEP	3
+	__le32 runmode;
+	__le32 sleepmode;
+	__le32 resetvectorlow;
+	__le32 resetvectorhigh;
+};
+
+static int scmi_imx_cpu_validate_cpuid(const struct scmi_protocol_handle *ph,
+				       u32 cpuid)
+{
+	struct scmi_imx_cpu_info *info = ph->get_priv(ph);
+
+	if (cpuid >= info->nr_cpu)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int scmi_imx_cpu_start(const struct scmi_protocol_handle *ph, u32 cpuid)
+{
+	struct scmi_xfer *t;
+	int ret;
+
+	ret = scmi_imx_cpu_validate_cpuid(ph, cpuid);
+	if (ret)
+		return ret;
+
+	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_CPU_START, sizeof(u32),
+				      0, &t);
+	if (ret)
+		return ret;
+
+	put_unaligned_le32(cpuid, t->tx.buf);
+	ret = ph->xops->do_xfer(ph, t);
+
+	ph->xops->xfer_put(ph, t);
+
+	return ret;
+}
+
+static int scmi_imx_cpu_stop(const struct scmi_protocol_handle *ph, u32 cpuid)
+{
+	struct scmi_xfer *t;
+	int ret;
+
+	ret = scmi_imx_cpu_validate_cpuid(ph, cpuid);
+	if (ret)
+		return ret;
+
+	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_CPU_STOP, sizeof(u32),
+				      0, &t);
+	if (ret)
+		return ret;
+
+	put_unaligned_le32(cpuid, t->tx.buf);
+	ret = ph->xops->do_xfer(ph, t);
+
+	ph->xops->xfer_put(ph, t);
+
+	return ret;
+}
+
+static int scmi_imx_cpu_reset_vector_set(const struct scmi_protocol_handle *ph,
+					 u32 cpuid, u64 vector, bool start,
+					 bool boot, bool resume)
+{
+	struct scmi_imx_cpu_reset_vector_set_in *in;
+	struct scmi_xfer *t;
+	int ret;
+
+	ret = scmi_imx_cpu_validate_cpuid(ph, cpuid);
+	if (ret)
+		return ret;
+
+	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_CPU_RESET_VECTOR_SET, sizeof(*in),
+				      0, &t);
+	if (ret)
+		return ret;
+
+	in = t->tx.buf;
+	in->cpuid = cpu_to_le32(cpuid);
+	in->flags = start ? CPU_VEC_FLAGS_START : 0;
+	in->flags |= boot ? CPU_VEC_FLAGS_BOOT : 0;
+	in->flags |= resume ? CPU_VEC_FLAGS_BOOT : 0;
+	in->resetvectorlow = cpu_to_le32(lower_32_bits(vector));
+	in->resetvectorhigh = cpu_to_le32(upper_32_bits(vector));
+	ret = ph->xops->do_xfer(ph, t);
+
+	ph->xops->xfer_put(ph, t);
+
+	return ret;
+}
+
+static int scmi_imx_cpu_started(const struct scmi_protocol_handle *ph, u32 cpuid,
+				bool *started)
+{
+	struct scmi_imx_cpu_info_get_out *out;
+	struct scmi_xfer *t;
+	int ret;
+
+	*started = false;
+	ret = scmi_imx_cpu_validate_cpuid(ph, cpuid);
+	if (ret)
+		return ret;
+
+	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_CPU_INFO_GET, sizeof(u32),
+				      0, &t);
+	if (ret)
+		return ret;
+
+	put_unaligned_le32(cpuid, t->tx.buf);
+	ret = ph->xops->do_xfer(ph, t);
+	if (!ret) {
+		out = t->rx.buf;
+		if ((out->runmode == CPU_RUN_MODE_START) ||
+		    (out->runmode == CPU_RUN_MODE_SLEEP))
+			*started = true;
+	}
+
+	ph->xops->xfer_put(ph, t);
+
+	return ret;
+}
+
+static const struct scmi_imx_cpu_proto_ops scmi_imx_cpu_proto_ops = {
+	.cpu_reset_vector_set = scmi_imx_cpu_reset_vector_set,
+	.cpu_start = scmi_imx_cpu_start,
+	.cpu_started = scmi_imx_cpu_started,
+	.cpu_stop = scmi_imx_cpu_stop,
+};
+
+static int scmi_imx_cpu_protocol_attributes_get(const struct scmi_protocol_handle *ph,
+						struct scmi_imx_cpu_info *info)
+{
+	struct scmi_msg_imx_cpu_protocol_attributes *attr;
+	struct scmi_xfer *t;
+	int ret;
+
+	ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0,
+				      sizeof(*attr), &t);
+	if (ret)
+		return ret;
+
+	attr = t->rx.buf;
+
+	ret = ph->xops->do_xfer(ph, t);
+	if (!ret) {
+		info->nr_cpu = SCMI_IMX_CPU_PROTO_ATTR_NUM_CPUS(attr->attributes);
+		dev_info(ph->dev, "i.MX SM CPU: %d cpus\n",
+			 info->nr_cpu);
+	}
+
+	ph->xops->xfer_put(ph, t);
+
+	return ret;
+}
+
+static int scmi_imx_cpu_attributes_get(const struct scmi_protocol_handle *ph,
+				       u32 cpuid, struct scmi_imx_cpu_info *info)
+{
+	struct scmi_msg_imx_cpu_attributes_out *out;
+	struct scmi_xfer *t;
+	int ret;
+
+	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_CPU_ATTRIBUTES, sizeof(u32), 0, &t);
+	if (ret)
+		return ret;
+
+	put_unaligned_le32(cpuid, t->tx.buf);
+	ret = ph->xops->do_xfer(ph, t);
+	if (!ret) {
+		out = t->rx.buf;
+		dev_info(ph->dev, "i.MX CPU: name: %s\n", out->name);
+	} else {
+		dev_err(ph->dev, "i.MX cpu: Failed to get info of cpu(%u)\n", cpuid);
+	}
+
+	ph->xops->xfer_put(ph, t);
+
+	return ret;
+}
+
+
+static int scmi_imx_cpu_protocol_init(const struct scmi_protocol_handle *ph)
+{
+	struct scmi_imx_cpu_info *info;
+	u32 version;
+	int ret, i;
+
+	ret = ph->xops->version_get(ph, &version);
+	if (ret)
+		return ret;
+
+	dev_info(ph->dev, "NXP SM CPU Protocol Version %d.%d\n",
+		 PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+
+	info = devm_kzalloc(ph->dev, sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	ret = scmi_imx_cpu_protocol_attributes_get(ph, info);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < info->nr_cpu; i++) {
+		ret = scmi_imx_cpu_attributes_get(ph, i, info);
+		if (ret)
+			return ret;
+	}
+
+	return ph->set_priv(ph, info, version);
+}
+
+static const struct scmi_protocol scmi_imx_cpu = {
+	.id = SCMI_PROTOCOL_IMX_CPU,
+	.owner = THIS_MODULE,
+	.instance_init = &scmi_imx_cpu_protocol_init,
+	.ops = &scmi_imx_cpu_proto_ops,
+	.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
+	.vendor_id = SCMI_IMX_VENDOR,
+	.sub_vendor_id = SCMI_IMX_SUBVENDOR,
+};
+module_scmi_protocol(scmi_imx_cpu);
+
+MODULE_DESCRIPTION("i.MX SCMI CPU driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/scmi_imx_protocol.h b/include/linux/scmi_imx_protocol.h
index 456559f021696ae51f40ed11e6f85089d430ce71..6521199cdab67c7e751e850def221d89898cd0f2 100644
--- a/include/linux/scmi_imx_protocol.h
+++ b/include/linux/scmi_imx_protocol.h
@@ -16,6 +16,7 @@ 
 
 #define SCMI_PROTOCOL_IMX_LMM	0x80
 #define	SCMI_PROTOCOL_IMX_BBM	0x81
+#define SCMI_PROTOCOL_IMX_CPU	0x82
 #define	SCMI_PROTOCOL_IMX_MISC	0x84
 
 #define SCMI_IMX_VENDOR		"NXP"
@@ -86,4 +87,13 @@  struct scmi_imx_lmm_proto_ops {
 			    u32 flags);
 };
 
+struct scmi_imx_cpu_proto_ops {
+	int (*cpu_reset_vector_set)(const struct scmi_protocol_handle *ph,
+				    u32 cpuid, u64 vector, bool start,
+				    bool boot, bool resume);
+	int (*cpu_start)(const struct scmi_protocol_handle *ph, u32 cpuid);
+	int (*cpu_started)(const struct scmi_protocol_handle *ph, u32 cpuid,
+			   bool *started);
+	int (*cpu_stop)(const struct scmi_protocol_handle *ph, u32 cpuid);
+};
 #endif