mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-12-28 00:35:01 +00:00
common: switch back from remove_new() to remove() callback
imx: fix format specifier zynqmp: setup IPI for each child node thead: Add th1520 driver and bindings qcom: add SM8750 and SAR2130p compatibles fix expected clocks for callbacks use IRQF_NO_SUSPEND for cpucp mtk-cmdq: switch to __pm_runtime_put_autosuspend() fix alloc size of clocks mpfs: fix reg properties ti-msgmgr: don't use of_match_ptr helper enable COMPILE_TEST build pcc: consider the PCC_ACK_FLAG arm_mhuv2: fix non-fatal improper reuse of variable -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE6EwehDt/SOnwFyTyf9lkf8eYP5UFAmdDggMACgkQf9lkf8eY P5W6HRAAgopdwICYcmLYC1UgpROVjVdu2MgOmnnu1Q0BSsl/uG1zRiwQgCkAg3Md ugHt4Way4L/LIgqTdfUIxJrS61Osqweo5C2hl8hm3RcBDZNs4C7j6UXdWgrNSkhr OgseoJMt3o3+DQBmN2qKvbPsyciV6NkA8b4ZP64c+C0V+ci22xWEuQSllHguX7x7 ggi1NK419ZME5FS+JREjwlq07DiCFEZ1Azzhb5xAtUcZ4c3yzOvVDJSTxI5EdUKF 6K1iPHjcFlxkeJA3O1QHMs5OAocuIC3/xvWVMs+GkoZMlp5PQgnc4I81DRdQIZzk gONizxrgljU8RGRiSGQXSY14EK4BeRHAZwOqhe5McsPRMbR0oy50eT1e+3iYen1s zEprrw9a2a/WMKNBLOxD1hbx8vHl1KwrYk81YIJ5Vna/xZ0w3Ig4D6OXQ/zgpm8B HkRRZKxYEGqHZWj1dxWUdcC9O6F0QHIhpYhfhXNOXaOk0lvOGTWH+wYwGgY6nOmV ureWVHcCOcghF6BjfIT7yH9HuA2igRrlekZPxYILgbL1QZtB3NbySk1ZZA0543aZ pakDKzo+jCRKK9I0w1WNXQj6Aa8KqHYycAf+7rPeFLf+j8cnNQ51HgC5QFSjAl+y Ch0txVvd01wjFIstlIzcBTUK7TeI93cuwXC6/22EUgoZu0vzz9w= =6PMb -----END PGP SIGNATURE----- Merge tag 'mailbox-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/jassibrar/mailbox Pull mailbox updates from Jassi Brar: "Common: - switch back from remove_new() to remove() callback imx: - fix format specifier zynqmp: - setup IPI for each child node thead: - Add th1520 driver and bindings qcom: - add SM8750 and SAR2130p compatibles - fix expected clocks for callbacks - use IRQF_NO_SUSPEND for cpucp mtk-cmdq: - switch to __pm_runtime_put_autosuspend() - fix alloc size of clocks mpfs: - fix reg properties ti-msgmgr: - don't use of_match_ptr helper - enable COMPILE_TEST build pcc: - consider the PCC_ACK_FLAG arm_mhuv2: - fix non-fatal improper reuse of variable" * tag 'mailbox-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/jassibrar/mailbox: mailbox: pcc: Check before sending MCTP PCC response ACK mailbox: Switch back to struct platform_driver::remove() mailbox: imx: Modify the incorrect format specifier mailbox: arm_mhuv2: clean up loop in get_irq_chan_comb() mailbox: zynqmp: setup IPI for each valid child node dt-bindings: mailbox: Add thead,th1520-mailbox bindings mailbox: Introduce support for T-head TH1520 Mailbox driver mailbox: mtk-cmdq: fix wrong use of sizeof in cmdq_get_clocks() dt-bindings: mailbox: qcom-ipcc: Add SM8750 dt-bindings: mailbox: qcom,apcs-kpss-global: correct expected clocks for fallbacks dt-bindings: mailbox: qcom-ipcc: Add SAR2130P compatible mailbox: ti-msgmgr: Allow building under COMPILE_TEST mailbox: ti-msgmgr: Remove use of of_match_ptr() helper mailbox: qcom-cpucp: Mark the irq with IRQF_NO_SUSPEND flag mailbox: mtk-cmdq-mailbox: Switch to __pm_runtime_put_autosuspend() mailbox: mpfs: support new, syscon based, devicetree configuration dt-bindings: mailbox: mpfs: fix reg properties
This commit is contained in:
commit
2c22dc1ee3
@ -15,6 +15,8 @@ properties:
|
||||
|
||||
reg:
|
||||
oneOf:
|
||||
- items:
|
||||
- description: mailbox data registers
|
||||
- items:
|
||||
- description: mailbox control & data registers
|
||||
- description: mailbox interrupt registers
|
||||
@ -23,6 +25,7 @@ properties:
|
||||
- description: mailbox control registers
|
||||
- description: mailbox interrupt registers
|
||||
- description: mailbox data registers
|
||||
deprecated: true
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
@ -41,12 +44,12 @@ additionalProperties: false
|
||||
examples:
|
||||
- |
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
mbox: mailbox@37020000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
mailbox@37020800 {
|
||||
compatible = "microchip,mpfs-mailbox";
|
||||
reg = <0x0 0x37020000 0x0 0x58>, <0x0 0x2000318C 0x0 0x40>,
|
||||
<0x0 0x37020800 0x0 0x100>;
|
||||
reg = <0x37020800 0x100>;
|
||||
interrupt-parent = <&L1>;
|
||||
interrupts = <96>;
|
||||
#mbox-cells = <1>;
|
||||
|
@ -165,12 +165,13 @@ allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,msm8953-apcs-kpss-global
|
||||
- qcom,msm8994-apcs-kpss-global
|
||||
- qcom,msm8996-apcs-hmss-global
|
||||
- qcom,qcm2290-apcs-hmss-global
|
||||
- qcom,sdm845-apss-shared
|
||||
contains:
|
||||
enum:
|
||||
- qcom,msm8953-apcs-kpss-global
|
||||
- qcom,msm8994-apcs-kpss-global
|
||||
- qcom,msm8996-apcs-hmss-global
|
||||
- qcom,qcm2290-apcs-hmss-global
|
||||
- qcom,sdm845-apss-shared
|
||||
then:
|
||||
properties:
|
||||
clocks: false
|
||||
|
@ -28,6 +28,7 @@ properties:
|
||||
- qcom,qdu1000-ipcc
|
||||
- qcom,sa8255p-ipcc
|
||||
- qcom,sa8775p-ipcc
|
||||
- qcom,sar2130p-ipcc
|
||||
- qcom,sc7280-ipcc
|
||||
- qcom,sc8280xp-ipcc
|
||||
- qcom,sdx75-ipcc
|
||||
@ -38,6 +39,7 @@ properties:
|
||||
- qcom,sm8450-ipcc
|
||||
- qcom,sm8550-ipcc
|
||||
- qcom,sm8650-ipcc
|
||||
- qcom,sm8750-ipcc
|
||||
- qcom,x1e80100-ipcc
|
||||
- const: qcom,ipcc
|
||||
|
||||
|
@ -0,0 +1,89 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mailbox/thead,th1520-mbox.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: T-head TH1520 Mailbox Controller
|
||||
|
||||
description:
|
||||
The T-head mailbox controller enables communication and coordination between
|
||||
cores within the SoC by passing messages (e.g., data, status, and control)
|
||||
through mailbox channels. It also allows one core to signal another processor
|
||||
using interrupts via the Interrupt Controller Unit (ICU).
|
||||
|
||||
maintainers:
|
||||
- Michal Wilczynski <m.wilczynski@samsung.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: thead,th1520-mbox
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Clock for the local mailbox
|
||||
- description: Clock for remote ICU 0
|
||||
- description: Clock for remote ICU 1
|
||||
- description: Clock for remote ICU 2
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: clk-local
|
||||
- const: clk-remote-icu0
|
||||
- const: clk-remote-icu1
|
||||
- const: clk-remote-icu2
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: Mailbox local base address
|
||||
- description: Remote ICU 0 base address
|
||||
- description: Remote ICU 1 base address
|
||||
- description: Remote ICU 2 base address
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: local
|
||||
- const: remote-icu0
|
||||
- const: remote-icu1
|
||||
- const: remote-icu2
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
'#mbox-cells':
|
||||
const: 1
|
||||
description:
|
||||
The one and only cell describes destination CPU ID.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- clocks
|
||||
- clock-names
|
||||
- reg
|
||||
- reg-names
|
||||
- interrupts
|
||||
- '#mbox-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/thead,th1520-clk-ap.h>
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
mailbox@ffffc38000 {
|
||||
compatible = "thead,th1520-mbox";
|
||||
reg = <0xff 0xffc38000 0x0 0x4000>,
|
||||
<0xff 0xffc44000 0x0 0x1000>,
|
||||
<0xff 0xffc4c000 0x0 0x1000>,
|
||||
<0xff 0xffc54000 0x0 0x1000>;
|
||||
reg-names = "local", "remote-icu0", "remote-icu1", "remote-icu2";
|
||||
clocks = <&clk CLK_MBOX0>, <&clk CLK_MBOX1>, <&clk CLK_MBOX2>,
|
||||
<&clk CLK_MBOX3>;
|
||||
clock-names = "clk-local", "clk-remote-icu0", "clk-remote-icu1",
|
||||
"clk-remote-icu2";
|
||||
interrupts = <28>;
|
||||
#mbox-cells = <1>;
|
||||
};
|
||||
};
|
@ -20079,10 +20079,12 @@ L: linux-riscv@lists.infradead.org
|
||||
S: Maintained
|
||||
T: git https://github.com/pdp7/linux.git
|
||||
F: Documentation/devicetree/bindings/clock/thead,th1520-clk-ap.yaml
|
||||
F: Documentation/devicetree/bindings/mailbox/thead,th1520-mbox.yaml
|
||||
F: Documentation/devicetree/bindings/net/thead,th1520-gmac.yaml
|
||||
F: Documentation/devicetree/bindings/pinctrl/thead,th1520-pinctrl.yaml
|
||||
F: arch/riscv/boot/dts/thead/
|
||||
F: drivers/clk/thead/clk-th1520-ap.c
|
||||
F: drivers/mailbox/mailbox-th1520.c
|
||||
F: drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c
|
||||
F: drivers/pinctrl/pinctrl-th1520.c
|
||||
F: include/dt-bindings/clock/thead,th1520-clk-ap.h
|
||||
|
@ -127,7 +127,7 @@ config STI_MBOX
|
||||
|
||||
config TI_MESSAGE_MANAGER
|
||||
tristate "Texas Instruments Message Manager Driver"
|
||||
depends on ARCH_KEYSTONE || ARCH_K3
|
||||
depends on ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST
|
||||
default ARCH_K3
|
||||
help
|
||||
An implementation of Message Manager slave driver for Keystone
|
||||
@ -168,6 +168,7 @@ config MAILBOX_TEST
|
||||
config POLARFIRE_SOC_MAILBOX
|
||||
tristate "PolarFire SoC (MPFS) Mailbox"
|
||||
depends on HAS_IOMEM
|
||||
depends on MFD_SYSCON
|
||||
depends on ARCH_MICROCHIP_POLARFIRE || COMPILE_TEST
|
||||
help
|
||||
This driver adds support for the PolarFire SoC (MPFS) mailbox controller.
|
||||
@ -295,4 +296,14 @@ config QCOM_IPCC
|
||||
acts as an interrupt controller for receiving interrupts from clients.
|
||||
Say Y here if you want to build this driver.
|
||||
|
||||
config THEAD_TH1520_MBOX
|
||||
tristate "T-head TH1520 Mailbox"
|
||||
depends on ARCH_THEAD || COMPILE_TEST
|
||||
help
|
||||
Mailbox driver implementation for the Thead TH-1520 platform. Enables
|
||||
two cores within the SoC to communicate and coordinate by passing
|
||||
messages. Could be used to communicate between E910 core, on which the
|
||||
kernel is running, and E902 core used for power management among other
|
||||
things.
|
||||
|
||||
endif
|
||||
|
@ -64,3 +64,5 @@ obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox.o
|
||||
obj-$(CONFIG_QCOM_CPUCP_MBOX) += qcom-cpucp-mbox.o
|
||||
|
||||
obj-$(CONFIG_QCOM_IPCC) += qcom-ipcc.o
|
||||
|
||||
obj-$(CONFIG_THEAD_TH1520_MBOX) += mailbox-th1520.o
|
||||
|
@ -500,7 +500,7 @@ static const struct mhuv2_protocol_ops mhuv2_data_transfer_ops = {
|
||||
static struct mbox_chan *get_irq_chan_comb(struct mhuv2 *mhu, u32 __iomem *reg)
|
||||
{
|
||||
struct mbox_chan *chans = mhu->mbox.chans;
|
||||
int channel = 0, i, offset = 0, windows, protocol, ch_wn;
|
||||
int channel = 0, i, j, offset = 0, windows, protocol, ch_wn;
|
||||
u32 stat;
|
||||
|
||||
for (i = 0; i < MHUV2_CMB_INT_ST_REG_CNT; i++) {
|
||||
@ -510,9 +510,9 @@ static struct mbox_chan *get_irq_chan_comb(struct mhuv2 *mhu, u32 __iomem *reg)
|
||||
|
||||
ch_wn = i * MHUV2_STAT_BITS + __builtin_ctz(stat);
|
||||
|
||||
for (i = 0; i < mhu->length; i += 2) {
|
||||
protocol = mhu->protocols[i];
|
||||
windows = mhu->protocols[i + 1];
|
||||
for (j = 0; j < mhu->length; j += 2) {
|
||||
protocol = mhu->protocols[j];
|
||||
windows = mhu->protocols[j + 1];
|
||||
|
||||
if (ch_wn >= offset + windows) {
|
||||
if (protocol == DOORBELL)
|
||||
|
@ -1675,7 +1675,7 @@ static struct platform_driver flexrm_mbox_driver = {
|
||||
.of_match_table = flexrm_mbox_of_match,
|
||||
},
|
||||
.probe = flexrm_mbox_probe,
|
||||
.remove_new = flexrm_mbox_remove,
|
||||
.remove = flexrm_mbox_remove,
|
||||
};
|
||||
module_platform_driver(flexrm_mbox_driver);
|
||||
|
||||
|
@ -1618,7 +1618,7 @@ static void pdc_remove(struct platform_device *pdev)
|
||||
|
||||
static struct platform_driver pdc_mbox_driver = {
|
||||
.probe = pdc_probe,
|
||||
.remove_new = pdc_remove,
|
||||
.remove = pdc_remove,
|
||||
.driver = {
|
||||
.name = "brcm-iproc-pdc-mbox",
|
||||
.of_match_table = pdc_mbox_of_match,
|
||||
|
@ -782,7 +782,7 @@ static int imx_mu_init_generic(struct imx_mu_priv *priv)
|
||||
cp->chan = &priv->mbox_chans[i];
|
||||
priv->mbox_chans[i].con_priv = cp;
|
||||
snprintf(cp->irq_desc, sizeof(cp->irq_desc),
|
||||
"%s[%i-%i]", dev_name(priv->dev), cp->type, cp->idx);
|
||||
"%s[%i-%u]", dev_name(priv->dev), cp->type, cp->idx);
|
||||
}
|
||||
|
||||
priv->mbox.num_chans = IMX_MU_CHANS;
|
||||
@ -819,7 +819,7 @@ static int imx_mu_init_specific(struct imx_mu_priv *priv)
|
||||
cp->chan = &priv->mbox_chans[i];
|
||||
priv->mbox_chans[i].con_priv = cp;
|
||||
snprintf(cp->irq_desc, sizeof(cp->irq_desc),
|
||||
"%s[%i-%i]", dev_name(priv->dev), cp->type, cp->idx);
|
||||
"%s[%i-%u]", dev_name(priv->dev), cp->type, cp->idx);
|
||||
}
|
||||
|
||||
priv->mbox.num_chans = num_chans;
|
||||
@ -1120,7 +1120,7 @@ static const struct dev_pm_ops imx_mu_pm_ops = {
|
||||
|
||||
static struct platform_driver imx_mu_driver = {
|
||||
.probe = imx_mu_probe,
|
||||
.remove_new = imx_mu_remove,
|
||||
.remove = imx_mu_remove,
|
||||
.driver = {
|
||||
.name = "imx_mu",
|
||||
.of_match_table = imx_mu_dt_ids,
|
||||
|
@ -13,12 +13,15 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mailbox_controller.h>
|
||||
#include <soc/microchip/mpfs.h>
|
||||
|
||||
#define MESSAGE_INT_OFFSET 0x18cu
|
||||
#define SERVICES_CR_OFFSET 0x50u
|
||||
#define SERVICES_SR_OFFSET 0x54u
|
||||
#define MAILBOX_REG_OFFSET 0x800u
|
||||
@ -68,6 +71,7 @@ struct mpfs_mbox {
|
||||
void __iomem *int_reg;
|
||||
struct mbox_chan chans[1];
|
||||
struct mpfs_mss_response *response;
|
||||
struct regmap *sysreg_scb, *control_scb;
|
||||
u16 resp_offset;
|
||||
};
|
||||
|
||||
@ -75,7 +79,10 @@ static bool mpfs_mbox_busy(struct mpfs_mbox *mbox)
|
||||
{
|
||||
u32 status;
|
||||
|
||||
status = readl_relaxed(mbox->ctrl_base + SERVICES_SR_OFFSET);
|
||||
if (mbox->control_scb)
|
||||
regmap_read(mbox->control_scb, SERVICES_SR_OFFSET, &status);
|
||||
else
|
||||
status = readl_relaxed(mbox->ctrl_base + SERVICES_SR_OFFSET);
|
||||
|
||||
return status & SCB_STATUS_BUSY_MASK;
|
||||
}
|
||||
@ -95,7 +102,11 @@ static bool mpfs_mbox_last_tx_done(struct mbox_chan *chan)
|
||||
* Failed services are intended to generated interrupts, but in reality
|
||||
* this does not happen, so the status must be checked here.
|
||||
*/
|
||||
val = readl_relaxed(mbox->ctrl_base + SERVICES_SR_OFFSET);
|
||||
if (mbox->control_scb)
|
||||
regmap_read(mbox->control_scb, SERVICES_SR_OFFSET, &val);
|
||||
else
|
||||
val = readl_relaxed(mbox->ctrl_base + SERVICES_SR_OFFSET);
|
||||
|
||||
response->resp_status = (val & SCB_STATUS_MASK) >> SCB_STATUS_POS;
|
||||
|
||||
return true;
|
||||
@ -143,7 +154,12 @@ static int mpfs_mbox_send_data(struct mbox_chan *chan, void *data)
|
||||
|
||||
tx_trigger = (opt_sel << SCB_CTRL_POS) & SCB_CTRL_MASK;
|
||||
tx_trigger |= SCB_CTRL_REQ_MASK | SCB_STATUS_NOTIFY_MASK;
|
||||
writel_relaxed(tx_trigger, mbox->ctrl_base + SERVICES_CR_OFFSET);
|
||||
|
||||
if (mbox->control_scb)
|
||||
regmap_write(mbox->control_scb, SERVICES_CR_OFFSET, tx_trigger);
|
||||
else
|
||||
writel_relaxed(tx_trigger, mbox->ctrl_base + SERVICES_CR_OFFSET);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -185,7 +201,10 @@ static irqreturn_t mpfs_mbox_inbox_isr(int irq, void *data)
|
||||
struct mbox_chan *chan = data;
|
||||
struct mpfs_mbox *mbox = (struct mpfs_mbox *)chan->con_priv;
|
||||
|
||||
writel_relaxed(0, mbox->int_reg);
|
||||
if (mbox->control_scb)
|
||||
regmap_write(mbox->sysreg_scb, MESSAGE_INT_OFFSET, 0);
|
||||
else
|
||||
writel_relaxed(0, mbox->int_reg);
|
||||
|
||||
mpfs_mbox_rx_data(chan);
|
||||
|
||||
@ -221,28 +240,62 @@ static const struct mbox_chan_ops mpfs_mbox_ops = {
|
||||
.last_tx_done = mpfs_mbox_last_tx_done,
|
||||
};
|
||||
|
||||
static inline int mpfs_mbox_syscon_probe(struct mpfs_mbox *mbox, struct platform_device *pdev)
|
||||
{
|
||||
mbox->control_scb = syscon_regmap_lookup_by_compatible("microchip,mpfs-control-scb");
|
||||
if (IS_ERR(mbox->control_scb))
|
||||
return PTR_ERR(mbox->control_scb);
|
||||
|
||||
mbox->sysreg_scb = syscon_regmap_lookup_by_compatible("microchip,mpfs-sysreg-scb");
|
||||
if (IS_ERR(mbox->sysreg_scb))
|
||||
return PTR_ERR(mbox->sysreg_scb);
|
||||
|
||||
mbox->mbox_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(mbox->ctrl_base))
|
||||
return PTR_ERR(mbox->mbox_base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int mpfs_mbox_old_format_probe(struct mpfs_mbox *mbox, struct platform_device *pdev)
|
||||
{
|
||||
dev_warn(&pdev->dev, "falling back to old devicetree format");
|
||||
|
||||
mbox->ctrl_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(mbox->ctrl_base))
|
||||
return PTR_ERR(mbox->ctrl_base);
|
||||
|
||||
mbox->int_reg = devm_platform_ioremap_resource(pdev, 1);
|
||||
if (IS_ERR(mbox->int_reg))
|
||||
return PTR_ERR(mbox->int_reg);
|
||||
|
||||
mbox->mbox_base = devm_platform_ioremap_resource(pdev, 2);
|
||||
if (IS_ERR(mbox->mbox_base)) // account for the old dt-binding w/ 2 regs
|
||||
mbox->mbox_base = mbox->ctrl_base + MAILBOX_REG_OFFSET;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpfs_mbox_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mpfs_mbox *mbox;
|
||||
struct resource *regs;
|
||||
int ret;
|
||||
|
||||
mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox), GFP_KERNEL);
|
||||
if (!mbox)
|
||||
return -ENOMEM;
|
||||
|
||||
mbox->ctrl_base = devm_platform_get_and_ioremap_resource(pdev, 0, ®s);
|
||||
if (IS_ERR(mbox->ctrl_base))
|
||||
return PTR_ERR(mbox->ctrl_base);
|
||||
|
||||
mbox->int_reg = devm_platform_get_and_ioremap_resource(pdev, 1, ®s);
|
||||
if (IS_ERR(mbox->int_reg))
|
||||
return PTR_ERR(mbox->int_reg);
|
||||
|
||||
mbox->mbox_base = devm_platform_get_and_ioremap_resource(pdev, 2, ®s);
|
||||
if (IS_ERR(mbox->mbox_base)) // account for the old dt-binding w/ 2 regs
|
||||
mbox->mbox_base = mbox->ctrl_base + MAILBOX_REG_OFFSET;
|
||||
|
||||
ret = mpfs_mbox_syscon_probe(mbox, pdev);
|
||||
if (ret) {
|
||||
/*
|
||||
* set this to null, so it can be used as the decision for to
|
||||
* regmap or not to regmap
|
||||
*/
|
||||
mbox->control_scb = NULL;
|
||||
ret = mpfs_mbox_old_format_probe(mbox, pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
mbox->irq = platform_get_irq(pdev, 0);
|
||||
if (mbox->irq < 0)
|
||||
return mbox->irq;
|
||||
|
@ -441,8 +441,8 @@ static struct platform_driver mbox_test_driver = {
|
||||
.name = "mailbox_test",
|
||||
.of_match_table = mbox_test_match,
|
||||
},
|
||||
.probe = mbox_test_probe,
|
||||
.remove_new = mbox_test_remove,
|
||||
.probe = mbox_test_probe,
|
||||
.remove = mbox_test_remove,
|
||||
};
|
||||
module_platform_driver(mbox_test_driver);
|
||||
|
||||
|
597
drivers/mailbox/mailbox-th1520.c
Normal file
597
drivers/mailbox/mailbox-th1520.c
Normal file
@ -0,0 +1,597 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2021 Alibaba Group Holding Limited.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mailbox_controller.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* Status Register */
|
||||
#define TH_1520_MBOX_STA 0x0
|
||||
#define TH_1520_MBOX_CLR 0x4
|
||||
#define TH_1520_MBOX_MASK 0xc
|
||||
|
||||
/* Transmit/receive data register:
|
||||
* INFO0 ~ INFO6
|
||||
*/
|
||||
#define TH_1520_MBOX_INFO_NUM 8
|
||||
#define TH_1520_MBOX_DATA_INFO_NUM 7
|
||||
#define TH_1520_MBOX_INFO0 0x14
|
||||
/* Transmit ack register: INFO7 */
|
||||
#define TH_1520_MBOX_INFO7 0x30
|
||||
|
||||
/* Generate remote icu IRQ Register */
|
||||
#define TH_1520_MBOX_GEN 0x10
|
||||
#define TH_1520_MBOX_GEN_RX_DATA BIT(6)
|
||||
#define TH_1520_MBOX_GEN_TX_ACK BIT(7)
|
||||
|
||||
#define TH_1520_MBOX_CHAN_RES_SIZE 0x1000
|
||||
#define TH_1520_MBOX_CHANS 4
|
||||
#define TH_1520_MBOX_CHAN_NAME_SIZE 20
|
||||
|
||||
#define TH_1520_MBOX_ACK_MAGIC 0xdeadbeaf
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/* store MBOX context across system-wide suspend/resume transitions */
|
||||
struct th1520_mbox_context {
|
||||
u32 intr_mask[TH_1520_MBOX_CHANS - 1];
|
||||
};
|
||||
#endif
|
||||
|
||||
enum th1520_mbox_icu_cpu_id {
|
||||
TH_1520_MBOX_ICU_KERNEL_CPU0, /* 910T */
|
||||
TH_1520_MBOX_ICU_CPU1, /* 902 */
|
||||
TH_1520_MBOX_ICU_CPU2, /* 906 */
|
||||
TH_1520_MBOX_ICU_CPU3, /* 910R */
|
||||
};
|
||||
|
||||
struct th1520_mbox_con_priv {
|
||||
enum th1520_mbox_icu_cpu_id idx;
|
||||
void __iomem *comm_local_base;
|
||||
void __iomem *comm_remote_base;
|
||||
char irq_desc[TH_1520_MBOX_CHAN_NAME_SIZE];
|
||||
struct mbox_chan *chan;
|
||||
};
|
||||
|
||||
struct th1520_mbox_priv {
|
||||
struct device *dev;
|
||||
void __iomem *local_icu[TH_1520_MBOX_CHANS];
|
||||
void __iomem *remote_icu[TH_1520_MBOX_CHANS - 1];
|
||||
void __iomem *cur_cpu_ch_base;
|
||||
spinlock_t mbox_lock; /* control register lock */
|
||||
|
||||
struct mbox_controller mbox;
|
||||
struct mbox_chan mbox_chans[TH_1520_MBOX_CHANS];
|
||||
struct clk_bulk_data clocks[TH_1520_MBOX_CHANS];
|
||||
struct th1520_mbox_con_priv con_priv[TH_1520_MBOX_CHANS];
|
||||
int irq;
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
struct th1520_mbox_context *ctx;
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct th1520_mbox_priv *
|
||||
to_th1520_mbox_priv(struct mbox_controller *mbox)
|
||||
{
|
||||
return container_of(mbox, struct th1520_mbox_priv, mbox);
|
||||
}
|
||||
|
||||
static void th1520_mbox_write(struct th1520_mbox_priv *priv, u32 val, u32 offs)
|
||||
{
|
||||
iowrite32(val, priv->cur_cpu_ch_base + offs);
|
||||
}
|
||||
|
||||
static u32 th1520_mbox_read(struct th1520_mbox_priv *priv, u32 offs)
|
||||
{
|
||||
return ioread32(priv->cur_cpu_ch_base + offs);
|
||||
}
|
||||
|
||||
static u32 th1520_mbox_rmw(struct th1520_mbox_priv *priv, u32 off, u32 set,
|
||||
u32 clr)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&priv->mbox_lock, flags);
|
||||
val = th1520_mbox_read(priv, off);
|
||||
val &= ~clr;
|
||||
val |= set;
|
||||
th1520_mbox_write(priv, val, off);
|
||||
spin_unlock_irqrestore(&priv->mbox_lock, flags);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void th1520_mbox_chan_write(struct th1520_mbox_con_priv *cp, u32 val,
|
||||
u32 offs, bool is_remote)
|
||||
{
|
||||
if (is_remote)
|
||||
iowrite32(val, cp->comm_remote_base + offs);
|
||||
else
|
||||
iowrite32(val, cp->comm_local_base + offs);
|
||||
}
|
||||
|
||||
static u32 th1520_mbox_chan_read(struct th1520_mbox_con_priv *cp, u32 offs,
|
||||
bool is_remote)
|
||||
{
|
||||
if (is_remote)
|
||||
return ioread32(cp->comm_remote_base + offs);
|
||||
else
|
||||
return ioread32(cp->comm_local_base + offs);
|
||||
}
|
||||
|
||||
static void th1520_mbox_chan_rmw(struct th1520_mbox_con_priv *cp, u32 off,
|
||||
u32 set, u32 clr, bool is_remote)
|
||||
{
|
||||
struct th1520_mbox_priv *priv = to_th1520_mbox_priv(cp->chan->mbox);
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&priv->mbox_lock, flags);
|
||||
val = th1520_mbox_chan_read(cp, off, is_remote);
|
||||
val &= ~clr;
|
||||
val |= set;
|
||||
th1520_mbox_chan_write(cp, val, off, is_remote);
|
||||
spin_unlock_irqrestore(&priv->mbox_lock, flags);
|
||||
}
|
||||
|
||||
static void th1520_mbox_chan_rd_data(struct th1520_mbox_con_priv *cp,
|
||||
void *data, bool is_remote)
|
||||
{
|
||||
u32 off = TH_1520_MBOX_INFO0;
|
||||
u32 *arg = data;
|
||||
u32 i;
|
||||
|
||||
/* read info0 ~ info6, totally 28 bytes
|
||||
* requires data memory size is 28 bytes
|
||||
*/
|
||||
for (i = 0; i < TH_1520_MBOX_DATA_INFO_NUM; i++) {
|
||||
*arg = th1520_mbox_chan_read(cp, off, is_remote);
|
||||
off += 4;
|
||||
arg++;
|
||||
}
|
||||
}
|
||||
|
||||
static void th1520_mbox_chan_wr_data(struct th1520_mbox_con_priv *cp,
|
||||
void *data, bool is_remote)
|
||||
{
|
||||
u32 off = TH_1520_MBOX_INFO0;
|
||||
u32 *arg = data;
|
||||
u32 i;
|
||||
|
||||
/* write info0 ~ info6, totally 28 bytes
|
||||
* requires data memory is 28 bytes valid data
|
||||
*/
|
||||
for (i = 0; i < TH_1520_MBOX_DATA_INFO_NUM; i++) {
|
||||
th1520_mbox_chan_write(cp, *arg, off, is_remote);
|
||||
off += 4;
|
||||
arg++;
|
||||
}
|
||||
}
|
||||
|
||||
static void th1520_mbox_chan_wr_ack(struct th1520_mbox_con_priv *cp, void *data,
|
||||
bool is_remote)
|
||||
{
|
||||
u32 off = TH_1520_MBOX_INFO7;
|
||||
u32 *arg = data;
|
||||
|
||||
th1520_mbox_chan_write(cp, *arg, off, is_remote);
|
||||
}
|
||||
|
||||
static int th1520_mbox_chan_id_to_mapbit(struct th1520_mbox_con_priv *cp)
|
||||
{
|
||||
int mapbit = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < TH_1520_MBOX_CHANS; i++) {
|
||||
if (i == cp->idx)
|
||||
return mapbit;
|
||||
|
||||
if (i != TH_1520_MBOX_ICU_KERNEL_CPU0)
|
||||
mapbit++;
|
||||
}
|
||||
|
||||
if (i == TH_1520_MBOX_CHANS)
|
||||
dev_err(cp->chan->mbox->dev, "convert to mapbit failed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t th1520_mbox_isr(int irq, void *p)
|
||||
{
|
||||
struct mbox_chan *chan = p;
|
||||
struct th1520_mbox_priv *priv = to_th1520_mbox_priv(chan->mbox);
|
||||
struct th1520_mbox_con_priv *cp = chan->con_priv;
|
||||
int mapbit = th1520_mbox_chan_id_to_mapbit(cp);
|
||||
u32 sta, dat[TH_1520_MBOX_DATA_INFO_NUM];
|
||||
u32 ack_magic = TH_1520_MBOX_ACK_MAGIC;
|
||||
u32 info0_data, info7_data;
|
||||
|
||||
sta = th1520_mbox_read(priv, TH_1520_MBOX_STA);
|
||||
if (!(sta & BIT(mapbit)))
|
||||
return IRQ_NONE;
|
||||
|
||||
/* clear chan irq bit in STA register */
|
||||
th1520_mbox_rmw(priv, TH_1520_MBOX_CLR, BIT(mapbit), 0);
|
||||
|
||||
/* info0 is the protocol word, should not be zero! */
|
||||
info0_data = th1520_mbox_chan_read(cp, TH_1520_MBOX_INFO0, false);
|
||||
if (info0_data) {
|
||||
/* read info0~info6 data */
|
||||
th1520_mbox_chan_rd_data(cp, dat, false);
|
||||
|
||||
/* clear local info0 */
|
||||
th1520_mbox_chan_write(cp, 0x0, TH_1520_MBOX_INFO0, false);
|
||||
|
||||
/* notify remote cpu */
|
||||
th1520_mbox_chan_wr_ack(cp, &ack_magic, true);
|
||||
/* CPU1 902/906 use polling mode to monitor info7 */
|
||||
if (cp->idx != TH_1520_MBOX_ICU_CPU1 &&
|
||||
cp->idx != TH_1520_MBOX_ICU_CPU2)
|
||||
th1520_mbox_chan_rmw(cp, TH_1520_MBOX_GEN,
|
||||
TH_1520_MBOX_GEN_TX_ACK, 0, true);
|
||||
|
||||
/* transfer the data to client */
|
||||
mbox_chan_received_data(chan, (void *)dat);
|
||||
}
|
||||
|
||||
/* info7 magic value mean the real ack signal, not generate bit7 */
|
||||
info7_data = th1520_mbox_chan_read(cp, TH_1520_MBOX_INFO7, false);
|
||||
if (info7_data == TH_1520_MBOX_ACK_MAGIC) {
|
||||
/* clear local info7 */
|
||||
th1520_mbox_chan_write(cp, 0x0, TH_1520_MBOX_INFO7, false);
|
||||
|
||||
/* notify framework the last TX has completed */
|
||||
mbox_chan_txdone(chan, 0);
|
||||
}
|
||||
|
||||
if (!info0_data && !info7_data)
|
||||
return IRQ_NONE;
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int th1520_mbox_send_data(struct mbox_chan *chan, void *data)
|
||||
{
|
||||
struct th1520_mbox_con_priv *cp = chan->con_priv;
|
||||
|
||||
th1520_mbox_chan_wr_data(cp, data, true);
|
||||
th1520_mbox_chan_rmw(cp, TH_1520_MBOX_GEN, TH_1520_MBOX_GEN_RX_DATA, 0,
|
||||
true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int th1520_mbox_startup(struct mbox_chan *chan)
|
||||
{
|
||||
struct th1520_mbox_priv *priv = to_th1520_mbox_priv(chan->mbox);
|
||||
struct th1520_mbox_con_priv *cp = chan->con_priv;
|
||||
u32 data[8] = {};
|
||||
int mask_bit;
|
||||
int ret;
|
||||
|
||||
/* clear local and remote generate and info0~info7 */
|
||||
th1520_mbox_chan_rmw(cp, TH_1520_MBOX_GEN, 0x0, 0xff, true);
|
||||
th1520_mbox_chan_rmw(cp, TH_1520_MBOX_GEN, 0x0, 0xff, false);
|
||||
th1520_mbox_chan_wr_ack(cp, &data[7], true);
|
||||
th1520_mbox_chan_wr_ack(cp, &data[7], false);
|
||||
th1520_mbox_chan_wr_data(cp, &data[0], true);
|
||||
th1520_mbox_chan_wr_data(cp, &data[0], false);
|
||||
|
||||
/* enable the chan mask */
|
||||
mask_bit = th1520_mbox_chan_id_to_mapbit(cp);
|
||||
th1520_mbox_rmw(priv, TH_1520_MBOX_MASK, BIT(mask_bit), 0);
|
||||
|
||||
/*
|
||||
* Mixing devm_ managed resources with manual IRQ handling is generally
|
||||
* discouraged due to potential complexities with resource management,
|
||||
* especially when dealing with shared interrupts. However, in this case,
|
||||
* the approach is safe and effective because:
|
||||
*
|
||||
* 1. Each mailbox channel requests its IRQ within the .startup() callback
|
||||
* and frees it within the .shutdown() callback.
|
||||
* 2. During device unbinding, the devm_ managed mailbox controller first
|
||||
* iterates through all channels, ensuring that their IRQs are freed before
|
||||
* any other devm_ resources are released.
|
||||
*
|
||||
* This ordering guarantees that no interrupts can be triggered from the device
|
||||
* while it is being unbound, preventing race conditions and ensuring system
|
||||
* stability.
|
||||
*/
|
||||
ret = request_irq(priv->irq, th1520_mbox_isr,
|
||||
IRQF_SHARED | IRQF_NO_SUSPEND, cp->irq_desc, chan);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "Unable to acquire IRQ %d\n", priv->irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void th1520_mbox_shutdown(struct mbox_chan *chan)
|
||||
{
|
||||
struct th1520_mbox_priv *priv = to_th1520_mbox_priv(chan->mbox);
|
||||
struct th1520_mbox_con_priv *cp = chan->con_priv;
|
||||
int mask_bit;
|
||||
|
||||
free_irq(priv->irq, chan);
|
||||
|
||||
/* clear the chan mask */
|
||||
mask_bit = th1520_mbox_chan_id_to_mapbit(cp);
|
||||
th1520_mbox_rmw(priv, TH_1520_MBOX_MASK, 0, BIT(mask_bit));
|
||||
}
|
||||
|
||||
static const struct mbox_chan_ops th1520_mbox_ops = {
|
||||
.send_data = th1520_mbox_send_data,
|
||||
.startup = th1520_mbox_startup,
|
||||
.shutdown = th1520_mbox_shutdown,
|
||||
};
|
||||
|
||||
static int th1520_mbox_init_generic(struct th1520_mbox_priv *priv)
|
||||
{
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
priv->ctx = devm_kzalloc(priv->dev, sizeof(*priv->ctx), GFP_KERNEL);
|
||||
if (!priv->ctx)
|
||||
return -ENOMEM;
|
||||
#endif
|
||||
/* Set default configuration */
|
||||
th1520_mbox_write(priv, 0xff, TH_1520_MBOX_CLR);
|
||||
th1520_mbox_write(priv, 0x0, TH_1520_MBOX_MASK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct mbox_chan *th1520_mbox_xlate(struct mbox_controller *mbox,
|
||||
const struct of_phandle_args *sp)
|
||||
{
|
||||
u32 chan;
|
||||
|
||||
if (sp->args_count != 1) {
|
||||
dev_err(mbox->dev, "Invalid argument count %d\n",
|
||||
sp->args_count);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
chan = sp->args[0]; /* comm remote channel */
|
||||
|
||||
if (chan >= mbox->num_chans) {
|
||||
dev_err(mbox->dev, "Not supported channel number: %d\n", chan);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (chan == TH_1520_MBOX_ICU_KERNEL_CPU0) {
|
||||
dev_err(mbox->dev, "Cannot communicate with yourself\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
return &mbox->chans[chan];
|
||||
}
|
||||
|
||||
static void __iomem *th1520_map_mmio(struct platform_device *pdev,
|
||||
char *res_name, size_t offset)
|
||||
{
|
||||
void __iomem *mapped;
|
||||
struct resource *res;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name);
|
||||
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get resource: %s\n", res_name);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
mapped = devm_ioremap(&pdev->dev, res->start + offset,
|
||||
resource_size(res) - offset);
|
||||
if (IS_ERR(mapped))
|
||||
dev_err(&pdev->dev, "Failed to map resource: %s\n", res_name);
|
||||
|
||||
return mapped;
|
||||
}
|
||||
|
||||
static void th1520_disable_clk(void *data)
|
||||
{
|
||||
struct th1520_mbox_priv *priv = data;
|
||||
|
||||
clk_bulk_disable_unprepare(ARRAY_SIZE(priv->clocks), priv->clocks);
|
||||
}
|
||||
|
||||
static int th1520_mbox_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct th1520_mbox_priv *priv;
|
||||
unsigned int remote_idx = 0;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->dev = dev;
|
||||
|
||||
priv->clocks[0].id = "clk-local";
|
||||
priv->clocks[1].id = "clk-remote-icu0";
|
||||
priv->clocks[2].id = "clk-remote-icu1";
|
||||
priv->clocks[3].id = "clk-remote-icu2";
|
||||
|
||||
ret = devm_clk_bulk_get(dev, ARRAY_SIZE(priv->clocks),
|
||||
priv->clocks);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to get clocks\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_bulk_prepare_enable(ARRAY_SIZE(priv->clocks), priv->clocks);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable clocks\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(dev, th1520_disable_clk, priv);
|
||||
if (ret) {
|
||||
clk_bulk_disable_unprepare(ARRAY_SIZE(priv->clocks), priv->clocks);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The address mappings in the device tree align precisely with those
|
||||
* outlined in the manual. However, register offsets within these
|
||||
* mapped regions are irregular, particularly for remote-icu0.
|
||||
* Consequently, th1520_map_mmio() requires an additional parameter to
|
||||
* handle this quirk.
|
||||
*/
|
||||
priv->local_icu[TH_1520_MBOX_ICU_KERNEL_CPU0] =
|
||||
th1520_map_mmio(pdev, "local", 0x0);
|
||||
if (IS_ERR(priv->local_icu[TH_1520_MBOX_ICU_KERNEL_CPU0]))
|
||||
return PTR_ERR(priv->local_icu[TH_1520_MBOX_ICU_KERNEL_CPU0]);
|
||||
|
||||
priv->remote_icu[0] = th1520_map_mmio(pdev, "remote-icu0", 0x4000);
|
||||
if (IS_ERR(priv->remote_icu[0]))
|
||||
return PTR_ERR(priv->remote_icu[0]);
|
||||
|
||||
priv->remote_icu[1] = th1520_map_mmio(pdev, "remote-icu1", 0x0);
|
||||
if (IS_ERR(priv->remote_icu[1]))
|
||||
return PTR_ERR(priv->remote_icu[1]);
|
||||
|
||||
priv->remote_icu[2] = th1520_map_mmio(pdev, "remote-icu2", 0x0);
|
||||
if (IS_ERR(priv->remote_icu[2]))
|
||||
return PTR_ERR(priv->remote_icu[2]);
|
||||
|
||||
priv->local_icu[TH_1520_MBOX_ICU_CPU1] =
|
||||
priv->local_icu[TH_1520_MBOX_ICU_KERNEL_CPU0] +
|
||||
TH_1520_MBOX_CHAN_RES_SIZE;
|
||||
priv->local_icu[TH_1520_MBOX_ICU_CPU2] =
|
||||
priv->local_icu[TH_1520_MBOX_ICU_CPU1] +
|
||||
TH_1520_MBOX_CHAN_RES_SIZE;
|
||||
priv->local_icu[TH_1520_MBOX_ICU_CPU3] =
|
||||
priv->local_icu[TH_1520_MBOX_ICU_CPU2] +
|
||||
TH_1520_MBOX_CHAN_RES_SIZE;
|
||||
|
||||
priv->cur_cpu_ch_base = priv->local_icu[TH_1520_MBOX_ICU_KERNEL_CPU0];
|
||||
|
||||
priv->irq = platform_get_irq(pdev, 0);
|
||||
if (priv->irq < 0)
|
||||
return priv->irq;
|
||||
|
||||
/* init the chans */
|
||||
for (i = 0; i < TH_1520_MBOX_CHANS; i++) {
|
||||
struct th1520_mbox_con_priv *cp = &priv->con_priv[i];
|
||||
|
||||
cp->idx = i;
|
||||
cp->chan = &priv->mbox_chans[i];
|
||||
priv->mbox_chans[i].con_priv = cp;
|
||||
snprintf(cp->irq_desc, sizeof(cp->irq_desc),
|
||||
"th1520_mbox_chan[%i]", cp->idx);
|
||||
|
||||
cp->comm_local_base = priv->local_icu[i];
|
||||
if (i != TH_1520_MBOX_ICU_KERNEL_CPU0) {
|
||||
cp->comm_remote_base = priv->remote_icu[remote_idx];
|
||||
remote_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock_init(&priv->mbox_lock);
|
||||
|
||||
priv->mbox.dev = dev;
|
||||
priv->mbox.ops = &th1520_mbox_ops;
|
||||
priv->mbox.chans = priv->mbox_chans;
|
||||
priv->mbox.num_chans = TH_1520_MBOX_CHANS;
|
||||
priv->mbox.of_xlate = th1520_mbox_xlate;
|
||||
priv->mbox.txdone_irq = true;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
ret = th1520_mbox_init_generic(priv);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to init mailbox context\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_mbox_controller_register(dev, &priv->mbox);
|
||||
}
|
||||
|
||||
static const struct of_device_id th1520_mbox_dt_ids[] = {
|
||||
{ .compatible = "thead,th1520-mbox" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, th1520_mbox_dt_ids);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int __maybe_unused th1520_mbox_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct th1520_mbox_priv *priv = dev_get_drvdata(dev);
|
||||
struct th1520_mbox_context *ctx = priv->ctx;
|
||||
u32 i;
|
||||
/*
|
||||
* ONLY interrupt mask bit should be stored and restores.
|
||||
* INFO data all assumed to be lost.
|
||||
*/
|
||||
for (i = 0; i < TH_1520_MBOX_CHANS; i++) {
|
||||
ctx->intr_mask[i] =
|
||||
ioread32(priv->local_icu[i] + TH_1520_MBOX_MASK);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused th1520_mbox_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct th1520_mbox_priv *priv = dev_get_drvdata(dev);
|
||||
struct th1520_mbox_context *ctx = priv->ctx;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < TH_1520_MBOX_CHANS; i++) {
|
||||
iowrite32(ctx->intr_mask[i],
|
||||
priv->local_icu[i] + TH_1520_MBOX_MASK);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __maybe_unused th1520_mbox_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct th1520_mbox_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
clk_bulk_disable_unprepare(ARRAY_SIZE(priv->clocks), priv->clocks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused th1520_mbox_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct th1520_mbox_priv *priv = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_bulk_prepare_enable(ARRAY_SIZE(priv->clocks), priv->clocks);
|
||||
if (ret)
|
||||
dev_err(dev, "Failed to enable clocks in runtime resume\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops th1520_mbox_pm_ops = {
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(th1520_mbox_suspend_noirq,
|
||||
th1520_mbox_resume_noirq)
|
||||
#endif
|
||||
SET_RUNTIME_PM_OPS(th1520_mbox_runtime_suspend,
|
||||
th1520_mbox_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver th1520_mbox_driver = {
|
||||
.probe = th1520_mbox_probe,
|
||||
.driver = {
|
||||
.name = "th1520-mbox",
|
||||
.of_match_table = th1520_mbox_dt_ids,
|
||||
.pm = &th1520_mbox_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(th1520_mbox_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Thead TH-1520 mailbox IPC driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -397,7 +397,7 @@ static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data)
|
||||
|
||||
task = kzalloc(sizeof(*task), GFP_ATOMIC);
|
||||
if (!task) {
|
||||
pm_runtime_put_autosuspend(cmdq->mbox.dev);
|
||||
__pm_runtime_put_autosuspend(cmdq->mbox.dev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@ -447,7 +447,7 @@ static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data)
|
||||
list_move_tail(&task->list_entry, &thread->task_busy_list);
|
||||
|
||||
pm_runtime_mark_last_busy(cmdq->mbox.dev);
|
||||
pm_runtime_put_autosuspend(cmdq->mbox.dev);
|
||||
__pm_runtime_put_autosuspend(cmdq->mbox.dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -495,7 +495,7 @@ static void cmdq_mbox_shutdown(struct mbox_chan *chan)
|
||||
spin_unlock_irqrestore(&thread->chan->lock, flags);
|
||||
|
||||
pm_runtime_mark_last_busy(cmdq->mbox.dev);
|
||||
pm_runtime_put_autosuspend(cmdq->mbox.dev);
|
||||
__pm_runtime_put_autosuspend(cmdq->mbox.dev);
|
||||
}
|
||||
|
||||
static int cmdq_mbox_flush(struct mbox_chan *chan, unsigned long timeout)
|
||||
@ -535,7 +535,7 @@ static int cmdq_mbox_flush(struct mbox_chan *chan, unsigned long timeout)
|
||||
out:
|
||||
spin_unlock_irqrestore(&thread->chan->lock, flags);
|
||||
pm_runtime_mark_last_busy(cmdq->mbox.dev);
|
||||
pm_runtime_put_autosuspend(cmdq->mbox.dev);
|
||||
__pm_runtime_put_autosuspend(cmdq->mbox.dev);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -550,7 +550,7 @@ static int cmdq_mbox_flush(struct mbox_chan *chan, unsigned long timeout)
|
||||
return -EFAULT;
|
||||
}
|
||||
pm_runtime_mark_last_busy(cmdq->mbox.dev);
|
||||
pm_runtime_put_autosuspend(cmdq->mbox.dev);
|
||||
__pm_runtime_put_autosuspend(cmdq->mbox.dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -584,7 +584,7 @@ static int cmdq_get_clocks(struct device *dev, struct cmdq *cmdq)
|
||||
struct clk_bulk_data *clks;
|
||||
|
||||
cmdq->clocks = devm_kcalloc(dev, cmdq->pdata->gce_num,
|
||||
sizeof(cmdq->clocks), GFP_KERNEL);
|
||||
sizeof(*cmdq->clocks), GFP_KERNEL);
|
||||
if (!cmdq->clocks)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -796,7 +796,7 @@ MODULE_DEVICE_TABLE(of, cmdq_of_ids);
|
||||
|
||||
static struct platform_driver cmdq_drv = {
|
||||
.probe = cmdq_probe,
|
||||
.remove_new = cmdq_remove,
|
||||
.remove = cmdq_remove,
|
||||
.driver = {
|
||||
.name = "mtk_cmdq",
|
||||
.pm = &cmdq_pm_ops,
|
||||
|
@ -269,6 +269,35 @@ static bool pcc_mbox_cmd_complete_check(struct pcc_chan_info *pchan)
|
||||
return !!val;
|
||||
}
|
||||
|
||||
static void check_and_ack(struct pcc_chan_info *pchan, struct mbox_chan *chan)
|
||||
{
|
||||
struct acpi_pcct_ext_pcc_shared_memory pcc_hdr;
|
||||
|
||||
if (pchan->type != ACPI_PCCT_TYPE_EXT_PCC_SLAVE_SUBSPACE)
|
||||
return;
|
||||
/* If the memory region has not been mapped, we cannot
|
||||
* determine if we need to send the message, but we still
|
||||
* need to set the cmd_update flag before returning.
|
||||
*/
|
||||
if (pchan->chan.shmem == NULL) {
|
||||
pcc_chan_reg_read_modify_write(&pchan->cmd_update);
|
||||
return;
|
||||
}
|
||||
memcpy_fromio(&pcc_hdr, pchan->chan.shmem,
|
||||
sizeof(struct acpi_pcct_ext_pcc_shared_memory));
|
||||
/*
|
||||
* The PCC slave subspace channel needs to set the command complete bit
|
||||
* after processing message. If the PCC_ACK_FLAG is set, it should also
|
||||
* ring the doorbell.
|
||||
*
|
||||
* The PCC master subspace channel clears chan_in_use to free channel.
|
||||
*/
|
||||
if (le32_to_cpup(&pcc_hdr.flags) & PCC_ACK_FLAG_MASK)
|
||||
pcc_send_data(chan, NULL);
|
||||
else
|
||||
pcc_chan_reg_read_modify_write(&pchan->cmd_update);
|
||||
}
|
||||
|
||||
/**
|
||||
* pcc_mbox_irq - PCC mailbox interrupt handler
|
||||
* @irq: interrupt number
|
||||
@ -306,14 +335,7 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)
|
||||
|
||||
mbox_chan_received_data(chan, NULL);
|
||||
|
||||
/*
|
||||
* The PCC slave subspace channel needs to set the command complete bit
|
||||
* and ring doorbell after processing message.
|
||||
*
|
||||
* The PCC master subspace channel clears chan_in_use to free channel.
|
||||
*/
|
||||
if (pchan->type == ACPI_PCCT_TYPE_EXT_PCC_SLAVE_SUBSPACE)
|
||||
pcc_send_data(chan, NULL);
|
||||
check_and_ack(pchan, chan);
|
||||
pchan->chan_in_use = false;
|
||||
|
||||
return IRQ_HANDLED;
|
||||
@ -365,14 +387,37 @@ EXPORT_SYMBOL_GPL(pcc_mbox_request_channel);
|
||||
void pcc_mbox_free_channel(struct pcc_mbox_chan *pchan)
|
||||
{
|
||||
struct mbox_chan *chan = pchan->mchan;
|
||||
struct pcc_chan_info *pchan_info;
|
||||
struct pcc_mbox_chan *pcc_mbox_chan;
|
||||
|
||||
if (!chan || !chan->cl)
|
||||
return;
|
||||
pchan_info = chan->con_priv;
|
||||
pcc_mbox_chan = &pchan_info->chan;
|
||||
if (pcc_mbox_chan->shmem) {
|
||||
iounmap(pcc_mbox_chan->shmem);
|
||||
pcc_mbox_chan->shmem = NULL;
|
||||
}
|
||||
|
||||
mbox_free_channel(chan);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcc_mbox_free_channel);
|
||||
|
||||
int pcc_mbox_ioremap(struct mbox_chan *chan)
|
||||
{
|
||||
struct pcc_chan_info *pchan_info;
|
||||
struct pcc_mbox_chan *pcc_mbox_chan;
|
||||
|
||||
if (!chan || !chan->cl)
|
||||
return -1;
|
||||
pchan_info = chan->con_priv;
|
||||
pcc_mbox_chan = &pchan_info->chan;
|
||||
pcc_mbox_chan->shmem = ioremap(pcc_mbox_chan->shmem_base_addr,
|
||||
pcc_mbox_chan->shmem_size);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcc_mbox_ioremap);
|
||||
|
||||
/**
|
||||
* pcc_send_data - Called from Mailbox Controller code. Used
|
||||
* here only to ring the channel doorbell. The PCC client
|
||||
|
@ -167,7 +167,7 @@ MODULE_DEVICE_TABLE(of, qcom_apcs_ipc_of_match);
|
||||
|
||||
static struct platform_driver qcom_apcs_ipc_driver = {
|
||||
.probe = qcom_apcs_ipc_probe,
|
||||
.remove_new = qcom_apcs_ipc_remove,
|
||||
.remove = qcom_apcs_ipc_remove,
|
||||
.driver = {
|
||||
.name = "qcom_apcs_ipc",
|
||||
.of_match_table = qcom_apcs_ipc_of_match,
|
||||
|
@ -346,7 +346,7 @@ static const struct dev_pm_ops qcom_ipcc_dev_pm_ops = {
|
||||
|
||||
static struct platform_driver qcom_ipcc_driver = {
|
||||
.probe = qcom_ipcc_probe,
|
||||
.remove_new = qcom_ipcc_remove,
|
||||
.remove = qcom_ipcc_remove,
|
||||
.driver = {
|
||||
.name = "qcom-ipcc",
|
||||
.of_match_table = qcom_ipcc_of_match,
|
||||
|
@ -379,7 +379,7 @@ static struct platform_driver stm32_ipcc_driver = {
|
||||
.of_match_table = stm32_ipcc_of_match,
|
||||
},
|
||||
.probe = stm32_ipcc_probe,
|
||||
.remove_new = stm32_ipcc_remove,
|
||||
.remove = stm32_ipcc_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(stm32_ipcc_driver);
|
||||
|
@ -307,8 +307,8 @@ static struct platform_driver sun6i_msgbox_driver = {
|
||||
.name = "sun6i-msgbox",
|
||||
.of_match_table = sun6i_msgbox_of_match,
|
||||
},
|
||||
.probe = sun6i_msgbox_probe,
|
||||
.remove_new = sun6i_msgbox_remove,
|
||||
.probe = sun6i_msgbox_probe,
|
||||
.remove = sun6i_msgbox_remove,
|
||||
};
|
||||
module_platform_driver(sun6i_msgbox_driver);
|
||||
|
||||
|
@ -951,7 +951,7 @@ static struct platform_driver tegra_hsp_driver = {
|
||||
.pm = &tegra_hsp_pm_ops,
|
||||
},
|
||||
.probe = tegra_hsp_probe,
|
||||
.remove_new = tegra_hsp_remove,
|
||||
.remove = tegra_hsp_remove,
|
||||
};
|
||||
|
||||
static int __init tegra_hsp_init(void)
|
||||
|
@ -920,7 +920,7 @@ static struct platform_driver ti_msgmgr_driver = {
|
||||
.probe = ti_msgmgr_probe,
|
||||
.driver = {
|
||||
.name = "ti-msgmgr",
|
||||
.of_match_table = of_match_ptr(ti_msgmgr_of_match),
|
||||
.of_match_table = ti_msgmgr_of_match,
|
||||
.pm = &ti_msgmgr_pm_ops,
|
||||
},
|
||||
};
|
||||
|
@ -940,10 +940,10 @@ static int zynqmp_ipi_probe(struct platform_device *pdev)
|
||||
pdata->num_mboxes = num_mboxes;
|
||||
|
||||
mbox = pdata->ipi_mboxes;
|
||||
mbox->setup_ipi_fn = ipi_fn;
|
||||
|
||||
for_each_available_child_of_node(np, nc) {
|
||||
mbox->pdata = pdata;
|
||||
mbox->setup_ipi_fn = ipi_fn;
|
||||
|
||||
ret = zynqmp_ipi_mbox_probe(mbox, nc);
|
||||
if (ret) {
|
||||
of_node_put(nc);
|
||||
@ -1015,7 +1015,7 @@ MODULE_DEVICE_TABLE(of, zynqmp_ipi_of_match);
|
||||
|
||||
static struct platform_driver zynqmp_ipi_driver = {
|
||||
.probe = zynqmp_ipi_probe,
|
||||
.remove_new = zynqmp_ipi_remove,
|
||||
.remove = zynqmp_ipi_remove,
|
||||
.driver = {
|
||||
.name = "zynqmp-ipi",
|
||||
.of_match_table = of_match_ptr(zynqmp_ipi_of_match),
|
||||
|
@ -12,6 +12,7 @@
|
||||
struct pcc_mbox_chan {
|
||||
struct mbox_chan *mchan;
|
||||
u64 shmem_base_addr;
|
||||
void __iomem *shmem;
|
||||
u64 shmem_size;
|
||||
u32 latency;
|
||||
u32 max_access_rate;
|
||||
@ -31,11 +32,13 @@ struct pcc_mbox_chan {
|
||||
#define PCC_CMD_COMPLETION_NOTIFY BIT(0)
|
||||
|
||||
#define MAX_PCC_SUBSPACES 256
|
||||
#define PCC_ACK_FLAG_MASK 0x1
|
||||
|
||||
#ifdef CONFIG_PCC
|
||||
extern struct pcc_mbox_chan *
|
||||
pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id);
|
||||
extern void pcc_mbox_free_channel(struct pcc_mbox_chan *chan);
|
||||
extern int pcc_mbox_ioremap(struct mbox_chan *chan);
|
||||
#else
|
||||
static inline struct pcc_mbox_chan *
|
||||
pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)
|
||||
@ -43,6 +46,10 @@ pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
static inline void pcc_mbox_free_channel(struct pcc_mbox_chan *chan) { }
|
||||
static inline int pcc_mbox_ioremap(struct mbox_chan *chan)
|
||||
{
|
||||
return 0;
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* _PCC_H */
|
||||
|
Loading…
Reference in New Issue
Block a user