Commit 21000823 authored by Olof Johansson's avatar Olof Johansson

Merge tag 'imx-drivers-5.3' of...

Merge tag 'imx-drivers-5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux into arm/drivers

i.MX driver changes for 5.3:
 - Add i.MX SCU based SoC bus driver for i.MX8QXP SoC support, which
   talks to SCU firmware for getting SoC ID and revision.
 - Update soc-imx8 bus driver to read imx8mm soc revision from anatop.
 - Add i.MX8MN SoC bus support into soc-imx8 driver.
 - Various small improvements on soc-imx8 bus driver.

* tag 'imx-drivers-5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux:
  soc: imx: Add i.MX8MN SoC driver support
  soc: imx8: Use existing of_root directly
  soc: imx8: Fix potential kernel dump in error path
  soc: imx: soc-imx8: Correct return value of error handle
  soc: imx: soc-imx8: Avoid unnecessary of_node_put() in error handling
  soc: imx: Add SCU SoC info driver support
  soc: imx: Read imx8mm soc revision from anatop
Signed-off-by: default avatarOlof Johansson <olof@lixom.net>
parents 0aa447e5 db10496c
......@@ -8,4 +8,13 @@ config IMX_GPCV2_PM_DOMAINS
select PM_GENERIC_DOMAINS
default y if SOC_IMX7D
config IMX_SCU_SOC
bool "i.MX System Controller Unit SoC info support"
depends on IMX_SCU
select SOC_BUS
help
If you say yes here you get support for the NXP i.MX System
Controller Unit SoC info module, it will provide the SoC info
like SoC family, ID and revision etc.
endmenu
......@@ -2,3 +2,4 @@
obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o
obj-$(CONFIG_IMX_GPCV2_PM_DOMAINS) += gpcv2.o
obj-$(CONFIG_ARCH_MXC) += soc-imx8.o
obj-$(CONFIG_IMX_SCU_SOC) += soc-imx-scu.o
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2019 NXP.
*/
#include <dt-bindings/firmware/imx/rsrc.h>
#include <linux/firmware/imx/sci.h>
#include <linux/slab.h>
#include <linux/sys_soc.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#define IMX_SCU_SOC_DRIVER_NAME "imx-scu-soc"
static struct imx_sc_ipc *soc_ipc_handle;
struct imx_sc_msg_misc_get_soc_id {
struct imx_sc_rpc_msg hdr;
union {
struct {
u32 control;
u16 resource;
} __packed req;
struct {
u32 id;
} resp;
} data;
} __packed;
static int imx_scu_soc_id(void)
{
struct imx_sc_msg_misc_get_soc_id msg;
struct imx_sc_rpc_msg *hdr = &msg.hdr;
int ret;
hdr->ver = IMX_SC_RPC_VERSION;
hdr->svc = IMX_SC_RPC_SVC_MISC;
hdr->func = IMX_SC_MISC_FUNC_GET_CONTROL;
hdr->size = 3;
msg.data.req.control = IMX_SC_C_ID;
msg.data.req.resource = IMX_SC_R_SYSTEM;
ret = imx_scu_call_rpc(soc_ipc_handle, &msg, true);
if (ret) {
pr_err("%s: get soc info failed, ret %d\n", __func__, ret);
return ret;
}
return msg.data.resp.id;
}
static int imx_scu_soc_probe(struct platform_device *pdev)
{
struct soc_device_attribute *soc_dev_attr;
struct soc_device *soc_dev;
int id, ret;
u32 val;
ret = imx_scu_get_handle(&soc_ipc_handle);
if (ret)
return ret;
soc_dev_attr = devm_kzalloc(&pdev->dev, 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)
return ret;
id = imx_scu_soc_id();
if (id < 0)
return -EINVAL;
/* format soc_id value passed from SCU firmware */
val = id & 0x1f;
soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "0x%x", val);
if (!soc_dev_attr->soc_id)
return -ENOMEM;
/* format revision value passed from SCU firmware */
val = (id >> 5) & 0xf;
val = (((val >> 2) + 1) << 4) | (val & 0x3);
soc_dev_attr->revision = kasprintf(GFP_KERNEL,
"%d.%d",
(val >> 4) & 0xf,
val & 0xf);
if (!soc_dev_attr->revision) {
ret = -ENOMEM;
goto free_soc_id;
}
soc_dev = soc_device_register(soc_dev_attr);
if (IS_ERR(soc_dev)) {
ret = PTR_ERR(soc_dev);
goto free_revision;
}
return 0;
free_revision:
kfree(soc_dev_attr->revision);
free_soc_id:
kfree(soc_dev_attr->soc_id);
return ret;
}
static struct platform_driver imx_scu_soc_driver = {
.driver = {
.name = IMX_SCU_SOC_DRIVER_NAME,
},
.probe = imx_scu_soc_probe,
};
static int __init imx_scu_soc_init(void)
{
struct platform_device *pdev;
struct device_node *np;
int ret;
np = of_find_compatible_node(NULL, NULL, "fsl,imx-scu");
if (!np)
return -ENODEV;
of_node_put(np);
ret = platform_driver_register(&imx_scu_soc_driver);
if (ret)
return ret;
pdev = platform_device_register_simple(IMX_SCU_SOC_DRIVER_NAME,
-1, NULL, 0);
if (IS_ERR(pdev))
platform_driver_unregister(&imx_scu_soc_driver);
return PTR_ERR_OR_ZERO(pdev);
}
device_initcall(imx_scu_soc_init);
......@@ -16,6 +16,9 @@
#define IMX8MQ_SW_INFO_B1 0x40
#define IMX8MQ_SW_MAGIC_B1 0xff0055aa
/* Same as ANADIG_DIGPROG_IMX7D */
#define ANADIG_DIGPROG_IMX8MM 0x800
struct imx8_soc_data {
char *name;
u32 (*soc_revision)(void);
......@@ -46,13 +49,45 @@ static u32 __init imx8mq_soc_revision(void)
return rev;
}
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);
iounmap(anatop_base);
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 imx8_soc_data imx8mm_soc_data = {
.name = "i.MX8MM",
.soc_revision = imx8mm_soc_revision,
};
static const struct imx8_soc_data imx8mn_soc_data = {
.name = "i.MX8MN",
.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, },
{ }
};
......@@ -65,7 +100,6 @@ 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;
......@@ -73,20 +107,19 @@ static int __init imx8_soc_init(void)
soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
if (!soc_dev_attr)
return -ENODEV;
return -ENOMEM;
soc_dev_attr->family = "Freescale i.MX";
root = of_find_node_by_path("/");
ret = of_property_read_string(root, "model", &soc_dev_attr->machine);
ret = of_property_read_string(of_root, "model", &soc_dev_attr->machine);
if (ret)
goto free_soc;
id = of_match_node(imx8_soc_match, root);
if (!id)
id = of_match_node(imx8_soc_match, of_root);
if (!id) {
ret = -ENODEV;
goto free_soc;
of_node_put(root);
}
data = id->data;
if (data) {
......@@ -96,20 +129,24 @@ static int __init imx8_soc_init(void)
}
soc_dev_attr->revision = imx8_revision(soc_rev);
if (!soc_dev_attr->revision)
if (!soc_dev_attr->revision) {
ret = -ENOMEM;
goto free_soc;
}
soc_dev = soc_device_register(soc_dev_attr);
if (IS_ERR(soc_dev))
if (IS_ERR(soc_dev)) {
ret = PTR_ERR(soc_dev);
goto free_rev;
}
return 0;
free_rev:
if (strcmp(soc_dev_attr->revision, "unknown"))
kfree(soc_dev_attr->revision);
free_soc:
kfree(soc_dev_attr);
of_node_put(root);
return -ENODEV;
return ret;
}
device_initcall(imx8_soc_init);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment