diff mbox series

[v2,4/6] firmware: arm_scmi: add initial support for i.MX MISC protocol

Message ID 20240405-imx95-bbm-misc-v2-v2-4-9fc9186856c2@nxp.com (mailing list archive)
State New, archived
Headers show
Series firmware: support i.MX95 SCMI BBM/MISC Extenstion | expand

Commit Message

Peng Fan (OSS) April 5, 2024, 12:39 p.m. UTC
From: Peng Fan <peng.fan@nxp.com>

The i.MX MISC protocol is for misc settings, such as gpio expander
wakeup.

Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 drivers/firmware/arm_scmi/Kconfig       |  10 ++
 drivers/firmware/arm_scmi/Makefile      |   1 +
 drivers/firmware/arm_scmi/imx-sm-misc.c | 305 ++++++++++++++++++++++++++++++++
 include/linux/scmi_imx_protocol.h       |  17 ++
 4 files changed, 333 insertions(+)

Comments

Marco Felsch April 5, 2024, 4:44 p.m. UTC | #1
Hi Peng,

On 24-04-05, Peng Fan (OSS) wrote:
> From: Peng Fan <peng.fan@nxp.com>
> 
> The i.MX MISC protocol is for misc settings, such as gpio expander
> wakeup.

Can you elaborate a bit more please?

Regards,
  Marco


> 
> Signed-off-by: Peng Fan <peng.fan@nxp.com>
> ---
>  drivers/firmware/arm_scmi/Kconfig       |  10 ++
>  drivers/firmware/arm_scmi/Makefile      |   1 +
>  drivers/firmware/arm_scmi/imx-sm-misc.c | 305 ++++++++++++++++++++++++++++++++
>  include/linux/scmi_imx_protocol.h       |  17 ++
>  4 files changed, 333 insertions(+)
> 
> diff --git a/drivers/firmware/arm_scmi/Kconfig b/drivers/firmware/arm_scmi/Kconfig
> index 56d11c9d9f47..bfeae92f6420 100644
> --- a/drivers/firmware/arm_scmi/Kconfig
> +++ b/drivers/firmware/arm_scmi/Kconfig
> @@ -191,3 +191,13 @@ config IMX_SCMI_BBM_EXT
>  	  and BUTTON.
>  
>  	  This driver can also be built as a module.
> +
> +config IMX_SCMI_MISC_EXT
> +	tristate "i.MX SCMI MISC EXTENSION"
> +	depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
> +	default y if ARCH_MXC
> +	help
> +	  This enables i.MX System MISC control logic such as gpio expander
> +	  wakeup
> +
> +	  This driver can also be built as a module.
> diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
> index 327687acf857..a23fde721222 100644
> --- a/drivers/firmware/arm_scmi/Makefile
> +++ b/drivers/firmware/arm_scmi/Makefile
> @@ -12,6 +12,7 @@ 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-$(CONFIG_IMX_SCMI_BBM_EXT) += imx-sm-bbm.o
> +scmi-protocols-$(CONFIG_IMX_SCMI_MISC_EXT) += imx-sm-misc.o
>  scmi-module-objs := $(scmi-driver-y) $(scmi-protocols-y) $(scmi-transport-y)
>  
>  obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-core.o
> diff --git a/drivers/firmware/arm_scmi/imx-sm-misc.c b/drivers/firmware/arm_scmi/imx-sm-misc.c
> new file mode 100644
> index 000000000000..1b0ec2281518
> --- /dev/null
> +++ b/drivers/firmware/arm_scmi/imx-sm-misc.c
> @@ -0,0 +1,305 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * System control and Management Interface (SCMI) NXP MISC Protocol
> + *
> + * Copyright 2024 NXP
> + */
> +
> +#define pr_fmt(fmt) "SCMI Notifications MISC - " fmt
> +
> +#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_misc_protocol_cmd {
> +	SCMI_IMX_MISC_CTRL_SET	= 0x3,
> +	SCMI_IMX_MISC_CTRL_GET	= 0x4,
> +	SCMI_IMX_MISC_CTRL_NOTIFY = 0x8,
> +};
> +
> +struct scmi_imx_misc_info {
> +	u32 version;
> +	u32 nr_dev_ctrl;
> +	u32 nr_brd_ctrl;
> +	u32 nr_reason;
> +};
> +
> +struct scmi_msg_imx_misc_protocol_attributes {
> +	__le32 attributes;
> +};
> +
> +#define GET_BRD_CTRLS_NR(x)	le32_get_bits((x), GENMASK(31, 24))
> +#define GET_REASONS_NR(x)	le32_get_bits((x), GENMASK(23, 16))
> +#define GET_DEV_CTRLS_NR(x)	le32_get_bits((x), GENMASK(15, 0))
> +#define BRD_CTRL_START_ID	BIT(15)
> +
> +struct scmi_imx_misc_ctrl_set_in {
> +	__le32 id;
> +	__le32 num;
> +	__le32 value[MISC_MAX_VAL];
> +};
> +
> +struct scmi_imx_misc_ctrl_notify_in {
> +	__le32 ctrl_id;
> +	__le32 flags;
> +};
> +
> +struct scmi_imx_misc_ctrl_notify_payld {
> +	__le32 ctrl_id;
> +	__le32 flags;
> +};
> +
> +struct scmi_imx_misc_ctrl_get_out {
> +	__le32 num;
> +	__le32 *val;
> +};
> +
> +static int scmi_imx_misc_attributes_get(const struct scmi_protocol_handle *ph,
> +					struct scmi_imx_misc_info *mi)
> +{
> +	int ret;
> +	struct scmi_xfer *t;
> +	struct scmi_msg_imx_misc_protocol_attributes *attr;
> +
> +	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) {
> +		mi->nr_dev_ctrl = GET_DEV_CTRLS_NR(attr->attributes);
> +		mi->nr_brd_ctrl = GET_BRD_CTRLS_NR(attr->attributes);
> +		mi->nr_reason = GET_REASONS_NR(attr->attributes);
> +		dev_info(ph->dev, "i.MX MISC NUM DEV CTRL: %d, NUM BRD CTRL: %d,NUM Reason: %d\n",
> +			 mi->nr_dev_ctrl, mi->nr_brd_ctrl, mi->nr_reason);
> +	}
> +
> +	ph->xops->xfer_put(ph, t);
> +
> +	return ret;
> +}
> +
> +static int scmi_imx_misc_ctrl_validate_id(const struct scmi_protocol_handle *ph,
> +					  u32 ctrl_id)
> +{
> +	struct scmi_imx_misc_info *mi = ph->get_priv(ph);
> +
> +	if ((ctrl_id < BRD_CTRL_START_ID) && (ctrl_id > mi->nr_dev_ctrl))
> +		return -EINVAL;
> +	if (ctrl_id >= BRD_CTRL_START_ID + mi->nr_brd_ctrl)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static int scmi_imx_misc_ctrl_notify(const struct scmi_protocol_handle *ph,
> +				     u32 ctrl_id, u32 flags)
> +{
> +	struct scmi_imx_misc_ctrl_notify_in *in;
> +	struct scmi_xfer *t;
> +	int ret;
> +
> +	ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
> +	if (ret)
> +		return ret;
> +
> +	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_NOTIFY,
> +				      sizeof(*in), 0, &t);
> +	if (ret)
> +		return ret;
> +
> +	in = t->tx.buf;
> +	in->ctrl_id = cpu_to_le32(ctrl_id);
> +	in->flags = cpu_to_le32(flags);
> +
> +	ret = ph->xops->do_xfer(ph, t);
> +
> +	ph->xops->xfer_put(ph, t);
> +
> +	return ret;
> +}
> +
> +static int
> +scmi_imx_misc_ctrl_set_notify_enabled(const struct scmi_protocol_handle *ph,
> +				      u8 evt_id, u32 src_id, bool enable)
> +{
> +	int ret;
> +
> +	ret = scmi_imx_misc_ctrl_notify(ph, src_id, enable ? evt_id : 0);
> +	if (ret)
> +		dev_err(ph->dev, "FAIL_ENABLED - evt[%X] src[%d] - ret:%d\n",
> +			evt_id, src_id, ret);
> +
> +	return ret;
> +}
> +
> +static int scmi_imx_misc_ctrl_get_num_sources(const struct scmi_protocol_handle *ph)
> +{
> +	return GENMASK(15, 0);
> +}
> +
> +static void *
> +scmi_imx_misc_ctrl_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)
> +{
> +	const struct scmi_imx_misc_ctrl_notify_payld *p = payld;
> +	struct scmi_imx_misc_ctrl_notify_report *r = report;
> +
> +	if (sizeof(*p) != payld_sz)
> +		return NULL;
> +
> +	r->timestamp = timestamp;
> +	r->ctrl_id = p->ctrl_id;
> +	r->flags = p->flags;
> +	*src_id = r->ctrl_id;
> +	dev_dbg(ph->dev, "%s: ctrl_id: %d flags: %d\n", __func__,
> +		r->ctrl_id, r->flags);
> +
> +	return r;
> +}
> +
> +static const struct scmi_event_ops scmi_imx_misc_event_ops = {
> +	.get_num_sources = scmi_imx_misc_ctrl_get_num_sources,
> +	.set_notify_enabled = scmi_imx_misc_ctrl_set_notify_enabled,
> +	.fill_custom_report = scmi_imx_misc_ctrl_fill_custom_report,
> +};
> +
> +static const struct scmi_event scmi_imx_misc_events[] = {
> +	{
> +		.id = SCMI_EVENT_IMX_MISC_CONTROL_DISABLED,
> +		.max_payld_sz = sizeof(struct scmi_imx_misc_ctrl_notify_payld),
> +		.max_report_sz = sizeof(struct scmi_imx_misc_ctrl_notify_report),
> +	},
> +	{
> +		.id = SCMI_EVENT_IMX_MISC_CONTROL_FALLING_EDGE,
> +		.max_payld_sz = sizeof(struct scmi_imx_misc_ctrl_notify_payld),
> +		.max_report_sz = sizeof(struct scmi_imx_misc_ctrl_notify_report),
> +	},
> +	{
> +		.id = SCMI_EVENT_IMX_MISC_CONTROL_RISING_EDGE,
> +		.max_payld_sz = sizeof(struct scmi_imx_misc_ctrl_notify_payld),
> +		.max_report_sz = sizeof(struct scmi_imx_misc_ctrl_notify_report),
> +	}
> +};
> +
> +static struct scmi_protocol_events scmi_imx_misc_protocol_events = {
> +	.queue_sz = SCMI_PROTO_QUEUE_SZ,
> +	.ops = &scmi_imx_misc_event_ops,
> +	.evts = scmi_imx_misc_events,
> +	.num_events = ARRAY_SIZE(scmi_imx_misc_events),
> +};
> +
> +static int scmi_imx_misc_protocol_init(const struct scmi_protocol_handle *ph)
> +{
> +	struct scmi_imx_misc_info *minfo;
> +	u32 version;
> +	int ret;
> +
> +	ret = ph->xops->version_get(ph, &version);
> +	if (ret)
> +		return ret;
> +
> +	dev_info(ph->dev, "NXP SM MISC Version %d.%d\n",
> +		 PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
> +
> +	minfo = devm_kzalloc(ph->dev, sizeof(*minfo), GFP_KERNEL);
> +	if (!minfo)
> +		return -ENOMEM;
> +
> +	ret = scmi_imx_misc_attributes_get(ph, minfo);
> +	if (ret)
> +		return ret;
> +
> +	return ph->set_priv(ph, minfo, version);
> +}
> +
> +static int scmi_imx_misc_ctrl_get(const struct scmi_protocol_handle *ph,
> +				  u32 ctrl_id, u32 *num, u32 *val)
> +{
> +	struct scmi_imx_misc_ctrl_get_out *out;
> +	struct scmi_xfer *t;
> +	int ret, i;
> +
> +	ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
> +	if (ret)
> +		return ret;
> +
> +	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_GET, sizeof(u32),
> +				      0, &t);
> +	if (ret)
> +		return ret;
> +
> +	put_unaligned_le32(ctrl_id, t->tx.buf);
> +	ret = ph->xops->do_xfer(ph, t);
> +	if (!ret) {
> +		out = t->rx.buf;
> +		*num = le32_to_cpu(out->num);
> +		for (i = 0; i < *num && i < MISC_MAX_VAL; i++)
> +			val[i] = le32_to_cpu(out->val[i]);
> +	}
> +
> +	ph->xops->xfer_put(ph, t);
> +
> +	return ret;
> +}
> +
> +static int scmi_imx_misc_ctrl_set(const struct scmi_protocol_handle *ph,
> +				  u32 ctrl_id, u32 num, u32 *val)
> +{
> +	struct scmi_imx_misc_ctrl_set_in *in;
> +	struct scmi_xfer *t;
> +	int ret, i;
> +
> +	ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
> +	if (ret)
> +		return ret;
> +
> +	if (num > MISC_MAX_VAL)
> +		return -EINVAL;
> +
> +	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_SET, sizeof(*in),
> +				      0, &t);
> +	if (ret)
> +		return ret;
> +
> +	in = t->tx.buf;
> +	in->id = cpu_to_le32(ctrl_id);
> +	in->num = cpu_to_le32(num);
> +	for (i = 0; i < num; i++)
> +		in->value[i] = cpu_to_le32(val[i]);
> +
> +	ret = ph->xops->do_xfer(ph, t);
> +
> +	ph->xops->xfer_put(ph, t);
> +
> +	return ret;
> +}
> +
> +static const struct scmi_imx_misc_proto_ops scmi_imx_misc_proto_ops = {
> +	.misc_ctrl_set = scmi_imx_misc_ctrl_set,
> +	.misc_ctrl_get = scmi_imx_misc_ctrl_get,
> +};
> +
> +static const struct scmi_protocol scmi_imx_misc = {
> +	.id = SCMI_PROTOCOL_IMX_MISC,
> +	.owner = THIS_MODULE,
> +	.instance_init = &scmi_imx_misc_protocol_init,
> +	.ops = &scmi_imx_misc_proto_ops,
> +	.events = &scmi_imx_misc_protocol_events,
> +	.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
> +};
> +module_scmi_protocol(scmi_imx_misc);
> diff --git a/include/linux/scmi_imx_protocol.h b/include/linux/scmi_imx_protocol.h
> index 90ce011a4429..a69bd4a20f0f 100644
> --- a/include/linux/scmi_imx_protocol.h
> +++ b/include/linux/scmi_imx_protocol.h
> @@ -13,8 +13,14 @@
>  #include <linux/notifier.h>
>  #include <linux/types.h>
>  
> +#define SCMI_PAYLOAD_LEN	100
> +
> +#define SCMI_ARRAY(X, Y)	((SCMI_PAYLOAD_LEN - (X)) / sizeof(Y))
> +#define MISC_MAX_VAL		SCMI_ARRAY(8, uint32_t)
> +
>  enum scmi_nxp_protocol {
>  	SCMI_PROTOCOL_IMX_BBM = 0x81,
> +	SCMI_PROTOCOL_IMX_MISC = 0x84,
>  };
>  
>  struct scmi_imx_bbm_proto_ops {
> @@ -42,4 +48,15 @@ struct scmi_imx_bbm_notif_report {
>  	unsigned int		rtc_id;
>  	unsigned int		rtc_evt;
>  };
> +
> +struct scmi_imx_misc_ctrl_notify_report {
> +	ktime_t			timestamp;
> +	unsigned int		ctrl_id;
> +	unsigned int		flags;
> +};
> +
> +struct scmi_imx_misc_proto_ops {
> +	int (*misc_ctrl_set)(const struct scmi_protocol_handle *ph, u32 id, u32 num, u32 *val);
> +	int (*misc_ctrl_get)(const struct scmi_protocol_handle *ph, u32 id, u32 *num, u32 *val);
> +};
>  #endif
> 
> -- 
> 2.37.1
> 
> 
>
Peng Fan April 7, 2024, 1:03 a.m. UTC | #2
> Subject: Re: [PATCH v2 4/6] firmware: arm_scmi: add initial support for i.MX
> MISC protocol
> 
> Hi Peng,
> 
> On 24-04-05, Peng Fan (OSS) wrote:
> > From: Peng Fan <peng.fan@nxp.com>
> >
> > The i.MX MISC protocol is for misc settings, such as gpio expander
> > wakeup.
> 
> Can you elaborate a bit more please?

The gpio expander is under M33(SCMI firmware used core) I2C control,
But the gpio expander supports board function such as PCIE_WAKEUP,
BTN_WAKEUP. So these are managed by MISC protocol.

SAI_CLK_MSEL in WAKEUP BLK CTRL is also managed by MISC Protocol.

And etc...

I will add more info in commit log in next version later, after I get more
reviews on the patchset.

Thanks,
Peng.

> 
> Regards,
>   Marco
> 
> 
> >
> > Signed-off-by: Peng Fan <peng.fan@nxp.com>
> > ---
> >  drivers/firmware/arm_scmi/Kconfig       |  10 ++
> >  drivers/firmware/arm_scmi/Makefile      |   1 +
> >  drivers/firmware/arm_scmi/imx-sm-misc.c | 305
> ++++++++++++++++++++++++++++++++
> >  include/linux/scmi_imx_protocol.h       |  17 ++
> >  4 files changed, 333 insertions(+)
> >
> > diff --git a/drivers/firmware/arm_scmi/Kconfig
> > b/drivers/firmware/arm_scmi/Kconfig
> > index 56d11c9d9f47..bfeae92f6420 100644
> > --- a/drivers/firmware/arm_scmi/Kconfig
> > +++ b/drivers/firmware/arm_scmi/Kconfig
> > @@ -191,3 +191,13 @@ config IMX_SCMI_BBM_EXT
> >  	  and BUTTON.
> >
> >  	  This driver can also be built as a module.
> > +
> > +config IMX_SCMI_MISC_EXT
> > +	tristate "i.MX SCMI MISC EXTENSION"
> > +	depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
> > +	default y if ARCH_MXC
> > +	help
> > +	  This enables i.MX System MISC control logic such as gpio expander
> > +	  wakeup
> > +
> > +	  This driver can also be built as a module.
> > diff --git a/drivers/firmware/arm_scmi/Makefile
> > b/drivers/firmware/arm_scmi/Makefile
> > index 327687acf857..a23fde721222 100644
> > --- a/drivers/firmware/arm_scmi/Makefile
> > +++ b/drivers/firmware/arm_scmi/Makefile
> > @@ -12,6 +12,7 @@ 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-$(CONFIG_IMX_SCMI_BBM_EXT) += imx-sm-bbm.o
> > +scmi-protocols-$(CONFIG_IMX_SCMI_MISC_EXT) += imx-sm-misc.o
> >  scmi-module-objs := $(scmi-driver-y) $(scmi-protocols-y)
> > $(scmi-transport-y)
> >
> >  obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-core.o diff --git
> > a/drivers/firmware/arm_scmi/imx-sm-misc.c
> > b/drivers/firmware/arm_scmi/imx-sm-misc.c
> > new file mode 100644
> > index 000000000000..1b0ec2281518
> > --- /dev/null
> > +++ b/drivers/firmware/arm_scmi/imx-sm-misc.c
> > @@ -0,0 +1,305 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * System control and Management Interface (SCMI) NXP MISC Protocol
> > + *
> > + * Copyright 2024 NXP
> > + */
> > +
> > +#define pr_fmt(fmt) "SCMI Notifications MISC - " fmt
> > +
> > +#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_misc_protocol_cmd {
> > +	SCMI_IMX_MISC_CTRL_SET	= 0x3,
> > +	SCMI_IMX_MISC_CTRL_GET	= 0x4,
> > +	SCMI_IMX_MISC_CTRL_NOTIFY = 0x8,
> > +};
> > +
> > +struct scmi_imx_misc_info {
> > +	u32 version;
> > +	u32 nr_dev_ctrl;
> > +	u32 nr_brd_ctrl;
> > +	u32 nr_reason;
> > +};
> > +
> > +struct scmi_msg_imx_misc_protocol_attributes {
> > +	__le32 attributes;
> > +};
> > +
> > +#define GET_BRD_CTRLS_NR(x)	le32_get_bits((x), GENMASK(31,
> 24))
> > +#define GET_REASONS_NR(x)	le32_get_bits((x), GENMASK(23, 16))
> > +#define GET_DEV_CTRLS_NR(x)	le32_get_bits((x), GENMASK(15, 0))
> > +#define BRD_CTRL_START_ID	BIT(15)
> > +
> > +struct scmi_imx_misc_ctrl_set_in {
> > +	__le32 id;
> > +	__le32 num;
> > +	__le32 value[MISC_MAX_VAL];
> > +};
> > +
> > +struct scmi_imx_misc_ctrl_notify_in {
> > +	__le32 ctrl_id;
> > +	__le32 flags;
> > +};
> > +
> > +struct scmi_imx_misc_ctrl_notify_payld {
> > +	__le32 ctrl_id;
> > +	__le32 flags;
> > +};
> > +
> > +struct scmi_imx_misc_ctrl_get_out {
> > +	__le32 num;
> > +	__le32 *val;
> > +};
> > +
> > +static int scmi_imx_misc_attributes_get(const struct scmi_protocol_handle
> *ph,
> > +					struct scmi_imx_misc_info *mi)
> > +{
> > +	int ret;
> > +	struct scmi_xfer *t;
> > +	struct scmi_msg_imx_misc_protocol_attributes *attr;
> > +
> > +	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) {
> > +		mi->nr_dev_ctrl = GET_DEV_CTRLS_NR(attr->attributes);
> > +		mi->nr_brd_ctrl = GET_BRD_CTRLS_NR(attr->attributes);
> > +		mi->nr_reason = GET_REASONS_NR(attr->attributes);
> > +		dev_info(ph->dev, "i.MX MISC NUM DEV CTRL: %d, NUM
> BRD CTRL: %d,NUM Reason: %d\n",
> > +			 mi->nr_dev_ctrl, mi->nr_brd_ctrl, mi->nr_reason);
> > +	}
> > +
> > +	ph->xops->xfer_put(ph, t);
> > +
> > +	return ret;
> > +}
> > +
> > +static int scmi_imx_misc_ctrl_validate_id(const struct
> scmi_protocol_handle *ph,
> > +					  u32 ctrl_id)
> > +{
> > +	struct scmi_imx_misc_info *mi = ph->get_priv(ph);
> > +
> > +	if ((ctrl_id < BRD_CTRL_START_ID) && (ctrl_id > mi->nr_dev_ctrl))
> > +		return -EINVAL;
> > +	if (ctrl_id >= BRD_CTRL_START_ID + mi->nr_brd_ctrl)
> > +		return -EINVAL;
> > +
> > +	return 0;
> > +}
> > +
> > +static int scmi_imx_misc_ctrl_notify(const struct scmi_protocol_handle
> *ph,
> > +				     u32 ctrl_id, u32 flags)
> > +{
> > +	struct scmi_imx_misc_ctrl_notify_in *in;
> > +	struct scmi_xfer *t;
> > +	int ret;
> > +
> > +	ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_NOTIFY,
> > +				      sizeof(*in), 0, &t);
> > +	if (ret)
> > +		return ret;
> > +
> > +	in = t->tx.buf;
> > +	in->ctrl_id = cpu_to_le32(ctrl_id);
> > +	in->flags = cpu_to_le32(flags);
> > +
> > +	ret = ph->xops->do_xfer(ph, t);
> > +
> > +	ph->xops->xfer_put(ph, t);
> > +
> > +	return ret;
> > +}
> > +
> > +static int
> > +scmi_imx_misc_ctrl_set_notify_enabled(const struct
> scmi_protocol_handle *ph,
> > +				      u8 evt_id, u32 src_id, bool enable) {
> > +	int ret;
> > +
> > +	ret = scmi_imx_misc_ctrl_notify(ph, src_id, enable ? evt_id : 0);
> > +	if (ret)
> > +		dev_err(ph->dev, "FAIL_ENABLED - evt[%X] src[%d] -
> ret:%d\n",
> > +			evt_id, src_id, ret);
> > +
> > +	return ret;
> > +}
> > +
> > +static int scmi_imx_misc_ctrl_get_num_sources(const struct
> > +scmi_protocol_handle *ph) {
> > +	return GENMASK(15, 0);
> > +}
> > +
> > +static void *
> > +scmi_imx_misc_ctrl_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)
> > +{
> > +	const struct scmi_imx_misc_ctrl_notify_payld *p = payld;
> > +	struct scmi_imx_misc_ctrl_notify_report *r = report;
> > +
> > +	if (sizeof(*p) != payld_sz)
> > +		return NULL;
> > +
> > +	r->timestamp = timestamp;
> > +	r->ctrl_id = p->ctrl_id;
> > +	r->flags = p->flags;
> > +	*src_id = r->ctrl_id;
> > +	dev_dbg(ph->dev, "%s: ctrl_id: %d flags: %d\n", __func__,
> > +		r->ctrl_id, r->flags);
> > +
> > +	return r;
> > +}
> > +
> > +static const struct scmi_event_ops scmi_imx_misc_event_ops = {
> > +	.get_num_sources = scmi_imx_misc_ctrl_get_num_sources,
> > +	.set_notify_enabled = scmi_imx_misc_ctrl_set_notify_enabled,
> > +	.fill_custom_report = scmi_imx_misc_ctrl_fill_custom_report,
> > +};
> > +
> > +static const struct scmi_event scmi_imx_misc_events[] = {
> > +	{
> > +		.id = SCMI_EVENT_IMX_MISC_CONTROL_DISABLED,
> > +		.max_payld_sz = sizeof(struct
> scmi_imx_misc_ctrl_notify_payld),
> > +		.max_report_sz = sizeof(struct
> scmi_imx_misc_ctrl_notify_report),
> > +	},
> > +	{
> > +		.id = SCMI_EVENT_IMX_MISC_CONTROL_FALLING_EDGE,
> > +		.max_payld_sz = sizeof(struct
> scmi_imx_misc_ctrl_notify_payld),
> > +		.max_report_sz = sizeof(struct
> scmi_imx_misc_ctrl_notify_report),
> > +	},
> > +	{
> > +		.id = SCMI_EVENT_IMX_MISC_CONTROL_RISING_EDGE,
> > +		.max_payld_sz = sizeof(struct
> scmi_imx_misc_ctrl_notify_payld),
> > +		.max_report_sz = sizeof(struct
> scmi_imx_misc_ctrl_notify_report),
> > +	}
> > +};
> > +
> > +static struct scmi_protocol_events scmi_imx_misc_protocol_events = {
> > +	.queue_sz = SCMI_PROTO_QUEUE_SZ,
> > +	.ops = &scmi_imx_misc_event_ops,
> > +	.evts = scmi_imx_misc_events,
> > +	.num_events = ARRAY_SIZE(scmi_imx_misc_events), };
> > +
> > +static int scmi_imx_misc_protocol_init(const struct
> > +scmi_protocol_handle *ph) {
> > +	struct scmi_imx_misc_info *minfo;
> > +	u32 version;
> > +	int ret;
> > +
> > +	ret = ph->xops->version_get(ph, &version);
> > +	if (ret)
> > +		return ret;
> > +
> > +	dev_info(ph->dev, "NXP SM MISC Version %d.%d\n",
> > +		 PROTOCOL_REV_MAJOR(version),
> PROTOCOL_REV_MINOR(version));
> > +
> > +	minfo = devm_kzalloc(ph->dev, sizeof(*minfo), GFP_KERNEL);
> > +	if (!minfo)
> > +		return -ENOMEM;
> > +
> > +	ret = scmi_imx_misc_attributes_get(ph, minfo);
> > +	if (ret)
> > +		return ret;
> > +
> > +	return ph->set_priv(ph, minfo, version); }
> > +
> > +static int scmi_imx_misc_ctrl_get(const struct scmi_protocol_handle *ph,
> > +				  u32 ctrl_id, u32 *num, u32 *val) {
> > +	struct scmi_imx_misc_ctrl_get_out *out;
> > +	struct scmi_xfer *t;
> > +	int ret, i;
> > +
> > +	ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_GET,
> sizeof(u32),
> > +				      0, &t);
> > +	if (ret)
> > +		return ret;
> > +
> > +	put_unaligned_le32(ctrl_id, t->tx.buf);
> > +	ret = ph->xops->do_xfer(ph, t);
> > +	if (!ret) {
> > +		out = t->rx.buf;
> > +		*num = le32_to_cpu(out->num);
> > +		for (i = 0; i < *num && i < MISC_MAX_VAL; i++)
> > +			val[i] = le32_to_cpu(out->val[i]);
> > +	}
> > +
> > +	ph->xops->xfer_put(ph, t);
> > +
> > +	return ret;
> > +}
> > +
> > +static int scmi_imx_misc_ctrl_set(const struct scmi_protocol_handle *ph,
> > +				  u32 ctrl_id, u32 num, u32 *val) {
> > +	struct scmi_imx_misc_ctrl_set_in *in;
> > +	struct scmi_xfer *t;
> > +	int ret, i;
> > +
> > +	ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
> > +	if (ret)
> > +		return ret;
> > +
> > +	if (num > MISC_MAX_VAL)
> > +		return -EINVAL;
> > +
> > +	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_SET,
> sizeof(*in),
> > +				      0, &t);
> > +	if (ret)
> > +		return ret;
> > +
> > +	in = t->tx.buf;
> > +	in->id = cpu_to_le32(ctrl_id);
> > +	in->num = cpu_to_le32(num);
> > +	for (i = 0; i < num; i++)
> > +		in->value[i] = cpu_to_le32(val[i]);
> > +
> > +	ret = ph->xops->do_xfer(ph, t);
> > +
> > +	ph->xops->xfer_put(ph, t);
> > +
> > +	return ret;
> > +}
> > +
> > +static const struct scmi_imx_misc_proto_ops scmi_imx_misc_proto_ops =
> {
> > +	.misc_ctrl_set = scmi_imx_misc_ctrl_set,
> > +	.misc_ctrl_get = scmi_imx_misc_ctrl_get, };
> > +
> > +static const struct scmi_protocol scmi_imx_misc = {
> > +	.id = SCMI_PROTOCOL_IMX_MISC,
> > +	.owner = THIS_MODULE,
> > +	.instance_init = &scmi_imx_misc_protocol_init,
> > +	.ops = &scmi_imx_misc_proto_ops,
> > +	.events = &scmi_imx_misc_protocol_events,
> > +	.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION, };
> > +module_scmi_protocol(scmi_imx_misc);
> > diff --git a/include/linux/scmi_imx_protocol.h
> > b/include/linux/scmi_imx_protocol.h
> > index 90ce011a4429..a69bd4a20f0f 100644
> > --- a/include/linux/scmi_imx_protocol.h
> > +++ b/include/linux/scmi_imx_protocol.h
> > @@ -13,8 +13,14 @@
> >  #include <linux/notifier.h>
> >  #include <linux/types.h>
> >
> > +#define SCMI_PAYLOAD_LEN	100
> > +
> > +#define SCMI_ARRAY(X, Y)	((SCMI_PAYLOAD_LEN - (X)) / sizeof(Y))
> > +#define MISC_MAX_VAL		SCMI_ARRAY(8, uint32_t)
> > +
> >  enum scmi_nxp_protocol {
> >  	SCMI_PROTOCOL_IMX_BBM = 0x81,
> > +	SCMI_PROTOCOL_IMX_MISC = 0x84,
> >  };
> >
> >  struct scmi_imx_bbm_proto_ops {
> > @@ -42,4 +48,15 @@ struct scmi_imx_bbm_notif_report {
> >  	unsigned int		rtc_id;
> >  	unsigned int		rtc_evt;
> >  };
> > +
> > +struct scmi_imx_misc_ctrl_notify_report {
> > +	ktime_t			timestamp;
> > +	unsigned int		ctrl_id;
> > +	unsigned int		flags;
> > +};
> > +
> > +struct scmi_imx_misc_proto_ops {
> > +	int (*misc_ctrl_set)(const struct scmi_protocol_handle *ph, u32 id,
> u32 num, u32 *val);
> > +	int (*misc_ctrl_get)(const struct scmi_protocol_handle *ph, u32 id,
> > +u32 *num, u32 *val); };
> >  #endif
> >
> > --
> > 2.37.1
> >
> >
> >
Marco Felsch April 7, 2024, 11:02 a.m. UTC | #3
Hi Peng,

On 24-04-07, Peng Fan wrote:
> > Subject: Re: [PATCH v2 4/6] firmware: arm_scmi: add initial support for i.MX
> > MISC protocol
> > 
> > Hi Peng,
> > 
> > On 24-04-05, Peng Fan (OSS) wrote:
> > > From: Peng Fan <peng.fan@nxp.com>
> > >
> > > The i.MX MISC protocol is for misc settings, such as gpio expander
> > > wakeup.
> > 
> > Can you elaborate a bit more please?
> 
> The gpio expander is under M33(SCMI firmware used core) I2C control,

Due to missing technical references I guess that your specific EVK has
an i2c-expander connected to the system-critical-i2c bus? The
system-critical-i2c should be only used for system critical topics like
PMIC control.

> But the gpio expander supports board function such as PCIE_WAKEUP,
> BTN_WAKEUP. So these are managed by MISC protocol.

This seems more like an specific i.MX95-EVK problem too me since you
have conneccted the i2c-gpio-expander to the system-critical-i2c bus
instead of using an bus available within Linux. Also can you please
provide me a link with the propsoal for the MISC protocol? I can't find
any references within the SCMI v3.2
https://developer.arm.com/documentation/den0056/e/ nor within the SCP
firmware git: https://github.com/ARM-software/SCP-firmware.

> SAI_CLK_MSEL in WAKEUP BLK CTRL is also managed by MISC Protocol.

You recently said that we need blk-ctrl drivers for managing/controlling
the GPR stuff within Linux since the SCMI firmware does not support
this. Now blk-ctrl GPR control is supported by the firmware?

Regards,
  Marco

> 
> And etc...
> 
> I will add more info in commit log in next version later, after I get more
> reviews on the patchset.
> 
> Thanks,
> Peng.
> 
> > 
> > Regards,
> >   Marco
> > 
> > 
> > >
> > > Signed-off-by: Peng Fan <peng.fan@nxp.com>
> > > ---
> > >  drivers/firmware/arm_scmi/Kconfig       |  10 ++
> > >  drivers/firmware/arm_scmi/Makefile      |   1 +
> > >  drivers/firmware/arm_scmi/imx-sm-misc.c | 305
> > ++++++++++++++++++++++++++++++++
> > >  include/linux/scmi_imx_protocol.h       |  17 ++
> > >  4 files changed, 333 insertions(+)
> > >
> > > diff --git a/drivers/firmware/arm_scmi/Kconfig
> > > b/drivers/firmware/arm_scmi/Kconfig
> > > index 56d11c9d9f47..bfeae92f6420 100644
> > > --- a/drivers/firmware/arm_scmi/Kconfig
> > > +++ b/drivers/firmware/arm_scmi/Kconfig
> > > @@ -191,3 +191,13 @@ config IMX_SCMI_BBM_EXT
> > >  	  and BUTTON.
> > >
> > >  	  This driver can also be built as a module.
> > > +
> > > +config IMX_SCMI_MISC_EXT
> > > +	tristate "i.MX SCMI MISC EXTENSION"
> > > +	depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
> > > +	default y if ARCH_MXC
> > > +	help
> > > +	  This enables i.MX System MISC control logic such as gpio expander
> > > +	  wakeup
> > > +
> > > +	  This driver can also be built as a module.
> > > diff --git a/drivers/firmware/arm_scmi/Makefile
> > > b/drivers/firmware/arm_scmi/Makefile
> > > index 327687acf857..a23fde721222 100644
> > > --- a/drivers/firmware/arm_scmi/Makefile
> > > +++ b/drivers/firmware/arm_scmi/Makefile
> > > @@ -12,6 +12,7 @@ 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-$(CONFIG_IMX_SCMI_BBM_EXT) += imx-sm-bbm.o
> > > +scmi-protocols-$(CONFIG_IMX_SCMI_MISC_EXT) += imx-sm-misc.o
> > >  scmi-module-objs := $(scmi-driver-y) $(scmi-protocols-y)
> > > $(scmi-transport-y)
> > >
> > >  obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-core.o diff --git
> > > a/drivers/firmware/arm_scmi/imx-sm-misc.c
> > > b/drivers/firmware/arm_scmi/imx-sm-misc.c
> > > new file mode 100644
> > > index 000000000000..1b0ec2281518
> > > --- /dev/null
> > > +++ b/drivers/firmware/arm_scmi/imx-sm-misc.c
> > > @@ -0,0 +1,305 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * System control and Management Interface (SCMI) NXP MISC Protocol
> > > + *
> > > + * Copyright 2024 NXP
> > > + */
> > > +
> > > +#define pr_fmt(fmt) "SCMI Notifications MISC - " fmt
> > > +
> > > +#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_misc_protocol_cmd {
> > > +	SCMI_IMX_MISC_CTRL_SET	= 0x3,
> > > +	SCMI_IMX_MISC_CTRL_GET	= 0x4,
> > > +	SCMI_IMX_MISC_CTRL_NOTIFY = 0x8,
> > > +};
> > > +
> > > +struct scmi_imx_misc_info {
> > > +	u32 version;
> > > +	u32 nr_dev_ctrl;
> > > +	u32 nr_brd_ctrl;
> > > +	u32 nr_reason;
> > > +};
> > > +
> > > +struct scmi_msg_imx_misc_protocol_attributes {
> > > +	__le32 attributes;
> > > +};
> > > +
> > > +#define GET_BRD_CTRLS_NR(x)	le32_get_bits((x), GENMASK(31,
> > 24))
> > > +#define GET_REASONS_NR(x)	le32_get_bits((x), GENMASK(23, 16))
> > > +#define GET_DEV_CTRLS_NR(x)	le32_get_bits((x), GENMASK(15, 0))
> > > +#define BRD_CTRL_START_ID	BIT(15)
> > > +
> > > +struct scmi_imx_misc_ctrl_set_in {
> > > +	__le32 id;
> > > +	__le32 num;
> > > +	__le32 value[MISC_MAX_VAL];
> > > +};
> > > +
> > > +struct scmi_imx_misc_ctrl_notify_in {
> > > +	__le32 ctrl_id;
> > > +	__le32 flags;
> > > +};
> > > +
> > > +struct scmi_imx_misc_ctrl_notify_payld {
> > > +	__le32 ctrl_id;
> > > +	__le32 flags;
> > > +};
> > > +
> > > +struct scmi_imx_misc_ctrl_get_out {
> > > +	__le32 num;
> > > +	__le32 *val;
> > > +};
> > > +
> > > +static int scmi_imx_misc_attributes_get(const struct scmi_protocol_handle
> > *ph,
> > > +					struct scmi_imx_misc_info *mi)
> > > +{
> > > +	int ret;
> > > +	struct scmi_xfer *t;
> > > +	struct scmi_msg_imx_misc_protocol_attributes *attr;
> > > +
> > > +	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) {
> > > +		mi->nr_dev_ctrl = GET_DEV_CTRLS_NR(attr->attributes);
> > > +		mi->nr_brd_ctrl = GET_BRD_CTRLS_NR(attr->attributes);
> > > +		mi->nr_reason = GET_REASONS_NR(attr->attributes);
> > > +		dev_info(ph->dev, "i.MX MISC NUM DEV CTRL: %d, NUM
> > BRD CTRL: %d,NUM Reason: %d\n",
> > > +			 mi->nr_dev_ctrl, mi->nr_brd_ctrl, mi->nr_reason);
> > > +	}
> > > +
> > > +	ph->xops->xfer_put(ph, t);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static int scmi_imx_misc_ctrl_validate_id(const struct
> > scmi_protocol_handle *ph,
> > > +					  u32 ctrl_id)
> > > +{
> > > +	struct scmi_imx_misc_info *mi = ph->get_priv(ph);
> > > +
> > > +	if ((ctrl_id < BRD_CTRL_START_ID) && (ctrl_id > mi->nr_dev_ctrl))
> > > +		return -EINVAL;
> > > +	if (ctrl_id >= BRD_CTRL_START_ID + mi->nr_brd_ctrl)
> > > +		return -EINVAL;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int scmi_imx_misc_ctrl_notify(const struct scmi_protocol_handle
> > *ph,
> > > +				     u32 ctrl_id, u32 flags)
> > > +{
> > > +	struct scmi_imx_misc_ctrl_notify_in *in;
> > > +	struct scmi_xfer *t;
> > > +	int ret;
> > > +
> > > +	ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_NOTIFY,
> > > +				      sizeof(*in), 0, &t);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	in = t->tx.buf;
> > > +	in->ctrl_id = cpu_to_le32(ctrl_id);
> > > +	in->flags = cpu_to_le32(flags);
> > > +
> > > +	ret = ph->xops->do_xfer(ph, t);
> > > +
> > > +	ph->xops->xfer_put(ph, t);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static int
> > > +scmi_imx_misc_ctrl_set_notify_enabled(const struct
> > scmi_protocol_handle *ph,
> > > +				      u8 evt_id, u32 src_id, bool enable) {
> > > +	int ret;
> > > +
> > > +	ret = scmi_imx_misc_ctrl_notify(ph, src_id, enable ? evt_id : 0);
> > > +	if (ret)
> > > +		dev_err(ph->dev, "FAIL_ENABLED - evt[%X] src[%d] -
> > ret:%d\n",
> > > +			evt_id, src_id, ret);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static int scmi_imx_misc_ctrl_get_num_sources(const struct
> > > +scmi_protocol_handle *ph) {
> > > +	return GENMASK(15, 0);
> > > +}
> > > +
> > > +static void *
> > > +scmi_imx_misc_ctrl_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)
> > > +{
> > > +	const struct scmi_imx_misc_ctrl_notify_payld *p = payld;
> > > +	struct scmi_imx_misc_ctrl_notify_report *r = report;
> > > +
> > > +	if (sizeof(*p) != payld_sz)
> > > +		return NULL;
> > > +
> > > +	r->timestamp = timestamp;
> > > +	r->ctrl_id = p->ctrl_id;
> > > +	r->flags = p->flags;
> > > +	*src_id = r->ctrl_id;
> > > +	dev_dbg(ph->dev, "%s: ctrl_id: %d flags: %d\n", __func__,
> > > +		r->ctrl_id, r->flags);
> > > +
> > > +	return r;
> > > +}
> > > +
> > > +static const struct scmi_event_ops scmi_imx_misc_event_ops = {
> > > +	.get_num_sources = scmi_imx_misc_ctrl_get_num_sources,
> > > +	.set_notify_enabled = scmi_imx_misc_ctrl_set_notify_enabled,
> > > +	.fill_custom_report = scmi_imx_misc_ctrl_fill_custom_report,
> > > +};
> > > +
> > > +static const struct scmi_event scmi_imx_misc_events[] = {
> > > +	{
> > > +		.id = SCMI_EVENT_IMX_MISC_CONTROL_DISABLED,
> > > +		.max_payld_sz = sizeof(struct
> > scmi_imx_misc_ctrl_notify_payld),
> > > +		.max_report_sz = sizeof(struct
> > scmi_imx_misc_ctrl_notify_report),
> > > +	},
> > > +	{
> > > +		.id = SCMI_EVENT_IMX_MISC_CONTROL_FALLING_EDGE,
> > > +		.max_payld_sz = sizeof(struct
> > scmi_imx_misc_ctrl_notify_payld),
> > > +		.max_report_sz = sizeof(struct
> > scmi_imx_misc_ctrl_notify_report),
> > > +	},
> > > +	{
> > > +		.id = SCMI_EVENT_IMX_MISC_CONTROL_RISING_EDGE,
> > > +		.max_payld_sz = sizeof(struct
> > scmi_imx_misc_ctrl_notify_payld),
> > > +		.max_report_sz = sizeof(struct
> > scmi_imx_misc_ctrl_notify_report),
> > > +	}
> > > +};
> > > +
> > > +static struct scmi_protocol_events scmi_imx_misc_protocol_events = {
> > > +	.queue_sz = SCMI_PROTO_QUEUE_SZ,
> > > +	.ops = &scmi_imx_misc_event_ops,
> > > +	.evts = scmi_imx_misc_events,
> > > +	.num_events = ARRAY_SIZE(scmi_imx_misc_events), };
> > > +
> > > +static int scmi_imx_misc_protocol_init(const struct
> > > +scmi_protocol_handle *ph) {
> > > +	struct scmi_imx_misc_info *minfo;
> > > +	u32 version;
> > > +	int ret;
> > > +
> > > +	ret = ph->xops->version_get(ph, &version);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	dev_info(ph->dev, "NXP SM MISC Version %d.%d\n",
> > > +		 PROTOCOL_REV_MAJOR(version),
> > PROTOCOL_REV_MINOR(version));
> > > +
> > > +	minfo = devm_kzalloc(ph->dev, sizeof(*minfo), GFP_KERNEL);
> > > +	if (!minfo)
> > > +		return -ENOMEM;
> > > +
> > > +	ret = scmi_imx_misc_attributes_get(ph, minfo);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	return ph->set_priv(ph, minfo, version); }
> > > +
> > > +static int scmi_imx_misc_ctrl_get(const struct scmi_protocol_handle *ph,
> > > +				  u32 ctrl_id, u32 *num, u32 *val) {
> > > +	struct scmi_imx_misc_ctrl_get_out *out;
> > > +	struct scmi_xfer *t;
> > > +	int ret, i;
> > > +
> > > +	ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_GET,
> > sizeof(u32),
> > > +				      0, &t);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	put_unaligned_le32(ctrl_id, t->tx.buf);
> > > +	ret = ph->xops->do_xfer(ph, t);
> > > +	if (!ret) {
> > > +		out = t->rx.buf;
> > > +		*num = le32_to_cpu(out->num);
> > > +		for (i = 0; i < *num && i < MISC_MAX_VAL; i++)
> > > +			val[i] = le32_to_cpu(out->val[i]);
> > > +	}
> > > +
> > > +	ph->xops->xfer_put(ph, t);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static int scmi_imx_misc_ctrl_set(const struct scmi_protocol_handle *ph,
> > > +				  u32 ctrl_id, u32 num, u32 *val) {
> > > +	struct scmi_imx_misc_ctrl_set_in *in;
> > > +	struct scmi_xfer *t;
> > > +	int ret, i;
> > > +
> > > +	ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	if (num > MISC_MAX_VAL)
> > > +		return -EINVAL;
> > > +
> > > +	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_SET,
> > sizeof(*in),
> > > +				      0, &t);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	in = t->tx.buf;
> > > +	in->id = cpu_to_le32(ctrl_id);
> > > +	in->num = cpu_to_le32(num);
> > > +	for (i = 0; i < num; i++)
> > > +		in->value[i] = cpu_to_le32(val[i]);
> > > +
> > > +	ret = ph->xops->do_xfer(ph, t);
> > > +
> > > +	ph->xops->xfer_put(ph, t);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static const struct scmi_imx_misc_proto_ops scmi_imx_misc_proto_ops =
> > {
> > > +	.misc_ctrl_set = scmi_imx_misc_ctrl_set,
> > > +	.misc_ctrl_get = scmi_imx_misc_ctrl_get, };
> > > +
> > > +static const struct scmi_protocol scmi_imx_misc = {
> > > +	.id = SCMI_PROTOCOL_IMX_MISC,
> > > +	.owner = THIS_MODULE,
> > > +	.instance_init = &scmi_imx_misc_protocol_init,
> > > +	.ops = &scmi_imx_misc_proto_ops,
> > > +	.events = &scmi_imx_misc_protocol_events,
> > > +	.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION, };
> > > +module_scmi_protocol(scmi_imx_misc);
> > > diff --git a/include/linux/scmi_imx_protocol.h
> > > b/include/linux/scmi_imx_protocol.h
> > > index 90ce011a4429..a69bd4a20f0f 100644
> > > --- a/include/linux/scmi_imx_protocol.h
> > > +++ b/include/linux/scmi_imx_protocol.h
> > > @@ -13,8 +13,14 @@
> > >  #include <linux/notifier.h>
> > >  #include <linux/types.h>
> > >
> > > +#define SCMI_PAYLOAD_LEN	100
> > > +
> > > +#define SCMI_ARRAY(X, Y)	((SCMI_PAYLOAD_LEN - (X)) / sizeof(Y))
> > > +#define MISC_MAX_VAL		SCMI_ARRAY(8, uint32_t)
> > > +
> > >  enum scmi_nxp_protocol {
> > >  	SCMI_PROTOCOL_IMX_BBM = 0x81,
> > > +	SCMI_PROTOCOL_IMX_MISC = 0x84,
> > >  };
> > >
> > >  struct scmi_imx_bbm_proto_ops {
> > > @@ -42,4 +48,15 @@ struct scmi_imx_bbm_notif_report {
> > >  	unsigned int		rtc_id;
> > >  	unsigned int		rtc_evt;
> > >  };
> > > +
> > > +struct scmi_imx_misc_ctrl_notify_report {
> > > +	ktime_t			timestamp;
> > > +	unsigned int		ctrl_id;
> > > +	unsigned int		flags;
> > > +};
> > > +
> > > +struct scmi_imx_misc_proto_ops {
> > > +	int (*misc_ctrl_set)(const struct scmi_protocol_handle *ph, u32 id,
> > u32 num, u32 *val);
> > > +	int (*misc_ctrl_get)(const struct scmi_protocol_handle *ph, u32 id,
> > > +u32 *num, u32 *val); };
> > >  #endif
> > >
> > > --
> > > 2.37.1
> > >
> > >
> > >
>
Peng Fan April 7, 2024, 11:16 a.m. UTC | #4
> Subject: Re: [PATCH v2 4/6] firmware: arm_scmi: add initial support for i.MX
> MISC protocol
> 
> Hi Peng,
> 
> On 24-04-07, Peng Fan wrote:
> > > Subject: Re: [PATCH v2 4/6] firmware: arm_scmi: add initial support
> > > for i.MX MISC protocol
> > >
> > > Hi Peng,
> > >
> > > On 24-04-05, Peng Fan (OSS) wrote:
> > > > From: Peng Fan <peng.fan@nxp.com>
> > > >
> > > > The i.MX MISC protocol is for misc settings, such as gpio expander
> > > > wakeup.
> > >
> > > Can you elaborate a bit more please?
> >
> > The gpio expander is under M33(SCMI firmware used core) I2C control,
> 
> Due to missing technical references I guess that your specific EVK has an i2c-
> expander connected to the system-critical-i2c bus? The system-critical-i2c
> should be only used for system critical topics like PMIC control.

Right.

> 
> > But the gpio expander supports board function such as PCIE_WAKEUP,
> > BTN_WAKEUP. So these are managed by MISC protocol.
> 
> This seems more like an specific i.MX95-EVK problem too me since you have
> conneccted the i2c-gpio-expander to the system-critical-i2c bus instead of
> using an bus available within Linux. Also can you please provide me a link
> with the propsoal for the MISC protocol? I can't find any references within the
> SCMI v3.2

It is i.MX VENDOR Extension, not a standard one in Spec.

> https://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fdevelo
> per.arm.com%2Fdocumentation%2Fden0056%2Fe%2F&data=05%7C02%7Cp
> eng.fan%40nxp.com%7C6120357a772045a0618808dc56f22c95%7C686ea1d
> 3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C638480845336536607%7CUnk
> nown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik
> 1haWwiLCJXVCI6Mn0%3D%7C0%7C%7C%7C&sdata=NI%2F8WMPuGzJwD74
> 1jcuknHZUR5uI2me9iEeWbeDKshE%3D&reserved=0 nor within the SCP
> firmware git:
> https://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub
> .com%2FARM-software%2FSCP-
> firmware&data=05%7C02%7Cpeng.fan%40nxp.com%7C6120357a772045a06
> 18808dc56f22c95%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C
> 638480845336550459%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAw
> MDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0%7C%7C%7C
> &sdata=N3bT9ItgvL4Z9xP1oxlmDTG%2FFjsXkuhJIA9wooJWfcM%3D&reserve
> d=0.
> 
> > SAI_CLK_MSEL in WAKEUP BLK CTRL is also managed by MISC Protocol.
> 
> You recently said that we need blk-ctrl drivers for managing/controlling the
> GPR stuff within Linux since the SCMI firmware does not support this. Now
> blk-ctrl GPR control is supported by the firmware?

AONMIX/WAKEUPMIX BLK CTRL is managed by SCMI firmware, for other
non system critical BLK CTRLs, they are managed by Linux directly, such as
GPU/VPU BLK CTRL and etc.

Regards,
Peng.
> 
> Regards,
>   Marco
> 
> >
> > And etc...
> >
> > I will add more info in commit log in next version later, after I get
> > more reviews on the patchset.
> >
> > Thanks,
> > Peng.
> >
> > >
> > > Regards,
> > >   Marco
> > >
> > >
> > > >
> > > > Signed-off-by: Peng Fan <peng.fan@nxp.com>
> > > > ---
> > > >  drivers/firmware/arm_scmi/Kconfig       |  10 ++
> > > >  drivers/firmware/arm_scmi/Makefile      |   1 +
> > > >  drivers/firmware/arm_scmi/imx-sm-misc.c | 305
> > > ++++++++++++++++++++++++++++++++
> > > >  include/linux/scmi_imx_protocol.h       |  17 ++
> > > >  4 files changed, 333 insertions(+)
> > > >
> > > > diff --git a/drivers/firmware/arm_scmi/Kconfig
> > > > b/drivers/firmware/arm_scmi/Kconfig
> > > > index 56d11c9d9f47..bfeae92f6420 100644
> > > > --- a/drivers/firmware/arm_scmi/Kconfig
> > > > +++ b/drivers/firmware/arm_scmi/Kconfig
> > > > @@ -191,3 +191,13 @@ config IMX_SCMI_BBM_EXT
> > > >  	  and BUTTON.
> > > >
> > > >  	  This driver can also be built as a module.
> > > > +
> > > > +config IMX_SCMI_MISC_EXT
> > > > +	tristate "i.MX SCMI MISC EXTENSION"
> > > > +	depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
> > > > +	default y if ARCH_MXC
> > > > +	help
> > > > +	  This enables i.MX System MISC control logic such as gpio expander
> > > > +	  wakeup
> > > > +
> > > > +	  This driver can also be built as a module.
> > > > diff --git a/drivers/firmware/arm_scmi/Makefile
> > > > b/drivers/firmware/arm_scmi/Makefile
> > > > index 327687acf857..a23fde721222 100644
> > > > --- a/drivers/firmware/arm_scmi/Makefile
> > > > +++ b/drivers/firmware/arm_scmi/Makefile
> > > > @@ -12,6 +12,7 @@ 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-$(CONFIG_IMX_SCMI_BBM_EXT) += imx-sm-bbm.o
> > > > +scmi-protocols-$(CONFIG_IMX_SCMI_MISC_EXT) += imx-sm-misc.o
> > > >  scmi-module-objs := $(scmi-driver-y) $(scmi-protocols-y)
> > > > $(scmi-transport-y)
> > > >
> > > >  obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-core.o diff --git
> > > > a/drivers/firmware/arm_scmi/imx-sm-misc.c
> > > > b/drivers/firmware/arm_scmi/imx-sm-misc.c
> > > > new file mode 100644
> > > > index 000000000000..1b0ec2281518
> > > > --- /dev/null
> > > > +++ b/drivers/firmware/arm_scmi/imx-sm-misc.c
> > > > @@ -0,0 +1,305 @@
> > > > +// SPDX-License-Identifier: GPL-2.0
> > > > +/*
> > > > + * System control and Management Interface (SCMI) NXP MISC
> > > > +Protocol
> > > > + *
> > > > + * Copyright 2024 NXP
> > > > + */
> > > > +
> > > > +#define pr_fmt(fmt) "SCMI Notifications MISC - " fmt
> > > > +
> > > > +#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_misc_protocol_cmd {
> > > > +	SCMI_IMX_MISC_CTRL_SET	= 0x3,
> > > > +	SCMI_IMX_MISC_CTRL_GET	= 0x4,
> > > > +	SCMI_IMX_MISC_CTRL_NOTIFY = 0x8, };
> > > > +
> > > > +struct scmi_imx_misc_info {
> > > > +	u32 version;
> > > > +	u32 nr_dev_ctrl;
> > > > +	u32 nr_brd_ctrl;
> > > > +	u32 nr_reason;
> > > > +};
> > > > +
> > > > +struct scmi_msg_imx_misc_protocol_attributes {
> > > > +	__le32 attributes;
> > > > +};
> > > > +
> > > > +#define GET_BRD_CTRLS_NR(x)	le32_get_bits((x), GENMASK(31,
> > > 24))
> > > > +#define GET_REASONS_NR(x)	le32_get_bits((x), GENMASK(23,
> 16))
> > > > +#define GET_DEV_CTRLS_NR(x)	le32_get_bits((x), GENMASK(15, 0))
> > > > +#define BRD_CTRL_START_ID	BIT(15)
> > > > +
> > > > +struct scmi_imx_misc_ctrl_set_in {
> > > > +	__le32 id;
> > > > +	__le32 num;
> > > > +	__le32 value[MISC_MAX_VAL];
> > > > +};
> > > > +
> > > > +struct scmi_imx_misc_ctrl_notify_in {
> > > > +	__le32 ctrl_id;
> > > > +	__le32 flags;
> > > > +};
> > > > +
> > > > +struct scmi_imx_misc_ctrl_notify_payld {
> > > > +	__le32 ctrl_id;
> > > > +	__le32 flags;
> > > > +};
> > > > +
> > > > +struct scmi_imx_misc_ctrl_get_out {
> > > > +	__le32 num;
> > > > +	__le32 *val;
> > > > +};
> > > > +
> > > > +static int scmi_imx_misc_attributes_get(const struct
> > > > +scmi_protocol_handle
> > > *ph,
> > > > +					struct scmi_imx_misc_info *mi) {
> > > > +	int ret;
> > > > +	struct scmi_xfer *t;
> > > > +	struct scmi_msg_imx_misc_protocol_attributes *attr;
> > > > +
> > > > +	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) {
> > > > +		mi->nr_dev_ctrl = GET_DEV_CTRLS_NR(attr->attributes);
> > > > +		mi->nr_brd_ctrl = GET_BRD_CTRLS_NR(attr->attributes);
> > > > +		mi->nr_reason = GET_REASONS_NR(attr->attributes);
> > > > +		dev_info(ph->dev, "i.MX MISC NUM DEV CTRL: %d, NUM
> > > BRD CTRL: %d,NUM Reason: %d\n",
> > > > +			 mi->nr_dev_ctrl, mi->nr_brd_ctrl, mi->nr_reason);
> > > > +	}
> > > > +
> > > > +	ph->xops->xfer_put(ph, t);
> > > > +
> > > > +	return ret;
> > > > +}
> > > > +
> > > > +static int scmi_imx_misc_ctrl_validate_id(const struct
> > > scmi_protocol_handle *ph,
> > > > +					  u32 ctrl_id)
> > > > +{
> > > > +	struct scmi_imx_misc_info *mi = ph->get_priv(ph);
> > > > +
> > > > +	if ((ctrl_id < BRD_CTRL_START_ID) && (ctrl_id > mi->nr_dev_ctrl))
> > > > +		return -EINVAL;
> > > > +	if (ctrl_id >= BRD_CTRL_START_ID + mi->nr_brd_ctrl)
> > > > +		return -EINVAL;
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +static int scmi_imx_misc_ctrl_notify(const struct
> > > > +scmi_protocol_handle
> > > *ph,
> > > > +				     u32 ctrl_id, u32 flags)
> > > > +{
> > > > +	struct scmi_imx_misc_ctrl_notify_in *in;
> > > > +	struct scmi_xfer *t;
> > > > +	int ret;
> > > > +
> > > > +	ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
> > > > +	if (ret)
> > > > +		return ret;
> > > > +
> > > > +	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_NOTIFY,
> > > > +				      sizeof(*in), 0, &t);
> > > > +	if (ret)
> > > > +		return ret;
> > > > +
> > > > +	in = t->tx.buf;
> > > > +	in->ctrl_id = cpu_to_le32(ctrl_id);
> > > > +	in->flags = cpu_to_le32(flags);
> > > > +
> > > > +	ret = ph->xops->do_xfer(ph, t);
> > > > +
> > > > +	ph->xops->xfer_put(ph, t);
> > > > +
> > > > +	return ret;
> > > > +}
> > > > +
> > > > +static int
> > > > +scmi_imx_misc_ctrl_set_notify_enabled(const struct
> > > scmi_protocol_handle *ph,
> > > > +				      u8 evt_id, u32 src_id, bool enable) {
> > > > +	int ret;
> > > > +
> > > > +	ret = scmi_imx_misc_ctrl_notify(ph, src_id, enable ? evt_id : 0);
> > > > +	if (ret)
> > > > +		dev_err(ph->dev, "FAIL_ENABLED - evt[%X] src[%d] -
> > > ret:%d\n",
> > > > +			evt_id, src_id, ret);
> > > > +
> > > > +	return ret;
> > > > +}
> > > > +
> > > > +static int scmi_imx_misc_ctrl_get_num_sources(const struct
> > > > +scmi_protocol_handle *ph) {
> > > > +	return GENMASK(15, 0);
> > > > +}
> > > > +
> > > > +static void *
> > > > +scmi_imx_misc_ctrl_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) {
> > > > +	const struct scmi_imx_misc_ctrl_notify_payld *p = payld;
> > > > +	struct scmi_imx_misc_ctrl_notify_report *r = report;
> > > > +
> > > > +	if (sizeof(*p) != payld_sz)
> > > > +		return NULL;
> > > > +
> > > > +	r->timestamp = timestamp;
> > > > +	r->ctrl_id = p->ctrl_id;
> > > > +	r->flags = p->flags;
> > > > +	*src_id = r->ctrl_id;
> > > > +	dev_dbg(ph->dev, "%s: ctrl_id: %d flags: %d\n", __func__,
> > > > +		r->ctrl_id, r->flags);
> > > > +
> > > > +	return r;
> > > > +}
> > > > +
> > > > +static const struct scmi_event_ops scmi_imx_misc_event_ops = {
> > > > +	.get_num_sources = scmi_imx_misc_ctrl_get_num_sources,
> > > > +	.set_notify_enabled = scmi_imx_misc_ctrl_set_notify_enabled,
> > > > +	.fill_custom_report = scmi_imx_misc_ctrl_fill_custom_report,
> > > > +};
> > > > +
> > > > +static const struct scmi_event scmi_imx_misc_events[] = {
> > > > +	{
> > > > +		.id = SCMI_EVENT_IMX_MISC_CONTROL_DISABLED,
> > > > +		.max_payld_sz = sizeof(struct
> > > scmi_imx_misc_ctrl_notify_payld),
> > > > +		.max_report_sz = sizeof(struct
> > > scmi_imx_misc_ctrl_notify_report),
> > > > +	},
> > > > +	{
> > > > +		.id = SCMI_EVENT_IMX_MISC_CONTROL_FALLING_EDGE,
> > > > +		.max_payld_sz = sizeof(struct
> > > scmi_imx_misc_ctrl_notify_payld),
> > > > +		.max_report_sz = sizeof(struct
> > > scmi_imx_misc_ctrl_notify_report),
> > > > +	},
> > > > +	{
> > > > +		.id = SCMI_EVENT_IMX_MISC_CONTROL_RISING_EDGE,
> > > > +		.max_payld_sz = sizeof(struct
> > > scmi_imx_misc_ctrl_notify_payld),
> > > > +		.max_report_sz = sizeof(struct
> > > scmi_imx_misc_ctrl_notify_report),
> > > > +	}
> > > > +};
> > > > +
> > > > +static struct scmi_protocol_events scmi_imx_misc_protocol_events = {
> > > > +	.queue_sz = SCMI_PROTO_QUEUE_SZ,
> > > > +	.ops = &scmi_imx_misc_event_ops,
> > > > +	.evts = scmi_imx_misc_events,
> > > > +	.num_events = ARRAY_SIZE(scmi_imx_misc_events), };
> > > > +
> > > > +static int scmi_imx_misc_protocol_init(const struct
> > > > +scmi_protocol_handle *ph) {
> > > > +	struct scmi_imx_misc_info *minfo;
> > > > +	u32 version;
> > > > +	int ret;
> > > > +
> > > > +	ret = ph->xops->version_get(ph, &version);
> > > > +	if (ret)
> > > > +		return ret;
> > > > +
> > > > +	dev_info(ph->dev, "NXP SM MISC Version %d.%d\n",
> > > > +		 PROTOCOL_REV_MAJOR(version),
> > > PROTOCOL_REV_MINOR(version));
> > > > +
> > > > +	minfo = devm_kzalloc(ph->dev, sizeof(*minfo), GFP_KERNEL);
> > > > +	if (!minfo)
> > > > +		return -ENOMEM;
> > > > +
> > > > +	ret = scmi_imx_misc_attributes_get(ph, minfo);
> > > > +	if (ret)
> > > > +		return ret;
> > > > +
> > > > +	return ph->set_priv(ph, minfo, version); }
> > > > +
> > > > +static int scmi_imx_misc_ctrl_get(const struct scmi_protocol_handle
> *ph,
> > > > +				  u32 ctrl_id, u32 *num, u32 *val) {
> > > > +	struct scmi_imx_misc_ctrl_get_out *out;
> > > > +	struct scmi_xfer *t;
> > > > +	int ret, i;
> > > > +
> > > > +	ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
> > > > +	if (ret)
> > > > +		return ret;
> > > > +
> > > > +	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_GET,
> > > sizeof(u32),
> > > > +				      0, &t);
> > > > +	if (ret)
> > > > +		return ret;
> > > > +
> > > > +	put_unaligned_le32(ctrl_id, t->tx.buf);
> > > > +	ret = ph->xops->do_xfer(ph, t);
> > > > +	if (!ret) {
> > > > +		out = t->rx.buf;
> > > > +		*num = le32_to_cpu(out->num);
> > > > +		for (i = 0; i < *num && i < MISC_MAX_VAL; i++)
> > > > +			val[i] = le32_to_cpu(out->val[i]);
> > > > +	}
> > > > +
> > > > +	ph->xops->xfer_put(ph, t);
> > > > +
> > > > +	return ret;
> > > > +}
> > > > +
> > > > +static int scmi_imx_misc_ctrl_set(const struct scmi_protocol_handle
> *ph,
> > > > +				  u32 ctrl_id, u32 num, u32 *val) {
> > > > +	struct scmi_imx_misc_ctrl_set_in *in;
> > > > +	struct scmi_xfer *t;
> > > > +	int ret, i;
> > > > +
> > > > +	ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
> > > > +	if (ret)
> > > > +		return ret;
> > > > +
> > > > +	if (num > MISC_MAX_VAL)
> > > > +		return -EINVAL;
> > > > +
> > > > +	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_SET,
> > > sizeof(*in),
> > > > +				      0, &t);
> > > > +	if (ret)
> > > > +		return ret;
> > > > +
> > > > +	in = t->tx.buf;
> > > > +	in->id = cpu_to_le32(ctrl_id);
> > > > +	in->num = cpu_to_le32(num);
> > > > +	for (i = 0; i < num; i++)
> > > > +		in->value[i] = cpu_to_le32(val[i]);
> > > > +
> > > > +	ret = ph->xops->do_xfer(ph, t);
> > > > +
> > > > +	ph->xops->xfer_put(ph, t);
> > > > +
> > > > +	return ret;
> > > > +}
> > > > +
> > > > +static const struct scmi_imx_misc_proto_ops
> > > > +scmi_imx_misc_proto_ops =
> > > {
> > > > +	.misc_ctrl_set = scmi_imx_misc_ctrl_set,
> > > > +	.misc_ctrl_get = scmi_imx_misc_ctrl_get, };
> > > > +
> > > > +static const struct scmi_protocol scmi_imx_misc = {
> > > > +	.id = SCMI_PROTOCOL_IMX_MISC,
> > > > +	.owner = THIS_MODULE,
> > > > +	.instance_init = &scmi_imx_misc_protocol_init,
> > > > +	.ops = &scmi_imx_misc_proto_ops,
> > > > +	.events = &scmi_imx_misc_protocol_events,
> > > > +	.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION, };
> > > > +module_scmi_protocol(scmi_imx_misc);
> > > > diff --git a/include/linux/scmi_imx_protocol.h
> > > > b/include/linux/scmi_imx_protocol.h
> > > > index 90ce011a4429..a69bd4a20f0f 100644
> > > > --- a/include/linux/scmi_imx_protocol.h
> > > > +++ b/include/linux/scmi_imx_protocol.h
> > > > @@ -13,8 +13,14 @@
> > > >  #include <linux/notifier.h>
> > > >  #include <linux/types.h>
> > > >
> > > > +#define SCMI_PAYLOAD_LEN	100
> > > > +
> > > > +#define SCMI_ARRAY(X, Y)	((SCMI_PAYLOAD_LEN - (X)) / sizeof(Y))
> > > > +#define MISC_MAX_VAL		SCMI_ARRAY(8, uint32_t)
> > > > +
> > > >  enum scmi_nxp_protocol {
> > > >  	SCMI_PROTOCOL_IMX_BBM = 0x81,
> > > > +	SCMI_PROTOCOL_IMX_MISC = 0x84,
> > > >  };
> > > >
> > > >  struct scmi_imx_bbm_proto_ops {
> > > > @@ -42,4 +48,15 @@ struct scmi_imx_bbm_notif_report {
> > > >  	unsigned int		rtc_id;
> > > >  	unsigned int		rtc_evt;
> > > >  };
> > > > +
> > > > +struct scmi_imx_misc_ctrl_notify_report {
> > > > +	ktime_t			timestamp;
> > > > +	unsigned int		ctrl_id;
> > > > +	unsigned int		flags;
> > > > +};
> > > > +
> > > > +struct scmi_imx_misc_proto_ops {
> > > > +	int (*misc_ctrl_set)(const struct scmi_protocol_handle *ph, u32
> > > > +id,
> > > u32 num, u32 *val);
> > > > +	int (*misc_ctrl_get)(const struct scmi_protocol_handle *ph, u32
> > > > +id,
> > > > +u32 *num, u32 *val); };
> > > >  #endif
> > > >
> > > > --
> > > > 2.37.1
> > > >
> > > >
> > > >
> >
diff mbox series

Patch

diff --git a/drivers/firmware/arm_scmi/Kconfig b/drivers/firmware/arm_scmi/Kconfig
index 56d11c9d9f47..bfeae92f6420 100644
--- a/drivers/firmware/arm_scmi/Kconfig
+++ b/drivers/firmware/arm_scmi/Kconfig
@@ -191,3 +191,13 @@  config IMX_SCMI_BBM_EXT
 	  and BUTTON.
 
 	  This driver can also be built as a module.
+
+config IMX_SCMI_MISC_EXT
+	tristate "i.MX SCMI MISC EXTENSION"
+	depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
+	default y if ARCH_MXC
+	help
+	  This enables i.MX System MISC control logic such as gpio expander
+	  wakeup
+
+	  This driver can also be built as a module.
diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index 327687acf857..a23fde721222 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -12,6 +12,7 @@  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-$(CONFIG_IMX_SCMI_BBM_EXT) += imx-sm-bbm.o
+scmi-protocols-$(CONFIG_IMX_SCMI_MISC_EXT) += imx-sm-misc.o
 scmi-module-objs := $(scmi-driver-y) $(scmi-protocols-y) $(scmi-transport-y)
 
 obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-core.o
diff --git a/drivers/firmware/arm_scmi/imx-sm-misc.c b/drivers/firmware/arm_scmi/imx-sm-misc.c
new file mode 100644
index 000000000000..1b0ec2281518
--- /dev/null
+++ b/drivers/firmware/arm_scmi/imx-sm-misc.c
@@ -0,0 +1,305 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System control and Management Interface (SCMI) NXP MISC Protocol
+ *
+ * Copyright 2024 NXP
+ */
+
+#define pr_fmt(fmt) "SCMI Notifications MISC - " fmt
+
+#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_misc_protocol_cmd {
+	SCMI_IMX_MISC_CTRL_SET	= 0x3,
+	SCMI_IMX_MISC_CTRL_GET	= 0x4,
+	SCMI_IMX_MISC_CTRL_NOTIFY = 0x8,
+};
+
+struct scmi_imx_misc_info {
+	u32 version;
+	u32 nr_dev_ctrl;
+	u32 nr_brd_ctrl;
+	u32 nr_reason;
+};
+
+struct scmi_msg_imx_misc_protocol_attributes {
+	__le32 attributes;
+};
+
+#define GET_BRD_CTRLS_NR(x)	le32_get_bits((x), GENMASK(31, 24))
+#define GET_REASONS_NR(x)	le32_get_bits((x), GENMASK(23, 16))
+#define GET_DEV_CTRLS_NR(x)	le32_get_bits((x), GENMASK(15, 0))
+#define BRD_CTRL_START_ID	BIT(15)
+
+struct scmi_imx_misc_ctrl_set_in {
+	__le32 id;
+	__le32 num;
+	__le32 value[MISC_MAX_VAL];
+};
+
+struct scmi_imx_misc_ctrl_notify_in {
+	__le32 ctrl_id;
+	__le32 flags;
+};
+
+struct scmi_imx_misc_ctrl_notify_payld {
+	__le32 ctrl_id;
+	__le32 flags;
+};
+
+struct scmi_imx_misc_ctrl_get_out {
+	__le32 num;
+	__le32 *val;
+};
+
+static int scmi_imx_misc_attributes_get(const struct scmi_protocol_handle *ph,
+					struct scmi_imx_misc_info *mi)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_msg_imx_misc_protocol_attributes *attr;
+
+	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) {
+		mi->nr_dev_ctrl = GET_DEV_CTRLS_NR(attr->attributes);
+		mi->nr_brd_ctrl = GET_BRD_CTRLS_NR(attr->attributes);
+		mi->nr_reason = GET_REASONS_NR(attr->attributes);
+		dev_info(ph->dev, "i.MX MISC NUM DEV CTRL: %d, NUM BRD CTRL: %d,NUM Reason: %d\n",
+			 mi->nr_dev_ctrl, mi->nr_brd_ctrl, mi->nr_reason);
+	}
+
+	ph->xops->xfer_put(ph, t);
+
+	return ret;
+}
+
+static int scmi_imx_misc_ctrl_validate_id(const struct scmi_protocol_handle *ph,
+					  u32 ctrl_id)
+{
+	struct scmi_imx_misc_info *mi = ph->get_priv(ph);
+
+	if ((ctrl_id < BRD_CTRL_START_ID) && (ctrl_id > mi->nr_dev_ctrl))
+		return -EINVAL;
+	if (ctrl_id >= BRD_CTRL_START_ID + mi->nr_brd_ctrl)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int scmi_imx_misc_ctrl_notify(const struct scmi_protocol_handle *ph,
+				     u32 ctrl_id, u32 flags)
+{
+	struct scmi_imx_misc_ctrl_notify_in *in;
+	struct scmi_xfer *t;
+	int ret;
+
+	ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
+	if (ret)
+		return ret;
+
+	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_NOTIFY,
+				      sizeof(*in), 0, &t);
+	if (ret)
+		return ret;
+
+	in = t->tx.buf;
+	in->ctrl_id = cpu_to_le32(ctrl_id);
+	in->flags = cpu_to_le32(flags);
+
+	ret = ph->xops->do_xfer(ph, t);
+
+	ph->xops->xfer_put(ph, t);
+
+	return ret;
+}
+
+static int
+scmi_imx_misc_ctrl_set_notify_enabled(const struct scmi_protocol_handle *ph,
+				      u8 evt_id, u32 src_id, bool enable)
+{
+	int ret;
+
+	ret = scmi_imx_misc_ctrl_notify(ph, src_id, enable ? evt_id : 0);
+	if (ret)
+		dev_err(ph->dev, "FAIL_ENABLED - evt[%X] src[%d] - ret:%d\n",
+			evt_id, src_id, ret);
+
+	return ret;
+}
+
+static int scmi_imx_misc_ctrl_get_num_sources(const struct scmi_protocol_handle *ph)
+{
+	return GENMASK(15, 0);
+}
+
+static void *
+scmi_imx_misc_ctrl_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)
+{
+	const struct scmi_imx_misc_ctrl_notify_payld *p = payld;
+	struct scmi_imx_misc_ctrl_notify_report *r = report;
+
+	if (sizeof(*p) != payld_sz)
+		return NULL;
+
+	r->timestamp = timestamp;
+	r->ctrl_id = p->ctrl_id;
+	r->flags = p->flags;
+	*src_id = r->ctrl_id;
+	dev_dbg(ph->dev, "%s: ctrl_id: %d flags: %d\n", __func__,
+		r->ctrl_id, r->flags);
+
+	return r;
+}
+
+static const struct scmi_event_ops scmi_imx_misc_event_ops = {
+	.get_num_sources = scmi_imx_misc_ctrl_get_num_sources,
+	.set_notify_enabled = scmi_imx_misc_ctrl_set_notify_enabled,
+	.fill_custom_report = scmi_imx_misc_ctrl_fill_custom_report,
+};
+
+static const struct scmi_event scmi_imx_misc_events[] = {
+	{
+		.id = SCMI_EVENT_IMX_MISC_CONTROL_DISABLED,
+		.max_payld_sz = sizeof(struct scmi_imx_misc_ctrl_notify_payld),
+		.max_report_sz = sizeof(struct scmi_imx_misc_ctrl_notify_report),
+	},
+	{
+		.id = SCMI_EVENT_IMX_MISC_CONTROL_FALLING_EDGE,
+		.max_payld_sz = sizeof(struct scmi_imx_misc_ctrl_notify_payld),
+		.max_report_sz = sizeof(struct scmi_imx_misc_ctrl_notify_report),
+	},
+	{
+		.id = SCMI_EVENT_IMX_MISC_CONTROL_RISING_EDGE,
+		.max_payld_sz = sizeof(struct scmi_imx_misc_ctrl_notify_payld),
+		.max_report_sz = sizeof(struct scmi_imx_misc_ctrl_notify_report),
+	}
+};
+
+static struct scmi_protocol_events scmi_imx_misc_protocol_events = {
+	.queue_sz = SCMI_PROTO_QUEUE_SZ,
+	.ops = &scmi_imx_misc_event_ops,
+	.evts = scmi_imx_misc_events,
+	.num_events = ARRAY_SIZE(scmi_imx_misc_events),
+};
+
+static int scmi_imx_misc_protocol_init(const struct scmi_protocol_handle *ph)
+{
+	struct scmi_imx_misc_info *minfo;
+	u32 version;
+	int ret;
+
+	ret = ph->xops->version_get(ph, &version);
+	if (ret)
+		return ret;
+
+	dev_info(ph->dev, "NXP SM MISC Version %d.%d\n",
+		 PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+
+	minfo = devm_kzalloc(ph->dev, sizeof(*minfo), GFP_KERNEL);
+	if (!minfo)
+		return -ENOMEM;
+
+	ret = scmi_imx_misc_attributes_get(ph, minfo);
+	if (ret)
+		return ret;
+
+	return ph->set_priv(ph, minfo, version);
+}
+
+static int scmi_imx_misc_ctrl_get(const struct scmi_protocol_handle *ph,
+				  u32 ctrl_id, u32 *num, u32 *val)
+{
+	struct scmi_imx_misc_ctrl_get_out *out;
+	struct scmi_xfer *t;
+	int ret, i;
+
+	ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
+	if (ret)
+		return ret;
+
+	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_GET, sizeof(u32),
+				      0, &t);
+	if (ret)
+		return ret;
+
+	put_unaligned_le32(ctrl_id, t->tx.buf);
+	ret = ph->xops->do_xfer(ph, t);
+	if (!ret) {
+		out = t->rx.buf;
+		*num = le32_to_cpu(out->num);
+		for (i = 0; i < *num && i < MISC_MAX_VAL; i++)
+			val[i] = le32_to_cpu(out->val[i]);
+	}
+
+	ph->xops->xfer_put(ph, t);
+
+	return ret;
+}
+
+static int scmi_imx_misc_ctrl_set(const struct scmi_protocol_handle *ph,
+				  u32 ctrl_id, u32 num, u32 *val)
+{
+	struct scmi_imx_misc_ctrl_set_in *in;
+	struct scmi_xfer *t;
+	int ret, i;
+
+	ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
+	if (ret)
+		return ret;
+
+	if (num > MISC_MAX_VAL)
+		return -EINVAL;
+
+	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_SET, sizeof(*in),
+				      0, &t);
+	if (ret)
+		return ret;
+
+	in = t->tx.buf;
+	in->id = cpu_to_le32(ctrl_id);
+	in->num = cpu_to_le32(num);
+	for (i = 0; i < num; i++)
+		in->value[i] = cpu_to_le32(val[i]);
+
+	ret = ph->xops->do_xfer(ph, t);
+
+	ph->xops->xfer_put(ph, t);
+
+	return ret;
+}
+
+static const struct scmi_imx_misc_proto_ops scmi_imx_misc_proto_ops = {
+	.misc_ctrl_set = scmi_imx_misc_ctrl_set,
+	.misc_ctrl_get = scmi_imx_misc_ctrl_get,
+};
+
+static const struct scmi_protocol scmi_imx_misc = {
+	.id = SCMI_PROTOCOL_IMX_MISC,
+	.owner = THIS_MODULE,
+	.instance_init = &scmi_imx_misc_protocol_init,
+	.ops = &scmi_imx_misc_proto_ops,
+	.events = &scmi_imx_misc_protocol_events,
+	.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
+};
+module_scmi_protocol(scmi_imx_misc);
diff --git a/include/linux/scmi_imx_protocol.h b/include/linux/scmi_imx_protocol.h
index 90ce011a4429..a69bd4a20f0f 100644
--- a/include/linux/scmi_imx_protocol.h
+++ b/include/linux/scmi_imx_protocol.h
@@ -13,8 +13,14 @@ 
 #include <linux/notifier.h>
 #include <linux/types.h>
 
+#define SCMI_PAYLOAD_LEN	100
+
+#define SCMI_ARRAY(X, Y)	((SCMI_PAYLOAD_LEN - (X)) / sizeof(Y))
+#define MISC_MAX_VAL		SCMI_ARRAY(8, uint32_t)
+
 enum scmi_nxp_protocol {
 	SCMI_PROTOCOL_IMX_BBM = 0x81,
+	SCMI_PROTOCOL_IMX_MISC = 0x84,
 };
 
 struct scmi_imx_bbm_proto_ops {
@@ -42,4 +48,15 @@  struct scmi_imx_bbm_notif_report {
 	unsigned int		rtc_id;
 	unsigned int		rtc_evt;
 };
+
+struct scmi_imx_misc_ctrl_notify_report {
+	ktime_t			timestamp;
+	unsigned int		ctrl_id;
+	unsigned int		flags;
+};
+
+struct scmi_imx_misc_proto_ops {
+	int (*misc_ctrl_set)(const struct scmi_protocol_handle *ph, u32 id, u32 num, u32 *val);
+	int (*misc_ctrl_get)(const struct scmi_protocol_handle *ph, u32 id, u32 *num, u32 *val);
+};
 #endif