Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   3)  * Copyright (c) 2017 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   4)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   5) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   6) #include <linux/clk.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   7) #include <linux/err.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   8) #include <linux/interrupt.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   9) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  10) #include <linux/mfd/syscon.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  11) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  12) #include <linux/of_address.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  13) #include <linux/of_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  14) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  15) #include <linux/regmap.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  16) #include <linux/remoteproc.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  17) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  18) #define IMX7D_SRC_SCR			0x0C
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  19) #define IMX7D_ENABLE_M4			BIT(3)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  20) #define IMX7D_SW_M4P_RST		BIT(2)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  21) #define IMX7D_SW_M4C_RST		BIT(1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  22) #define IMX7D_SW_M4C_NON_SCLR_RST	BIT(0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  23) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  24) #define IMX7D_M4_RST_MASK		(IMX7D_ENABLE_M4 | IMX7D_SW_M4P_RST \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  25) 					 | IMX7D_SW_M4C_RST \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  26) 					 | IMX7D_SW_M4C_NON_SCLR_RST)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  27) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  28) #define IMX7D_M4_START			(IMX7D_ENABLE_M4 | IMX7D_SW_M4P_RST \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  29) 					 | IMX7D_SW_M4C_RST)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  30) #define IMX7D_M4_STOP			IMX7D_SW_M4C_NON_SCLR_RST
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  31) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  32) /* Address: 0x020D8000 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  33) #define IMX6SX_SRC_SCR			0x00
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  34) #define IMX6SX_ENABLE_M4		BIT(22)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  35) #define IMX6SX_SW_M4P_RST		BIT(12)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  36) #define IMX6SX_SW_M4C_NON_SCLR_RST	BIT(4)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  37) #define IMX6SX_SW_M4C_RST		BIT(3)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  38) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  39) #define IMX6SX_M4_START			(IMX6SX_ENABLE_M4 | IMX6SX_SW_M4P_RST \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  40) 					 | IMX6SX_SW_M4C_RST)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  41) #define IMX6SX_M4_STOP			IMX6SX_SW_M4C_NON_SCLR_RST
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  42) #define IMX6SX_M4_RST_MASK		(IMX6SX_ENABLE_M4 | IMX6SX_SW_M4P_RST \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  43) 					 | IMX6SX_SW_M4C_NON_SCLR_RST \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  44) 					 | IMX6SX_SW_M4C_RST)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  45) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  46) #define IMX7D_RPROC_MEM_MAX		8
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  47) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  48) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  49)  * struct imx_rproc_mem - slim internal memory structure
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  50)  * @cpu_addr: MPU virtual address of the memory region
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  51)  * @sys_addr: Bus address used to access the memory region
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  52)  * @size: Size of the memory region
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  53)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  54) struct imx_rproc_mem {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  55) 	void __iomem *cpu_addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  56) 	phys_addr_t sys_addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  57) 	size_t size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  58) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  59) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  60) /* att flags */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  61) /* M4 own area. Can be mapped at probe */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  62) #define ATT_OWN		BIT(1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  63) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  64) /* address translation table */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  65) struct imx_rproc_att {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  66) 	u32 da;	/* device address (From Cortex M4 view)*/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  67) 	u32 sa;	/* system bus address */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  68) 	u32 size; /* size of reg range */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  69) 	int flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  70) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  71) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  72) struct imx_rproc_dcfg {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  73) 	u32				src_reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  74) 	u32				src_mask;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  75) 	u32				src_start;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  76) 	u32				src_stop;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  77) 	const struct imx_rproc_att	*att;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  78) 	size_t				att_size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  79) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  80) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  81) struct imx_rproc {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  82) 	struct device			*dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  83) 	struct regmap			*regmap;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  84) 	struct rproc			*rproc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  85) 	const struct imx_rproc_dcfg	*dcfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  86) 	struct imx_rproc_mem		mem[IMX7D_RPROC_MEM_MAX];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  87) 	struct clk			*clk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  88) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  89) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  90) static const struct imx_rproc_att imx_rproc_att_imx7d[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  91) 	/* dev addr , sys addr  , size	    , flags */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  92) 	/* OCRAM_S (M4 Boot code) - alias */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  93) 	{ 0x00000000, 0x00180000, 0x00008000, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  94) 	/* OCRAM_S (Code) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  95) 	{ 0x00180000, 0x00180000, 0x00008000, ATT_OWN },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  96) 	/* OCRAM (Code) - alias */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  97) 	{ 0x00900000, 0x00900000, 0x00020000, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  98) 	/* OCRAM_EPDC (Code) - alias */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  99) 	{ 0x00920000, 0x00920000, 0x00020000, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) 	/* OCRAM_PXP (Code) - alias */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) 	{ 0x00940000, 0x00940000, 0x00008000, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) 	/* TCML (Code) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) 	{ 0x1FFF8000, 0x007F8000, 0x00008000, ATT_OWN },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) 	/* DDR (Code) - alias, first part of DDR (Data) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) 	{ 0x10000000, 0x80000000, 0x0FFF0000, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) 	/* TCMU (Data) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) 	{ 0x20000000, 0x00800000, 0x00008000, ATT_OWN },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) 	/* OCRAM (Data) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) 	{ 0x20200000, 0x00900000, 0x00020000, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) 	/* OCRAM_EPDC (Data) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) 	{ 0x20220000, 0x00920000, 0x00020000, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) 	/* OCRAM_PXP (Data) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) 	{ 0x20240000, 0x00940000, 0x00008000, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) 	/* DDR (Data) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) 	{ 0x80000000, 0x80000000, 0x60000000, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) static const struct imx_rproc_att imx_rproc_att_imx6sx[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) 	/* dev addr , sys addr  , size	    , flags */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) 	/* TCML (M4 Boot Code) - alias */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) 	{ 0x00000000, 0x007F8000, 0x00008000, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) 	/* OCRAM_S (Code) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) 	{ 0x00180000, 0x008F8000, 0x00004000, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) 	/* OCRAM_S (Code) - alias */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) 	{ 0x00180000, 0x008FC000, 0x00004000, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) 	/* TCML (Code) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) 	{ 0x1FFF8000, 0x007F8000, 0x00008000, ATT_OWN },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) 	/* DDR (Code) - alias, first part of DDR (Data) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) 	{ 0x10000000, 0x80000000, 0x0FFF8000, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) 	/* TCMU (Data) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) 	{ 0x20000000, 0x00800000, 0x00008000, ATT_OWN },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) 	/* OCRAM_S (Data) - alias? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) 	{ 0x208F8000, 0x008F8000, 0x00004000, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) 	/* DDR (Data) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) 	{ 0x80000000, 0x80000000, 0x60000000, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) static const struct imx_rproc_dcfg imx_rproc_cfg_imx7d = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) 	.src_reg	= IMX7D_SRC_SCR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) 	.src_mask	= IMX7D_M4_RST_MASK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) 	.src_start	= IMX7D_M4_START,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) 	.src_stop	= IMX7D_M4_STOP,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) 	.att		= imx_rproc_att_imx7d,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) 	.att_size	= ARRAY_SIZE(imx_rproc_att_imx7d),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) static const struct imx_rproc_dcfg imx_rproc_cfg_imx6sx = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) 	.src_reg	= IMX6SX_SRC_SCR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) 	.src_mask	= IMX6SX_M4_RST_MASK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) 	.src_start	= IMX6SX_M4_START,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) 	.src_stop	= IMX6SX_M4_STOP,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) 	.att		= imx_rproc_att_imx6sx,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) 	.att_size	= ARRAY_SIZE(imx_rproc_att_imx6sx),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) static int imx_rproc_start(struct rproc *rproc)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) 	struct imx_rproc *priv = rproc->priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) 	const struct imx_rproc_dcfg *dcfg = priv->dcfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) 	struct device *dev = priv->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) 	int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) 	ret = regmap_update_bits(priv->regmap, dcfg->src_reg,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) 				 dcfg->src_mask, dcfg->src_start);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) 	if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) 		dev_err(dev, "Failed to enable M4!\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) 	return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) static int imx_rproc_stop(struct rproc *rproc)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) 	struct imx_rproc *priv = rproc->priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) 	const struct imx_rproc_dcfg *dcfg = priv->dcfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) 	struct device *dev = priv->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) 	int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) 	ret = regmap_update_bits(priv->regmap, dcfg->src_reg,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) 				 dcfg->src_mask, dcfg->src_stop);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) 	if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) 		dev_err(dev, "Failed to stop M4!\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) 	return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) static int imx_rproc_da_to_sys(struct imx_rproc *priv, u64 da,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) 			       size_t len, u64 *sys)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) 	const struct imx_rproc_dcfg *dcfg = priv->dcfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) 	int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) 	/* parse address translation table */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) 	for (i = 0; i < dcfg->att_size; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) 		const struct imx_rproc_att *att = &dcfg->att[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) 		if (da >= att->da && da + len < att->da + att->size) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) 			unsigned int offset = da - att->da;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) 			*sys = att->sa + offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) 			return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) 	dev_warn(priv->dev, "Translation failed: da = 0x%llx len = 0x%zx\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) 		 da, len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) 	return -ENOENT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) static void *imx_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) 	struct imx_rproc *priv = rproc->priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) 	void *va = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) 	u64 sys;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) 	int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) 	if (len == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) 		return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) 	/*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) 	 * On device side we have many aliases, so we need to convert device
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) 	 * address (M4) to system bus address first.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) 	if (imx_rproc_da_to_sys(priv, da, len, &sys))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) 		return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) 	for (i = 0; i < IMX7D_RPROC_MEM_MAX; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) 		if (sys >= priv->mem[i].sys_addr && sys + len <
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) 		    priv->mem[i].sys_addr +  priv->mem[i].size) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) 			unsigned int offset = sys - priv->mem[i].sys_addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) 			/* __force to make sparse happy with type conversion */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) 			va = (__force void *)(priv->mem[i].cpu_addr + offset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) 			break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) 	dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%zx va = 0x%p\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) 		da, len, va);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) 	return va;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) static const struct rproc_ops imx_rproc_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) 	.start		= imx_rproc_start,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) 	.stop		= imx_rproc_stop,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) 	.da_to_va       = imx_rproc_da_to_va,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) static int imx_rproc_addr_init(struct imx_rproc *priv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) 			       struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) 	const struct imx_rproc_dcfg *dcfg = priv->dcfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) 	struct device *dev = &pdev->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) 	struct device_node *np = dev->of_node;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) 	int a, b = 0, err, nph;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) 	/* remap required addresses */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) 	for (a = 0; a < dcfg->att_size; a++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) 		const struct imx_rproc_att *att = &dcfg->att[a];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) 		if (!(att->flags & ATT_OWN))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) 			continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) 		if (b >= IMX7D_RPROC_MEM_MAX)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) 			break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) 		priv->mem[b].cpu_addr = devm_ioremap(&pdev->dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) 						     att->sa, att->size);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) 		if (!priv->mem[b].cpu_addr) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) 			dev_err(dev, "devm_ioremap_resource failed\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) 			return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) 		priv->mem[b].sys_addr = att->sa;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) 		priv->mem[b].size = att->size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) 		b++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) 	/* memory-region is optional property */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) 	nph = of_count_phandle_with_args(np, "memory-region", NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) 	if (nph <= 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) 		return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) 	/* remap optional addresses */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) 	for (a = 0; a < nph; a++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) 		struct device_node *node;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) 		struct resource res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) 		node = of_parse_phandle(np, "memory-region", a);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) 		err = of_address_to_resource(node, 0, &res);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) 		if (err) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) 			dev_err(dev, "unable to resolve memory region\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) 			return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) 		if (b >= IMX7D_RPROC_MEM_MAX)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) 			break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) 		priv->mem[b].cpu_addr = devm_ioremap_resource(&pdev->dev, &res);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) 		if (IS_ERR(priv->mem[b].cpu_addr)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) 			dev_err(dev, "devm_ioremap_resource failed\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) 			err = PTR_ERR(priv->mem[b].cpu_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) 			return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) 		priv->mem[b].sys_addr = res.start;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) 		priv->mem[b].size = resource_size(&res);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) 		b++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) static int imx_rproc_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) 	struct device *dev = &pdev->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) 	struct device_node *np = dev->of_node;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) 	struct imx_rproc *priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) 	struct rproc *rproc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) 	struct regmap_config config = { .name = "imx-rproc" };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) 	const struct imx_rproc_dcfg *dcfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) 	struct regmap *regmap;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) 	int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) 	regmap = syscon_regmap_lookup_by_phandle(np, "syscon");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) 	if (IS_ERR(regmap)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) 		dev_err(dev, "failed to find syscon\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) 		return PTR_ERR(regmap);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) 	regmap_attach_dev(dev, regmap, &config);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) 	/* set some other name then imx */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) 	rproc = rproc_alloc(dev, "imx-rproc", &imx_rproc_ops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) 			    NULL, sizeof(*priv));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) 	if (!rproc)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) 		return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) 	dcfg = of_device_get_match_data(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) 	if (!dcfg) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) 		ret = -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) 		goto err_put_rproc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) 	priv = rproc->priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) 	priv->rproc = rproc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) 	priv->regmap = regmap;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) 	priv->dcfg = dcfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) 	priv->dev = dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) 	dev_set_drvdata(dev, rproc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) 	ret = imx_rproc_addr_init(priv, pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) 	if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) 		dev_err(dev, "failed on imx_rproc_addr_init\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) 		goto err_put_rproc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) 	priv->clk = devm_clk_get(dev, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) 	if (IS_ERR(priv->clk)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) 		dev_err(dev, "Failed to get clock\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) 		ret = PTR_ERR(priv->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) 		goto err_put_rproc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) 	/*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) 	 * clk for M4 block including memory. Should be
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) 	 * enabled before .start for FW transfer.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) 	ret = clk_prepare_enable(priv->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) 	if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) 		dev_err(&rproc->dev, "Failed to enable clock\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) 		goto err_put_rproc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) 	ret = rproc_add(rproc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) 	if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) 		dev_err(dev, "rproc_add failed\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) 		goto err_put_clk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) err_put_clk:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) 	clk_disable_unprepare(priv->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) err_put_rproc:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) 	rproc_free(rproc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) 	return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) static int imx_rproc_remove(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) 	struct rproc *rproc = platform_get_drvdata(pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) 	struct imx_rproc *priv = rproc->priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395) 	clk_disable_unprepare(priv->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) 	rproc_del(rproc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) 	rproc_free(rproc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) static const struct of_device_id imx_rproc_of_match[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) 	{ .compatible = "fsl,imx7d-cm4", .data = &imx_rproc_cfg_imx7d },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) 	{ .compatible = "fsl,imx6sx-cm4", .data = &imx_rproc_cfg_imx6sx },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) 	{},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) MODULE_DEVICE_TABLE(of, imx_rproc_of_match);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) static struct platform_driver imx_rproc_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) 	.probe = imx_rproc_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) 	.remove = imx_rproc_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) 	.driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) 		.name = "imx-rproc",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) 		.of_match_table = imx_rproc_of_match,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) 	},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) module_platform_driver(imx_rproc_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) MODULE_LICENSE("GPL v2");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) MODULE_DESCRIPTION("IMX6SX/7D remote processor control driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>");