Commit d51f8f63 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'mailbox-v6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/jassibrar/mailbox

Pull mailbox updates from Jassi Brar:
 "broadcom:
   - remove unused pdc_dma_map

  imx:
   - fix TXDB_V2 channel race condition

  mediatek:
   - cleanup and refactor driver
   - add bindings for gce-props

  omap:
   - fix mailbox interrupt sharing

  qcom:
   - add bindings for SA8775p
   - add CPUCP driver

  zynqmp:
   - make polling period configurable"

* tag 'mailbox-v6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/jassibrar/mailbox:
  mailbox: mtk-cmdq: Move devm_mbox_controller_register() after devm_pm_runtime_enable()
  mailbox: zynqmp-ipi: Make polling period configurable
  mailbox: qcom-cpucp: fix 64BIT dependency
  mailbox: Add support for QTI CPUCP mailbox controller
  dt-bindings: mailbox: qcom: Add CPUCP mailbox controller bindings
  dt-bindings: remoteproc: qcom,sa8775p-pas: Document the SA8775p ADSP, CDSP and GPDSP
  mailbox: mtk-cmdq: add missing MODULE_DESCRIPTION() macro
  mailbox: bcm-pdc: remove unused struct 'pdc_dma_map'
  mailbox: imx: fix TXDB_V2 channel race condition
  mailbox: omap: Fix mailbox interrupt sharing
  mailbox: mtk-cmdq: Dynamically allocate clk_bulk_data structure
  mailbox: mtk-cmdq: Move and partially refactor clocks probe
  mailbox: mtk-cmdq: Stop requiring name for GCE clock
  dt-bindings: mailbox: Add mediatek,gce-props.yaml
