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

Merge tag 'mailbox-v5.8' of git://git.linaro.org/landing-teams/working/fujitsu/integration

Pull mailbox updates from Jassi Brar:
"qcom:
- new controller driver for IPCC
- reorg the of_device data
- add support for ipq6018 platform

spreadtrum:
- new sprd controller driver

imx:
- implement suspend/resume PM support

misc:
- make pcc driver struct static
- fix return value in imx_mu_scu
- disable clock before bailout in imx probe
- remove duplicate error mssg in zynqmp probe
- fix header size in imx.scu
- check for null instead of is-err in zynqmp"

* tag 'mailbox-v5.8' of git://git.linaro.org/landing-teams/working/fujitsu/integration:
mailbox: qcom: Add ipq6018 apcs compatible
mailbox: qcom: Add clock driver name in apcs mailbox driver data
dt-bindings: mailbox: Add YAML schemas for QCOM APCS global block
mailbox: imx: ONLY IPC MU needs IRQF_NO_SUSPEND flag
mailbox: imx: Add runtime PM callback to handle MU clocks
mailbox: imx: Add context save/restore for suspend/resume
MAINTAINERS: Add entry for Qualcomm IPCC driver
mailbox: Add support for Qualcomm IPCC
dt-bindings: mailbox: Add devicetree binding for Qcom IPCC
mailbox: zynqmp-ipi: Fix NULL vs IS_ERR() check in zynqmp_ipi_mbox_probe()
mailbox: imx-mailbox: fix scu msg header size check
mailbox: sprd: Add Spreadtrum mailbox driver
dt-bindings: mailbox: Add the Spreadtrum mailbox documentation
mailbox: ZynqMP IPI: Delete an error message in zynqmp_ipi_probe()
mailbox: imx: Disable the clock on devm_mbox_controller_register() failure
mailbox: imx: Fix return in imx_mu_scu_xlate()
mailbox: imx: Support runtime PM
mailbox: pcc: make pcc_mbox_driver static

