diff options
author | Olof Johansson <olof@lixom.net> | 2019-04-29 09:25:21 -0700 |
---|---|---|
committer | Olof Johansson <olof@lixom.net> | 2019-04-29 09:25:21 -0700 |
commit | f99552d9eb78e7d5cfd639c8f7a7457d71683074 (patch) | |
tree | f18ef24ced1ed0909b44cc60337a2487b5b14875 /drivers | |
parent | ab7b7c715e9a3b74d4e2ee33e6cd5d1bde5332cc (diff) | |
parent | a7e26f356ca12906a164d83c9e9f8527ee7da022 (diff) | |
download | linux-f99552d9eb78e7d5cfd639c8f7a7457d71683074.tar.bz2 |
Merge tag 'imx-drivers-5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux into arm/drivers
i.MX drivers change for 5.2:
- A series from Aisheng to generalize the SCU powerdomain driver
for easier adding new SCU based platforms like imx8qm.
- Add a generic i.MX8 SoC driver for reporting SoC and platform
information.
- Replace explicit polling loop with a call to regmap_read_poll_timeout()
for gpcv2 driver to avoid code repetition.
- Use devm_platform_ioremap_resource() to simplify gpc/gpcv2 driver
code a bit.
- Add general IRQ support for imx-scu driver, so that interrupt of
device like RTC, thermal and watchdog can be handled.
* tag 'imx-drivers-5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux:
soc: imx: Add generic i.MX8 SoC driver
firmware: imx: enable imx scu general irq function
soc: imx: gpcv2: use devm_platform_ioremap_resource() to simplify code
soc: imx: gpc: use devm_platform_ioremap_resource() to simplify code
firmware: imx: scu-pd: decouple the SS information from domain names
firmware: imx: scu-pd: add specifying the base of domain name index support
firmware: imx: scu-pd: use bool to set postfix
soc: imx: gpcv2: Make use of regmap_read_poll_timeout()
Signed-off-by: Olof Johansson <olof@lixom.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/firmware/imx/Makefile | 2 | ||||
-rw-r--r-- | drivers/firmware/imx/imx-scu-irq.c | 168 | ||||
-rw-r--r-- | drivers/firmware/imx/imx-scu.c | 6 | ||||
-rw-r--r-- | drivers/firmware/imx/scu-pd.c | 121 | ||||
-rw-r--r-- | drivers/soc/imx/Makefile | 1 | ||||
-rw-r--r-- | drivers/soc/imx/gpc.c | 4 | ||||
-rw-r--r-- | drivers/soc/imx/gpcv2.c | 43 | ||||
-rw-r--r-- | drivers/soc/imx/soc-imx8.c | 115 |
8 files changed, 371 insertions, 89 deletions
diff --git a/drivers/firmware/imx/Makefile b/drivers/firmware/imx/Makefile index 1b2e15b3c9ca..802c4ad8e8f9 100644 --- a/drivers/firmware/imx/Makefile +++ b/drivers/firmware/imx/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o +obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o obj-$(CONFIG_IMX_SCU_PD) += scu-pd.o diff --git a/drivers/firmware/imx/imx-scu-irq.c b/drivers/firmware/imx/imx-scu-irq.c new file mode 100644 index 000000000000..043833ad3c1a --- /dev/null +++ b/drivers/firmware/imx/imx-scu-irq.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 NXP + * + * Implementation of the SCU IRQ functions using MU. + * + */ + +#include <dt-bindings/firmware/imx/rsrc.h> +#include <linux/firmware/imx/ipc.h> +#include <linux/mailbox_client.h> + +#define IMX_SC_IRQ_FUNC_ENABLE 1 +#define IMX_SC_IRQ_FUNC_STATUS 2 +#define IMX_SC_IRQ_NUM_GROUP 4 + +static u32 mu_resource_id; + +struct imx_sc_msg_irq_get_status { + struct imx_sc_rpc_msg hdr; + union { + struct { + u16 resource; + u8 group; + u8 reserved; + } __packed req; + struct { + u32 status; + } resp; + } data; +}; + +struct imx_sc_msg_irq_enable { + struct imx_sc_rpc_msg hdr; + u32 mask; + u16 resource; + u8 group; + u8 enable; +} __packed; + +static struct imx_sc_ipc *imx_sc_irq_ipc_handle; +static struct work_struct imx_sc_irq_work; +static ATOMIC_NOTIFIER_HEAD(imx_scu_irq_notifier_chain); + +int imx_scu_irq_register_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register( + &imx_scu_irq_notifier_chain, nb); +} +EXPORT_SYMBOL(imx_scu_irq_register_notifier); + +int imx_scu_irq_unregister_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister( + &imx_scu_irq_notifier_chain, nb); +} +EXPORT_SYMBOL(imx_scu_irq_unregister_notifier); + +static int imx_scu_irq_notifier_call_chain(unsigned long status, u8 *group) +{ + return atomic_notifier_call_chain(&imx_scu_irq_notifier_chain, + status, (void *)group); +} + +static void imx_scu_irq_work_handler(struct work_struct *work) +{ + struct imx_sc_msg_irq_get_status msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + u32 irq_status; + int ret; + u8 i; + + for (i = 0; i < IMX_SC_IRQ_NUM_GROUP; i++) { + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_IRQ; + hdr->func = IMX_SC_IRQ_FUNC_STATUS; + hdr->size = 2; + + msg.data.req.resource = mu_resource_id; + msg.data.req.group = i; + + ret = imx_scu_call_rpc(imx_sc_irq_ipc_handle, &msg, true); + if (ret) { + pr_err("get irq group %d status failed, ret %d\n", + i, ret); + return; + } + + irq_status = msg.data.resp.status; + if (!irq_status) + continue; + + imx_scu_irq_notifier_call_chain(irq_status, &i); + } +} + +int imx_scu_irq_group_enable(u8 group, u32 mask, u8 enable) +{ + struct imx_sc_msg_irq_enable msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + int ret; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_IRQ; + hdr->func = IMX_SC_IRQ_FUNC_ENABLE; + hdr->size = 3; + + msg.resource = mu_resource_id; + msg.group = group; + msg.mask = mask; + msg.enable = enable; + + ret = imx_scu_call_rpc(imx_sc_irq_ipc_handle, &msg, true); + if (ret) + pr_err("enable irq failed, group %d, mask %d, ret %d\n", + group, mask, ret); + + return ret; +} +EXPORT_SYMBOL(imx_scu_irq_group_enable); + +static void imx_scu_irq_callback(struct mbox_client *c, void *msg) +{ + schedule_work(&imx_sc_irq_work); +} + +int imx_scu_enable_general_irq_channel(struct device *dev) +{ + struct of_phandle_args spec; + struct mbox_client *cl; + struct mbox_chan *ch; + int ret = 0, i = 0; + + ret = imx_scu_get_handle(&imx_sc_irq_ipc_handle); + if (ret) + return ret; + + cl = devm_kzalloc(dev, sizeof(*cl), GFP_KERNEL); + if (!cl) + return -ENOMEM; + + cl->dev = dev; + cl->rx_callback = imx_scu_irq_callback; + + /* SCU general IRQ uses general interrupt channel 3 */ + ch = mbox_request_channel_byname(cl, "gip3"); + if (IS_ERR(ch)) { + ret = PTR_ERR(ch); + dev_err(dev, "failed to request mbox chan gip3, ret %d\n", ret); + devm_kfree(dev, cl); + return ret; + } + + INIT_WORK(&imx_sc_irq_work, imx_scu_irq_work_handler); + + if (!of_parse_phandle_with_args(dev->of_node, "mboxes", + "#mbox-cells", 0, &spec)) + i = of_alias_get_id(spec.np, "mu"); + + /* use mu1 as general mu irq channel if failed */ + if (i < 0) + i = 1; + + mu_resource_id = IMX_SC_R_MU_0A + i; + + return ret; +} +EXPORT_SYMBOL(imx_scu_enable_general_irq_channel); diff --git a/drivers/firmware/imx/imx-scu.c b/drivers/firmware/imx/imx-scu.c index 2bb1a19c413f..04a24a863d6e 100644 --- a/drivers/firmware/imx/imx-scu.c +++ b/drivers/firmware/imx/imx-scu.c @@ -10,6 +10,7 @@ #include <linux/err.h> #include <linux/firmware/imx/types.h> #include <linux/firmware/imx/ipc.h> +#include <linux/firmware/imx/sci.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/kernel.h> @@ -246,6 +247,11 @@ static int imx_scu_probe(struct platform_device *pdev) imx_sc_ipc_handle = sc_ipc; + ret = imx_scu_enable_general_irq_channel(dev); + if (ret) + dev_warn(dev, + "failed to enable general irq channel: %d\n", ret); + dev_info(dev, "NXP i.MX SCU Initialized\n"); return devm_of_platform_populate(dev); diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c index 39a94c7177fc..480cec69e2c9 100644 --- a/drivers/firmware/imx/scu-pd.c +++ b/drivers/firmware/imx/scu-pd.c @@ -74,7 +74,10 @@ struct imx_sc_pd_range { char *name; u32 rsrc; u8 num; + + /* add domain index */ bool postfix; + u8 start_from; }; struct imx_sc_pd_soc { @@ -84,71 +87,75 @@ struct imx_sc_pd_soc { static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { /* LSIO SS */ - { "lsio-pwm", IMX_SC_R_PWM_0, 8, 1 }, - { "lsio-gpio", IMX_SC_R_GPIO_0, 8, 1 }, - { "lsio-gpt", IMX_SC_R_GPT_0, 5, 1 }, - { "lsio-kpp", IMX_SC_R_KPP, 1, 0 }, - { "lsio-fspi", IMX_SC_R_FSPI_0, 2, 1 }, - { "lsio-mu", IMX_SC_R_MU_0A, 14, 1 }, + { "pwm", IMX_SC_R_PWM_0, 8, true, 0 }, + { "gpio", IMX_SC_R_GPIO_0, 8, true, 0 }, + { "gpt", IMX_SC_R_GPT_0, 5, true, 0 }, + { "kpp", IMX_SC_R_KPP, 1, false, 0 }, + { "fspi", IMX_SC_R_FSPI_0, 2, true, 0 }, + { "mu", IMX_SC_R_MU_0A, 14, true, 0 }, /* CONN SS */ - { "con-usb", IMX_SC_R_USB_0, 2, 1 }, - { "con-usb0phy", IMX_SC_R_USB_0_PHY, 1, 0 }, - { "con-usb2", IMX_SC_R_USB_2, 1, 0 }, - { "con-usb2phy", IMX_SC_R_USB_2_PHY, 1, 0 }, - { "con-sdhc", IMX_SC_R_SDHC_0, 3, 1 }, - { "con-enet", IMX_SC_R_ENET_0, 2, 1 }, - { "con-nand", IMX_SC_R_NAND, 1, 0 }, - { "con-mlb", IMX_SC_R_MLB_0, 1, 1 }, - - /* Audio DMA SS */ - { "adma-audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, 0 }, - { "adma-audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, 0 }, - { "adma-audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, 0 }, - { "adma-dma0-ch", IMX_SC_R_DMA_0_CH0, 16, 1 }, - { "adma-dma1-ch", IMX_SC_R_DMA_1_CH0, 16, 1 }, - { "adma-dma2-ch", IMX_SC_R_DMA_2_CH0, 5, 1 }, - { "adma-asrc0", IMX_SC_R_ASRC_0, 1, 0 }, - { "adma-asrc1", IMX_SC_R_ASRC_1, 1, 0 }, - { "adma-esai0", IMX_SC_R_ESAI_0, 1, 0 }, - { "adma-spdif0", IMX_SC_R_SPDIF_0, 1, 0 }, - { "adma-sai", IMX_SC_R_SAI_0, 3, 1 }, - { "adma-amix", IMX_SC_R_AMIX, 1, 0 }, - { "adma-mqs0", IMX_SC_R_MQS_0, 1, 0 }, - { "adma-dsp", IMX_SC_R_DSP, 1, 0 }, - { "adma-dsp-ram", IMX_SC_R_DSP_RAM, 1, 0 }, - { "adma-can", IMX_SC_R_CAN_0, 3, 1 }, - { "adma-ftm", IMX_SC_R_FTM_0, 2, 1 }, - { "adma-lpi2c", IMX_SC_R_I2C_0, 4, 1 }, - { "adma-adc", IMX_SC_R_ADC_0, 1, 1 }, - { "adma-lcd", IMX_SC_R_LCD_0, 1, 1 }, - { "adma-lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, 1 }, - { "adma-lpuart", IMX_SC_R_UART_0, 4, 1 }, - { "adma-lpspi", IMX_SC_R_SPI_0, 4, 1 }, - - /* VPU SS */ - { "vpu", IMX_SC_R_VPU, 1, 0 }, - { "vpu-pid", IMX_SC_R_VPU_PID0, 8, 1 }, - { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, 0 }, - { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, 0 }, + { "usb", IMX_SC_R_USB_0, 2, true, 0 }, + { "usb0phy", IMX_SC_R_USB_0_PHY, 1, false, 0 }, + { "usb2", IMX_SC_R_USB_2, 1, false, 0 }, + { "usb2phy", IMX_SC_R_USB_2_PHY, 1, false, 0 }, + { "sdhc", IMX_SC_R_SDHC_0, 3, true, 0 }, + { "enet", IMX_SC_R_ENET_0, 2, true, 0 }, + { "nand", IMX_SC_R_NAND, 1, false, 0 }, + { "mlb", IMX_SC_R_MLB_0, 1, true, 0 }, + + /* AUDIO SS */ + { "audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false, 0 }, + { "audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false, 0 }, + { "audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false, 0 }, + { "dma0-ch", IMX_SC_R_DMA_0_CH0, 16, true, 0 }, + { "dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true, 0 }, + { "dma2-ch", IMX_SC_R_DMA_2_CH0, 5, true, 0 }, + { "asrc0", IMX_SC_R_ASRC_0, 1, false, 0 }, + { "asrc1", IMX_SC_R_ASRC_1, 1, false, 0 }, + { "esai0", IMX_SC_R_ESAI_0, 1, false, 0 }, + { "spdif0", IMX_SC_R_SPDIF_0, 1, false, 0 }, + { "sai", IMX_SC_R_SAI_0, 3, true, 0 }, + { "amix", IMX_SC_R_AMIX, 1, false, 0 }, + { "mqs0", IMX_SC_R_MQS_0, 1, false, 0 }, + { "dsp", IMX_SC_R_DSP, 1, false, 0 }, + { "dsp-ram", IMX_SC_R_DSP_RAM, 1, false, 0 }, + + /* DMA SS */ + { "can", IMX_SC_R_CAN_0, 3, true, 0 }, + { "ftm", IMX_SC_R_FTM_0, 2, true, 0 }, + { "lpi2c", IMX_SC_R_I2C_0, 4, true, 0 }, + { "adc", IMX_SC_R_ADC_0, 1, true, 0 }, + { "lcd", IMX_SC_R_LCD_0, 1, true, 0 }, + { "lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 }, + { "lpuart", IMX_SC_R_UART_0, 4, true, 0 }, + { "lpspi", IMX_SC_R_SPI_0, 4, true, 0 }, + + /* VPU SS */ + { "vpu", IMX_SC_R_VPU, 1, false, 0 }, + { "vpu-pid", IMX_SC_R_VPU_PID0, 8, true, 0 }, + { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, false, 0 }, + { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, false, 0 }, /* GPU SS */ - { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, 1 }, + { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, true, 0 }, /* HSIO SS */ - { "hsio-pcie-b", IMX_SC_R_PCIE_B, 1, 0 }, - { "hsio-serdes-1", IMX_SC_R_SERDES_1, 1, 0 }, - { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, 0 }, + { "pcie-b", IMX_SC_R_PCIE_B, 1, false, 0 }, + { "serdes-1", IMX_SC_R_SERDES_1, 1, false, 0 }, + { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, false, 0 }, + + /* MIPI SS */ + { "mipi0", IMX_SC_R_MIPI_0, 1, false, 0 }, + { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, false, 0 }, + { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, true, 0 }, - /* MIPI/LVDS SS */ - { "mipi0", IMX_SC_R_MIPI_0, 1, 0 }, - { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, 0 }, - { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, 1 }, - { "lvds0", IMX_SC_R_LVDS_0, 1, 0 }, + /* LVDS SS */ + { "lvds0", IMX_SC_R_LVDS_0, 1, false, 0 }, /* DC SS */ - { "dc0", IMX_SC_R_DC_0, 1, 0 }, - { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, 1 }, + { "dc0", IMX_SC_R_DC_0, 1, false, 0 }, + { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, true, 0 }, }; static const struct imx_sc_pd_soc imx8qxp_scu_pd = { @@ -236,7 +243,7 @@ imx_scu_add_pm_domain(struct device *dev, int idx, if (pd_ranges->postfix) snprintf(sc_pd->name, sizeof(sc_pd->name), - "%s%i", pd_ranges->name, idx); + "%s%i", pd_ranges->name, pd_ranges->start_from + idx); else snprintf(sc_pd->name, sizeof(sc_pd->name), "%s", pd_ranges->name); diff --git a/drivers/soc/imx/Makefile b/drivers/soc/imx/Makefile index 506a6f3c2b9b..d6b529e06d9a 100644 --- a/drivers/soc/imx/Makefile +++ b/drivers/soc/imx/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o obj-$(CONFIG_IMX_GPCV2_PM_DOMAINS) += gpcv2.o +obj-$(CONFIG_ARCH_MXC) += soc-imx8.o diff --git a/drivers/soc/imx/gpc.c b/drivers/soc/imx/gpc.c index 7d14a4b4e82a..a8f1e47ce698 100644 --- a/drivers/soc/imx/gpc.c +++ b/drivers/soc/imx/gpc.c @@ -406,7 +406,6 @@ static int imx_gpc_probe(struct platform_device *pdev) const struct imx_gpc_dt_data *of_id_data = of_id->data; struct device_node *pgc_node; struct regmap *regmap; - struct resource *res; void __iomem *base; int ret; @@ -417,8 +416,7 @@ static int imx_gpc_probe(struct platform_device *pdev) !pgc_node) return 0; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c index 176f473127b6..31b8d002d855 100644 --- a/drivers/soc/imx/gpcv2.c +++ b/drivers/soc/imx/gpcv2.c @@ -136,8 +136,8 @@ static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd, 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); - unsigned long deadline; int i, ret = 0; + u32 pxx_req; regmap_update_bits(domain->regmap, GPC_PGC_CPU_MAPPING, domain->bits.map, domain->bits.map); @@ -169,30 +169,19 @@ static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd, * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait * for PUP_REQ/PDN_REQ bit to be cleared */ - deadline = jiffies + msecs_to_jiffies(1); - while (true) { - u32 pxx_req; - - regmap_read(domain->regmap, offset, &pxx_req); - - if (!(pxx_req & domain->bits.pxx)) - break; - - if (time_after(jiffies, deadline)) { - dev_err(domain->dev, "falied to command PGC\n"); - ret = -ETIMEDOUT; - /* - * 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; - break; - } - - cpu_relax(); + ret = regmap_read_poll_timeout(domain->regmap, offset, pxx_req, + !(pxx_req & domain->bits.pxx), + 0, USEC_PER_MSEC); + if (ret) { + dev_err(domain->dev, "failed 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) @@ -574,7 +563,6 @@ static int imx_gpcv2_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *pgc_np, *np; struct regmap *regmap; - struct resource *res; void __iomem *base; int ret; @@ -584,8 +572,7 @@ static int imx_gpcv2_probe(struct platform_device *pdev) return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/soc/imx/soc-imx8.c b/drivers/soc/imx/soc-imx8.c new file mode 100644 index 000000000000..fc6429f9170a --- /dev/null +++ b/drivers/soc/imx/soc-imx8.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP. + */ + +#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/of.h> + +#define REV_B1 0x21 + +#define IMX8MQ_SW_INFO_B1 0x40 +#define IMX8MQ_SW_MAGIC_B1 0xff0055aa + +struct imx8_soc_data { + char *name; + u32 (*soc_revision)(void); +}; + +static u32 __init imx8mq_soc_revision(void) +{ + struct device_node *np; + void __iomem *ocotp_base; + u32 magic; + u32 rev = 0; + + np = of_find_compatible_node(NULL, NULL, "fsl,imx8mq-ocotp"); + if (!np) + goto out; + + 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; + + iounmap(ocotp_base); + +out: + of_node_put(np); + return rev; +} + +static const struct imx8_soc_data imx8mq_soc_data = { + .name = "i.MX8MQ", + .soc_revision = imx8mq_soc_revision, +}; + +static const struct of_device_id imx8_soc_match[] = { + { .compatible = "fsl,imx8mq", .data = &imx8mq_soc_data, }, + { } +}; + +#define imx8_revision(soc_rev) \ + soc_rev ? \ + kasprintf(GFP_KERNEL, "%d.%d", (soc_rev >> 4) & 0xf, soc_rev & 0xf) : \ + "unknown" + +static int __init imx8_soc_init(void) +{ + struct soc_device_attribute *soc_dev_attr; + struct soc_device *soc_dev; + struct device_node *root; + const struct of_device_id *id; + u32 soc_rev = 0; + const struct imx8_soc_data *data; + int ret; + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + return -ENODEV; + + soc_dev_attr->family = "Freescale i.MX"; + + root = of_find_node_by_path("/"); + ret = of_property_read_string(root, "model", &soc_dev_attr->machine); + if (ret) + goto free_soc; + + id = of_match_node(imx8_soc_match, root); + if (!id) + goto free_soc; + + of_node_put(root); + + data = id->data; + if (data) { + soc_dev_attr->soc_id = data->name; + if (data->soc_revision) + soc_rev = data->soc_revision(); + } + + soc_dev_attr->revision = imx8_revision(soc_rev); + if (!soc_dev_attr->revision) + goto free_soc; + + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR(soc_dev)) + goto free_rev; + + return 0; + +free_rev: + kfree(soc_dev_attr->revision); +free_soc: + kfree(soc_dev_attr); + of_node_put(root); + return -ENODEV; +} +device_initcall(imx8_soc_init); |