parents 71bed1ec a8bd68e4
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mailbox/mediatek,gce-props.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MediaTek Global Command Engine Common Properties
maintainers:
- Houlong Wei <houlong.wei@mediatek.com>
description:
The Global Command Engine (GCE) is an instruction based, multi-threaded,
single-core command dispatcher for MediaTek hardware. The Command Queue
(CMDQ) mailbox driver is a driver for GCE, implemented using the Linux
mailbox framework. It is used to receive messages from mailbox consumers
and configure GCE to execute the specified instruction set in the message.
We use mediatek,gce-mailbox.yaml to define the properties for CMDQ mailbox
driver. A device driver that uses the CMDQ driver to configure its hardware
registers is a mailbox consumer. The mailbox consumer can request a mailbox
channel corresponding to a GCE hardware thread to send a message, specifying
that the GCE thread to configure its hardware. The mailbox provider can also
reserve a mailbox channel to configure GCE hardware register by the specific
GCE thread. This binding defines the common GCE properties for both mailbox
provider and consumers.
properties:
mediatek,gce-events:
description:
GCE has an event table in SRAM, consisting of 1024 event IDs (0~1023).
Each event ID has a boolean event value with the default value 0.
The property mediatek,gce-events is used to obtain the event IDs.
Some gce-events are hardware-bound and cannot be changed by software.
For instance, in MT8195, when VDO0_MUTEX is stream done, VDO_MUTEX will
send an event signal to GCE, setting the value of event ID 597 to 1.
Similarly, in MT8188, the value of event ID 574 will be set to 1 when
VOD0_MUTEX is stream done.
On the other hand, some gce-events are not hardware-bound and can be
changed by software. For example, in MT8188, we can set the value of
event ID 855, which is not bound to any hardware, to 1 when the driver
in the secure world completes a task. However, in MT8195, event ID 855
is already bound to VDEC_LAT1, so we need to select another event ID to
achieve the same purpose. This event ID can be any ID that is not bound
to any hardware and is not yet used in any software driver.
To determine if the event ID is bound to the hardware or used by a
software driver, refer to the GCE header
include/dt-bindings/gce/<chip>-gce.h of each chip.
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 1
maxItems: 32
additionalProperties: true
# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/mailbox/qcom,cpucp-mbox.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Technologies, Inc. CPUCP Mailbox Controller
maintainers:
- Sibi Sankar <quic_sibis@quicinc.com>
description:
The CPUSS Control Processor (CPUCP) mailbox controller enables communication
between AP and CPUCP by acting as a doorbell between them.
properties:
compatible:
items:
- const: qcom,x1e80100-cpucp-mbox
reg:
items:
- description: CPUCP rx register region
- description: CPUCP tx register region
interrupts:
maxItems: 1
"#mbox-cells":
const: 1
required:
- compatible
- reg
- interrupts
- "#mbox-cells"
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
mailbox@17430000 {
compatible = "qcom,x1e80100-cpucp-mbox";
reg = <0x17430000 0x10000>, <0x18830000 0x10000>;
interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
#mbox-cells = <1>;
};
# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/remoteproc/qcom,sa8775p-pas.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm SA8775p Peripheral Authentication Service
maintainers:
- Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
description:
Qualcomm SA8775p SoC Peripheral Authentication Service loads and boots firmware
on the Qualcomm DSP Hexagon cores.
properties:
compatible:
enum:
- qcom,sa8775p-adsp-pas
- qcom,sa8775p-cdsp0-pas
- qcom,sa8775p-cdsp1-pas
- qcom,sa8775p-gpdsp0-pas
- qcom,sa8775p-gpdsp1-pas
reg:
maxItems: 1
clocks:
items:
- description: XO clock
clock-names:
items:
- const: xo
qcom,qmp:
$ref: /schemas/types.yaml#/definitions/phandle
description: Reference to the AOSS side-channel message RAM.
firmware-name:
$ref: /schemas/types.yaml#/definitions/string-array
items:
- description: Firmware name of the Hexagon core
memory-region:
items:
- description: Memory region for main Firmware authentication
interrupts:
maxItems: 5
interrupt-names:
maxItems: 5
required:
- compatible
- reg
- memory-region
allOf:
- $ref: /schemas/remoteproc/qcom,pas-common.yaml#
- if:
properties:
compatible:
enum:
- qcom,sa8775p-adsp-pas
then:
properties:
power-domains:
items:
- description: LCX power domain
- description: LMX power domain
power-domain-names:
items:
- const: lcx
- const: lmx
- if:
properties:
compatible:
enum:
- qcom,sa8775p-cdsp0-pas
- qcom,sa8775p-cdsp1-pas
then:
properties:
power-domains:
items:
- description: CX power domain
- description: MXC power domain
- description: NSP0 power domain
power-domain-names:
items:
- const: cx
- const: mxc
- const: nsp
- if:
properties:
compatible:
enum:
- qcom,sa8775p-gpdsp0-pas
- qcom,sa8775p-gpdsp1-pas
then:
properties:
power-domains:
items:
- description: CX power domain
- description: MXC power domain
power-domain-names:
items:
- const: cx
- const: mxc
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/qcom,rpmh.h>
#include <dt-bindings/interconnect/qcom,sa8775p-rpmh.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/mailbox/qcom-ipcc.h>
#include <dt-bindings/power/qcom,rpmhpd.h>
remoteproc@30000000 {
compatible = "qcom,sa8775p-adsp-pas";
reg = <0x30000000 0x100>;
interrupts-extended = <&pdc 6 IRQ_TYPE_EDGE_RISING>,
<&smp2p_adsp_in 0 IRQ_TYPE_EDGE_RISING>,
<&smp2p_adsp_in 2 IRQ_TYPE_EDGE_RISING>,
<&smp2p_adsp_in 1 IRQ_TYPE_EDGE_RISING>,
<&smp2p_adsp_in 3 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "wdog", "fatal", "ready", "handover", "stop-ack";
clocks = <&rpmhcc RPMH_CXO_CLK>;
clock-names = "xo";
power-domains = <&rpmhpd RPMHPD_LCX>, <&rpmhpd RPMHPD_LMX>;
power-domain-names = "lcx", "lmx";
interconnects = <&lpass_ag_noc MASTER_LPASS_PROC 0 &mc_virt SLAVE_EBI1 0>;
memory-region = <&pil_adsp_mem>;
qcom,qmp = <&aoss_qmp>;
qcom,smem-states = <&smp2p_adsp_out 0>;
qcom,smem-state-names = "stop";
glink-edge {
interrupts-extended = <&ipcc IPCC_CLIENT_LPASS
IPCC_MPROC_SIGNAL_GLINK_QMP
IRQ_TYPE_EDGE_RISING>;
mboxes = <&ipcc IPCC_CLIENT_LPASS IPCC_MPROC_SIGNAL_GLINK_QMP>;
label = "lpass";
qcom,remote-pid = <2>;
};
};
...@@ -18749,6 +18749,13 @@ S: Maintained ...@@ -18749,6 +18749,13 @@ S: Maintained
F: Documentation/devicetree/bindings/power/avs/qcom,cpr.yaml F: Documentation/devicetree/bindings/power/avs/qcom,cpr.yaml
F: drivers/pmdomain/qcom/cpr.c F: drivers/pmdomain/qcom/cpr.c
QUALCOMM CPUCP MAILBOX DRIVER
M: Sibi Sankar <quic_sibis@quicinc.com>
L: linux-arm-msm@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml
F: drivers/mailbox/qcom-cpucp-mbox.c
QUALCOMM CPUFREQ DRIVER MSM8996/APQ8096 QUALCOMM CPUFREQ DRIVER MSM8996/APQ8096
M: Ilia Lin <ilia.lin@kernel.org> M: Ilia Lin <ilia.lin@kernel.org>
L: linux-pm@vger.kernel.org L: linux-pm@vger.kernel.org
......
...@@ -276,6 +276,14 @@ config SPRD_MBOX ...@@ -276,6 +276,14 @@ config SPRD_MBOX
to send message between application processors and MCU. Say Y here if to send message between application processors and MCU. Say Y here if
you want to build the Spreatrum mailbox controller driver. you want to build the Spreatrum mailbox controller driver.
config QCOM_CPUCP_MBOX
tristate "Qualcomm Technologies, Inc. CPUCP mailbox driver"
depends on (ARCH_QCOM || COMPILE_TEST) && 64BIT
help
Qualcomm Technologies, Inc. CPUSS Control Processor (CPUCP) mailbox
controller driver enables communication between AP and CPUCP. Say
Y here if you want to build this driver.
config QCOM_IPCC config QCOM_IPCC
tristate "Qualcomm Technologies, Inc. IPCC driver" tristate "Qualcomm Technologies, Inc. IPCC driver"
depends on ARCH_QCOM || COMPILE_TEST depends on ARCH_QCOM || COMPILE_TEST
......
...@@ -61,4 +61,6 @@ obj-$(CONFIG_SUN6I_MSGBOX) += sun6i-msgbox.o ...@@ -61,4 +61,6 @@ obj-$(CONFIG_SUN6I_MSGBOX) += sun6i-msgbox.o
obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox.o 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_QCOM_IPCC) += qcom-ipcc.o
...@@ -158,10 +158,6 @@ enum pdc_hw { ...@@ -158,10 +158,6 @@ enum pdc_hw {
PDC_HW /* PDC/MDE hardware (i.e. Northstar 2, Pegasus) */ PDC_HW /* PDC/MDE hardware (i.e. Northstar 2, Pegasus) */
}; };
struct pdc_dma_map {
void *ctx; /* opaque context associated with frame */
};
/* dma descriptor */ /* dma descriptor */
struct dma64dd { struct dma64dd {
u32 ctrl1; /* misc control bits */ u32 ctrl1; /* misc control bits */
......
...@@ -225,6 +225,8 @@ static int imx_mu_generic_tx(struct imx_mu_priv *priv, ...@@ -225,6 +225,8 @@ static int imx_mu_generic_tx(struct imx_mu_priv *priv,
void *data) void *data)
{ {
u32 *arg = data; u32 *arg = data;
u32 val;
int ret;
switch (cp->type) { switch (cp->type) {
case IMX_MU_TYPE_TX: case IMX_MU_TYPE_TX:
...@@ -236,7 +238,13 @@ static int imx_mu_generic_tx(struct imx_mu_priv *priv, ...@@ -236,7 +238,13 @@ static int imx_mu_generic_tx(struct imx_mu_priv *priv,
queue_work(system_bh_wq, &cp->txdb_work); queue_work(system_bh_wq, &cp->txdb_work);
break; break;
case IMX_MU_TYPE_TXDB_V2: case IMX_MU_TYPE_TXDB_V2:
imx_mu_xcr_rmw(priv, IMX_MU_GCR, IMX_MU_xCR_GIRn(priv->dcfg->type, cp->idx), 0); imx_mu_write(priv, IMX_MU_xCR_GIRn(priv->dcfg->type, cp->idx),
priv->dcfg->xCR[IMX_MU_GCR]);
ret = readl_poll_timeout(priv->base + priv->dcfg->xCR[IMX_MU_GCR], val,
!(val & IMX_MU_xCR_GIRn(priv->dcfg->type, cp->idx)),
0, 1000);
if (ret)
dev_warn_ratelimited(priv->dev, "channel type: %d failure\n", cp->type);
break; break;
default: default:
dev_warn_ratelimited(priv->dev, "Send data on wrong channel type: %d\n", cp->type); dev_warn_ratelimited(priv->dev, "Send data on wrong channel type: %d\n", cp->type);
......
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
#define CMDQ_OP_CODE_MASK (0xff << CMDQ_OP_CODE_SHIFT) #define CMDQ_OP_CODE_MASK (0xff << CMDQ_OP_CODE_SHIFT)
#define CMDQ_NUM_CMD(t) (t->cmd_buf_size / CMDQ_INST_SIZE) #define CMDQ_NUM_CMD(t) (t->cmd_buf_size / CMDQ_INST_SIZE)
#define CMDQ_GCE_NUM_MAX (2)
#define CMDQ_CURR_IRQ_STATUS 0x10 #define CMDQ_CURR_IRQ_STATUS 0x10
#define CMDQ_SYNC_TOKEN_UPDATE 0x68 #define CMDQ_SYNC_TOKEN_UPDATE 0x68
...@@ -81,7 +80,7 @@ struct cmdq { ...@@ -81,7 +80,7 @@ struct cmdq {
u32 irq_mask; u32 irq_mask;
const struct gce_plat *pdata; const struct gce_plat *pdata;
struct cmdq_thread *thread; struct cmdq_thread *thread;
struct clk_bulk_data clocks[CMDQ_GCE_NUM_MAX]; struct clk_bulk_data *clocks;
bool suspended; bool suspended;
}; };
...@@ -578,16 +577,64 @@ static struct mbox_chan *cmdq_xlate(struct mbox_controller *mbox, ...@@ -578,16 +577,64 @@ static struct mbox_chan *cmdq_xlate(struct mbox_controller *mbox,
return &mbox->chans[ind]; return &mbox->chans[ind];
} }
static int cmdq_get_clocks(struct device *dev, struct cmdq *cmdq)
{
static const char * const gce_name = "gce";
struct device_node *node, *parent = dev->of_node->parent;
struct clk_bulk_data *clks;
cmdq->clocks = devm_kcalloc(dev, cmdq->pdata->gce_num,
sizeof(cmdq->clocks), GFP_KERNEL);
if (!cmdq->clocks)
return -ENOMEM;
if (cmdq->pdata->gce_num == 1) {
clks = &cmdq->clocks[0];
clks->id = gce_name;
clks->clk = devm_clk_get(dev, NULL);
if (IS_ERR(clks->clk))
return dev_err_probe(dev, PTR_ERR(clks->clk),
"failed to get gce clock\n");
return 0;
}
/*
* If there is more than one GCE, get the clocks for the others too,
* as the clock of the main GCE must be enabled for additional IPs
* to be reachable.
*/
for_each_child_of_node(parent, node) {
int alias_id = of_alias_get_id(node, gce_name);
if (alias_id < 0 || alias_id >= cmdq->pdata->gce_num)
continue;
clks = &cmdq->clocks[alias_id];
clks->id = devm_kasprintf(dev, GFP_KERNEL, "gce%d", alias_id);
if (!clks->id) {
of_node_put(node);
return -ENOMEM;
}
clks->clk = of_clk_get(node, 0);
if (IS_ERR(clks->clk)) {
of_node_put(node);
return dev_err_probe(dev, PTR_ERR(clks->clk),
"failed to get gce%d clock\n", alias_id);
}
}
return 0;
}
static int cmdq_probe(struct platform_device *pdev) static int cmdq_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct cmdq *cmdq; struct cmdq *cmdq;
int err, i; int err, i;
struct device_node *phandle = dev->of_node;
struct device_node *node;
int alias_id = 0;
static const char * const clk_name = "gce";
static const char * const clk_names[] = { "gce0", "gce1" };
cmdq = devm_kzalloc(dev, sizeof(*cmdq), GFP_KERNEL); cmdq = devm_kzalloc(dev, sizeof(*cmdq), GFP_KERNEL);
if (!cmdq) if (!cmdq)
...@@ -612,29 +659,9 @@ static int cmdq_probe(struct platform_device *pdev) ...@@ -612,29 +659,9 @@ static int cmdq_probe(struct platform_device *pdev)
dev_dbg(dev, "cmdq device: addr:0x%p, va:0x%p, irq:%d\n", dev_dbg(dev, "cmdq device: addr:0x%p, va:0x%p, irq:%d\n",
dev, cmdq->base, cmdq->irq); dev, cmdq->base, cmdq->irq);
if (cmdq->pdata->gce_num > 1) { err = cmdq_get_clocks(dev, cmdq);
for_each_child_of_node(phandle->parent, node) { if (err)
alias_id = of_alias_get_id(node, clk_name); return err;
if (alias_id >= 0 && alias_id < cmdq->pdata->gce_num) {
cmdq->clocks[alias_id].id = clk_names[alias_id];
cmdq->clocks[alias_id].clk = of_clk_get(node, 0);
if (IS_ERR(cmdq->clocks[alias_id].clk)) {
of_node_put(node);
return dev_err_probe(dev,
PTR_ERR(cmdq->clocks[alias_id].clk),
"failed to get gce clk: %d\n",
alias_id);
}
}
}
} else {
cmdq->clocks[alias_id].id = clk_name;
cmdq->clocks[alias_id].clk = devm_clk_get(&pdev->dev, clk_name);
if (IS_ERR(cmdq->clocks[alias_id].clk)) {
return dev_err_probe(dev, PTR_ERR(cmdq->clocks[alias_id].clk),
"failed to get gce clk\n");
}
}
cmdq->mbox.dev = dev; cmdq->mbox.dev = dev;
cmdq->mbox.chans = devm_kcalloc(dev, cmdq->pdata->thread_nr, cmdq->mbox.chans = devm_kcalloc(dev, cmdq->pdata->thread_nr,
...@@ -662,12 +689,6 @@ static int cmdq_probe(struct platform_device *pdev) ...@@ -662,12 +689,6 @@ static int cmdq_probe(struct platform_device *pdev)
cmdq->mbox.chans[i].con_priv = (void *)&cmdq->thread[i]; cmdq->mbox.chans[i].con_priv = (void *)&cmdq->thread[i];
} }
err = devm_mbox_controller_register(dev, &cmdq->mbox);
if (err < 0) {
dev_err(dev, "failed to register mailbox: %d\n", err);
return err;
}
platform_set_drvdata(pdev, cmdq); platform_set_drvdata(pdev, cmdq);
WARN_ON(clk_bulk_prepare(cmdq->pdata->gce_num, cmdq->clocks)); WARN_ON(clk_bulk_prepare(cmdq->pdata->gce_num, cmdq->clocks));
...@@ -695,6 +716,12 @@ static int cmdq_probe(struct platform_device *pdev) ...@@ -695,6 +716,12 @@ static int cmdq_probe(struct platform_device *pdev)
pm_runtime_set_autosuspend_delay(dev, CMDQ_MBOX_AUTOSUSPEND_DELAY_MS); pm_runtime_set_autosuspend_delay(dev, CMDQ_MBOX_AUTOSUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(dev); pm_runtime_use_autosuspend(dev);
err = devm_mbox_controller_register(dev, &cmdq->mbox);
if (err < 0) {
dev_err(dev, "failed to register mailbox: %d\n", err);
return err;
}
return 0; return 0;
} }
...@@ -790,4 +817,5 @@ static void __exit cmdq_drv_exit(void) ...@@ -790,4 +817,5 @@ static void __exit cmdq_drv_exit(void)
subsys_initcall(cmdq_drv_init); subsys_initcall(cmdq_drv_init);
module_exit(cmdq_drv_exit); module_exit(cmdq_drv_exit);
MODULE_DESCRIPTION("Mediatek Command Queue(CMDQ) Mailbox driver");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
...@@ -230,7 +230,8 @@ static int omap_mbox_startup(struct omap_mbox *mbox) ...@@ -230,7 +230,8 @@ static int omap_mbox_startup(struct omap_mbox *mbox)
int ret = 0; int ret = 0;
ret = request_threaded_irq(mbox->irq, NULL, mbox_interrupt, ret = request_threaded_irq(mbox->irq, NULL, mbox_interrupt,
IRQF_ONESHOT, mbox->name, mbox); IRQF_SHARED | IRQF_ONESHOT, mbox->name,
mbox);
if (unlikely(ret)) { if (unlikely(ret)) {
pr_err("failed to register mailbox interrupt:%d\n", ret); pr_err("failed to register mailbox interrupt:%d\n", ret);
return ret; return ret;
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/mailbox_controller.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#define APSS_CPUCP_IPC_CHAN_SUPPORTED 3
#define APSS_CPUCP_MBOX_CMD_OFF 0x4
/* Tx Registers */
#define APSS_CPUCP_TX_MBOX_CMD(i) (0x100 + ((i) * 8))
/* Rx Registers */
#define APSS_CPUCP_RX_MBOX_CMD(i) (0x100 + ((i) * 8))
#define APSS_CPUCP_RX_MBOX_MAP 0x4000
#define APSS_CPUCP_RX_MBOX_STAT 0x4400
#define APSS_CPUCP_RX_MBOX_CLEAR 0x4800
#define APSS_CPUCP_RX_MBOX_EN 0x4c00
#define APSS_CPUCP_RX_MBOX_CMD_MASK GENMASK_ULL(63, 0)
/**
* struct qcom_cpucp_mbox - Holder for the mailbox driver
* @chans: The mailbox channel
* @mbox: The mailbox controller
* @tx_base: Base address of the CPUCP tx registers
* @rx_base: Base address of the CPUCP rx registers
*/
struct qcom_cpucp_mbox {
struct mbox_chan chans[APSS_CPUCP_IPC_CHAN_SUPPORTED];
struct mbox_controller mbox;
void __iomem *tx_base;
void __iomem *rx_base;
};
static inline int channel_number(struct mbox_chan *chan)
{
return chan - chan->mbox->chans;
}
static irqreturn_t qcom_cpucp_mbox_irq_fn(int irq, void *data)
{
struct qcom_cpucp_mbox *cpucp = data;
u64 status;
int i;
status = readq(cpucp->rx_base + APSS_CPUCP_RX_MBOX_STAT);
for_each_set_bit(i, (unsigned long *)&status, APSS_CPUCP_IPC_CHAN_SUPPORTED) {
u32 val = readl(cpucp->rx_base + APSS_CPUCP_RX_MBOX_CMD(i) + APSS_CPUCP_MBOX_CMD_OFF);
struct mbox_chan *chan = &cpucp->chans[i];
unsigned long flags;
/* Provide mutual exclusion with changes to chan->cl */
spin_lock_irqsave(&chan->lock, flags);
if (chan->cl)
mbox_chan_received_data(chan, &val);
writeq(BIT(i), cpucp->rx_base + APSS_CPUCP_RX_MBOX_CLEAR);
spin_unlock_irqrestore(&chan->lock, flags);
}
return IRQ_HANDLED;
}
static int qcom_cpucp_mbox_startup(struct mbox_chan *chan)
{
struct qcom_cpucp_mbox *cpucp = container_of(chan->mbox, struct qcom_cpucp_mbox, mbox);
unsigned long chan_id = channel_number(chan);
u64 val;
val = readq(cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN);
val |= BIT(chan_id);
writeq(val, cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN);
return 0;
}
static void qcom_cpucp_mbox_shutdown(struct mbox_chan *chan)
{
struct qcom_cpucp_mbox *cpucp = container_of(chan->mbox, struct qcom_cpucp_mbox, mbox);
unsigned long chan_id = channel_number(chan);
u64 val;
val = readq(cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN);
val &= ~BIT(chan_id);
writeq(val, cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN);
}
static int qcom_cpucp_mbox_send_data(struct mbox_chan *chan, void *data)
{
struct qcom_cpucp_mbox *cpucp = container_of(chan->mbox, struct qcom_cpucp_mbox, mbox);
unsigned long chan_id = channel_number(chan);
u32 *val = data;
writel(*val, cpucp->tx_base + APSS_CPUCP_TX_MBOX_CMD(chan_id) + APSS_CPUCP_MBOX_CMD_OFF);
return 0;
}
static const struct mbox_chan_ops qcom_cpucp_mbox_chan_ops = {
.startup = qcom_cpucp_mbox_startup,
.send_data = qcom_cpucp_mbox_send_data,
.shutdown = qcom_cpucp_mbox_shutdown
};
static int qcom_cpucp_mbox_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct qcom_cpucp_mbox *cpucp;
struct mbox_controller *mbox;
int irq, ret;
cpucp = devm_kzalloc(dev, sizeof(*cpucp), GFP_KERNEL);
if (!cpucp)
return -ENOMEM;
cpucp->rx_base = devm_of_iomap(dev, dev->of_node, 0, NULL);
if (IS_ERR(cpucp->rx_base))
return PTR_ERR(cpucp->rx_base);
cpucp->tx_base = devm_of_iomap(dev, dev->of_node, 1, NULL);
if (IS_ERR(cpucp->tx_base))
return PTR_ERR(cpucp->tx_base);
writeq(0, cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN);
writeq(0, cpucp->rx_base + APSS_CPUCP_RX_MBOX_CLEAR);
writeq(0, cpucp->rx_base + APSS_CPUCP_RX_MBOX_MAP);
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
ret = devm_request_irq(dev, irq, qcom_cpucp_mbox_irq_fn,
IRQF_TRIGGER_HIGH, "apss_cpucp_mbox", cpucp);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to register irq: %d\n", irq);
writeq(APSS_CPUCP_RX_MBOX_CMD_MASK, cpucp->rx_base + APSS_CPUCP_RX_MBOX_MAP);
mbox = &cpucp->mbox;
mbox->dev = dev;
mbox->num_chans = APSS_CPUCP_IPC_CHAN_SUPPORTED;
mbox->chans = cpucp->chans;
mbox->ops = &qcom_cpucp_mbox_chan_ops;
ret = devm_mbox_controller_register(dev, mbox);
if (ret)
return dev_err_probe(dev, ret, "Failed to create mailbox\n");
return 0;
}
static const struct of_device_id qcom_cpucp_mbox_of_match[] = {
{ .compatible = "qcom,x1e80100-cpucp-mbox" },
{}
};
MODULE_DEVICE_TABLE(of, qcom_cpucp_mbox_of_match);
static struct platform_driver qcom_cpucp_mbox_driver = {
.probe = qcom_cpucp_mbox_probe,
.driver = {
.name = "qcom_cpucp_mbox",
.of_match_table = qcom_cpucp_mbox_of_match,
},
};
static int __init qcom_cpucp_mbox_init(void)
{
return platform_driver_register(&qcom_cpucp_mbox_driver);
}
core_initcall(qcom_cpucp_mbox_init);
static void __exit qcom_cpucp_mbox_exit(void)
{
platform_driver_unregister(&qcom_cpucp_mbox_driver);
}
module_exit(qcom_cpucp_mbox_exit);
MODULE_DESCRIPTION("QTI CPUCP MBOX Driver");
MODULE_LICENSE("GPL");
...@@ -64,6 +64,13 @@ ...@@ -64,6 +64,13 @@
#define MAX_SGI 16 #define MAX_SGI 16
/*
* Module parameters
*/
static int tx_poll_period = 5;
module_param_named(tx_poll_period, tx_poll_period, int, 0644);
MODULE_PARM_DESC(tx_poll_period, "Poll period waiting for ack after send.");
/** /**
* struct zynqmp_ipi_mchan - Description of a Xilinx ZynqMP IPI mailbox channel * struct zynqmp_ipi_mchan - Description of a Xilinx ZynqMP IPI mailbox channel
* @is_opened: indicate if the IPI channel is opened * @is_opened: indicate if the IPI channel is opened
...@@ -537,7 +544,7 @@ static int zynqmp_ipi_mbox_probe(struct zynqmp_ipi_mbox *ipi_mbox, ...@@ -537,7 +544,7 @@ static int zynqmp_ipi_mbox_probe(struct zynqmp_ipi_mbox *ipi_mbox,
mbox->num_chans = 2; mbox->num_chans = 2;
mbox->txdone_irq = false; mbox->txdone_irq = false;
mbox->txdone_poll = true; mbox->txdone_poll = true;
mbox->txpoll_period = 5; mbox->txpoll_period = tx_poll_period;
mbox->of_xlate = zynqmp_ipi_of_xlate; mbox->of_xlate = zynqmp_ipi_of_xlate;
chans = devm_kzalloc(mdev, 2 * sizeof(*chans), GFP_KERNEL); chans = devm_kzalloc(mdev, 2 * sizeof(*chans), GFP_KERNEL);
if (!chans) if (!chans)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment