Commit 7242bbf4 authored by Peng Fan's avatar Peng Fan Committed by Sudeep Holla
Browse files

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: default avatarCristian Marussi <cristian.marussi@arm.com>
Signed-off-by: default avatarPeng Fan <peng.fan@nxp.com>
Message-Id: <20250408-imx-lmm-cpu-v4-5-4c5f4a456e49@nxp.com>
Signed-off-by: default avatarSudeep Holla <sudeep.holla@arm.com>
parent e68c305b
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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
+11 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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
+91 −0
Original line number Diff line number Diff line
// 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");
+14 −0
Original line number Diff line number Diff line
@@ -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