i.MX drivers update for 5.4:
- A series from Anson Huang to add UID support for i.MX8 SoC and SCU drivers. - A series from Daniel Baluta to add DSP IPC driver for communication between host AP (Linux) and the firmware running on DSP embedded in i.MX8 SoCs. - A small fix for GPCv2 error code printing. - Switch from module_platform_driver_probe() to module_platform_driver() for imx-weim driver, as we need the driver to probe again when device is present later. - Add optional burst clock mode support for imx-weim driver. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJdYoCEAAoJEFBXWFqHsHzOkDgH/jWNgp3QR3omPQGX80oM6/ga 3x78GDJyQfzZKWyOJbWz12tzXWlD1fmGIZy7SeuvXlYUbNnE1WU6ARFGDoBpjbew DOYoPT9x7eJemSdnEE43g2vtspEzb6Afvb7dr78OPaik24eY0dFx90U0tzptmuVY gXxkAHo2ez6txXTu5Id9Z5Deitukb8NoK/9yyzTeD0oYn2hCooAp5fAl1rKgfLnf MpG29TCK8dxr/j7tzsbt2nutJJYy1Ybh/0WpOMXnBwdAz20JT7MU87DMil0//Q02 0Es/P2xH7K4r3ca9mupDtXZps2DbnmYJorvxpfHuMXcyq0uxGhl1/zyhTv+LW4c= =5mGq -----END PGP SIGNATURE----- Merge tag 'imx-drivers-5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux into arm/drivers i.MX drivers update for 5.4: - A series from Anson Huang to add UID support for i.MX8 SoC and SCU drivers. - A series from Daniel Baluta to add DSP IPC driver for communication between host AP (Linux) and the firmware running on DSP embedded in i.MX8 SoCs. - A small fix for GPCv2 error code printing. - Switch from module_platform_driver_probe() to module_platform_driver() for imx-weim driver, as we need the driver to probe again when device is present later. - Add optional burst clock mode support for imx-weim driver. * tag 'imx-drivers-5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux: soc: imx: gpcv2: Print the correct error code bus: imx-weim: use module_platform_driver() firmware: imx: Add DSP IPC protocol interface soc: imx-scu: Add SoC UID(unique identifier) support bus: imx-weim: optionally enable burst clock mode firmware: imx: scu-pd: Add IRQSTR_DSP PD range firmware: imx: scu-pd: Add mu13 b side PD range firmware: imx: scu-pd: Rename mu PD range to mu_a soc: imx8: Add i.MX8MM UID(unique identifier) support soc: imx8: Add i.MX8MQ UID(unique identifier) support Link: https://lore.kernel.org/r/20190825153237.28829-1-shawnguo@kernel.org Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
commit
bff00fdf03
|
@ -19,6 +19,8 @@ struct imx_weim_devtype {
|
|||
unsigned int cs_count;
|
||||
unsigned int cs_regs_count;
|
||||
unsigned int cs_stride;
|
||||
unsigned int wcr_offset;
|
||||
unsigned int wcr_bcm;
|
||||
};
|
||||
|
||||
static const struct imx_weim_devtype imx1_weim_devtype = {
|
||||
|
@ -37,6 +39,8 @@ static const struct imx_weim_devtype imx50_weim_devtype = {
|
|||
.cs_count = 4,
|
||||
.cs_regs_count = 6,
|
||||
.cs_stride = 0x18,
|
||||
.wcr_offset = 0x90,
|
||||
.wcr_bcm = BIT(0),
|
||||
};
|
||||
|
||||
static const struct imx_weim_devtype imx51_weim_devtype = {
|
||||
|
@ -183,8 +187,7 @@ static int __init weim_timing_setup(struct device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __init weim_parse_dt(struct platform_device *pdev,
|
||||
void __iomem *base)
|
||||
static int weim_parse_dt(struct platform_device *pdev, void __iomem *base)
|
||||
{
|
||||
const struct of_device_id *of_id = of_match_device(weim_id_table,
|
||||
&pdev->dev);
|
||||
|
@ -192,6 +195,7 @@ static int __init weim_parse_dt(struct platform_device *pdev,
|
|||
struct device_node *child;
|
||||
int ret, have_child = 0;
|
||||
struct cs_timing_state ts = {};
|
||||
u32 reg;
|
||||
|
||||
if (devtype == &imx50_weim_devtype) {
|
||||
ret = imx_weim_gpr_setup(pdev);
|
||||
|
@ -199,6 +203,17 @@ static int __init weim_parse_dt(struct platform_device *pdev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (of_property_read_bool(pdev->dev.of_node, "fsl,burst-clk-enable")) {
|
||||
if (devtype->wcr_bcm) {
|
||||
reg = readl(base + devtype->wcr_offset);
|
||||
writel(reg | devtype->wcr_bcm,
|
||||
base + devtype->wcr_offset);
|
||||
} else {
|
||||
dev_err(&pdev->dev, "burst clk mode not supported.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
for_each_available_child_of_node(pdev->dev.of_node, child) {
|
||||
ret = weim_timing_setup(&pdev->dev, child, base, devtype, &ts);
|
||||
if (ret)
|
||||
|
@ -217,7 +232,7 @@ static int __init weim_parse_dt(struct platform_device *pdev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int __init weim_probe(struct platform_device *pdev)
|
||||
static int weim_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct clk *clk;
|
||||
|
@ -254,8 +269,9 @@ static struct platform_driver weim_driver = {
|
|||
.name = "imx-weim",
|
||||
.of_match_table = weim_id_table,
|
||||
},
|
||||
.probe = weim_probe,
|
||||
};
|
||||
module_platform_driver_probe(weim_driver, weim_probe);
|
||||
module_platform_driver(weim_driver);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor Inc.");
|
||||
MODULE_DESCRIPTION("i.MX EIM Controller Driver");
|
||||
|
|
|
@ -1,4 +1,15 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config IMX_DSP
|
||||
bool "IMX DSP Protocol driver"
|
||||
depends on IMX_MBOX
|
||||
help
|
||||
This enables DSP IPC protocol between host AP (Linux)
|
||||
and the firmware running on DSP.
|
||||
DSP exists on some i.MX8 processors (e.g i.MX8QM, i.MX8QXP).
|
||||
|
||||
It acts like a doorbell. Client might use shared memory to
|
||||
exchange information with DSP side.
|
||||
|
||||
config IMX_SCU
|
||||
bool "IMX SCU Protocol driver"
|
||||
depends on IMX_MBOX
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_IMX_DSP) += imx-dsp.o
|
||||
obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o
|
||||
obj-$(CONFIG_IMX_SCU_PD) += scu-pd.o
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2019 NXP
|
||||
* Author: Daniel Baluta <daniel.baluta@nxp.com>
|
||||
*
|
||||
* Implementation of the DSP IPC interface (host side)
|
||||
*/
|
||||
|
||||
#include <linux/firmware/imx/dsp.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/*
|
||||
* imx_dsp_ring_doorbell - triggers an interrupt on the other side (DSP)
|
||||
*
|
||||
* @dsp: DSP IPC handle
|
||||
* @chan_idx: index of the channel where to trigger the interrupt
|
||||
*
|
||||
* Returns non-negative value for success, negative value for error
|
||||
*/
|
||||
int imx_dsp_ring_doorbell(struct imx_dsp_ipc *ipc, unsigned int idx)
|
||||
{
|
||||
int ret;
|
||||
struct imx_dsp_chan *dsp_chan;
|
||||
|
||||
if (idx >= DSP_MU_CHAN_NUM)
|
||||
return -EINVAL;
|
||||
|
||||
dsp_chan = &ipc->chans[idx];
|
||||
ret = mbox_send_message(dsp_chan->ch, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(imx_dsp_ring_doorbell);
|
||||
|
||||
/*
|
||||
* imx_dsp_handle_rx - rx callback used by imx mailbox
|
||||
*
|
||||
* @c: mbox client
|
||||
* @msg: message received
|
||||
*
|
||||
* Users of DSP IPC will need to privde handle_reply and handle_request
|
||||
* callbacks.
|
||||
*/
|
||||
static void imx_dsp_handle_rx(struct mbox_client *c, void *msg)
|
||||
{
|
||||
struct imx_dsp_chan *chan = container_of(c, struct imx_dsp_chan, cl);
|
||||
|
||||
if (chan->idx == 0) {
|
||||
chan->ipc->ops->handle_reply(chan->ipc);
|
||||
} else {
|
||||
chan->ipc->ops->handle_request(chan->ipc);
|
||||
imx_dsp_ring_doorbell(chan->ipc, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int imx_dsp_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct imx_dsp_ipc *dsp_ipc;
|
||||
struct imx_dsp_chan *dsp_chan;
|
||||
struct mbox_client *cl;
|
||||
char *chan_name;
|
||||
int ret;
|
||||
int i, j;
|
||||
|
||||
device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent);
|
||||
|
||||
dsp_ipc = devm_kzalloc(dev, sizeof(*dsp_ipc), GFP_KERNEL);
|
||||
if (!dsp_ipc)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < DSP_MU_CHAN_NUM; i++) {
|
||||
if (i < 2)
|
||||
chan_name = kasprintf(GFP_KERNEL, "txdb%d", i);
|
||||
else
|
||||
chan_name = kasprintf(GFP_KERNEL, "rxdb%d", i - 2);
|
||||
|
||||
if (!chan_name)
|
||||
return -ENOMEM;
|
||||
|
||||
dsp_chan = &dsp_ipc->chans[i];
|
||||
cl = &dsp_chan->cl;
|
||||
cl->dev = dev;
|
||||
cl->tx_block = false;
|
||||
cl->knows_txdone = true;
|
||||
cl->rx_callback = imx_dsp_handle_rx;
|
||||
|
||||
dsp_chan->ipc = dsp_ipc;
|
||||
dsp_chan->idx = i % 2;
|
||||
dsp_chan->ch = mbox_request_channel_byname(cl, chan_name);
|
||||
if (IS_ERR(dsp_chan->ch)) {
|
||||
ret = PTR_ERR(dsp_chan->ch);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to request mbox chan %s ret %d\n",
|
||||
chan_name, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "request mbox chan %s\n", chan_name);
|
||||
/* chan_name is not used anymore by framework */
|
||||
kfree(chan_name);
|
||||
}
|
||||
|
||||
dsp_ipc->dev = dev;
|
||||
|
||||
dev_set_drvdata(dev, dsp_ipc);
|
||||
|
||||
dev_info(dev, "NXP i.MX DSP IPC initialized\n");
|
||||
|
||||
return devm_of_platform_populate(dev);
|
||||
out:
|
||||
kfree(chan_name);
|
||||
for (j = 0; j < i; j++) {
|
||||
dsp_chan = &dsp_ipc->chans[j];
|
||||
mbox_free_channel(dsp_chan->ch);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx_dsp_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct imx_dsp_chan *dsp_chan;
|
||||
struct imx_dsp_ipc *dsp_ipc;
|
||||
int i;
|
||||
|
||||
dsp_ipc = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
for (i = 0; i < DSP_MU_CHAN_NUM; i++) {
|
||||
dsp_chan = &dsp_ipc->chans[i];
|
||||
mbox_free_channel(dsp_chan->ch);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver imx_dsp_driver = {
|
||||
.driver = {
|
||||
.name = "imx-dsp",
|
||||
},
|
||||
.probe = imx_dsp_probe,
|
||||
.remove = imx_dsp_remove,
|
||||
};
|
||||
builtin_platform_driver(imx_dsp_driver);
|
||||
|
||||
MODULE_AUTHOR("Daniel Baluta <daniel.baluta@nxp.com>");
|
||||
MODULE_DESCRIPTION("IMX DSP IPC protocol driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -92,7 +92,8 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = {
|
|||
{ "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 },
|
||||
{ "mu_a", IMX_SC_R_MU_0A, 14, true, 0 },
|
||||
{ "mu_b", IMX_SC_R_MU_13B, 1, true, 13 },
|
||||
|
||||
/* CONN SS */
|
||||
{ "usb", IMX_SC_R_USB_0, 2, true, 0 },
|
||||
|
@ -130,6 +131,7 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = {
|
|||
{ "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 },
|
||||
{ "irqstr_dsp", IMX_SC_R_IRQSTR_DSP, 1, false, 0 },
|
||||
|
||||
/* VPU SS */
|
||||
{ "vpu", IMX_SC_R_VPU, 1, false, 0 },
|
||||
|
|
|
@ -198,7 +198,7 @@ static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd,
|
|||
err = regulator_disable(domain->regulator);
|
||||
if (err)
|
||||
dev_err(domain->dev,
|
||||
"failed to disable regulator: %d\n", ret);
|
||||
"failed to disable regulator: %d\n", err);
|
||||
/* Preserve earlier error code */
|
||||
ret = ret ?: err;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,40 @@ struct imx_sc_msg_misc_get_soc_id {
|
|||
} data;
|
||||
} __packed;
|
||||
|
||||
struct imx_sc_msg_misc_get_soc_uid {
|
||||
struct imx_sc_rpc_msg hdr;
|
||||
u32 uid_low;
|
||||
u32 uid_high;
|
||||
} __packed;
|
||||
|
||||
static ssize_t soc_uid_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct imx_sc_msg_misc_get_soc_uid msg;
|
||||
struct imx_sc_rpc_msg *hdr = &msg.hdr;
|
||||
u64 soc_uid;
|
||||
int ret;
|
||||
|
||||
hdr->ver = IMX_SC_RPC_VERSION;
|
||||
hdr->svc = IMX_SC_RPC_SVC_MISC;
|
||||
hdr->func = IMX_SC_MISC_FUNC_UNIQUE_ID;
|
||||
hdr->size = 1;
|
||||
|
||||
ret = imx_scu_call_rpc(soc_ipc_handle, &msg, false);
|
||||
if (ret) {
|
||||
pr_err("%s: get soc uid failed, ret %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
soc_uid = msg.uid_high;
|
||||
soc_uid <<= 32;
|
||||
soc_uid |= msg.uid_low;
|
||||
|
||||
return sprintf(buf, "%016llX\n", soc_uid);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(soc_uid);
|
||||
|
||||
static int imx_scu_soc_id(void)
|
||||
{
|
||||
struct imx_sc_msg_misc_get_soc_id msg;
|
||||
|
@ -102,6 +136,11 @@ static int imx_scu_soc_probe(struct platform_device *pdev)
|
|||
goto free_revision;
|
||||
}
|
||||
|
||||
ret = device_create_file(soc_device_to_device(soc_dev),
|
||||
&dev_attr_soc_uid);
|
||||
if (ret)
|
||||
goto free_revision;
|
||||
|
||||
return 0;
|
||||
|
||||
free_revision:
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
#define IMX8MQ_SW_INFO_B1 0x40
|
||||
#define IMX8MQ_SW_MAGIC_B1 0xff0055aa
|
||||
|
||||
#define OCOTP_UID_LOW 0x410
|
||||
#define OCOTP_UID_HIGH 0x420
|
||||
|
||||
/* Same as ANADIG_DIGPROG_IMX7D */
|
||||
#define ANADIG_DIGPROG_IMX8MM 0x800
|
||||
|
||||
|
@ -24,6 +27,16 @@ struct imx8_soc_data {
|
|||
u32 (*soc_revision)(void);
|
||||
};
|
||||
|
||||
static u64 soc_uid;
|
||||
|
||||
static ssize_t soc_uid_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%016llX\n", soc_uid);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(soc_uid);
|
||||
|
||||
static u32 __init imx8mq_soc_revision(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
@ -42,6 +55,10 @@ static u32 __init imx8mq_soc_revision(void)
|
|||
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);
|
||||
|
||||
iounmap(ocotp_base);
|
||||
|
||||
out:
|
||||
|
@ -49,6 +66,26 @@ out:
|
|||
return rev;
|
||||
}
|
||||
|
||||
static void __init imx8mm_soc_uid(void)
|
||||
{
|
||||
void __iomem *ocotp_base;
|
||||
struct device_node *np;
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx8mm-ocotp");
|
||||
if (!np)
|
||||
return;
|
||||
|
||||
ocotp_base = of_iomap(np, 0);
|
||||
WARN_ON(!ocotp_base);
|
||||
|
||||
soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH);
|
||||
soc_uid <<= 32;
|
||||
soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW);
|
||||
|
||||
iounmap(ocotp_base);
|
||||
of_node_put(np);
|
||||
}
|
||||
|
||||
static u32 __init imx8mm_soc_revision(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
@ -66,6 +103,9 @@ static u32 __init imx8mm_soc_revision(void)
|
|||
|
||||
iounmap(anatop_base);
|
||||
of_node_put(np);
|
||||
|
||||
imx8mm_soc_uid();
|
||||
|
||||
return rev;
|
||||
}
|
||||
|
||||
|
@ -140,6 +180,11 @@ static int __init imx8_soc_init(void)
|
|||
goto free_rev;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright 2019 NXP
|
||||
*
|
||||
* Header file for the DSP IPC implementation
|
||||
*/
|
||||
|
||||
#ifndef _IMX_DSP_IPC_H
|
||||
#define _IMX_DSP_IPC_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
|
||||
#define DSP_MU_CHAN_NUM 4
|
||||
|
||||
struct imx_dsp_chan {
|
||||
struct imx_dsp_ipc *ipc;
|
||||
struct mbox_client cl;
|
||||
struct mbox_chan *ch;
|
||||
char *name;
|
||||
int idx;
|
||||
};
|
||||
|
||||
struct imx_dsp_ops {
|
||||
void (*handle_reply)(struct imx_dsp_ipc *ipc);
|
||||
void (*handle_request)(struct imx_dsp_ipc *ipc);
|
||||
};
|
||||
|
||||
struct imx_dsp_ipc {
|
||||
/* Host <-> DSP communication uses 2 txdb and 2 rxdb channels */
|
||||
struct imx_dsp_chan chans[DSP_MU_CHAN_NUM];
|
||||
struct device *dev;
|
||||
struct imx_dsp_ops *ops;
|
||||
void *private_data;
|
||||
};
|
||||
|
||||
static inline void imx_dsp_set_data(struct imx_dsp_ipc *ipc, void *data)
|
||||
{
|
||||
if (!ipc)
|
||||
return;
|
||||
|
||||
ipc->private_data = data;
|
||||
}
|
||||
|
||||
static inline void *imx_dsp_get_data(struct imx_dsp_ipc *ipc)
|
||||
{
|
||||
if (!ipc)
|
||||
return NULL;
|
||||
|
||||
return ipc->private_data;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_IMX_DSP)
|
||||
|
||||
int imx_dsp_ring_doorbell(struct imx_dsp_ipc *dsp, unsigned int chan_idx);
|
||||
|
||||
#else
|
||||
|
||||
static inline int imx_dsp_ring_doorbell(struct imx_dsp_ipc *ipc,
|
||||
unsigned int chan_idx)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* _IMX_DSP_IPC_H */
|
Loading…
Reference in New Issue