firmware: imx: Add i.MX95 SCMI LMM driver

The i.MX95 System manager exports SCMI LMM protocol for linux to manage
Logical Machines. The driver is to use the LMM Protocol interface to
boot, shutdown a LM.

Reviewed-by: Cristian Marussi <cristian.marussi@arm.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Message-Id: <20250408-imx-lmm-cpu-v4-5-4c5f4a456e49@nxp.com>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
pull/1253/head
Peng Fan 2025-04-08 16:44:29 +08:00 committed by Sudeep Holla
parent e68c305bc2
commit 7242bbf418
5 changed files with 118 additions and 0 deletions

View File

@ -26,6 +26,7 @@ config IMX_SCMI_CPU_EXT
config IMX_SCMI_LMM_EXT
tristate "i.MX SCMI LMM EXTENSION"
depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
depends on IMX_SCMI_LMM_DRV
default y if ARCH_MXC
help
This enables i.MX System Logical Machine Protocol to

View File

@ -23,6 +23,17 @@ config IMX_SCU
This driver manages the IPC interface between host CPU and the
SCU firmware running on M4.
config IMX_SCMI_LMM_DRV
tristate "IMX SCMI LMM Protocol driver"
depends on ARCH_MXC || COMPILE_TEST
default y if ARCH_MXC
help
The System Controller Management Interface firmware (SCMI FW) is
a low-level system function which runs on a dedicated Cortex-M
core that could provide Logical Machine management features.
This driver can also be built as a module.
config IMX_SCMI_MISC_DRV
tristate "IMX SCMI MISC Protocol driver"
depends on ARCH_MXC || COMPILE_TEST

View File

@ -2,3 +2,4 @@
obj-$(CONFIG_IMX_DSP) += imx-dsp.o
obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o rm.o imx-scu-soc.o
obj-${CONFIG_IMX_SCMI_MISC_DRV} += sm-misc.o
obj-${CONFIG_IMX_SCMI_LMM_DRV} += sm-lmm.o

View File

@ -0,0 +1,91 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2025 NXP
*/
#include <linux/firmware/imx/sm.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>
static const struct scmi_imx_lmm_proto_ops *imx_lmm_ops;
static struct scmi_protocol_handle *ph;
int scmi_imx_lmm_info(u32 lmid, struct scmi_imx_lmm_info *info)
{
if (!ph)
return -EPROBE_DEFER;
if (!info)
return -EINVAL;
return imx_lmm_ops->lmm_info(ph, lmid, info);
};
EXPORT_SYMBOL(scmi_imx_lmm_info);
int scmi_imx_lmm_reset_vector_set(u32 lmid, u32 cpuid, u32 flags, u64 vector)
{
if (!ph)
return -EPROBE_DEFER;
return imx_lmm_ops->lmm_reset_vector_set(ph, lmid, cpuid, flags, vector);
}
EXPORT_SYMBOL(scmi_imx_lmm_reset_vector_set);
int scmi_imx_lmm_operation(u32 lmid, enum scmi_imx_lmm_op op, u32 flags)
{
if (!ph)
return -EPROBE_DEFER;
switch (op) {
case SCMI_IMX_LMM_BOOT:
return imx_lmm_ops->lmm_power_boot(ph, lmid, true);
case SCMI_IMX_LMM_POWER_ON:
return imx_lmm_ops->lmm_power_boot(ph, lmid, false);
case SCMI_IMX_LMM_SHUTDOWN:
return imx_lmm_ops->lmm_shutdown(ph, lmid, flags);
default:
break;
}
return -EINVAL;
}
EXPORT_SYMBOL(scmi_imx_lmm_operation);
static int scmi_imx_lmm_probe(struct scmi_device *sdev)
{
const struct scmi_handle *handle = sdev->handle;
if (!handle)
return -ENODEV;
if (imx_lmm_ops) {
dev_err(&sdev->dev, "lmm already initialized\n");
return -EEXIST;
}
imx_lmm_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_IMX_LMM, &ph);
if (IS_ERR(imx_lmm_ops))
return PTR_ERR(imx_lmm_ops);
return 0;
}
static const struct scmi_device_id scmi_id_table[] = {
{ SCMI_PROTOCOL_IMX_LMM, "imx-lmm" },
{ },
};
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
static struct scmi_driver scmi_imx_lmm_driver = {
.name = "scmi-imx-lmm",
.probe = scmi_imx_lmm_probe,
.id_table = scmi_id_table,
};
module_scmi_driver(scmi_imx_lmm_driver);
MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
MODULE_DESCRIPTION("IMX SM LMM driver");
MODULE_LICENSE("GPL");

View File

@ -8,6 +8,7 @@
#include <linux/bitfield.h>
#include <linux/errno.h>
#include <linux/scmi_imx_protocol.h>
#include <linux/types.h>
#define SCMI_IMX_CTRL_PDM_CLK_SEL 0 /* AON PDM clock sel */
@ -20,4 +21,17 @@
int scmi_imx_misc_ctrl_get(u32 id, u32 *num, u32 *val);
int scmi_imx_misc_ctrl_set(u32 id, u32 val);
enum scmi_imx_lmm_op {
SCMI_IMX_LMM_BOOT,
SCMI_IMX_LMM_POWER_ON,
SCMI_IMX_LMM_SHUTDOWN,
};
/* For shutdown pperation */
#define SCMI_IMX_LMM_OP_FORCEFUL 0
#define SCMI_IMX_LMM_OP_GRACEFUL BIT(0)
int scmi_imx_lmm_operation(u32 lmid, enum scmi_imx_lmm_op op, u32 flags);
int scmi_imx_lmm_info(u32 lmid, struct scmi_imx_lmm_info *info);
int scmi_imx_lmm_reset_vector_set(u32 lmid, u32 cpuid, u32 flags, u64 vector);
#endif