Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+342 -6
+20 -4
drivers/bus/imx-weim.c
··· 19 19 unsigned int cs_count; 20 20 unsigned int cs_regs_count; 21 21 unsigned int cs_stride; 22 + unsigned int wcr_offset; 23 + unsigned int wcr_bcm; 22 24 }; 23 25 24 26 static const struct imx_weim_devtype imx1_weim_devtype = { ··· 39 37 .cs_count = 4, 40 38 .cs_regs_count = 6, 41 39 .cs_stride = 0x18, 40 + .wcr_offset = 0x90, 41 + .wcr_bcm = BIT(0), 42 42 }; 43 43 44 44 static const struct imx_weim_devtype imx51_weim_devtype = { ··· 187 183 return 0; 188 184 } 189 185 190 - static int __init weim_parse_dt(struct platform_device *pdev, 191 - void __iomem *base) 186 + static int weim_parse_dt(struct platform_device *pdev, void __iomem *base) 192 187 { 193 188 const struct of_device_id *of_id = of_match_device(weim_id_table, 194 189 &pdev->dev); ··· 195 192 struct device_node *child; 196 193 int ret, have_child = 0; 197 194 struct cs_timing_state ts = {}; 195 + u32 reg; 198 196 199 197 if (devtype == &imx50_weim_devtype) { 200 198 ret = imx_weim_gpr_setup(pdev); 201 199 if (ret) 202 200 return ret; 201 + } 202 + 203 + if (of_property_read_bool(pdev->dev.of_node, "fsl,burst-clk-enable")) { 204 + if (devtype->wcr_bcm) { 205 + reg = readl(base + devtype->wcr_offset); 206 + writel(reg | devtype->wcr_bcm, 207 + base + devtype->wcr_offset); 208 + } else { 209 + dev_err(&pdev->dev, "burst clk mode not supported.\n"); 210 + return -EINVAL; 211 + } 203 212 } 204 213 205 214 for_each_available_child_of_node(pdev->dev.of_node, child) { ··· 232 217 return ret; 233 218 } 234 219 235 - static int __init weim_probe(struct platform_device *pdev) 220 + static int weim_probe(struct platform_device *pdev) 236 221 { 237 222 struct resource *res; 238 223 struct clk *clk; ··· 269 254 .name = "imx-weim", 270 255 .of_match_table = weim_id_table, 271 256 }, 257 + .probe = weim_probe, 272 258 }; 273 - module_platform_driver_probe(weim_driver, weim_probe); 259 + module_platform_driver(weim_driver); 274 260 275 261 MODULE_AUTHOR("Freescale Semiconductor Inc."); 276 262 MODULE_DESCRIPTION("i.MX EIM Controller Driver");
+11
drivers/firmware/imx/Kconfig
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 + config IMX_DSP 3 + bool "IMX DSP Protocol driver" 4 + depends on IMX_MBOX 5 + help 6 + This enables DSP IPC protocol between host AP (Linux) 7 + and the firmware running on DSP. 8 + DSP exists on some i.MX8 processors (e.g i.MX8QM, i.MX8QXP). 9 + 10 + It acts like a doorbell. Client might use shared memory to 11 + exchange information with DSP side. 12 + 2 13 config IMX_SCU 3 14 bool "IMX SCU Protocol driver" 4 15 depends on IMX_MBOX
+1
drivers/firmware/imx/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 + obj-$(CONFIG_IMX_DSP) += imx-dsp.o 2 3 obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o 3 4 obj-$(CONFIG_IMX_SCU_PD) += scu-pd.o
+155
drivers/firmware/imx/imx-dsp.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * Copyright 2019 NXP 4 + * Author: Daniel Baluta <daniel.baluta@nxp.com> 5 + * 6 + * Implementation of the DSP IPC interface (host side) 7 + */ 8 + 9 + #include <linux/firmware/imx/dsp.h> 10 + #include <linux/kernel.h> 11 + #include <linux/mailbox_client.h> 12 + #include <linux/module.h> 13 + #include <linux/of_platform.h> 14 + #include <linux/platform_device.h> 15 + #include <linux/slab.h> 16 + 17 + /* 18 + * imx_dsp_ring_doorbell - triggers an interrupt on the other side (DSP) 19 + * 20 + * @dsp: DSP IPC handle 21 + * @chan_idx: index of the channel where to trigger the interrupt 22 + * 23 + * Returns non-negative value for success, negative value for error 24 + */ 25 + int imx_dsp_ring_doorbell(struct imx_dsp_ipc *ipc, unsigned int idx) 26 + { 27 + int ret; 28 + struct imx_dsp_chan *dsp_chan; 29 + 30 + if (idx >= DSP_MU_CHAN_NUM) 31 + return -EINVAL; 32 + 33 + dsp_chan = &ipc->chans[idx]; 34 + ret = mbox_send_message(dsp_chan->ch, NULL); 35 + if (ret < 0) 36 + return ret; 37 + 38 + return 0; 39 + } 40 + EXPORT_SYMBOL(imx_dsp_ring_doorbell); 41 + 42 + /* 43 + * imx_dsp_handle_rx - rx callback used by imx mailbox 44 + * 45 + * @c: mbox client 46 + * @msg: message received 47 + * 48 + * Users of DSP IPC will need to privde handle_reply and handle_request 49 + * callbacks. 50 + */ 51 + static void imx_dsp_handle_rx(struct mbox_client *c, void *msg) 52 + { 53 + struct imx_dsp_chan *chan = container_of(c, struct imx_dsp_chan, cl); 54 + 55 + if (chan->idx == 0) { 56 + chan->ipc->ops->handle_reply(chan->ipc); 57 + } else { 58 + chan->ipc->ops->handle_request(chan->ipc); 59 + imx_dsp_ring_doorbell(chan->ipc, 1); 60 + } 61 + } 62 + 63 + static int imx_dsp_probe(struct platform_device *pdev) 64 + { 65 + struct device *dev = &pdev->dev; 66 + struct imx_dsp_ipc *dsp_ipc; 67 + struct imx_dsp_chan *dsp_chan; 68 + struct mbox_client *cl; 69 + char *chan_name; 70 + int ret; 71 + int i, j; 72 + 73 + device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent); 74 + 75 + dsp_ipc = devm_kzalloc(dev, sizeof(*dsp_ipc), GFP_KERNEL); 76 + if (!dsp_ipc) 77 + return -ENOMEM; 78 + 79 + for (i = 0; i < DSP_MU_CHAN_NUM; i++) { 80 + if (i < 2) 81 + chan_name = kasprintf(GFP_KERNEL, "txdb%d", i); 82 + else 83 + chan_name = kasprintf(GFP_KERNEL, "rxdb%d", i - 2); 84 + 85 + if (!chan_name) 86 + return -ENOMEM; 87 + 88 + dsp_chan = &dsp_ipc->chans[i]; 89 + cl = &dsp_chan->cl; 90 + cl->dev = dev; 91 + cl->tx_block = false; 92 + cl->knows_txdone = true; 93 + cl->rx_callback = imx_dsp_handle_rx; 94 + 95 + dsp_chan->ipc = dsp_ipc; 96 + dsp_chan->idx = i % 2; 97 + dsp_chan->ch = mbox_request_channel_byname(cl, chan_name); 98 + if (IS_ERR(dsp_chan->ch)) { 99 + ret = PTR_ERR(dsp_chan->ch); 100 + if (ret != -EPROBE_DEFER) 101 + dev_err(dev, "Failed to request mbox chan %s ret %d\n", 102 + chan_name, ret); 103 + goto out; 104 + } 105 + 106 + dev_dbg(dev, "request mbox chan %s\n", chan_name); 107 + /* chan_name is not used anymore by framework */ 108 + kfree(chan_name); 109 + } 110 + 111 + dsp_ipc->dev = dev; 112 + 113 + dev_set_drvdata(dev, dsp_ipc); 114 + 115 + dev_info(dev, "NXP i.MX DSP IPC initialized\n"); 116 + 117 + return devm_of_platform_populate(dev); 118 + out: 119 + kfree(chan_name); 120 + for (j = 0; j < i; j++) { 121 + dsp_chan = &dsp_ipc->chans[j]; 122 + mbox_free_channel(dsp_chan->ch); 123 + } 124 + 125 + return ret; 126 + } 127 + 128 + static int imx_dsp_remove(struct platform_device *pdev) 129 + { 130 + struct imx_dsp_chan *dsp_chan; 131 + struct imx_dsp_ipc *dsp_ipc; 132 + int i; 133 + 134 + dsp_ipc = dev_get_drvdata(&pdev->dev); 135 + 136 + for (i = 0; i < DSP_MU_CHAN_NUM; i++) { 137 + dsp_chan = &dsp_ipc->chans[i]; 138 + mbox_free_channel(dsp_chan->ch); 139 + } 140 + 141 + return 0; 142 + } 143 + 144 + static struct platform_driver imx_dsp_driver = { 145 + .driver = { 146 + .name = "imx-dsp", 147 + }, 148 + .probe = imx_dsp_probe, 149 + .remove = imx_dsp_remove, 150 + }; 151 + builtin_platform_driver(imx_dsp_driver); 152 + 153 + MODULE_AUTHOR("Daniel Baluta <daniel.baluta@nxp.com>"); 154 + MODULE_DESCRIPTION("IMX DSP IPC protocol driver"); 155 + MODULE_LICENSE("GPL v2");
+3 -1
drivers/firmware/imx/scu-pd.c
··· 92 92 { "gpt", IMX_SC_R_GPT_0, 5, true, 0 }, 93 93 { "kpp", IMX_SC_R_KPP, 1, false, 0 }, 94 94 { "fspi", IMX_SC_R_FSPI_0, 2, true, 0 }, 95 - { "mu", IMX_SC_R_MU_0A, 14, true, 0 }, 95 + { "mu_a", IMX_SC_R_MU_0A, 14, true, 0 }, 96 + { "mu_b", IMX_SC_R_MU_13B, 1, true, 13 }, 96 97 97 98 /* CONN SS */ 98 99 { "usb", IMX_SC_R_USB_0, 2, true, 0 }, ··· 131 130 { "lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 }, 132 131 { "lpuart", IMX_SC_R_UART_0, 4, true, 0 }, 133 132 { "lpspi", IMX_SC_R_SPI_0, 4, true, 0 }, 133 + { "irqstr_dsp", IMX_SC_R_IRQSTR_DSP, 1, false, 0 }, 134 134 135 135 /* VPU SS */ 136 136 { "vpu", IMX_SC_R_VPU, 1, false, 0 },
+1 -1
drivers/soc/imx/gpcv2.c
··· 198 198 err = regulator_disable(domain->regulator); 199 199 if (err) 200 200 dev_err(domain->dev, 201 - "failed to disable regulator: %d\n", ret); 201 + "failed to disable regulator: %d\n", err); 202 202 /* Preserve earlier error code */ 203 203 ret = ret ?: err; 204 204 }
+39
drivers/soc/imx/soc-imx-scu.c
··· 27 27 } data; 28 28 } __packed; 29 29 30 + struct imx_sc_msg_misc_get_soc_uid { 31 + struct imx_sc_rpc_msg hdr; 32 + u32 uid_low; 33 + u32 uid_high; 34 + } __packed; 35 + 36 + static ssize_t soc_uid_show(struct device *dev, 37 + struct device_attribute *attr, char *buf) 38 + { 39 + struct imx_sc_msg_misc_get_soc_uid msg; 40 + struct imx_sc_rpc_msg *hdr = &msg.hdr; 41 + u64 soc_uid; 42 + int ret; 43 + 44 + hdr->ver = IMX_SC_RPC_VERSION; 45 + hdr->svc = IMX_SC_RPC_SVC_MISC; 46 + hdr->func = IMX_SC_MISC_FUNC_UNIQUE_ID; 47 + hdr->size = 1; 48 + 49 + ret = imx_scu_call_rpc(soc_ipc_handle, &msg, false); 50 + if (ret) { 51 + pr_err("%s: get soc uid failed, ret %d\n", __func__, ret); 52 + return ret; 53 + } 54 + 55 + soc_uid = msg.uid_high; 56 + soc_uid <<= 32; 57 + soc_uid |= msg.uid_low; 58 + 59 + return sprintf(buf, "%016llX\n", soc_uid); 60 + } 61 + 62 + static DEVICE_ATTR_RO(soc_uid); 63 + 30 64 static int imx_scu_soc_id(void) 31 65 { 32 66 struct imx_sc_msg_misc_get_soc_id msg; ··· 135 101 ret = PTR_ERR(soc_dev); 136 102 goto free_revision; 137 103 } 104 + 105 + ret = device_create_file(soc_device_to_device(soc_dev), 106 + &dev_attr_soc_uid); 107 + if (ret) 108 + goto free_revision; 138 109 139 110 return 0; 140 111
+45
drivers/soc/imx/soc-imx8.c
··· 16 16 #define IMX8MQ_SW_INFO_B1 0x40 17 17 #define IMX8MQ_SW_MAGIC_B1 0xff0055aa 18 18 19 + #define OCOTP_UID_LOW 0x410 20 + #define OCOTP_UID_HIGH 0x420 21 + 19 22 /* Same as ANADIG_DIGPROG_IMX7D */ 20 23 #define ANADIG_DIGPROG_IMX8MM 0x800 21 24 ··· 26 23 char *name; 27 24 u32 (*soc_revision)(void); 28 25 }; 26 + 27 + static u64 soc_uid; 28 + 29 + static ssize_t soc_uid_show(struct device *dev, 30 + struct device_attribute *attr, char *buf) 31 + { 32 + return sprintf(buf, "%016llX\n", soc_uid); 33 + } 34 + 35 + static DEVICE_ATTR_RO(soc_uid); 29 36 30 37 static u32 __init imx8mq_soc_revision(void) 31 38 { ··· 55 42 if (magic == IMX8MQ_SW_MAGIC_B1) 56 43 rev = REV_B1; 57 44 45 + soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH); 46 + soc_uid <<= 32; 47 + soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW); 48 + 58 49 iounmap(ocotp_base); 59 50 60 51 out: 61 52 of_node_put(np); 62 53 return rev; 54 + } 55 + 56 + static void __init imx8mm_soc_uid(void) 57 + { 58 + void __iomem *ocotp_base; 59 + struct device_node *np; 60 + 61 + np = of_find_compatible_node(NULL, NULL, "fsl,imx8mm-ocotp"); 62 + if (!np) 63 + return; 64 + 65 + ocotp_base = of_iomap(np, 0); 66 + WARN_ON(!ocotp_base); 67 + 68 + soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH); 69 + soc_uid <<= 32; 70 + soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW); 71 + 72 + iounmap(ocotp_base); 73 + of_node_put(np); 63 74 } 64 75 65 76 static u32 __init imx8mm_soc_revision(void) ··· 103 66 104 67 iounmap(anatop_base); 105 68 of_node_put(np); 69 + 70 + imx8mm_soc_uid(); 71 + 106 72 return rev; 107 73 } 108 74 ··· 179 139 ret = PTR_ERR(soc_dev); 180 140 goto free_rev; 181 141 } 142 + 143 + ret = device_create_file(soc_device_to_device(soc_dev), 144 + &dev_attr_soc_uid); 145 + if (ret) 146 + goto free_rev; 182 147 183 148 if (IS_ENABLED(CONFIG_ARM_IMX_CPUFREQ_DT)) 184 149 platform_device_register_simple("imx-cpufreq-dt", -1, NULL, 0);
+67
include/linux/firmware/imx/dsp.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0+ */ 2 + /* 3 + * Copyright 2019 NXP 4 + * 5 + * Header file for the DSP IPC implementation 6 + */ 7 + 8 + #ifndef _IMX_DSP_IPC_H 9 + #define _IMX_DSP_IPC_H 10 + 11 + #include <linux/device.h> 12 + #include <linux/types.h> 13 + #include <linux/mailbox_client.h> 14 + 15 + #define DSP_MU_CHAN_NUM 4 16 + 17 + struct imx_dsp_chan { 18 + struct imx_dsp_ipc *ipc; 19 + struct mbox_client cl; 20 + struct mbox_chan *ch; 21 + char *name; 22 + int idx; 23 + }; 24 + 25 + struct imx_dsp_ops { 26 + void (*handle_reply)(struct imx_dsp_ipc *ipc); 27 + void (*handle_request)(struct imx_dsp_ipc *ipc); 28 + }; 29 + 30 + struct imx_dsp_ipc { 31 + /* Host <-> DSP communication uses 2 txdb and 2 rxdb channels */ 32 + struct imx_dsp_chan chans[DSP_MU_CHAN_NUM]; 33 + struct device *dev; 34 + struct imx_dsp_ops *ops; 35 + void *private_data; 36 + }; 37 + 38 + static inline void imx_dsp_set_data(struct imx_dsp_ipc *ipc, void *data) 39 + { 40 + if (!ipc) 41 + return; 42 + 43 + ipc->private_data = data; 44 + } 45 + 46 + static inline void *imx_dsp_get_data(struct imx_dsp_ipc *ipc) 47 + { 48 + if (!ipc) 49 + return NULL; 50 + 51 + return ipc->private_data; 52 + } 53 + 54 + #if IS_ENABLED(CONFIG_IMX_DSP) 55 + 56 + int imx_dsp_ring_doorbell(struct imx_dsp_ipc *dsp, unsigned int chan_idx); 57 + 58 + #else 59 + 60 + static inline int imx_dsp_ring_doorbell(struct imx_dsp_ipc *ipc, 61 + unsigned int chan_idx) 62 + { 63 + return -ENOTSUPP; 64 + } 65 + 66 + #endif 67 + #endif /* _IMX_DSP_IPC_H */