+1097 -132
-88
Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.txt
··· 1 - Binding for the Qualcomm APCS global block 2 - ========================================== 3 - 4 - This binding describes the APCS "global" block found in various Qualcomm 5 - platforms. 6 - 7 - - compatible: 8 - Usage: required 9 - Value type: <string> 10 - Definition: must be one of: 11 - "qcom,msm8916-apcs-kpss-global", 12 - "qcom,msm8996-apcs-hmss-global" 13 - "qcom,msm8998-apcs-hmss-global" 14 - "qcom,qcs404-apcs-apps-global" 15 - "qcom,sc7180-apss-shared" 16 - "qcom,sdm845-apss-shared" 17 - "qcom,sm8150-apss-shared" 18 - "qcom,ipq8074-apcs-apps-global" 19 - 20 - - reg: 21 - Usage: required 22 - Value type: <prop-encoded-array> 23 - Definition: must specify the base address and size of the global block 24 - 25 - - clocks: 26 - Usage: required if #clock-names property is present 27 - Value type: <phandle array> 28 - Definition: phandles to the two parent clocks of the clock driver. 29 - 30 - - #mbox-cells: 31 - Usage: required 32 - Value type: <u32> 33 - Definition: as described in mailbox.txt, must be 1 34 - 35 - - #clock-cells: 36 - Usage: optional 37 - Value type: <u32> 38 - Definition: as described in clock.txt, must be 0 39 - 40 - - clock-names: 41 - Usage: required if the platform data based clock driver needs to 42 - retrieve the parent clock names from device tree. 43 - This will requires two mandatory clocks to be defined. 44 - Value type: <string-array> 45 - Definition: must be "pll" and "aux" 46 - 47 - = EXAMPLE 48 - The following example describes the APCS HMSS found in MSM8996 and part of the 49 - GLINK RPM referencing the "rpm_hlos" doorbell therein. 50 - 51 - apcs_glb: mailbox@9820000 { 52 - compatible = "qcom,msm8996-apcs-hmss-global"; 53 - reg = <0x9820000 0x1000>; 54 - 55 - #mbox-cells = <1>; 56 - }; 57 - 58 - rpm-glink { 59 - compatible = "qcom,glink-rpm"; 60 - 61 - interrupts = <GIC_SPI 168 IRQ_TYPE_EDGE_RISING>; 62 - 63 - qcom,rpm-msg-ram = <&rpm_msg_ram>; 64 - 65 - mboxes = <&apcs_glb 0>; 66 - mbox-names = "rpm_hlos"; 67 - }; 68 - 69 - Below is another example of the APCS binding on MSM8916 platforms: 70 - 71 - apcs: mailbox@b011000 { 72 - compatible = "qcom,msm8916-apcs-kpss-global"; 73 - reg = <0xb011000 0x1000>; 74 - #mbox-cells = <1>; 75 - clocks = <&a53pll>; 76 - #clock-cells = <0>; 77 - }; 78 - 79 - Below is another example of the APCS binding on QCS404 platforms: 80 - 81 - apcs_glb: mailbox@b011000 { 82 - compatible = "qcom,qcs404-apcs-apps-global", "syscon"; 83 - reg = <0x0b011000 0x1000>; 84 - #mbox-cells = <1>; 85 - clocks = <&apcs_hfpll>, <&gcc GCC_GPLL0_AO_OUT_MAIN>; 86 - clock-names = "pll", "aux"; 87 - #clock-cells = <0>; 88 - };
+86
Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: "http://devicetree.org/schemas/mailbox/qcom,apcs-kpss-global.yaml#" 5 + $schema: "http://devicetree.org/meta-schemas/core.yaml#" 6 + 7 + title: Qualcomm APCS global block bindings 8 + 9 + description: 10 + This binding describes the APCS "global" block found in various Qualcomm 11 + platforms. 12 + 13 + maintainers: 14 + - Sivaprakash Murugesan <sivaprak@codeaurora.org> 15 + 16 + properties: 17 + compatible: 18 + enum: 19 + - qcom,ipq8074-apcs-apps-global 20 + - qcom,msm8916-apcs-kpss-global 21 + - qcom,msm8996-apcs-hmss-global 22 + - qcom,msm8998-apcs-hmss-global 23 + - qcom,qcs404-apcs-apps-global 24 + - qcom,sc7180-apss-shared 25 + - qcom,sdm845-apss-shared 26 + - qcom,sm8150-apss-shared 27 + 28 + reg: 29 + maxItems: 1 30 + 31 + clocks: 32 + description: phandles to the parent clocks of the clock driver 33 + items: 34 + - description: primary pll parent of the clock driver 35 + - description: auxiliary parent 36 + 37 + '#mbox-cells': 38 + const: 1 39 + 40 + '#clock-cells': 41 + const: 0 42 + 43 + clock-names: 44 + items: 45 + - const: pll 46 + - const: aux 47 + 48 + required: 49 + - compatible 50 + - reg 51 + - '#mbox-cells' 52 + 53 + additionalProperties: false 54 + 55 + examples: 56 + 57 + # Example apcs with msm8996 58 + - | 59 + #include <dt-bindings/interrupt-controller/arm-gic.h> 60 + apcs_glb: mailbox@9820000 { 61 + compatible = "qcom,msm8996-apcs-hmss-global"; 62 + reg = <0x9820000 0x1000>; 63 + 64 + #mbox-cells = <1>; 65 + }; 66 + 67 + rpm-glink { 68 + compatible = "qcom,glink-rpm"; 69 + interrupts = <GIC_SPI 168 IRQ_TYPE_EDGE_RISING>; 70 + qcom,rpm-msg-ram = <&rpm_msg_ram>; 71 + mboxes = <&apcs_glb 0>; 72 + mbox-names = "rpm_hlos"; 73 + }; 74 + 75 + # Example apcs with qcs404 76 + - | 77 + #define GCC_APSS_AHB_CLK_SRC 1 78 + #define GCC_GPLL0_AO_OUT_MAIN 123 79 + apcs: mailbox@b011000 { 80 + compatible = "qcom,qcs404-apcs-apps-global"; 81 + reg = <0x0b011000 0x1000>; 82 + #mbox-cells = <1>; 83 + clocks = <&apcs_hfpll>, <&gcc GCC_GPLL0_AO_OUT_MAIN>; 84 + clock-names = "pll", "aux"; 85 + #clock-cells = <0>; 86 + };
+80
Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml
··· 1 + # SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/mailbox/qcom-ipcc.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Qualcomm Technologies, Inc. Inter-Processor Communication Controller 8 + 9 + maintainers: 10 + - Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> 11 + 12 + description: 13 + The Inter-Processor Communication Controller (IPCC) is a centralized hardware 14 + to route interrupts across various subsystems. It involves a three-level 15 + addressing scheme called protocol, client and signal. For example, consider an 16 + entity on the Application Processor Subsystem (APSS) that wants to listen to 17 + Modem's interrupts via Shared Memory Point to Point (SMP2P) interface. In such 18 + a case, the client would be Modem (client-id is 2) and the signal would be 19 + SMP2P (signal-id is 2). The SMP2P itself falls under the Multiprocessor (MPROC) 20 + protocol (protocol-id is 0). Refer include/dt-bindings/mailbox/qcom-ipcc.h 21 + for the list of such IDs. 22 + 23 + properties: 24 + compatible: 25 + items: 26 + - enum: 27 + - qcom,sm8250-ipcc 28 + - const: qcom,ipcc 29 + 30 + reg: 31 + maxItems: 1 32 + 33 + interrupts: 34 + maxItems: 1 35 + 36 + interrupt-controller: true 37 + 38 + "#interrupt-cells": 39 + const: 3 40 + description: 41 + The first cell is the client-id, the second cell is the signal-id and the 42 + third cell is the interrupt type. 43 + 44 + "#mbox-cells": 45 + const: 2 46 + description: 47 + The first cell is the client-id, and the second cell is the signal-id. 48 + 49 + required: 50 + - compatible 51 + - reg 52 + - interrupts 53 + - interrupt-controller 54 + - "#interrupt-cells" 55 + - "#mbox-cells" 56 + 57 + additionalProperties: false 58 + 59 + examples: 60 + - | 61 + #include <dt-bindings/interrupt-controller/arm-gic.h> 62 + #include <dt-bindings/mailbox/qcom-ipcc.h> 63 + 64 + mailbox@408000 { 65 + compatible = "qcom,sm8250-ipcc", "qcom,ipcc"; 66 + reg = <0x408000 0x1000>; 67 + interrupts = <GIC_SPI 229 IRQ_TYPE_LEVEL_HIGH>; 68 + interrupt-controller; 69 + #interrupt-cells = <3>; 70 + #mbox-cells = <2>; 71 + }; 72 + 73 + smp2p-modem { 74 + compatible = "qcom,smp2p"; 75 + interrupts-extended = <&ipcc_mproc IPCC_CLIENT_MPSS 76 + IPCC_MPROC_SIGNAL_SMP2P IRQ_TYPE_EDGE_RISING>; 77 + mboxes = <&ipcc_mproc IPCC_CLIENT_MPSS IPCC_MPROC_SIGNAL_SMP2P>; 78 + 79 + /* Other SMP2P fields */ 80 + };
+60
Documentation/devicetree/bindings/mailbox/sprd-mailbox.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: "http://devicetree.org/schemas/mailbox/sprd-mailbox.yaml#" 5 + $schema: "http://devicetree.org/meta-schemas/core.yaml#" 6 + 7 + title: Spreadtrum mailbox controller bindings 8 + 9 + maintainers: 10 + - Orson Zhai <orsonzhai@gmail.com> 11 + - Baolin Wang <baolin.wang7@gmail.com> 12 + - Chunyan Zhang <zhang.lyra@gmail.com> 13 + 14 + properties: 15 + compatible: 16 + enum: 17 + - sprd,sc9860-mailbox 18 + 19 + reg: 20 + items: 21 + - description: inbox registers' base address 22 + - description: outbox registers' base address 23 + 24 + interrupts: 25 + items: 26 + - description: inbox interrupt 27 + - description: outbox interrupt 28 + 29 + clocks: 30 + maxItems: 1 31 + 32 + clock-names: 33 + items: 34 + - const: enable 35 + 36 + "#mbox-cells": 37 + const: 1 38 + 39 + required: 40 + - compatible 41 + - reg 42 + - interrupts 43 + - "#mbox-cells" 44 + - clocks 45 + - clock-names 46 + 47 + additionalProperties: false 48 + 49 + examples: 50 + - | 51 + #include <dt-bindings/interrupt-controller/arm-gic.h> 52 + mailbox: mailbox@400a0000 { 53 + compatible = "sprd,sc9860-mailbox"; 54 + reg = <0 0x400a0000 0 0x8000>, <0 0x400a8000 0 0x8000>; 55 + #mbox-cells = <1>; 56 + clock-names = "enable"; 57 + clocks = <&aon_gate 53>; 58 + interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; 59 + }; 60 + ...
+8
MAINTAINERS
··· 14187 14187 S: Maintained 14188 14188 F: drivers/iommu/qcom_iommu.c 14189 14189 14190 + QUALCOMM IPCC MAILBOX DRIVER 14191 + M: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> 14192 + L: linux-arm-msm@vger.kernel.org 14193 + S: Supported 14194 + F: Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml 14195 + F: drivers/mailbox/qcom-ipcc.c 14196 + F: include/dt-bindings/mailbox/qcom-ipcc.h 14197 + 14190 14198 QUALCOMM RMNET DRIVER 14191 14199 M: Subash Abhinov Kasiviswanathan <subashab@codeaurora.org> 14192 14200 M: Sean Tranchetti <stranche@codeaurora.org>
+18
drivers/mailbox/Kconfig
··· 236 236 various Allwinner SoCs. This mailbox is used for communication 237 237 between the application CPUs and the power management coprocessor. 238 238 239 + config SPRD_MBOX 240 + tristate "Spreadtrum Mailbox" 241 + depends on ARCH_SPRD || COMPILE_TEST 242 + help 243 + Mailbox driver implementation for the Spreadtrum platform. It is used 244 + to send message between application processors and MCU. Say Y here if 245 + you want to build the Spreatrum mailbox controller driver. 246 + 247 + config QCOM_IPCC 248 + bool "Qualcomm Technologies, Inc. IPCC driver" 249 + depends on ARCH_QCOM || COMPILE_TEST 250 + help 251 + Qualcomm Technologies, Inc. Inter-Processor Communication Controller 252 + (IPCC) driver for MSM devices. The driver provides mailbox support for 253 + sending interrupts to the clients. On the other hand, the driver also 254 + acts as an interrupt controller for receiving interrupts from clients. 255 + Say Y here if you want to build this driver. 256 + 239 257 endif
+4
drivers/mailbox/Makefile
··· 50 50 obj-$(CONFIG_ZYNQMP_IPI_MBOX) += zynqmp-ipi-mailbox.o 51 51 52 52 obj-$(CONFIG_SUN6I_MSGBOX) += sun6i-msgbox.o 53 + 54 + obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox.o 55 + 56 + obj-$(CONFIG_QCOM_IPCC) += qcom-ipcc.o
+107 -10
drivers/mailbox/imx-mailbox.c
··· 12 12 #include <linux/mailbox_controller.h> 13 13 #include <linux/module.h> 14 14 #include <linux/of_device.h> 15 + #include <linux/pm_runtime.h> 15 16 #include <linux/slab.h> 16 17 17 18 #define IMX_MU_xSR_GIPn(x) BIT(28 + (3 - (x))) ··· 66 65 const struct imx_mu_dcfg *dcfg; 67 66 struct clk *clk; 68 67 int irq; 68 + 69 + u32 xcr; 69 70 70 71 bool side_b; 71 72 }; ··· 157 154 158 155 switch (cp->type) { 159 156 case IMX_MU_TYPE_TX: 160 - if (msg->hdr.size > sizeof(*msg)) { 157 + /* 158 + * msg->hdr.size specifies the number of u32 words while 159 + * sizeof yields bytes. 160 + */ 161 + 162 + if (msg->hdr.size > sizeof(*msg) / 4) { 161 163 /* 162 164 * The real message size can be different to 163 165 * struct imx_sc_rpc_msg_max size 164 166 */ 165 - dev_err(priv->dev, "Exceed max msg size (%zu) on TX, got: %i\n", sizeof(*msg), msg->hdr.size); 167 + dev_err(priv->dev, "Maximal message size (%zu bytes) exceeded on TX; got: %i bytes\n", sizeof(*msg), msg->hdr.size << 2); 166 168 return -EINVAL; 167 169 } 168 170 ··· 206 198 imx_mu_xcr_rmw(priv, 0, IMX_MU_xCR_RIEn(0)); 207 199 *data++ = imx_mu_read(priv, priv->dcfg->xRR[0]); 208 200 209 - if (msg.hdr.size > sizeof(msg)) { 210 - dev_err(priv->dev, "Exceed max msg size (%zu) on RX, got: %i\n", 211 - sizeof(msg), msg.hdr.size); 201 + if (msg.hdr.size > sizeof(msg) / 4) { 202 + dev_err(priv->dev, "Maximal message size (%zu bytes) exceeded on RX; got: %i bytes\n", sizeof(msg), msg.hdr.size << 2); 212 203 return -EINVAL; 213 204 } 214 205 ··· 292 285 { 293 286 struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox); 294 287 struct imx_mu_con_priv *cp = chan->con_priv; 288 + unsigned long irq_flag = IRQF_SHARED; 295 289 int ret; 296 290 291 + pm_runtime_get_sync(priv->dev); 297 292 if (cp->type == IMX_MU_TYPE_TXDB) { 298 293 /* Tx doorbell don't have ACK support */ 299 294 tasklet_init(&cp->txdb_tasklet, imx_mu_txdb_tasklet, ··· 303 294 return 0; 304 295 } 305 296 306 - ret = request_irq(priv->irq, imx_mu_isr, IRQF_SHARED | 307 - IRQF_NO_SUSPEND, cp->irq_desc, chan); 297 + /* IPC MU should be with IRQF_NO_SUSPEND set */ 298 + if (!priv->dev->pm_domain) 299 + irq_flag |= IRQF_NO_SUSPEND; 300 + 301 + ret = request_irq(priv->irq, imx_mu_isr, irq_flag, 302 + cp->irq_desc, chan); 308 303 if (ret) { 309 304 dev_err(priv->dev, 310 305 "Unable to acquire IRQ %d\n", priv->irq); ··· 336 323 337 324 if (cp->type == IMX_MU_TYPE_TXDB) { 338 325 tasklet_kill(&cp->txdb_tasklet); 326 + pm_runtime_put_sync(priv->dev); 339 327 return; 340 328 } 341 329 ··· 355 341 } 356 342 357 343 free_irq(priv->irq, chan); 344 + pm_runtime_put_sync(priv->dev); 358 345 } 359 346 360 347 static const struct mbox_chan_ops imx_mu_ops = { ··· 389 374 break; 390 375 default: 391 376 dev_err(mbox->dev, "Invalid chan type: %d\n", type); 392 - return NULL; 377 + return ERR_PTR(-EINVAL); 393 378 } 394 379 395 380 if (chan >= mbox->num_chans) { ··· 523 508 524 509 platform_set_drvdata(pdev, priv); 525 510 526 - return devm_mbox_controller_register(dev, &priv->mbox); 511 + ret = devm_mbox_controller_register(dev, &priv->mbox); 512 + if (ret) { 513 + clk_disable_unprepare(priv->clk); 514 + return ret; 515 + } 516 + 517 + pm_runtime_enable(dev); 518 + 519 + ret = pm_runtime_get_sync(dev); 520 + if (ret < 0) { 521 + pm_runtime_put_noidle(dev); 522 + goto disable_runtime_pm; 523 + } 524 + 525 + ret = pm_runtime_put_sync(dev); 526 + if (ret < 0) 527 + goto disable_runtime_pm; 528 + 529 + clk_disable_unprepare(priv->clk); 530 + 531 + return 0; 532 + 533 + disable_runtime_pm: 534 + pm_runtime_disable(dev); 535 + clk_disable_unprepare(priv->clk); 536 + return ret; 527 537 } 528 538 529 539 static int imx_mu_remove(struct platform_device *pdev) 530 540 { 531 541 struct imx_mu_priv *priv = platform_get_drvdata(pdev); 532 542 533 - clk_disable_unprepare(priv->clk); 543 + pm_runtime_disable(priv->dev); 534 544 535 545 return 0; 536 546 } ··· 598 558 }; 599 559 MODULE_DEVICE_TABLE(of, imx_mu_dt_ids); 600 560 561 + static int imx_mu_suspend_noirq(struct device *dev) 562 + { 563 + struct imx_mu_priv *priv = dev_get_drvdata(dev); 564 + 565 + if (!priv->clk) 566 + priv->xcr = imx_mu_read(priv, priv->dcfg->xCR); 567 + 568 + return 0; 569 + } 570 + 571 + static int imx_mu_resume_noirq(struct device *dev) 572 + { 573 + struct imx_mu_priv *priv = dev_get_drvdata(dev); 574 + 575 + /* 576 + * ONLY restore MU when context lost, the TIE could 577 + * be set during noirq resume as there is MU data 578 + * communication going on, and restore the saved 579 + * value will overwrite the TIE and cause MU data 580 + * send failed, may lead to system freeze. This issue 581 + * is observed by testing freeze mode suspend. 582 + */ 583 + if (!imx_mu_read(priv, priv->dcfg->xCR) && !priv->clk) 584 + imx_mu_write(priv, priv->xcr, priv->dcfg->xCR); 585 + 586 + return 0; 587 + } 588 + 589 + static int imx_mu_runtime_suspend(struct device *dev) 590 + { 591 + struct imx_mu_priv *priv = dev_get_drvdata(dev); 592 + 593 + clk_disable_unprepare(priv->clk); 594 + 595 + return 0; 596 + } 597 + 598 + static int imx_mu_runtime_resume(struct device *dev) 599 + { 600 + struct imx_mu_priv *priv = dev_get_drvdata(dev); 601 + int ret; 602 + 603 + ret = clk_prepare_enable(priv->clk); 604 + if (ret) 605 + dev_err(dev, "failed to enable clock\n"); 606 + 607 + return ret; 608 + } 609 + 610 + static const struct dev_pm_ops imx_mu_pm_ops = { 611 + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_mu_suspend_noirq, 612 + imx_mu_resume_noirq) 613 + SET_RUNTIME_PM_OPS(imx_mu_runtime_suspend, 614 + imx_mu_runtime_resume, NULL) 615 + }; 616 + 601 617 static struct platform_driver imx_mu_driver = { 602 618 .probe = imx_mu_probe, 603 619 .remove = imx_mu_remove, 604 620 .driver = { 605 621 .name = "imx_mu", 606 622 .of_match_table = imx_mu_dt_ids, 623 + .pm = &imx_mu_pm_ops, 607 624 }, 608 625 }; 609 626 module_platform_driver(imx_mu_driver);
+1 -1
drivers/mailbox/pcc.c
··· 568 568 return ret; 569 569 } 570 570 571 - struct platform_driver pcc_mbox_driver = { 571 + static struct platform_driver pcc_mbox_driver = { 572 572 .probe = pcc_mbox_probe, 573 573 .driver = { 574 574 .name = "PCCT",
+43 -18
drivers/mailbox/qcom-apcs-ipc-mailbox.c
··· 24 24 struct platform_device *clk; 25 25 }; 26 26 27 + struct qcom_apcs_ipc_data { 28 + int offset; 29 + char *clk_name; 30 + }; 31 + 32 + static const struct qcom_apcs_ipc_data ipq6018_apcs_data = { 33 + .offset = 8, .clk_name = "qcom,apss-ipq6018-clk" 34 + }; 35 + 36 + static const struct qcom_apcs_ipc_data ipq8074_apcs_data = { 37 + .offset = 8, .clk_name = NULL 38 + }; 39 + 40 + static const struct qcom_apcs_ipc_data msm8916_apcs_data = { 41 + .offset = 8, .clk_name = "qcom-apcs-msm8916-clk" 42 + }; 43 + 44 + static const struct qcom_apcs_ipc_data msm8996_apcs_data = { 45 + .offset = 16, .clk_name = NULL 46 + }; 47 + 48 + static const struct qcom_apcs_ipc_data msm8998_apcs_data = { 49 + .offset = 8, .clk_name = NULL 50 + }; 51 + 52 + static const struct qcom_apcs_ipc_data apps_shared_apcs_data = { 53 + .offset = 12, .clk_name = NULL 54 + }; 55 + 27 56 static const struct regmap_config apcs_regmap_config = { 28 57 .reg_bits = 32, 29 58 .reg_stride = 4, ··· 77 48 static int qcom_apcs_ipc_probe(struct platform_device *pdev) 78 49 { 79 50 struct qcom_apcs_ipc *apcs; 51 + const struct qcom_apcs_ipc_data *apcs_data; 80 52 struct regmap *regmap; 81 53 struct resource *res; 82 - unsigned long offset; 83 54 void __iomem *base; 84 55 unsigned long i; 85 56 int ret; 86 - const struct of_device_id apcs_clk_match_table[] = { 87 - { .compatible = "qcom,msm8916-apcs-kpss-global", }, 88 - { .compatible = "qcom,qcs404-apcs-apps-global", }, 89 - {} 90 - }; 91 57 92 58 apcs = devm_kzalloc(&pdev->dev, sizeof(*apcs), GFP_KERNEL); 93 59 if (!apcs) ··· 97 73 if (IS_ERR(regmap)) 98 74 return PTR_ERR(regmap); 99 75 100 - offset = (unsigned long)of_device_get_match_data(&pdev->dev); 76 + apcs_data = of_device_get_match_data(&pdev->dev); 101 77 102 78 apcs->regmap = regmap; 103 - apcs->offset = offset; 79 + apcs->offset = apcs_data->offset; 104 80 105 81 /* Initialize channel identifiers */ 106 82 for (i = 0; i < ARRAY_SIZE(apcs->mbox_chans); i++) ··· 117 93 return ret; 118 94 } 119 95 120 - if (of_match_device(apcs_clk_match_table, &pdev->dev)) { 96 + if (apcs_data->clk_name) { 121 97 apcs->clk = platform_device_register_data(&pdev->dev, 122 - "qcom-apcs-msm8916-clk", 98 + apcs_data->clk_name, 123 99 PLATFORM_DEVID_NONE, 124 100 NULL, 0); 125 101 if (IS_ERR(apcs->clk)) ··· 143 119 144 120 /* .data is the offset of the ipc register within the global block */ 145 121 static const struct of_device_id qcom_apcs_ipc_of_match[] = { 146 - { .compatible = "qcom,msm8916-apcs-kpss-global", .data = (void *)8 }, 147 - { .compatible = "qcom,msm8996-apcs-hmss-global", .data = (void *)16 }, 148 - { .compatible = "qcom,msm8998-apcs-hmss-global", .data = (void *)8 }, 149 - { .compatible = "qcom,qcs404-apcs-apps-global", .data = (void *)8 }, 150 - { .compatible = "qcom,sc7180-apss-shared", .data = (void *)12 }, 151 - { .compatible = "qcom,sdm845-apss-shared", .data = (void *)12 }, 152 - { .compatible = "qcom,sm8150-apss-shared", .data = (void *)12 }, 153 - { .compatible = "qcom,ipq8074-apcs-apps-global", .data = (void *)8 }, 122 + { .compatible = "qcom,ipq6018-apcs-apps-global", .data = &ipq6018_apcs_data }, 123 + { .compatible = "qcom,ipq8074-apcs-apps-global", .data = &ipq8074_apcs_data }, 124 + { .compatible = "qcom,msm8916-apcs-kpss-global", .data = &msm8916_apcs_data }, 125 + { .compatible = "qcom,msm8996-apcs-hmss-global", .data = &msm8996_apcs_data }, 126 + { .compatible = "qcom,msm8998-apcs-hmss-global", .data = &msm8998_apcs_data }, 127 + { .compatible = "qcom,qcs404-apcs-apps-global", .data = &msm8916_apcs_data }, 128 + { .compatible = "qcom,sc7180-apss-shared", .data = &apps_shared_apcs_data }, 129 + { .compatible = "qcom,sdm845-apss-shared", .data = &apps_shared_apcs_data }, 130 + { .compatible = "qcom,sm8150-apss-shared", .data = &apps_shared_apcs_data }, 154 131 {} 155 132 }; 156 133 MODULE_DEVICE_TABLE(of, qcom_apcs_ipc_of_match);
+286
drivers/mailbox/qcom-ipcc.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. 4 + */ 5 + 6 + #include <linux/bitfield.h> 7 + #include <linux/interrupt.h> 8 + #include <linux/irq.h> 9 + #include <linux/irqdomain.h> 10 + #include <linux/mailbox_controller.h> 11 + #include <linux/module.h> 12 + #include <linux/platform_device.h> 13 + 14 + #include <dt-bindings/mailbox/qcom-ipcc.h> 15 + 16 + #define IPCC_MBOX_MAX_CHAN 48 17 + 18 + /* IPCC Register offsets */ 19 + #define IPCC_REG_SEND_ID 0x0c 20 + #define IPCC_REG_RECV_ID 0x10 21 + #define IPCC_REG_RECV_SIGNAL_ENABLE 0x14 22 + #define IPCC_REG_RECV_SIGNAL_DISABLE 0x18 23 + #define IPCC_REG_RECV_SIGNAL_CLEAR 0x1c 24 + #define IPCC_REG_CLIENT_CLEAR 0x38 25 + 26 + #define IPCC_SIGNAL_ID_MASK GENMASK(15, 0) 27 + #define IPCC_CLIENT_ID_MASK GENMASK(31, 16) 28 + 29 + #define IPCC_NO_PENDING_IRQ GENMASK(31, 0) 30 + 31 + /** 32 + * struct qcom_ipcc_chan_info - Per-mailbox-channel info 33 + * @client_id: The client-id to which the interrupt has to be triggered 34 + * @signal_id: The signal-id to which the interrupt has to be triggered 35 + */ 36 + struct qcom_ipcc_chan_info { 37 + u16 client_id; 38 + u16 signal_id; 39 + }; 40 + 41 + /** 42 + * struct qcom_ipcc - Holder for the mailbox driver 43 + * @dev: Device associated with this instance 44 + * @base: Base address of the IPCC frame associated to APSS 45 + * @irq_domain: The irq_domain associated with this instance 46 + * @chan: The mailbox channels array 47 + * @mchan: The per-mailbox channel info array 48 + * @mbox: The mailbox controller 49 + * @irq: Summary irq 50 + */ 51 + struct qcom_ipcc { 52 + struct device *dev; 53 + void __iomem *base; 54 + struct irq_domain *irq_domain; 55 + struct mbox_chan chan[IPCC_MBOX_MAX_CHAN]; 56 + struct qcom_ipcc_chan_info mchan[IPCC_MBOX_MAX_CHAN]; 57 + struct mbox_controller mbox; 58 + int irq; 59 + }; 60 + 61 + static inline struct qcom_ipcc *to_qcom_ipcc(struct mbox_controller *mbox) 62 + { 63 + return container_of(mbox, struct qcom_ipcc, mbox); 64 + } 65 + 66 + static inline u32 qcom_ipcc_get_hwirq(u16 client_id, u16 signal_id) 67 + { 68 + return FIELD_PREP(IPCC_CLIENT_ID_MASK, client_id) | 69 + FIELD_PREP(IPCC_SIGNAL_ID_MASK, signal_id); 70 + } 71 + 72 + static irqreturn_t qcom_ipcc_irq_fn(int irq, void *data) 73 + { 74 + struct qcom_ipcc *ipcc = data; 75 + u32 hwirq; 76 + int virq; 77 + 78 + for (;;) { 79 + hwirq = readl(ipcc->base + IPCC_REG_RECV_ID); 80 + if (hwirq == IPCC_NO_PENDING_IRQ) 81 + break; 82 + 83 + virq = irq_find_mapping(ipcc->irq_domain, hwirq); 84 + writel(hwirq, ipcc->base + IPCC_REG_RECV_SIGNAL_CLEAR); 85 + generic_handle_irq(virq); 86 + } 87 + 88 + return IRQ_HANDLED; 89 + } 90 + 91 + static void qcom_ipcc_mask_irq(struct irq_data *irqd) 92 + { 93 + struct qcom_ipcc *ipcc = irq_data_get_irq_chip_data(irqd); 94 + irq_hw_number_t hwirq = irqd_to_hwirq(irqd); 95 + 96 + writel(hwirq, ipcc->base + IPCC_REG_RECV_SIGNAL_DISABLE); 97 + } 98 + 99 + static void qcom_ipcc_unmask_irq(struct irq_data *irqd) 100 + { 101 + struct qcom_ipcc *ipcc = irq_data_get_irq_chip_data(irqd); 102 + irq_hw_number_t hwirq = irqd_to_hwirq(irqd); 103 + 104 + writel(hwirq, ipcc->base + IPCC_REG_RECV_SIGNAL_ENABLE); 105 + } 106 + 107 + static struct irq_chip qcom_ipcc_irq_chip = { 108 + .name = "ipcc", 109 + .irq_mask = qcom_ipcc_mask_irq, 110 + .irq_unmask = qcom_ipcc_unmask_irq, 111 + .flags = IRQCHIP_SKIP_SET_WAKE, 112 + }; 113 + 114 + static int qcom_ipcc_domain_map(struct irq_domain *d, unsigned int irq, 115 + irq_hw_number_t hw) 116 + { 117 + struct qcom_ipcc *ipcc = d->host_data; 118 + 119 + irq_set_chip_and_handler(irq, &qcom_ipcc_irq_chip, handle_level_irq); 120 + irq_set_chip_data(irq, ipcc); 121 + irq_set_noprobe(irq); 122 + 123 + return 0; 124 + } 125 + 126 + static int qcom_ipcc_domain_xlate(struct irq_domain *d, 127 + struct device_node *node, const u32 *intspec, 128 + unsigned int intsize, 129 + unsigned long *out_hwirq, 130 + unsigned int *out_type) 131 + { 132 + if (intsize != 3) 133 + return -EINVAL; 134 + 135 + *out_hwirq = qcom_ipcc_get_hwirq(intspec[0], intspec[1]); 136 + *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; 137 + 138 + return 0; 139 + } 140 + 141 + static const struct irq_domain_ops qcom_ipcc_irq_ops = { 142 + .map = qcom_ipcc_domain_map, 143 + .xlate = qcom_ipcc_domain_xlate, 144 + }; 145 + 146 + static int qcom_ipcc_mbox_send_data(struct mbox_chan *chan, void *data) 147 + { 148 + struct qcom_ipcc *ipcc = to_qcom_ipcc(chan->mbox); 149 + struct qcom_ipcc_chan_info *mchan = chan->con_priv; 150 + u32 hwirq; 151 + 152 + hwirq = qcom_ipcc_get_hwirq(mchan->client_id, mchan->signal_id); 153 + writel(hwirq, ipcc->base + IPCC_REG_SEND_ID); 154 + 155 + return 0; 156 + } 157 + 158 + static struct mbox_chan *qcom_ipcc_mbox_xlate(struct mbox_controller *mbox, 159 + const struct of_phandle_args *ph) 160 + { 161 + struct qcom_ipcc *ipcc = to_qcom_ipcc(mbox); 162 + struct qcom_ipcc_chan_info *mchan; 163 + struct mbox_chan *chan; 164 + unsigned int i; 165 + 166 + if (ph->args_count != 2) 167 + return ERR_PTR(-EINVAL); 168 + 169 + for (i = 0; i < IPCC_MBOX_MAX_CHAN; i++) { 170 + chan = &ipcc->chan[i]; 171 + if (!chan->con_priv) { 172 + mchan = &ipcc->mchan[i]; 173 + mchan->client_id = ph->args[0]; 174 + mchan->signal_id = ph->args[1]; 175 + chan->con_priv = mchan; 176 + break; 177 + } 178 + 179 + chan = NULL; 180 + } 181 + 182 + return chan ?: ERR_PTR(-EBUSY); 183 + } 184 + 185 + static const struct mbox_chan_ops ipcc_mbox_chan_ops = { 186 + .send_data = qcom_ipcc_mbox_send_data, 187 + }; 188 + 189 + static int qcom_ipcc_setup_mbox(struct qcom_ipcc *ipcc) 190 + { 191 + struct mbox_controller *mbox; 192 + struct device *dev = ipcc->dev; 193 + 194 + mbox = &ipcc->mbox; 195 + mbox->dev = dev; 196 + mbox->num_chans = IPCC_MBOX_MAX_CHAN; 197 + mbox->chans = ipcc->chan; 198 + mbox->ops = &ipcc_mbox_chan_ops; 199 + mbox->of_xlate = qcom_ipcc_mbox_xlate; 200 + mbox->txdone_irq = false; 201 + mbox->txdone_poll = false; 202 + 203 + return devm_mbox_controller_register(dev, mbox); 204 + } 205 + 206 + static int qcom_ipcc_probe(struct platform_device *pdev) 207 + { 208 + struct qcom_ipcc *ipcc; 209 + int ret; 210 + 211 + ipcc = devm_kzalloc(&pdev->dev, sizeof(*ipcc), GFP_KERNEL); 212 + if (!ipcc) 213 + return -ENOMEM; 214 + 215 + ipcc->dev = &pdev->dev; 216 + 217 + ipcc->base = devm_platform_ioremap_resource(pdev, 0); 218 + if (IS_ERR(ipcc->base)) 219 + return PTR_ERR(ipcc->base); 220 + 221 + ipcc->irq = platform_get_irq(pdev, 0); 222 + if (ipcc->irq < 0) 223 + return ipcc->irq; 224 + 225 + ipcc->irq_domain = irq_domain_add_tree(pdev->dev.of_node, 226 + &qcom_ipcc_irq_ops, ipcc); 227 + if (!ipcc->irq_domain) 228 + return -ENOMEM; 229 + 230 + ret = qcom_ipcc_setup_mbox(ipcc); 231 + if (ret) 232 + goto err_mbox; 233 + 234 + ret = devm_request_irq(&pdev->dev, ipcc->irq, qcom_ipcc_irq_fn, 235 + IRQF_TRIGGER_HIGH, "ipcc", ipcc); 236 + if (ret < 0) { 237 + dev_err(&pdev->dev, "Failed to register the irq: %d\n", ret); 238 + goto err_mbox; 239 + } 240 + 241 + enable_irq_wake(ipcc->irq); 242 + platform_set_drvdata(pdev, ipcc); 243 + 244 + return 0; 245 + 246 + err_mbox: 247 + irq_domain_remove(ipcc->irq_domain); 248 + 249 + return ret; 250 + } 251 + 252 + static int qcom_ipcc_remove(struct platform_device *pdev) 253 + { 254 + struct qcom_ipcc *ipcc = platform_get_drvdata(pdev); 255 + 256 + disable_irq_wake(ipcc->irq); 257 + irq_domain_remove(ipcc->irq_domain); 258 + 259 + return 0; 260 + } 261 + 262 + static const struct of_device_id qcom_ipcc_of_match[] = { 263 + { .compatible = "qcom,ipcc"}, 264 + {} 265 + }; 266 + MODULE_DEVICE_TABLE(of, qcom_ipcc_of_match); 267 + 268 + static struct platform_driver qcom_ipcc_driver = { 269 + .probe = qcom_ipcc_probe, 270 + .remove = qcom_ipcc_remove, 271 + .driver = { 272 + .name = "qcom-ipcc", 273 + .of_match_table = qcom_ipcc_of_match, 274 + }, 275 + }; 276 + 277 + static int __init qcom_ipcc_init(void) 278 + { 279 + return platform_driver_register(&qcom_ipcc_driver); 280 + } 281 + arch_initcall(qcom_ipcc_init); 282 + 283 + MODULE_AUTHOR("Venkata Narendra Kumar Gutta <vnkgutta@codeaurora.org>"); 284 + MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>"); 285 + MODULE_DESCRIPTION("Qualcomm Technologies, Inc. IPCC driver"); 286 + MODULE_LICENSE("GPL v2");
+361
drivers/mailbox/sprd-mailbox.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Spreadtrum mailbox driver 4 + * 5 + * Copyright (c) 2020 Spreadtrum Communications Inc. 6 + */ 7 + 8 + #include <linux/delay.h> 9 + #include <linux/err.h> 10 + #include <linux/interrupt.h> 11 + #include <linux/io.h> 12 + #include <linux/mailbox_controller.h> 13 + #include <linux/module.h> 14 + #include <linux/platform_device.h> 15 + #include <linux/clk.h> 16 + 17 + #define SPRD_MBOX_ID 0x0 18 + #define SPRD_MBOX_MSG_LOW 0x4 19 + #define SPRD_MBOX_MSG_HIGH 0x8 20 + #define SPRD_MBOX_TRIGGER 0xc 21 + #define SPRD_MBOX_FIFO_RST 0x10 22 + #define SPRD_MBOX_FIFO_STS 0x14 23 + #define SPRD_MBOX_IRQ_STS 0x18 24 + #define SPRD_MBOX_IRQ_MSK 0x1c 25 + #define SPRD_MBOX_LOCK 0x20 26 + #define SPRD_MBOX_FIFO_DEPTH 0x24 27 + 28 + /* Bit and mask definiation for inbox's SPRD_MBOX_FIFO_STS register */ 29 + #define SPRD_INBOX_FIFO_DELIVER_MASK GENMASK(23, 16) 30 + #define SPRD_INBOX_FIFO_OVERLOW_MASK GENMASK(15, 8) 31 + #define SPRD_INBOX_FIFO_DELIVER_SHIFT 16 32 + #define SPRD_INBOX_FIFO_BUSY_MASK GENMASK(7, 0) 33 + 34 + /* Bit and mask definiation for SPRD_MBOX_IRQ_STS register */ 35 + #define SPRD_MBOX_IRQ_CLR BIT(0) 36 + 37 + /* Bit and mask definiation for outbox's SPRD_MBOX_FIFO_STS register */ 38 + #define SPRD_OUTBOX_FIFO_FULL BIT(0) 39 + #define SPRD_OUTBOX_FIFO_WR_SHIFT 16 40 + #define SPRD_OUTBOX_FIFO_RD_SHIFT 24 41 + #define SPRD_OUTBOX_FIFO_POS_MASK GENMASK(7, 0) 42 + 43 + /* Bit and mask definiation for inbox's SPRD_MBOX_IRQ_MSK register */ 44 + #define SPRD_INBOX_FIFO_BLOCK_IRQ BIT(0) 45 + #define SPRD_INBOX_FIFO_OVERFLOW_IRQ BIT(1) 46 + #define SPRD_INBOX_FIFO_DELIVER_IRQ BIT(2) 47 + #define SPRD_INBOX_FIFO_IRQ_MASK GENMASK(2, 0) 48 + 49 + /* Bit and mask definiation for outbox's SPRD_MBOX_IRQ_MSK register */ 50 + #define SPRD_OUTBOX_FIFO_NOT_EMPTY_IRQ BIT(0) 51 + #define SPRD_OUTBOX_FIFO_IRQ_MASK GENMASK(4, 0) 52 + 53 + #define SPRD_MBOX_CHAN_MAX 8 54 + 55 + struct sprd_mbox_priv { 56 + struct mbox_controller mbox; 57 + struct device *dev; 58 + void __iomem *inbox_base; 59 + void __iomem *outbox_base; 60 + struct clk *clk; 61 + u32 outbox_fifo_depth; 62 + 63 + struct mbox_chan chan[SPRD_MBOX_CHAN_MAX]; 64 + }; 65 + 66 + static struct sprd_mbox_priv *to_sprd_mbox_priv(struct mbox_controller *mbox) 67 + { 68 + return container_of(mbox, struct sprd_mbox_priv, mbox); 69 + } 70 + 71 + static u32 sprd_mbox_get_fifo_len(struct sprd_mbox_priv *priv, u32 fifo_sts) 72 + { 73 + u32 wr_pos = (fifo_sts >> SPRD_OUTBOX_FIFO_WR_SHIFT) & 74 + SPRD_OUTBOX_FIFO_POS_MASK; 75 + u32 rd_pos = (fifo_sts >> SPRD_OUTBOX_FIFO_RD_SHIFT) & 76 + SPRD_OUTBOX_FIFO_POS_MASK; 77 + u32 fifo_len; 78 + 79 + /* 80 + * If the read pointer is equal with write pointer, which means the fifo 81 + * is full or empty. 82 + */ 83 + if (wr_pos == rd_pos) { 84 + if (fifo_sts & SPRD_OUTBOX_FIFO_FULL) 85 + fifo_len = priv->outbox_fifo_depth; 86 + else 87 + fifo_len = 0; 88 + } else if (wr_pos > rd_pos) { 89 + fifo_len = wr_pos - rd_pos; 90 + } else { 91 + fifo_len = priv->outbox_fifo_depth - rd_pos + wr_pos; 92 + } 93 + 94 + return fifo_len; 95 + } 96 + 97 + static irqreturn_t sprd_mbox_outbox_isr(int irq, void *data) 98 + { 99 + struct sprd_mbox_priv *priv = data; 100 + struct mbox_chan *chan; 101 + u32 fifo_sts, fifo_len, msg[2]; 102 + int i, id; 103 + 104 + fifo_sts = readl(priv->outbox_base + SPRD_MBOX_FIFO_STS); 105 + 106 + fifo_len = sprd_mbox_get_fifo_len(priv, fifo_sts); 107 + if (!fifo_len) { 108 + dev_warn_ratelimited(priv->dev, "spurious outbox interrupt\n"); 109 + return IRQ_NONE; 110 + } 111 + 112 + for (i = 0; i < fifo_len; i++) { 113 + msg[0] = readl(priv->outbox_base + SPRD_MBOX_MSG_LOW); 114 + msg[1] = readl(priv->outbox_base + SPRD_MBOX_MSG_HIGH); 115 + id = readl(priv->outbox_base + SPRD_MBOX_ID); 116 + 117 + chan = &priv->chan[id]; 118 + mbox_chan_received_data(chan, (void *)msg); 119 + 120 + /* Trigger to update outbox FIFO pointer */ 121 + writel(0x1, priv->outbox_base + SPRD_MBOX_TRIGGER); 122 + } 123 + 124 + /* Clear irq status after reading all message. */ 125 + writel(SPRD_MBOX_IRQ_CLR, priv->outbox_base + SPRD_MBOX_IRQ_STS); 126 + 127 + return IRQ_HANDLED; 128 + } 129 + 130 + static irqreturn_t sprd_mbox_inbox_isr(int irq, void *data) 131 + { 132 + struct sprd_mbox_priv *priv = data; 133 + struct mbox_chan *chan; 134 + u32 fifo_sts, send_sts, busy, id; 135 + 136 + fifo_sts = readl(priv->inbox_base + SPRD_MBOX_FIFO_STS); 137 + 138 + /* Get the inbox data delivery status */ 139 + send_sts = (fifo_sts & SPRD_INBOX_FIFO_DELIVER_MASK) >> 140 + SPRD_INBOX_FIFO_DELIVER_SHIFT; 141 + if (!send_sts) { 142 + dev_warn_ratelimited(priv->dev, "spurious inbox interrupt\n"); 143 + return IRQ_NONE; 144 + } 145 + 146 + while (send_sts) { 147 + id = __ffs(send_sts); 148 + send_sts &= (send_sts - 1); 149 + 150 + chan = &priv->chan[id]; 151 + 152 + /* 153 + * Check if the message was fetched by remote traget, if yes, 154 + * that means the transmission has been completed. 155 + */ 156 + busy = fifo_sts & SPRD_INBOX_FIFO_BUSY_MASK; 157 + if (!(busy & BIT(id))) 158 + mbox_chan_txdone(chan, 0); 159 + } 160 + 161 + /* Clear FIFO delivery and overflow status */ 162 + writel(fifo_sts & 163 + (SPRD_INBOX_FIFO_DELIVER_MASK | SPRD_INBOX_FIFO_OVERLOW_MASK), 164 + priv->inbox_base + SPRD_MBOX_FIFO_RST); 165 + 166 + /* Clear irq status */ 167 + writel(SPRD_MBOX_IRQ_CLR, priv->inbox_base + SPRD_MBOX_IRQ_STS); 168 + 169 + return IRQ_HANDLED; 170 + } 171 + 172 + static int sprd_mbox_send_data(struct mbox_chan *chan, void *msg) 173 + { 174 + struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox); 175 + unsigned long id = (unsigned long)chan->con_priv; 176 + u32 *data = msg; 177 + 178 + /* Write data into inbox FIFO, and only support 8 bytes every time */ 179 + writel(data[0], priv->inbox_base + SPRD_MBOX_MSG_LOW); 180 + writel(data[1], priv->inbox_base + SPRD_MBOX_MSG_HIGH); 181 + 182 + /* Set target core id */ 183 + writel(id, priv->inbox_base + SPRD_MBOX_ID); 184 + 185 + /* Trigger remote request */ 186 + writel(0x1, priv->inbox_base + SPRD_MBOX_TRIGGER); 187 + 188 + return 0; 189 + } 190 + 191 + static int sprd_mbox_flush(struct mbox_chan *chan, unsigned long timeout) 192 + { 193 + struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox); 194 + unsigned long id = (unsigned long)chan->con_priv; 195 + u32 busy; 196 + 197 + timeout = jiffies + msecs_to_jiffies(timeout); 198 + 199 + while (time_before(jiffies, timeout)) { 200 + busy = readl(priv->inbox_base + SPRD_MBOX_FIFO_STS) & 201 + SPRD_INBOX_FIFO_BUSY_MASK; 202 + if (!(busy & BIT(id))) { 203 + mbox_chan_txdone(chan, 0); 204 + return 0; 205 + } 206 + 207 + udelay(1); 208 + } 209 + 210 + return -ETIME; 211 + } 212 + 213 + static int sprd_mbox_startup(struct mbox_chan *chan) 214 + { 215 + struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox); 216 + u32 val; 217 + 218 + /* Select outbox FIFO mode and reset the outbox FIFO status */ 219 + writel(0x0, priv->outbox_base + SPRD_MBOX_FIFO_RST); 220 + 221 + /* Enable inbox FIFO overflow and delivery interrupt */ 222 + val = readl(priv->inbox_base + SPRD_MBOX_IRQ_MSK); 223 + val &= ~(SPRD_INBOX_FIFO_OVERFLOW_IRQ | SPRD_INBOX_FIFO_DELIVER_IRQ); 224 + writel(val, priv->inbox_base + SPRD_MBOX_IRQ_MSK); 225 + 226 + /* Enable outbox FIFO not empty interrupt */ 227 + val = readl(priv->outbox_base + SPRD_MBOX_IRQ_MSK); 228 + val &= ~SPRD_OUTBOX_FIFO_NOT_EMPTY_IRQ; 229 + writel(val, priv->outbox_base + SPRD_MBOX_IRQ_MSK); 230 + 231 + return 0; 232 + } 233 + 234 + static void sprd_mbox_shutdown(struct mbox_chan *chan) 235 + { 236 + struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox); 237 + 238 + /* Disable inbox & outbox interrupt */ 239 + writel(SPRD_INBOX_FIFO_IRQ_MASK, priv->inbox_base + SPRD_MBOX_IRQ_MSK); 240 + writel(SPRD_OUTBOX_FIFO_IRQ_MASK, priv->outbox_base + SPRD_MBOX_IRQ_MSK); 241 + } 242 + 243 + static const struct mbox_chan_ops sprd_mbox_ops = { 244 + .send_data = sprd_mbox_send_data, 245 + .flush = sprd_mbox_flush, 246 + .startup = sprd_mbox_startup, 247 + .shutdown = sprd_mbox_shutdown, 248 + }; 249 + 250 + static void sprd_mbox_disable(void *data) 251 + { 252 + struct sprd_mbox_priv *priv = data; 253 + 254 + clk_disable_unprepare(priv->clk); 255 + } 256 + 257 + static int sprd_mbox_probe(struct platform_device *pdev) 258 + { 259 + struct device *dev = &pdev->dev; 260 + struct sprd_mbox_priv *priv; 261 + int ret, inbox_irq, outbox_irq; 262 + unsigned long id; 263 + 264 + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 265 + if (!priv) 266 + return -ENOMEM; 267 + 268 + priv->dev = dev; 269 + 270 + /* 271 + * The Spreadtrum mailbox uses an inbox to send messages to the target 272 + * core, and uses an outbox to receive messages from other cores. 273 + * 274 + * Thus the mailbox controller supplies 2 different register addresses 275 + * and IRQ numbers for inbox and outbox. 276 + */ 277 + priv->inbox_base = devm_platform_ioremap_resource(pdev, 0); 278 + if (IS_ERR(priv->inbox_base)) 279 + return PTR_ERR(priv->inbox_base); 280 + 281 + priv->outbox_base = devm_platform_ioremap_resource(pdev, 1); 282 + if (IS_ERR(priv->outbox_base)) 283 + return PTR_ERR(priv->outbox_base); 284 + 285 + priv->clk = devm_clk_get(dev, "enable"); 286 + if (IS_ERR(priv->clk)) { 287 + dev_err(dev, "failed to get mailbox clock\n"); 288 + return PTR_ERR(priv->clk); 289 + } 290 + 291 + ret = clk_prepare_enable(priv->clk); 292 + if (ret) 293 + return ret; 294 + 295 + ret = devm_add_action_or_reset(dev, sprd_mbox_disable, priv); 296 + if (ret) { 297 + dev_err(dev, "failed to add mailbox disable action\n"); 298 + return ret; 299 + } 300 + 301 + inbox_irq = platform_get_irq(pdev, 0); 302 + if (inbox_irq < 0) 303 + return inbox_irq; 304 + 305 + ret = devm_request_irq(dev, inbox_irq, sprd_mbox_inbox_isr, 306 + IRQF_NO_SUSPEND, dev_name(dev), priv); 307 + if (ret) { 308 + dev_err(dev, "failed to request inbox IRQ: %d\n", ret); 309 + return ret; 310 + } 311 + 312 + outbox_irq = platform_get_irq(pdev, 1); 313 + if (outbox_irq < 0) 314 + return outbox_irq; 315 + 316 + ret = devm_request_irq(dev, outbox_irq, sprd_mbox_outbox_isr, 317 + IRQF_NO_SUSPEND, dev_name(dev), priv); 318 + if (ret) { 319 + dev_err(dev, "failed to request outbox IRQ: %d\n", ret); 320 + return ret; 321 + } 322 + 323 + /* Get the default outbox FIFO depth */ 324 + priv->outbox_fifo_depth = 325 + readl(priv->outbox_base + SPRD_MBOX_FIFO_DEPTH) + 1; 326 + priv->mbox.dev = dev; 327 + priv->mbox.chans = &priv->chan[0]; 328 + priv->mbox.num_chans = SPRD_MBOX_CHAN_MAX; 329 + priv->mbox.ops = &sprd_mbox_ops; 330 + priv->mbox.txdone_irq = true; 331 + 332 + for (id = 0; id < SPRD_MBOX_CHAN_MAX; id++) 333 + priv->chan[id].con_priv = (void *)id; 334 + 335 + ret = devm_mbox_controller_register(dev, &priv->mbox); 336 + if (ret) { 337 + dev_err(dev, "failed to register mailbox: %d\n", ret); 338 + return ret; 339 + } 340 + 341 + return 0; 342 + } 343 + 344 + static const struct of_device_id sprd_mbox_of_match[] = { 345 + { .compatible = "sprd,sc9860-mailbox", }, 346 + { }, 347 + }; 348 + MODULE_DEVICE_TABLE(of, sprd_mbox_of_match); 349 + 350 + static struct platform_driver sprd_mbox_driver = { 351 + .driver = { 352 + .name = "sprd-mailbox", 353 + .of_match_table = sprd_mbox_of_match, 354 + }, 355 + .probe = sprd_mbox_probe, 356 + }; 357 + module_platform_driver(sprd_mbox_driver); 358 + 359 + MODULE_AUTHOR("Baolin Wang <baolin.wang@unisoc.com>"); 360 + MODULE_DESCRIPTION("Spreadtrum mailbox driver"); 361 + MODULE_LICENSE("GPL v2");
+10 -15
drivers/mailbox/zynqmp-ipi-mailbox.c
··· 504 504 mchan->req_buf_size = resource_size(&res); 505 505 mchan->req_buf = devm_ioremap(mdev, res.start, 506 506 mchan->req_buf_size); 507 - if (IS_ERR(mchan->req_buf)) { 507 + if (!mchan->req_buf) { 508 508 dev_err(mdev, "Unable to map IPI buffer I/O memory\n"); 509 - ret = PTR_ERR(mchan->req_buf); 510 - return ret; 509 + return -ENOMEM; 511 510 } 512 511 } else if (ret != -ENODEV) { 513 512 dev_err(mdev, "Unmatched resource %s, %d.\n", name, ret); ··· 519 520 mchan->resp_buf_size = resource_size(&res); 520 521 mchan->resp_buf = devm_ioremap(mdev, res.start, 521 522 mchan->resp_buf_size); 522 - if (IS_ERR(mchan->resp_buf)) { 523 + if (!mchan->resp_buf) { 523 524 dev_err(mdev, "Unable to map IPI buffer I/O memory\n"); 524 - ret = PTR_ERR(mchan->resp_buf); 525 - return ret; 525 + return -ENOMEM; 526 526 } 527 527 } else if (ret != -ENODEV) { 528 528 dev_err(mdev, "Unmatched resource %s.\n", name); ··· 541 543 mchan->req_buf_size = resource_size(&res); 542 544 mchan->req_buf = devm_ioremap(mdev, res.start, 543 545 mchan->req_buf_size); 544 - if (IS_ERR(mchan->req_buf)) { 546 + if (!mchan->req_buf) { 545 547 dev_err(mdev, "Unable to map IPI buffer I/O memory\n"); 546 - ret = PTR_ERR(mchan->req_buf); 547 - return ret; 548 + return -ENOMEM; 548 549 } 549 550 } else if (ret != -ENODEV) { 550 551 dev_err(mdev, "Unmatched resource %s.\n", name); ··· 556 559 mchan->resp_buf_size = resource_size(&res); 557 560 mchan->resp_buf = devm_ioremap(mdev, res.start, 558 561 mchan->resp_buf_size); 559 - if (IS_ERR(mchan->resp_buf)) { 562 + if (!mchan->resp_buf) { 560 563 dev_err(mdev, "Unable to map IPI buffer I/O memory\n"); 561 - ret = PTR_ERR(mchan->resp_buf); 562 - return ret; 564 + return -ENOMEM; 563 565 } 564 566 } else if (ret != -ENODEV) { 565 567 dev_err(mdev, "Unmatched resource %s.\n", name); ··· 664 668 665 669 /* IPI IRQ */ 666 670 ret = platform_get_irq(pdev, 0); 667 - if (ret < 0) { 668 - dev_err(dev, "unable to find IPI IRQ.\n"); 671 + if (ret < 0) 669 672 goto free_mbox_dev; 670 - } 673 + 671 674 pdata->irq = ret; 672 675 ret = devm_request_irq(dev, pdata->irq, zynqmp_ipi_interrupt, 673 676 IRQF_SHARED, dev_name(dev), pdata);
+33
include/dt-bindings/mailbox/qcom-ipcc.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ 2 + /* 3 + * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. 4 + */ 5 + 6 + #ifndef __DT_BINDINGS_MAILBOX_IPCC_H 7 + #define __DT_BINDINGS_MAILBOX_IPCC_H 8 + 9 + /* Signal IDs for MPROC protocol */ 10 + #define IPCC_MPROC_SIGNAL_GLINK_QMP 0 11 + #define IPCC_MPROC_SIGNAL_SMP2P 2 12 + #define IPCC_MPROC_SIGNAL_PING 3 13 + 14 + /* Client IDs */ 15 + #define IPCC_CLIENT_AOP 0 16 + #define IPCC_CLIENT_TZ 1 17 + #define IPCC_CLIENT_MPSS 2 18 + #define IPCC_CLIENT_LPASS 3 19 + #define IPCC_CLIENT_SLPI 4 20 + #define IPCC_CLIENT_SDC 5 21 + #define IPCC_CLIENT_CDSP 6 22 + #define IPCC_CLIENT_NPU 7 23 + #define IPCC_CLIENT_APSS 8 24 + #define IPCC_CLIENT_GPU 9 25 + #define IPCC_CLIENT_CVP 10 26 + #define IPCC_CLIENT_CAM 11 27 + #define IPCC_CLIENT_VPU 12 28 + #define IPCC_CLIENT_PCIE0 13 29 + #define IPCC_CLIENT_PCIE1 14 30 + #define IPCC_CLIENT_PCIE2 15 31 + #define IPCC_CLIENT_SPSS 16 32 + 33 + #endif