diff options
Diffstat (limited to 'drivers/soc/imx')
-rw-r--r-- | drivers/soc/imx/Kconfig | 15 | ||||
-rw-r--r-- | drivers/soc/imx/Makefile | 3 | ||||
-rw-r--r-- | drivers/soc/imx/gpcv2.c | 516 | ||||
-rw-r--r-- | drivers/soc/imx/imx8m-featctrl.c | 90 | ||||
-rw-r--r-- | drivers/soc/imx/soc-imx8m.c | 296 |
5 files changed, 398 insertions, 522 deletions
diff --git a/drivers/soc/imx/Kconfig b/drivers/soc/imx/Kconfig index 742d13c4f5..333a134d3f 100644 --- a/drivers/soc/imx/Kconfig +++ b/drivers/soc/imx/Kconfig @@ -1,10 +1,15 @@ # SPDX-License-Identifier: GPL-2.0-only menu "i.MX SoC drivers" -config IMX_GPCV2_PM_DOMAINS - bool "i.MX GPCv2 PM domains" - depends on ARCH_IMX7 || ARCH_IMX8MQ - select PM_GENERIC_DOMAINS - default y if ARCH_IMX7 || ARCH_IMX8MQ +config IMX8M_FEATCTRL + bool "i.MX8M feature controller" + depends on ARCH_IMX8M + select FEATURE_CONTROLLER + select IMX_OCOTP + select NVMEM + default y + help + This driver disables device tree nodes that are not applicable + to Lite variants of i.MX8M SoCs. endmenu diff --git a/drivers/soc/imx/Makefile b/drivers/soc/imx/Makefile index 3a8a8d0b00..65b2677f7a 100644 --- a/drivers/soc/imx/Makefile +++ b/drivers/soc/imx/Makefile @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_IMX_GPCV2_PM_DOMAINS) += gpcv2.o +obj-$(CONFIG_IMX8M_FEATCTRL) += imx8m-featctrl.o +obj-$(CONFIG_ARCH_IMX8M) += soc-imx8m.o diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c deleted file mode 100644 index a0e78ce55e..0000000000 --- a/drivers/soc/imx/gpcv2.c +++ /dev/null @@ -1,516 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright 2017 Impinj, Inc - * Author: Andrey Smirnov <andrew.smirnov@gmail.com> - * - * Based on the code of analogus driver: - * - * Copyright 2015-2017 Pengutronix, Lucas Stach <kernel@pengutronix.de> - */ - -#include <of_device.h> -#include <common.h> -#include <clock.h> -#include <abort.h> -#include <malloc.h> -#include <io.h> -#include <init.h> -#include <linux/iopoll.h> - -#include <pm_domain.h> -#include <regulator.h> -#include <dt-bindings/power/imx7-power.h> - -#include <dt-bindings/power/imx8mq-power.h> - -#define GPC_LPCR_A_BSC 0x000 - -#define GPC_PGC_CPU_MAPPING 0x0ec - -#define IMX7_USB_HSIC_PHY_A_DOMAIN BIT(6) -#define IMX7_USB_OTG2_PHY_A_DOMAIN BIT(5) -#define IMX7_USB_OTG1_PHY_A_DOMAIN BIT(4) -#define IMX7_PCIE_PHY_A_DOMAIN BIT(3) -#define IMX7_MIPI_PHY_A_DOMAIN BIT(2) - -#define IMX8M_PCIE2_A53_DOMAIN BIT(15) -#define IMX8M_MIPI_CSI2_A53_DOMAIN BIT(14) -#define IMX8M_MIPI_CSI1_A53_DOMAIN BIT(13) -#define IMX8M_DISP_A53_DOMAIN BIT(12) -#define IMX8M_HDMI_A53_DOMAIN BIT(11) -#define IMX8M_VPU_A53_DOMAIN BIT(10) -#define IMX8M_GPU_A53_DOMAIN BIT(9) -#define IMX8M_DDR2_A53_DOMAIN BIT(8) -#define IMX8M_DDR1_A53_DOMAIN BIT(7) -#define IMX8M_OTG2_A53_DOMAIN BIT(5) -#define IMX8M_OTG1_A53_DOMAIN BIT(4) -#define IMX8M_PCIE1_A53_DOMAIN BIT(3) -#define IMX8M_MIPI_A53_DOMAIN BIT(2) - -#define GPC_PU_PGC_SW_PUP_REQ 0x0f8 -#define GPC_PU_PGC_SW_PDN_REQ 0x104 - -#define IMX7_USB_HSIC_PHY_SW_Pxx_REQ BIT(4) -#define IMX7_USB_OTG2_PHY_SW_Pxx_REQ BIT(3) -#define IMX7_USB_OTG1_PHY_SW_Pxx_REQ BIT(2) -#define IMX7_PCIE_PHY_SW_Pxx_REQ BIT(1) -#define IMX7_MIPI_PHY_SW_Pxx_REQ BIT(0) - -#define IMX8M_PCIE2_SW_Pxx_REQ BIT(13) -#define IMX8M_MIPI_CSI2_SW_Pxx_REQ BIT(12) -#define IMX8M_MIPI_CSI1_SW_Pxx_REQ BIT(11) -#define IMX8M_DISP_SW_Pxx_REQ BIT(10) -#define IMX8M_HDMI_SW_Pxx_REQ BIT(9) -#define IMX8M_VPU_SW_Pxx_REQ BIT(8) -#define IMX8M_GPU_SW_Pxx_REQ BIT(7) -#define IMX8M_DDR2_SW_Pxx_REQ BIT(6) -#define IMX8M_DDR1_SW_Pxx_REQ BIT(5) -#define IMX8M_OTG2_SW_Pxx_REQ BIT(3) -#define IMX8M_OTG1_SW_Pxx_REQ BIT(2) -#define IMX8M_PCIE1_SW_Pxx_REQ BIT(1) -#define IMX8M_MIPI_SW_Pxx_REQ BIT(0) - -#define GPC_M4_PU_PDN_FLG 0x1bc - -/* - * The PGC offset values in Reference Manual - * (Rev. 1, 01/2018 and the older ones) GPC chapter's - * GPC_PGC memory map are incorrect, below offset - * values are from design RTL. - */ -#define IMX7_PGC_MIPI 16 -#define IMX7_PGC_PCIE 17 -#define IMX7_PGC_USB_HSIC 20 - - -#define IMX8M_PGC_MIPI 16 -#define IMX8M_PGC_PCIE1 17 -#define IMX8M_PGC_OTG1 18 -#define IMX8M_PGC_OTG2 19 -#define IMX8M_PGC_DDR1 21 -#define IMX8M_PGC_GPU 23 -#define IMX8M_PGC_VPU 24 -#define IMX8M_PGC_DISP 26 -#define IMX8M_PGC_MIPI_CSI1 27 -#define IMX8M_PGC_MIPI_CSI2 28 -#define IMX8M_PGC_PCIE2 29 - -#define GPC_PGC_CTRL(n) (0x800 + (n) * 0x40) -#define GPC_PGC_SR(n) (GPC_PGC_CTRL(n) + 0xc) - -#define GPC_PGC_CTRL_PCR BIT(0) - -struct imx_pgc_domain { - struct generic_pm_domain genpd; - void __iomem *base; - struct regulator *regulator; - - unsigned int pgc; - - const struct { - u32 pxx; - u32 map; - } bits; - - const int voltage; - struct device_d *dev; -}; - -struct imx_pgc_domain_data { - const struct imx_pgc_domain *domains; - size_t domains_num; -}; - -static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd, - bool on) -{ - struct imx_pgc_domain *domain = container_of(genpd, - struct imx_pgc_domain, - genpd); - unsigned int offset = on ? - GPC_PU_PGC_SW_PUP_REQ : GPC_PU_PGC_SW_PDN_REQ; - const bool enable_power_control = !on; - const bool has_regulator = !IS_ERR(domain->regulator); - int ret = 0; - unsigned int mapping, ctrl = 0, pxx; - - mapping = readl(domain->base + GPC_PGC_CPU_MAPPING); - mapping |= domain->bits.map; - writel(mapping, domain->base + GPC_PGC_CPU_MAPPING); - - if (has_regulator && on) { - ret = regulator_enable(domain->regulator); - if (ret) { - dev_err(domain->dev, "failed to enable regulator\n"); - goto unmap; - } - } - - if (enable_power_control) { - ctrl = readl(domain->base + GPC_PGC_CTRL(domain->pgc)); - ctrl |= GPC_PGC_CTRL_PCR; - writel(ctrl, domain->base + GPC_PGC_CTRL(domain->pgc)); - } - - pxx = readl(domain->base + offset); - pxx |= domain->bits.pxx; - writel(pxx, domain->base + offset); - - /* - * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait - * for PUP_REQ/PDN_REQ bit to be cleared - */ - ret = readl_poll_timeout(domain->base + offset, pxx, - !(pxx & domain->bits.pxx), MSECOND); - if (ret < 0) { - dev_err(domain->dev, "falied to command PGC\n"); - /* - * If we were in a process of enabling a - * domain and failed we might as well disable - * the regulator we just enabled. And if it - * was the opposite situation and we failed to - * power down -- keep the regulator on - */ - on = !on; - } - - if (enable_power_control) { - ctrl &= ~GPC_PGC_CTRL_PCR; - writel(ctrl, domain->base + GPC_PGC_CTRL(domain->pgc)); - } - - if (has_regulator && !on) { - int err; - - err = regulator_disable(domain->regulator); - if (err) - dev_err(domain->dev, - "failed to disable regulator: %d\n", ret); - /* Preserve earlier error code */ - ret = ret ?: err; - } -unmap: - mapping &= ~domain->bits.map; - writel(mapping, domain->base + GPC_PGC_CPU_MAPPING); - - return ret; -} - -static int imx_gpc_pu_pgc_sw_pup_req(struct generic_pm_domain *genpd) -{ - return imx_gpc_pu_pgc_sw_pxx_req(genpd, true); -} - -static int imx_gpc_pu_pgc_sw_pdn_req(struct generic_pm_domain *genpd) -{ - return imx_gpc_pu_pgc_sw_pxx_req(genpd, false); -} - -static const struct imx_pgc_domain imx7_pgc_domains[] = { - [IMX7_POWER_DOMAIN_MIPI_PHY] = { - .genpd = { - .name = "mipi-phy", - }, - .bits = { - .pxx = IMX7_MIPI_PHY_SW_Pxx_REQ, - .map = IMX7_MIPI_PHY_A_DOMAIN, - }, - .voltage = 1000000, - .pgc = IMX7_PGC_MIPI, - }, - - [IMX7_POWER_DOMAIN_PCIE_PHY] = { - .genpd = { - .name = "pcie-phy", - }, - .bits = { - .pxx = IMX7_PCIE_PHY_SW_Pxx_REQ, - .map = IMX7_PCIE_PHY_A_DOMAIN, - }, - .voltage = 1000000, - .pgc = IMX7_PGC_PCIE, - }, - - [IMX7_POWER_DOMAIN_USB_HSIC_PHY] = { - .genpd = { - .name = "usb-hsic-phy", - }, - .bits = { - .pxx = IMX7_USB_HSIC_PHY_SW_Pxx_REQ, - .map = IMX7_USB_HSIC_PHY_A_DOMAIN, - }, - .voltage = 1200000, - .pgc = IMX7_PGC_USB_HSIC, - }, -}; - -static const struct imx_pgc_domain_data imx7_pgc_domain_data = { - .domains = imx7_pgc_domains, - .domains_num = ARRAY_SIZE(imx7_pgc_domains), -}; - -static const struct imx_pgc_domain imx8m_pgc_domains[] = { - [IMX8M_POWER_DOMAIN_MIPI] = { - .genpd = { - .name = "mipi", - }, - .bits = { - .pxx = IMX8M_MIPI_SW_Pxx_REQ, - .map = IMX8M_MIPI_A53_DOMAIN, - }, - .pgc = IMX8M_PGC_MIPI, - }, - - [IMX8M_POWER_DOMAIN_PCIE1] = { - .genpd = { - .name = "pcie1", - }, - .bits = { - .pxx = IMX8M_PCIE1_SW_Pxx_REQ, - .map = IMX8M_PCIE1_A53_DOMAIN, - }, - .pgc = IMX8M_PGC_PCIE1, - }, - - [IMX8M_POWER_DOMAIN_USB_OTG1] = { - .genpd = { - .name = "usb-otg1", - }, - .bits = { - .pxx = IMX8M_OTG1_SW_Pxx_REQ, - .map = IMX8M_OTG1_A53_DOMAIN, - }, - .pgc = IMX8M_PGC_OTG1, - }, - - [IMX8M_POWER_DOMAIN_USB_OTG2] = { - .genpd = { - .name = "usb-otg2", - }, - .bits = { - .pxx = IMX8M_OTG2_SW_Pxx_REQ, - .map = IMX8M_OTG2_A53_DOMAIN, - }, - .pgc = IMX8M_PGC_OTG2, - }, - - [IMX8M_POWER_DOMAIN_DDR1] = { - .genpd = { - .name = "ddr1", - }, - .bits = { - .pxx = IMX8M_DDR1_SW_Pxx_REQ, - .map = IMX8M_DDR2_A53_DOMAIN, - }, - .pgc = IMX8M_PGC_DDR1, - }, - - [IMX8M_POWER_DOMAIN_GPU] = { - .genpd = { - .name = "gpu", - }, - .bits = { - .pxx = IMX8M_GPU_SW_Pxx_REQ, - .map = IMX8M_GPU_A53_DOMAIN, - }, - .pgc = IMX8M_PGC_GPU, - }, - - [IMX8M_POWER_DOMAIN_VPU] = { - .genpd = { - .name = "vpu", - }, - .bits = { - .pxx = IMX8M_VPU_SW_Pxx_REQ, - .map = IMX8M_VPU_A53_DOMAIN, - }, - .pgc = IMX8M_PGC_VPU, - }, - - [IMX8M_POWER_DOMAIN_DISP] = { - .genpd = { - .name = "disp", - }, - .bits = { - .pxx = IMX8M_DISP_SW_Pxx_REQ, - .map = IMX8M_DISP_A53_DOMAIN, - }, - .pgc = IMX8M_PGC_DISP, - }, - - [IMX8M_POWER_DOMAIN_MIPI_CSI1] = { - .genpd = { - .name = "mipi-csi1", - }, - .bits = { - .pxx = IMX8M_MIPI_CSI1_SW_Pxx_REQ, - .map = IMX8M_MIPI_CSI1_A53_DOMAIN, - }, - .pgc = IMX8M_PGC_MIPI_CSI1, - }, - - [IMX8M_POWER_DOMAIN_MIPI_CSI2] = { - .genpd = { - .name = "mipi-csi2", - }, - .bits = { - .pxx = IMX8M_MIPI_CSI2_SW_Pxx_REQ, - .map = IMX8M_MIPI_CSI2_A53_DOMAIN, - }, - .pgc = IMX8M_PGC_MIPI_CSI2, - }, - - [IMX8M_POWER_DOMAIN_PCIE2] = { - .genpd = { - .name = "pcie2", - }, - .bits = { - .pxx = IMX8M_PCIE2_SW_Pxx_REQ, - .map = IMX8M_PCIE2_A53_DOMAIN, - }, - .pgc = IMX8M_PGC_PCIE2, - }, -}; - -static const struct imx_pgc_domain_data imx8m_pgc_domain_data = { - .domains = imx8m_pgc_domains, - .domains_num = ARRAY_SIZE(imx8m_pgc_domains), -}; - -static int imx_pgc_domain_probe(struct device_d *dev) -{ - struct imx_pgc_domain *domain = dev->priv; - int ret; - - domain->dev = dev; - - domain->regulator = regulator_get(domain->dev, "power"); - if (IS_ERR(domain->regulator)) { - if (PTR_ERR(domain->regulator) != -ENODEV) { - if (PTR_ERR(domain->regulator) != -EPROBE_DEFER) - dev_err(domain->dev, "Failed to get domain's regulator\n"); - return PTR_ERR(domain->regulator); - } - } else { - regulator_set_voltage(domain->regulator, - domain->voltage, domain->voltage); - } - - ret = pm_genpd_init(&domain->genpd, NULL, true); - if (ret) { - dev_err(domain->dev, "Failed to init power domain\n"); - return ret; - } - - ret = of_genpd_add_provider_simple(domain->dev->device_node, - &domain->genpd); - if (ret) { - dev_err(domain->dev, "Failed to add genpd provider\n"); - } - - return ret; -} - -static const struct platform_device_id imx_pgc_domain_id[] = { - { "imx-pgc-domain", }, - { }, -}; - -static struct driver_d imx_pgc_domain_driver = { - .name = "imx-pgc", - .probe = imx_pgc_domain_probe, - .id_table = imx_pgc_domain_id, -}; -coredevice_platform_driver(imx_pgc_domain_driver); - -static int imx_gpcv2_probe(struct device_d *dev) -{ - static const struct imx_pgc_domain_data *domain_data; - struct device_node *pgc_np, *np; - struct resource *res; - void __iomem *base; - int ret, pass = 0; - - pgc_np = of_get_child_by_name(dev->device_node, "pgc"); - if (!pgc_np) { - dev_err(dev, "No power domains specified in DT\n"); - return -EINVAL; - } - - res = dev_request_mem_resource(dev, 0); - if (IS_ERR(res)) - return PTR_ERR(res); - - base = IOMEM(res->start); - - domain_data = of_device_get_match_data(dev); - - /* - * Run two passes for the registration of the PGC domain platform - * devices: first all devices that are not part of a power-domain - * themselves, then all the others. This avoids -EPROBE_DEFER being - * returned for nested domains, that need their parent PGC domains - * to be present on probe. - */ -again: - for_each_child_of_node(pgc_np, np) { - bool child_domain = of_property_read_bool(np, "power-domains"); - struct imx_pgc_domain *domain; - struct device_d *pd_dev; - u32 domain_index; - - if ((pass == 0 && child_domain) || (pass == 1 && !child_domain)) - continue; - - ret = of_property_read_u32(np, "reg", &domain_index); - if (ret) { - dev_err(dev, "Failed to read 'reg' property\n"); - return ret; - } - - if (domain_index >= domain_data->domains_num) { - dev_warn(dev, - "Domain index %d is out of bounds\n", - domain_index); - continue; - } - - domain = xmemdup(&domain_data->domains[domain_index], - sizeof(domain_data->domains[domain_index])); - domain->base = base; - domain->genpd.power_on = imx_gpc_pu_pgc_sw_pup_req; - domain->genpd.power_off = imx_gpc_pu_pgc_sw_pdn_req; - - pd_dev = xzalloc(sizeof(*pd_dev)); - pd_dev->device_node = np; - pd_dev->device_node->dev = pd_dev; - pd_dev->id = domain_index; - pd_dev->parent = dev; - pd_dev->priv = domain; - dev_set_name(pd_dev, imx_pgc_domain_id[0].name); - - ret = platform_device_register(pd_dev); - if (ret) - return ret; - } - - if (pass == 0) { - pass++; - goto again; - } - - return 0; -} - -static const struct of_device_id imx_gpcv2_dt_ids[] = { - { .compatible = "fsl,imx7d-gpc", .data = &imx7_pgc_domain_data }, - { .compatible = "fsl,imx8mq-gpc", .data = &imx8m_pgc_domain_data, }, - { } -}; - -static struct driver_d imx_gpcv2_driver = { - .name = "imx7d-gpc", - .probe = imx_gpcv2_probe, - .of_compatible = DRV_OF_COMPAT(imx_gpcv2_dt_ids), -}; -coredevice_platform_driver(imx_gpcv2_driver); diff --git a/drivers/soc/imx/imx8m-featctrl.c b/drivers/soc/imx/imx8m-featctrl.c new file mode 100644 index 0000000000..31579aff7e --- /dev/null +++ b/drivers/soc/imx/imx8m-featctrl.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2022 Ahmad Fatoum, Pengutronix + +#include <common.h> +#include <linux/bitmap.h> +#include <featctrl.h> +#include <soc/imx8m/featctrl.h> + +#include <dt-bindings/features/imx8m.h> + +struct imx_feat { + struct feature_controller feat; + unsigned long features[BITS_TO_LONGS(IMX8M_FEAT_END)]; +}; + +static struct imx_feat *to_imx_feat(struct feature_controller *feat) +{ + return container_of(feat, struct imx_feat, feat); +} + +static int imx8m_feat_check(struct feature_controller *feat, int idx) +{ + struct imx_feat *priv = to_imx_feat(feat); + + if (idx > IMX8M_FEAT_END) + return -EINVAL; + + return test_bit(idx, priv->features) ? FEATCTRL_OKAY : FEATCTRL_GATED; +} + +static inline bool is_fused(u32 val, u32 bitmask) +{ + return bitmask && (val & bitmask) == bitmask; +} + +#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) + +static void check_cpus(u32 mask, u32 reg, unsigned long *features) +{ + switch (field_get(mask, reg)) { + case 0b11: + clear_bit(IMX8M_FEAT_CPU_DUAL, features); + fallthrough; + case 0b10: + clear_bit(IMX8M_FEAT_CPU_QUAD, features); + } +} + +int imx8m_feat_ctrl_init(struct device *dev, u32 tester3, u32 tester4, + const struct imx8m_featctrl_data *data) +{ + unsigned long *features; + struct imx_feat *priv; + + if (!dev || !data) + return -ENODEV; + + dev_dbg(dev, "tester3 = 0x%08x, tester4 = 0x%08x\n", tester3, tester4); + + priv = xzalloc(sizeof(*priv)); + features = priv->features; + + bitmap_fill(features, IMX8M_FEAT_END); + + if (is_fused(tester3, data->tester3.vpu_bitmask) || + is_fused(tester4, data->tester4.vpu_bitmask)) + clear_bit(IMX8M_FEAT_VPU, features); + if (is_fused(tester4, data->tester4.gpu_bitmask)) + clear_bit(IMX8M_FEAT_GPU, features); + if (is_fused(tester4, data->tester4.mipi_dsi_bitmask)) + clear_bit(IMX8M_FEAT_MIPI_DSI, features); + if (is_fused(tester4, data->tester4.isp_bitmask)) + clear_bit(IMX8M_FEAT_ISP, features); + if (is_fused(tester4, data->tester4.npu_bitmask)) + clear_bit(IMX8M_FEAT_NPU, features); + if (is_fused(tester4, data->tester4.lvds_bitmask)) + clear_bit(IMX8M_FEAT_LVDS, features); + if (is_fused(tester4, data->tester4.dsp_bitmask)) + clear_bit(IMX8M_FEAT_DSP, features); + + if (data->tester3.cpu_bitmask) + check_cpus(data->tester3.cpu_bitmask, tester3, features); + else if (data->tester4.cpu_bitmask) + check_cpus(data->tester4.cpu_bitmask, tester4, features); + + priv->feat.dev = dev; + priv->feat.check = imx8m_feat_check; + + return feature_controller_register(&priv->feat); +} diff --git a/drivers/soc/imx/soc-imx8m.c b/drivers/soc/imx/soc-imx8m.c new file mode 100644 index 0000000000..1b47c914de --- /dev/null +++ b/drivers/soc/imx/soc-imx8m.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0 +// SPDX-FileCopyrightText: 2024 Marco Felsch, Pengutronix +/* + * Based on Linux drivers/soc/imx/soc-imx8m.c: + * Copyright 2019 NXP. + */ + +#include <init.h> +#include <of.h> +#include <of_address.h> +#include <pm_domain.h> + +#include <asm/optee.h> +#include <asm-generic/memory_layout.h> + +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/sys_soc.h> +#include <linux/arm-smccc.h> +#include <linux/clk.h> + +#include <mach/imx/generic.h> +#include <mach/imx/imx8m-regs.h> +#include <mach/imx/reset-reason.h> +#include <mach/imx/revision.h> +#include <mach/imx/scratch.h> +#include <mach/imx/tzasc.h> + +#include <tee/optee.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 OCOTP_UID_LOW 0x410 +#define OCOTP_UID_HIGH 0x420 + +#define IMX8MP_OCOTP_UID_OFFSET 0x10 + +/* Same as ANADIG_DIGPROG_IMX7D */ +#define ANADIG_DIGPROG_IMX8MM 0x800 + +struct imx8_soc_data { + char *name; + u32 (*soc_revision)(void); + void (*save_boot_loc)(void); +}; + +static u64 soc_uid; + +#ifdef CONFIG_HAVE_ARM_SMCCC +static u32 imx8mq_soc_revision_from_atf(void) +{ + struct arm_smccc_res res; + + 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; +} +#else +static inline u32 imx8mq_soc_revision_from_atf(void) { return 0; }; +#endif + +static u32 __init imx8mq_soc_revision(void) +{ + struct device_node *np; + void __iomem *ocotp_base; + u32 magic; + u32 rev; + struct clk *clk; + + np = of_find_compatible_node(NULL, NULL, "fsl,imx8mq-ocotp"); + if (!np) + return 0; + + ocotp_base = of_iomap(np, 0); + WARN_ON(!ocotp_base); + clk = of_clk_get_by_name(np, NULL); + if (IS_ERR(clk)) { + WARN_ON(IS_ERR(clk)); + return 0; + } + + clk_prepare_enable(clk); + + /* + * 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; + soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW); + + /* Keep the OCOTP clk on for the TF-A else the CPU stuck */ + of_node_put(np); + + return rev; +} + +static void __init imx8mm_soc_uid(void) +{ + void __iomem *ocotp_base; + struct device_node *np; + struct clk *clk; + 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) + return; + + ocotp_base = of_iomap(np, 0); + WARN_ON(!ocotp_base); + clk = of_clk_get_by_name(np, NULL); + if (IS_ERR(clk)) { + WARN_ON(IS_ERR(clk)); + return; + } + + clk_prepare_enable(clk); + + soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH + offset); + soc_uid <<= 32; + soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW + offset); + + /* Keep the OCOTP clk on for the TF-A else the CPU stuck */ + of_node_put(np); +} + +static u32 __init imx8mm_soc_revision(void) +{ + struct device_node *np; + void __iomem *anatop_base; + u32 rev; + + np = of_find_compatible_node(NULL, NULL, "fsl,imx8mm-anatop"); + if (!np) + return 0; + + anatop_base = of_iomap(np, 0); + WARN_ON(!anatop_base); + + rev = readl_relaxed(anatop_base + ANADIG_DIGPROG_IMX8MM); + + of_node_put(np); + + imx8mm_soc_uid(); + + return rev; +} + +static const struct imx8_soc_data imx8mq_soc_data = { + .name = "i.MX8MQ", + .soc_revision = imx8mq_soc_revision, + .save_boot_loc = imx8mq_boot_save_loc, +}; + +static const struct imx8_soc_data imx8mm_soc_data = { + .name = "i.MX8MM", + .soc_revision = imx8mm_soc_revision, + .save_boot_loc = imx8mm_boot_save_loc, +}; + +static const struct imx8_soc_data imx8mn_soc_data = { + .name = "i.MX8MN", + .soc_revision = imx8mm_soc_revision, + .save_boot_loc = imx8mn_boot_save_loc, +}; + +static const struct imx8_soc_data imx8mp_soc_data = { + .name = "i.MX8MP", + .soc_revision = imx8mm_soc_revision, + .save_boot_loc = imx8mp_boot_save_loc, +}; + +static __maybe_unused 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, }, + { } +}; + +static int imx8_soc_imx8m_init(struct soc_device_attribute *soc_dev_attr) +{ + void __iomem *src = IOMEM(MX8M_SRC_BASE_ADDR); + const char *uid = soc_dev_attr->serial_number; + const char *cputypestr = soc_dev_attr->soc_id; + + genpd_activate(); + + /* + * Reset reasons seem to be identical to that of i.MX7 + */ + imx_set_reset_reason(src + IMX7_SRC_SRSR, imx7_reset_reasons); + pr_info("%s unique ID: %s\n", cputypestr, uid); + + if (IS_ENABLED(CONFIG_PBL_OPTEE) && imx8m_tzc380_is_enabled()) { + static struct of_optee_fixup_data optee_fixup_data = { + .shm_size = OPTEE_SHM_SIZE, + .method = "smc", + }; + + optee_set_membase(imx_scratch_get_optee_hdr()); + of_optee_fixup(of_get_root_node(), &optee_fixup_data); + of_register_fixup(of_optee_fixup, &optee_fixup_data); + } + + return 0; +} + +#define imx8_revision(soc_rev) \ + soc_rev ? \ + xasprintf("%d.%d", (soc_rev >> 4) & 0xf, soc_rev & 0xf) : \ + "unknown" + +static int __init imx8_soc_init(void) +{ + struct device_node *of_root = of_get_root_node(); + struct soc_device_attribute *soc_dev_attr; + struct soc_device *soc_dev; + const struct of_device_id *id; + u32 soc_rev = 0; + const struct imx8_soc_data *data; + int ret; + + id = of_match_node(imx8_soc_match, of_root); + if (!id) + return 0; + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + return -ENOMEM; + + soc_dev_attr->family = "Freescale i.MX"; + + ret = of_property_read_string(of_root, "model", &soc_dev_attr->machine); + if (ret) + goto free_soc; + + data = id->data; + if (data) { + soc_dev_attr->soc_id = data->name; + if (data->soc_revision) + soc_rev = data->soc_revision(); + if (data->save_boot_loc) + data->save_boot_loc(); + } + + soc_dev_attr->revision = imx8_revision(soc_rev); + if (!soc_dev_attr->revision) { + ret = -ENOMEM; + goto free_soc; + } + + soc_dev_attr->serial_number = xasprintf("%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_serial_number; + } + + imx_set_silicon_revision(soc_dev_attr->soc_id, soc_rev); + + return imx8_soc_imx8m_init(soc_dev_attr); + +free_serial_number: + kfree(soc_dev_attr->serial_number); +free_rev: + if (strcmp(soc_dev_attr->revision, "unknown")) + kfree(soc_dev_attr->revision); +free_soc: + kfree(soc_dev_attr); + return ret; +} +/* Aligned with imx_init() to not cause regressions */ +postcore_initcall(imx8_soc_init); +MODULE_LICENSE("GPL"); |