diff options
Diffstat (limited to 'drivers/soc/imx/soc-imx8.c')
-rw-r--r-- | drivers/soc/imx/soc-imx8.c | 170 |
1 files changed, 154 insertions, 16 deletions
diff --git a/drivers/soc/imx/soc-imx8.c b/drivers/soc/imx/soc-imx8.c index b9831576dd25..c2525082730f 100644 --- a/drivers/soc/imx/soc-imx8.c +++ b/drivers/soc/imx/soc-imx8.c @@ -3,22 +3,48 @@ * Copyright 2019 NXP. */ +#include <linux/arm-smccc.h> #include <linux/init.h> #include <linux/io.h> #include <linux/of_address.h> #include <linux/slab.h> #include <linux/sys_soc.h> #include <linux/platform_device.h> +#include <linux/arm-smccc.h> #include <linux/of.h> +#include <soc/imx/src.h> + #define REV_B1 0x21 #define IMX8MQ_SW_INFO_B1 0x40 #define IMX8MQ_SW_MAGIC_B1 0xff0055aa +#define IMX_SIP_GET_SOC_INFO 0xc2000006 + +#define IMX_SIP_NOC 0xc2000008 +#define IMX_SIP_NOC_LCDIF 0x0 +#define IMX_SIP_NOC_PRIORITY 0x1 +#define NOC_GPU_PRIORITY 0x10 +#define NOC_DCSS_PRIORITY 0x11 +#define NOC_VPU_PRIORITY 0x12 +#define NOC_CPU_PRIORITY 0x13 +#define NOC_MIX_PRIORITY 0x14 + #define OCOTP_UID_LOW 0x410 #define OCOTP_UID_HIGH 0x420 +#define OCOTP_IMX8MP_ANA_TRIM_1 0xd70 +#define OCOTP_IMX8MP_LDO_MASK 0x1f +#define OCOTP_IMX8MP_LDO0_SHIFT 16 +#define OCOTP_IMX8MP_LDO1_SHIFT 24 +#define OCOTP_IMX8MP_ANA_TRIM_2 0xd80 +#define OCOTP_IMX8MP_LDO2_SHIFT 0 + +#define OCOTP_IMX8MP_LDO_NUM 3 + +#define IMX8MP_OCOTP_UID_OFFSET 0x10 + /* Same as ANADIG_DIGPROG_IMX7D */ #define ANADIG_DIGPROG_IMX8MM 0x800 @@ -28,14 +54,19 @@ struct imx8_soc_data { }; static u64 soc_uid; +static int ldo_trim[3] = { -1, -1, -1 }; -static ssize_t soc_uid_show(struct device *dev, - struct device_attribute *attr, char *buf) +static u32 imx8mq_soc_revision_from_atf(void) { - return sprintf(buf, "%016llX\n", soc_uid); -} + struct arm_smccc_res res; -static DEVICE_ATTR_RO(soc_uid); + arm_smccc_smc(IMX_SIP_GET_SOC_INFO, 0, 0, 0, 0, 0, 0, 0, &res); + + if (res.a0 == SMCCC_RET_NOT_SUPPORTED) + return 0; + else + return res.a0 & 0xff; +} static u32 __init imx8mq_soc_revision(void) { @@ -51,9 +82,16 @@ static u32 __init imx8mq_soc_revision(void) ocotp_base = of_iomap(np, 0); WARN_ON(!ocotp_base); - magic = readl_relaxed(ocotp_base + IMX8MQ_SW_INFO_B1); - if (magic == IMX8MQ_SW_MAGIC_B1) - rev = REV_B1; + /* + * SOC revision on older imx8mq is not available in fuses so query + * the value from ATF instead. + */ + rev = imx8mq_soc_revision_from_atf(); + if (!rev) { + magic = readl_relaxed(ocotp_base + IMX8MQ_SW_INFO_B1); + if (magic == IMX8MQ_SW_MAGIC_B1) + rev = REV_B1; + } soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH); soc_uid <<= 32; @@ -70,6 +108,8 @@ static void __init imx8mm_soc_uid(void) { void __iomem *ocotp_base; struct device_node *np; + u32 offset = of_machine_is_compatible("fsl,imx8mp") ? + IMX8MP_OCOTP_UID_OFFSET : 0; np = of_find_compatible_node(NULL, NULL, "fsl,imx8mm-ocotp"); if (!np) @@ -78,11 +118,45 @@ static void __init imx8mm_soc_uid(void) ocotp_base = of_iomap(np, 0); WARN_ON(!ocotp_base); - soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH); + soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH + offset); soc_uid <<= 32; - soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW); + soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW + offset); + + iounmap(ocotp_base); + of_node_put(np); +} + +static void __init imx8mp_read_ldo_trim(void) +{ + void __iomem *ocotp_base; + struct device_node *np; + u32 fuse; + + if (!of_machine_is_compatible("fsl,imx8mp")) + return; + + np = of_find_compatible_node(NULL, NULL, "fsl,imx8mp-ocotp"); + if (!np) + goto out; + + ocotp_base = of_iomap(np, 0); + if (!ocotp_base){ + WARN_ON(!ocotp_base); + goto out; + } + + fuse = readl_relaxed(ocotp_base + OCOTP_IMX8MP_ANA_TRIM_2); + ldo_trim[2] = fuse & OCOTP_IMX8MP_LDO_MASK; + + fuse = readl_relaxed(ocotp_base + OCOTP_IMX8MP_ANA_TRIM_1); + ldo_trim[1] = (fuse >> OCOTP_IMX8MP_LDO1_SHIFT) & + OCOTP_IMX8MP_LDO_MASK; + ldo_trim[0] = (fuse >> OCOTP_IMX8MP_LDO0_SHIFT) & + OCOTP_IMX8MP_LDO_MASK; iounmap(ocotp_base); + +out: of_node_put(np); } @@ -106,6 +180,8 @@ static u32 __init imx8mm_soc_revision(void) imx8mm_soc_uid(); + imx8mp_read_ldo_trim(); + return rev; } @@ -124,10 +200,16 @@ static const struct imx8_soc_data imx8mn_soc_data = { .soc_revision = imx8mm_soc_revision, }; +static const struct imx8_soc_data imx8mp_soc_data = { + .name = "i.MX8MP", + .soc_revision = imx8mm_soc_revision, +}; + static const struct of_device_id imx8_soc_match[] = { { .compatible = "fsl,imx8mq", .data = &imx8mq_soc_data, }, { .compatible = "fsl,imx8mm", .data = &imx8mm_soc_data, }, { .compatible = "fsl,imx8mn", .data = &imx8mn_soc_data, }, + { .compatible = "fsl,imx8mp", .data = &imx8mp_soc_data, }, { } }; @@ -136,6 +218,23 @@ static const struct of_device_id imx8_soc_match[] = { kasprintf(GFP_KERNEL, "%d.%d", (soc_rev >> 4) & 0xf, soc_rev & 0xf) : \ "unknown" +static void __init imx8mq_noc_init(void) +{ + struct arm_smccc_res res; + + pr_info("Config NOC for VPU and CPU\n"); + + arm_smccc_smc(IMX_SIP_NOC, IMX_SIP_NOC_PRIORITY, NOC_CPU_PRIORITY, + 0x80000300, 0, 0, 0, 0, &res); + if (res.a0) + pr_err("Config NOC for CPU fail!\n"); + + arm_smccc_smc(IMX_SIP_NOC, IMX_SIP_NOC_PRIORITY, NOC_VPU_PRIORITY, + 0x80000300, 0, 0, 0, 0, &res); + if (res.a0) + pr_err("Config NOC for VPU fail!\n"); +} + static int __init imx8_soc_init(void) { struct soc_device_attribute *soc_dev_attr; @@ -174,22 +273,28 @@ static int __init imx8_soc_init(void) goto free_soc; } + soc_dev_attr->serial_number = kasprintf(GFP_KERNEL, "%016llX", soc_uid); + if (!soc_dev_attr->serial_number) { + ret = -ENOMEM; + goto free_rev; + } + soc_dev = soc_device_register(soc_dev_attr); if (IS_ERR(soc_dev)) { ret = PTR_ERR(soc_dev); - goto free_rev; + goto free_serial_number; } - ret = device_create_file(soc_device_to_device(soc_dev), - &dev_attr_soc_uid); - if (ret) - goto free_rev; - if (IS_ENABLED(CONFIG_ARM_IMX_CPUFREQ_DT)) platform_device_register_simple("imx-cpufreq-dt", -1, NULL, 0); + if (of_machine_is_compatible("fsl,imx8mq")) + imx8mq_noc_init(); + return 0; +free_serial_number: + kfree(soc_dev_attr->serial_number); free_rev: if (strcmp(soc_dev_attr->revision, "unknown")) kfree(soc_dev_attr->revision); @@ -198,3 +303,36 @@ free_soc: return ret; } device_initcall(imx8_soc_init); + +#define FSL_SIP_SRC 0xc2000005 +#define FSL_SIP_SRC_M4_START 0x00 +#define FSL_SIP_SRC_M4_STARTED 0x01 + +/* To indicate M4 enabled or not on i.MX8MQ */ +static bool m4_is_enabled; +bool imx_src_is_m4_enabled(void) +{ + return m4_is_enabled; +} +EXPORT_SYMBOL_GPL(imx_src_is_m4_enabled); + +int check_m4_enabled(void) +{ + struct arm_smccc_res res; + + arm_smccc_smc(FSL_SIP_SRC, FSL_SIP_SRC_M4_STARTED, 0, + 0, 0, 0, 0, 0, &res); + m4_is_enabled = !!res.a0; + + if (m4_is_enabled) + printk("M4 is started\n"); + + return 0; +} +EXPORT_SYMBOL_GPL(check_m4_enabled); + +int imx8mp_get_ldo_trim(int ldo) +{ + return ldo_trim[ldo]; +} +EXPORT_SYMBOL_GPL(imx8mp_get_ldo_trim); |