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

Pull mailbox updates from Jassi Brar:

 - mhuv2:
     - convert channel translation to fw_xlate()
     - use generic device property APIs

 - axiado:
     - add AX3005 mailbox controller driver and DT bindings

 - bcm2835:
     - use platform_get_irq()
     - simplify probe error handling

 - cix:
     - fix DT property string typo
     - use dev_err_probe()

 - exynos:
     - add Exynos850 mailbox driver support and DT bindings

 - microchip:
     - add null check for devm_kasprintf()

 - pcc:
     - fix missed-interrupt command timeout
     - verify shared memory signature
     - notify clients on polled completion

 - qcom:
     - fix CPUCP PREEMPT_RT deadlock and NULL data crash
     - fix IPCC duplicate channel allocation across holes
     - add IPQ5210 APCS and Nord IPCC bindings

 - riscv:
     - validate RPMI notification buffer lengths and event payload bounds

 - rockchip:
     - manage peripheral clock with devm helper
     - drop unused struct field

 - ti-msgmgr:
     - convert bindings to DT schema

 - remove redundant dev_err()/dev_err_probe() on IRQ request failures
   across drivers

* tag 'mailbox-v7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/jassibrar/mailbox: (22 commits)
  mailbox: add Axiado AX3005 mailbox driver
  dt-bindings: mailbox: add Axiado AX3005 mailbox
  dt-bindings: mailbox: Convert TI Message Manager to DT schema
  mailbox: cix: fix DT property name string typo and use dev_err_probe()
  mailbox: riscv-sbi-mpxy: validate RPMI notification lengths
  mailbox: bcm2835: use platform_get_irq and simplify probe
  mailbox: qcom-ipcc: fix duplicate channel allocation across holes
  mailbox: mchp-ipc-sbi: Add null check for devm_kasprintf()
  mailbox: Remove redundant dev_err()/dev_err_probe()
  dt-bindings: mailbox: qcom: Add IPQ5210 APCS compatible
  dt-bindings: mailbox: qcom-ipcc: Document Nord IPCC
  mailbox: exynos: Add support for Exynos850 mailbox
  dt-bindings: mailbox: google,gs101-mbox: Add samsung,exynos850-mbox
  mailbox: pcc: Fix command timeout due to missed interrupt
  mailbox: pcc: Check shared memory signature on request
  mailbox: pcc: Notify clients on polled completion
  mailbox: rockchip: drop unneeded runtime pointer (pclk)
  mailbox: rockchip: disable pclk on probe failure and unbind
  mailbox: qcom-cpucp: handle NULL data in send_data callback
  mailbox: qcom-cpucp: fix PREEMPT_RT self-deadlock in IRQ handler
  ...
This commit is contained in:
Linus Torvalds
2026-08-24 10:03:49 -07:00
33 changed files with 814 additions and 198 deletions

View File

@@ -0,0 +1,96 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mailbox/axiado,ax3005-mailbox.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Axiado AX3005 Mailbox Controller
maintainers:
- Krishnakumar Kesavan <kkesavan@axiado.com>
- Prasad Bolisetty <pbolisetty@axiado.com>
- Swark Yang <syang@axiado.com>
description:
The Axiado mailbox controller provides inter-processor communication
channels between subsystems within the Axiado SoC. The hardware has
separate TX and RX register regions. Only the RX channels (channels 8-15)
generate interrupts; TX channels are purely poll/write-based.
properties:
compatible:
const: axiado,ax3005-mailbox
reg:
items:
- description: TX mailbox register region
- description: RX mailbox register region
reg-names:
items:
- const: tx
- const: rx
interrupts:
description:
One interrupt per RX channel. The hardware exposes eight RX interrupt
lines corresponding to channels 8 through 15.
maxItems: 8
interrupt-names:
description:
Named interrupt lines for each RX channel, from ch8 to ch15.
items:
- const: ch8
- const: ch9
- const: ch10
- const: ch11
- const: ch12
- const: ch13
- const: ch14
- const: ch15
"#mbox-cells":
description:
Number of cells in a mailbox specifier. A single cell encodes the
channel index as seen by the client.
const: 1
required:
- compatible
- reg
- reg-names
- interrupts
- interrupt-names
- "#mbox-cells"
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
soc {
#address-cells = <2>;
#size-cells = <2>;
mailbox@330f2000 {
compatible = "axiado,ax3005-mailbox";
reg = <0x0 0x330f2000 0x0 0x2000>,
<0x0 0x331b0000 0x0 0x1000>;
reg-names = "tx", "rx";
/* Only RX channels (8-15) have IRQs */
interrupts = <GIC_SPI 242 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 243 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 244 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 245 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 246 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 247 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 248 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 249 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "ch8", "ch9", "ch10", "ch11",
"ch12", "ch13", "ch14", "ch15";
interrupt-parent = <&gic500>;
#mbox-cells = <1>;
};
};

View File

@@ -20,7 +20,9 @@ description:
properties:
compatible:
const: google,gs101-mbox
enum:
- google,gs101-mbox
- samsung,exynos850-mbox
reg:
maxItems: 1

View File

@@ -19,6 +19,7 @@ properties:
- items:
- enum:
- qcom,ipq5018-apcs-apps-global
- qcom,ipq5210-apcs-apps-global
- qcom,ipq5332-apcs-apps-global
- qcom,ipq5424-apcs-apps-global
- qcom,ipq8074-apcs-apps-global

View File

@@ -30,6 +30,7 @@ properties:
- qcom,kaanapali-ipcc
- qcom,maili-ipcc
- qcom,milos-ipcc
- qcom,nord-ipcc
- qcom,qcs8300-ipcc
- qcom,qdu1000-ipcc
- qcom,sa8255p-ipcc

View File

@@ -0,0 +1,67 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mailbox/ti,k2g-message-manager.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments Message Manager
maintainers:
- Eduard Bostina <egbostina@gmail.com>
description:
The Texas Instruments Message Manager is a mailbox controller that has
configurable queues selectable at SoC (System on Chip) integration. The
Message manager is broken up into queues in different address regions
that are called "proxies" - each instance is unidirectional and is
instantiated at SoC integration level to indicate receive or transmit path.
properties:
compatible:
const: ti,k2g-message-manager
reg:
maxItems: 2
reg-names:
items:
- const: queue_proxy_region
- const: queue_state_debug_region
"#mbox-cells":
const: 2
description:
Contains the queue ID and proxy ID in that order referring to the
transfer path.
interrupts:
maxItems: 2
interrupt-names:
items:
- const: rx_005
- const: rx_057
required:
- compatible
- reg
- reg-names
- "#mbox-cells"
- interrupts
- interrupt-names
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
msgmgr@2a00000 {
compatible = "ti,k2g-message-manager";
#mbox-cells = <2>;
reg-names = "queue_proxy_region", "queue_state_debug_region";
reg = <0x02a00000 0x400000>, <0x028c3400 0x400>;
interrupt-names = "rx_005", "rx_057";
interrupts = <GIC_SPI 324 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 327 IRQ_TYPE_LEVEL_HIGH>;
};

View File

@@ -1,50 +0,0 @@
Texas Instruments' Message Manager Driver
========================================
The Texas Instruments' Message Manager is a mailbox controller that has
configurable queues selectable at SoC(System on Chip) integration. The Message
manager is broken up into queues in different address regions that are called
"proxies" - each instance is unidirectional and is instantiated at SoC
integration level to indicate receive or transmit path.
Message Manager Device Node:
===========================
Required properties:
--------------------
- compatible: Shall be: "ti,k2g-message-manager"
- reg-names queue_proxy_region - Map the queue proxy region.
queue_state_debug_region - Map the queue state debug
region.
- reg: Contains the register map per reg-names.
- #mbox-cells Shall be 2. Contains the queue ID and proxy ID in that
order referring to the transfer path.
- interrupt-names: Contains interrupt names matching the rx transfer path
for a given SoC. Receive interrupts shall be of the
format: "rx_<QID>".
For ti,k2g-message-manager, this shall contain:
"rx_005", "rx_057"
- interrupts: Contains the interrupt information corresponding to
interrupt-names property.
Example(K2G):
------------
msgmgr: msgmgr@2a00000 {
compatible = "ti,k2g-message-manager";
#mbox-cells = <2>;
reg-names = "queue_proxy_region", "queue_state_debug_region";
reg = <0x02a00000 0x400000>, <0x028c3400 0x400>;
interrupt-names = "rx_005", "rx_057";
interrupts = <GIC_SPI 324 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 327 IRQ_TYPE_LEVEL_HIGH>;
};
pmmc: pmmc {
[...]
mbox-names = "rx", "tx";
# RX queue ID is 5, proxy ID is 2
# TX queue ID is 0, proxy ID is 0
mboxes= <&msgmgr 5 2>,
<&msgmgr 0 0>;
[...]
};

View File

@@ -4444,6 +4444,14 @@ F: Documentation/devicetree/bindings/phy/axiado,ax3000-emmc-phy.yaml
F: drivers/phy/axiado/Kconfig
F: drivers/phy/axiado/phy-axiado-emmc.c
AXIADO MAILBOX DRIVER
M: Krishnakumar Kesavan <kkesavan@axiado.com>
M: Prasad Bolisetty <pbolisetty@axiado.com>
M: Swark Yang <syang@axiado.com>
S: Supported
F: Documentation/devicetree/bindings/mailbox/axiado,ax3005-mailbox.yaml
F: drivers/mailbox/axiado-mailbox.c
AXI SPI ENGINE
M: Michael Hennerich <michael.hennerich@analog.com>
M: Nuno Sá <nuno.sa@analog.com>

View File

@@ -399,4 +399,14 @@ config RISCV_SBI_MPXY_MBOX
or HS-mode hypervisor). Say Y here, unless you are sure you do not
need this.
config AXIADO_MAILBOX
tristate "Axiado mailbox driver"
depends on ARCH_AXIADO || COMPILE_TEST
depends on OF
help
Enable support for the Axiado mailbox controller. The driver provides
communication channels between the host CPU and the coprocessor on
Axiado SoCs.
If unsure, say N.
endif

View File

@@ -84,3 +84,5 @@ obj-$(CONFIG_CIX_MBOX) += cix-mailbox.o
obj-$(CONFIG_BCM74110_MAILBOX) += bcm74110-mailbox.o
obj-$(CONFIG_RISCV_SBI_MPXY_MBOX) += riscv-sbi-mpxy-mbox.o
obj-$(CONFIG_AXIADO_MAILBOX) += axiado-mailbox.o

View File

@@ -318,7 +318,6 @@ static int mhu_db_probe(struct amba_device *adev, const struct amba_id *id)
mhu_db_mbox_rx_handler,
IRQF_ONESHOT, "mhu_db_link", mhu);
if (err) {
dev_err(dev, "Can't claim IRQ %d\n", irq);
mbox_controller_unregister(&mhu->mbox);
return err;
}

View File

@@ -789,14 +789,14 @@ static const struct mbox_chan_ops mhuv2_receiver_ops = {
.last_tx_done = mhuv2_receiver_last_tx_done,
};
static struct mbox_chan *mhuv2_mbox_of_xlate(struct mbox_controller *mbox,
const struct of_phandle_args *pa)
static struct mbox_chan *mhuv2_mbox_fw_xlate(struct mbox_controller *mbox,
const struct fwnode_reference_args *pa)
{
struct mhuv2 *mhu = mhu_from_mbox(mbox);
struct mbox_chan *chans = mbox->chans;
int channel = 0, i, offset, doorbell, protocol, windows;
if (pa->args_count != 2)
if (pa->nargs != 2)
return ERR_PTR(-EINVAL);
offset = pa->args[0];
@@ -828,7 +828,7 @@ static struct mbox_chan *mhuv2_mbox_of_xlate(struct mbox_controller *mbox,
}
out:
dev_err(mbox->dev, "Couldn't xlate to a valid channel (%d: %d)\n",
dev_err(mbox->dev, "Couldn't xlate to a valid channel (%llu: %d)\n",
pa->args[0], doorbell);
return ERR_PTR(-ENODEV);
}
@@ -931,11 +931,10 @@ static int mhuv2_allocate_channels(struct mhuv2 *mhu)
static int mhuv2_parse_channels(struct mhuv2 *mhu)
{
struct device *dev = mhu->mbox.dev;
const struct device_node *np = dev->of_node;
int ret, count;
u32 *protocols;
count = of_property_count_u32_elems(np, MHUV2_PROTOCOL_PROP);
count = device_property_count_u32(dev, MHUV2_PROTOCOL_PROP);
if (count <= 0 || count % 2) {
dev_err(dev, "Invalid %s property (%d)\n", MHUV2_PROTOCOL_PROP,
count);
@@ -946,7 +945,7 @@ static int mhuv2_parse_channels(struct mhuv2 *mhu)
if (!protocols)
return -ENOMEM;
ret = of_property_read_u32_array(np, MHUV2_PROTOCOL_PROP, protocols, count);
ret = device_property_read_u32_array(dev, MHUV2_PROTOCOL_PROP, protocols, count);
if (ret) {
dev_err(dev, "Failed to read %s property: %d\n",
MHUV2_PROTOCOL_PROP, ret);
@@ -986,10 +985,7 @@ static int mhuv2_tx_init(struct amba_device *adev, struct mhuv2 *mhu,
ret = devm_request_threaded_irq(dev, adev->irq[0], NULL,
mhuv2_sender_interrupt,
IRQF_ONESHOT, "mhuv2-tx", mhu);
if (ret) {
dev_err(dev, "Failed to request tx IRQ, fallback to polling mode: %d\n",
ret);
} else {
if (!ret) {
mhu->mbox.txdone_irq = true;
mhu->mbox.txdone_poll = false;
mhu->irq = adev->irq[0];
@@ -1039,10 +1035,8 @@ static int mhuv2_rx_init(struct amba_device *adev, struct mhuv2 *mhu,
ret = devm_request_threaded_irq(dev, mhu->irq, NULL,
mhuv2_receiver_interrupt, IRQF_ONESHOT,
"mhuv2-rx", mhu);
if (ret) {
dev_err(dev, "Failed to request rx IRQ\n");
if (ret)
return ret;
}
/* Mask all the channel windows */
for (i = 0; i < mhu->windows; i++)
@@ -1071,7 +1065,7 @@ static int mhuv2_probe(struct amba_device *adev, const struct amba_id *id)
return -ENOMEM;
mhu->mbox.dev = dev;
mhu->mbox.of_xlate = mhuv2_mbox_of_xlate;
mhu->mbox.fw_xlate = mhuv2_mbox_fw_xlate;
if (of_device_is_compatible(np, "arm,mhuv2-tx"))
ret = mhuv2_tx_init(adev, mhu, reg);

View File

@@ -982,8 +982,7 @@ static int mhuv3_setup_pbx(struct mhuv3 *mhu)
mhuv3_pbx_comb_interrupt,
IRQF_ONESHOT, "mhuv3-pbx", mhu);
if (ret)
return dev_err_probe(dev, ret,
"Failed to request PBX IRQ\n");
return ret;
mhu->mbox.txdone_irq = true;
mhu->mbox.txdone_poll = false;
@@ -1020,7 +1019,7 @@ static int mhuv3_setup_mbx(struct mhuv3 *mhu)
mhuv3_mbx_comb_interrupt, IRQF_ONESHOT,
"mhuv3-mbx", mhu);
if (ret)
return dev_err_probe(dev, ret, "Failed to request MBX IRQ\n");
return ret;
for (i = 0; i < NUM_EXT; i++)
if (mhu->ext[i])

View File

@@ -108,10 +108,8 @@ static int a37xx_mbox_startup(struct mbox_chan *chan)
ret = devm_request_irq(mbox->dev, mbox->irq, a37xx_mbox_irq_handler, 0,
DRIVER_NAME, chan);
if (ret < 0) {
dev_err(mbox->dev, "Cannot request irq\n");
if (ret < 0)
return ret;
}
/* enable IRQ generation */
reg = readl(mbox->base + RWTM_HOST_INT_MASK);

View File

@@ -0,0 +1,395 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2021-2026 Axiado Corporation (or its affiliates).
*/
#include <linux/atomic.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/bitfield.h>
#include <linux/debugfs.h>
#include <linux/iopoll.h>
#include <linux/mailbox_controller.h>
#include <linux/math.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/unaligned.h>
#define AXIADO_MBOX_TX_CHANS 8 /* 0-7 */
#define AXIADO_MBOX_RX_CHANS 8 /* 8-15 */
#define AXIADO_MBOX_CHAN_STRIDE 0x4
#define AXIADO_MBOX_TX_REG_STRIDE 0x40
#define AXIADO_MBOX_RX_REG_STRIDE 0x30
#define AXIADO_MBOX_RX_POLL_US 10
#define AXIADO_MBOX_RX_TIMEOUT_US 1000
/* Mailbox CSR bit definitions */
#define AXIADO_MBOX_CSR_EMPTY BIT(0) /* 1 = FIFO empty; 0 = data available */
#define AXIADO_MBOX_CSR_OVERFLOW BIT(2) /* write 1 to clear (W1C) */
#define AXIADO_MBOX_CSR_UNDERFLOW BIT(3) /* write 1 to clear (W1C) */
#define AXIADO_MBOX_CSR_FLUSH BIT(4) /* W1TRG flush; startup/shutdown */
#define AXIADO_MBOX_CSR_LEVEL GENMASK(11, 5)
#define AXIADO_MBOX_CSR_ERRORS (AXIADO_MBOX_CSR_OVERFLOW | \
AXIADO_MBOX_CSR_UNDERFLOW)
struct axiado_mbox_data {
u8 num_chans;
u16 msg_size;
};
struct axiado_channel_data {
void __iomem *mbox_reg;
void __iomem *csr_reg;
void *rx_buffer;
u8 channel_num;
int irq;
struct mbox_chan *chan;
bool active;
atomic_t fifo_errors;
};
/* mailbox side */
struct axiado_mbox {
struct mbox_controller mbox;
const struct axiado_mbox_data *drv_data;
void __iomem *tx_base;
void __iomem *rx_base;
struct dentry *debugfs_dir;
};
/*
* Checks OVERFLOW/UNDERFLOW, logs and W1C-clears them if set. Shared by the
* TX and RX paths; RX additionally flushes the FIFO afterwards to resync
* framing, which TX must not do since it could discard data the peer has
* not read yet.
*/
static bool axiado_mbox_clear_fifo_errors(struct axiado_channel_data *priv)
{
struct device *dev = priv->chan->mbox->dev;
u32 errors;
errors = readl(priv->csr_reg) & AXIADO_MBOX_CSR_ERRORS;
if (!errors)
return false;
writel(errors, priv->csr_reg);
atomic_inc(&priv->fifo_errors);
dev_warn_ratelimited(dev, "Channel %u FIFO error: %#x\n",
priv->channel_num, errors);
return true;
}
static int axiado_mbox_send_data(struct mbox_chan *chan, void *data)
{
struct axiado_mbox *mb = dev_get_drvdata(chan->mbox->dev);
struct axiado_channel_data *priv = chan->con_priv;
unsigned int idx = priv->channel_num;
u8 tail[sizeof(u32)] = { 0 };
const u8 *buf = data;
unsigned int tail_len;
unsigned int offset;
u32 msg_len;
if (!data)
return -EINVAL;
/*
* The Axiado mailbox message ABI stores the total message length,
* including this length word, as a little-endian byte count in the
* first word of every message.
*/
msg_len = get_unaligned_le32(data);
if (msg_len < sizeof(u32) || msg_len > mb->drv_data->msg_size)
return -EINVAL;
/* Only touch the hardware after validating the message. */
axiado_mbox_clear_fifo_errors(priv);
if (!(readl(priv->csr_reg) & AXIADO_MBOX_CSR_EMPTY)) {
dev_warn_ratelimited(mb->mbox.dev, "Channel %u is busy\n", idx);
return -EBUSY;
}
for (offset = 0; offset + sizeof(u32) <= msg_len;
offset += sizeof(u32))
writel(get_unaligned_le32(buf + offset), priv->mbox_reg);
tail_len = msg_len - offset;
if (tail_len) {
memcpy(tail, buf + offset, tail_len);
writel(get_unaligned_le32(tail), priv->mbox_reg);
}
dev_dbg(mb->mbox.dev, "%s: Ch-%u sent\n", __func__, idx);
return 0;
}
static irqreturn_t axiado_rx_thread(int irq, void *dev_id)
{
struct axiado_channel_data *priv = dev_id;
struct mbox_chan *chan = priv->chan;
struct axiado_mbox *mb = dev_get_drvdata(chan->mbox->dev);
u8 *buf = priv->rx_buffer;
unsigned int num_words;
unsigned int remaining;
unsigned int i;
u32 msg_len;
u32 word;
u32 csr;
int ret;
/*
* Clear and log/count any pending errors, but don't discard data
* on their account alone: a rejected overflow write doesn't
* corrupt what was already safely queued ahead of it, so let the
* length-based read below decide whether what's here is usable.
*/
axiado_mbox_clear_fifo_errors(priv);
csr = readl(priv->csr_reg);
if (csr & AXIADO_MBOX_CSR_EMPTY)
return IRQ_NONE;
/*
* The first word contains the total message length in bytes,
* including the length word itself.
*/
word = readl(priv->mbox_reg);
msg_len = word;
put_unaligned_le32(word, buf);
if (msg_len < sizeof(u32) || msg_len > mb->drv_data->msg_size)
goto invalid_message;
num_words = DIV_ROUND_UP(msg_len, sizeof(u32));
remaining = num_words - 1;
/*
* The not-empty interrupt may occur as soon as the first DW enters
* the FIFO. Wait until all remaining DWs of this message arrive.
*/
if (remaining) {
ret = readl_poll_timeout(priv->csr_reg, csr,
(csr & AXIADO_MBOX_CSR_ERRORS) ||
FIELD_GET(AXIADO_MBOX_CSR_LEVEL, csr) >=
remaining,
AXIADO_MBOX_RX_POLL_US,
AXIADO_MBOX_RX_TIMEOUT_US);
if (ret)
goto incomplete_message;
axiado_mbox_clear_fifo_errors(priv);
}
for (i = 1; i < num_words; i++) {
word = readl(priv->mbox_reg);
put_unaligned_le32(word, buf + i * sizeof(u32));
}
axiado_mbox_clear_fifo_errors(priv);
if (READ_ONCE(priv->active))
mbox_chan_received_data(chan, priv->rx_buffer);
return IRQ_HANDLED;
incomplete_message:
dev_warn_ratelimited(chan->mbox->dev,
"Channel %u received an incomplete message\n",
priv->channel_num);
invalid_message:
writel(AXIADO_MBOX_CSR_FLUSH, priv->csr_reg);
return IRQ_HANDLED;
}
static int axiado_mbox_startup(struct mbox_chan *chan)
{
struct axiado_channel_data *priv = chan->con_priv;
/*
* Only flush on a genuine error. A blind flush here would discard
* a message the peer legitimately sent before this side started
* up (e.g. during normal boot sequencing), which is not corrupt
* and does not need resyncing.
*/
if (axiado_mbox_clear_fifo_errors(priv))
writel(AXIADO_MBOX_CSR_FLUSH, priv->csr_reg);
WRITE_ONCE(priv->active, true);
if (priv->channel_num >= AXIADO_MBOX_TX_CHANS)
enable_irq(priv->irq);
return 0;
}
static void axiado_mbox_shutdown(struct mbox_chan *chan)
{
struct axiado_channel_data *priv = chan->con_priv;
WRITE_ONCE(priv->active, false);
if (priv->channel_num >= AXIADO_MBOX_TX_CHANS)
disable_irq(priv->irq);
if (axiado_mbox_clear_fifo_errors(priv))
writel(AXIADO_MBOX_CSR_FLUSH, priv->csr_reg);
}
static bool axiado_mbox_last_tx_done(struct mbox_chan *chan)
{
struct axiado_channel_data *priv = chan->con_priv;
return !!(readl(priv->csr_reg) & AXIADO_MBOX_CSR_EMPTY);
}
static const struct mbox_chan_ops axiado_mbox_chan_ops = {
.send_data = axiado_mbox_send_data,
.startup = axiado_mbox_startup,
.shutdown = axiado_mbox_shutdown,
.last_tx_done = axiado_mbox_last_tx_done,
};
static void axiado_mbox_debugfs_remove(void *dentry)
{
debugfs_remove_recursive(dentry);
}
static int axiado_mbox_probe(struct platform_device *pdev)
{
const struct axiado_mbox_data *drv_data;
struct axiado_channel_data *ch_data;
struct device *dev = &pdev->dev;
struct axiado_mbox *mb;
unsigned int irq_idx = 0;
unsigned int rx_chan;
unsigned int i;
int ret;
drv_data = device_get_match_data(dev);
if (!drv_data)
return -ENODEV;
mb = devm_kzalloc(dev, sizeof(*mb), GFP_KERNEL);
if (!mb)
return -ENOMEM;
mb->mbox.dev = dev;
mb->mbox.num_chans = drv_data->num_chans;
mb->mbox.chans = devm_kcalloc(&pdev->dev,
drv_data->num_chans,
sizeof(*mb->mbox.chans),
GFP_KERNEL);
if (!mb->mbox.chans)
return -ENOMEM;
mb->tx_base = devm_platform_ioremap_resource_byname(pdev, "tx");
if (IS_ERR(mb->tx_base))
return PTR_ERR(mb->tx_base);
mb->rx_base = devm_platform_ioremap_resource_byname(pdev, "rx");
if (IS_ERR(mb->rx_base))
return PTR_ERR(mb->rx_base);
ch_data = devm_kcalloc(&pdev->dev,
drv_data->num_chans,
sizeof(*ch_data),
GFP_KERNEL);
if (!ch_data)
return -ENOMEM;
mb->debugfs_dir = debugfs_create_dir(dev_name(dev), NULL);
ret = devm_add_action_or_reset(dev, axiado_mbox_debugfs_remove,
mb->debugfs_dir);
if (ret)
return ret;
for (i = 0; i < drv_data->num_chans; i++) {
char name[16];
ch_data[i].channel_num = i;
ch_data[i].chan = &mb->mbox.chans[i];
mb->mbox.chans[i].con_priv = &ch_data[i];
snprintf(name, sizeof(name), "chan%u-errors", i);
debugfs_create_atomic_t(name, 0444, mb->debugfs_dir,
&ch_data[i].fifo_errors);
if (i < AXIADO_MBOX_TX_CHANS) {
ch_data[i].mbox_reg = mb->tx_base + (i * AXIADO_MBOX_CHAN_STRIDE);
ch_data[i].csr_reg = mb->tx_base + AXIADO_MBOX_TX_REG_STRIDE +
(i * AXIADO_MBOX_CHAN_STRIDE);
ch_data[i].irq = -1;
continue;
}
ch_data[i].rx_buffer = devm_kzalloc(dev, drv_data->msg_size,
GFP_KERNEL);
if (!ch_data[i].rx_buffer)
return -ENOMEM;
rx_chan = i - AXIADO_MBOX_TX_CHANS;
ch_data[i].mbox_reg = mb->rx_base +
(rx_chan * AXIADO_MBOX_CHAN_STRIDE);
ch_data[i].csr_reg = mb->rx_base + AXIADO_MBOX_RX_REG_STRIDE +
(rx_chan * AXIADO_MBOX_CHAN_STRIDE);
ch_data[i].irq = platform_get_irq(pdev, irq_idx++);
if (ch_data[i].irq < 0)
return dev_err_probe(dev, ch_data[i].irq,
"Failed to get IRQ for channel %u\n", i);
ret = devm_request_threaded_irq(dev, ch_data[i].irq, NULL,
axiado_rx_thread,
IRQF_ONESHOT | IRQF_NO_AUTOEN,
dev_name(dev), &ch_data[i]);
if (ret)
return dev_err_probe(dev, ret,
"Failed to request IRQ for channel %u\n", i);
dev_dbg(dev, "RX chan %u -> irq %d\n", i, ch_data[i].irq);
}
platform_set_drvdata(pdev, mb);
mb->drv_data = drv_data;
mb->mbox.ops = &axiado_mbox_chan_ops;
mb->mbox.txdone_irq = false;
mb->mbox.txdone_poll = true;
mb->mbox.txpoll_period = 5;
return devm_mbox_controller_register(dev, &mb->mbox);
}
static const struct axiado_mbox_data axiado_drv_data = {
.num_chans = AXIADO_MBOX_TX_CHANS + AXIADO_MBOX_RX_CHANS,
.msg_size = 256,
};
static const struct of_device_id axiado_mbox_of_match[] = {
{ .compatible = "axiado,ax3005-mailbox", .data = &axiado_drv_data },
{ }
};
MODULE_DEVICE_TABLE(of, axiado_mbox_of_match);
static struct platform_driver axiado_mbox_driver = {
.driver = {
.name = "axiado-mailbox",
.of_match_table = axiado_mbox_of_match,
},
.probe = axiado_mbox_probe,
};
module_platform_driver(axiado_mbox_driver);
MODULE_AUTHOR("Axiado Corporation");
MODULE_DESCRIPTION("Axiado Mailbox driver");
MODULE_LICENSE("GPL");

View File

@@ -1403,11 +1403,8 @@ static int pdc_interrupts_init(struct pdc_state *pdcs)
err = devm_request_irq(dev, pdcs->pdc_irq, pdc_irq_handler, 0,
dev_name(dev), dev);
if (err) {
dev_err(dev, "IRQ %u request failed with err %d\n",
pdcs->pdc_irq, err);
if (err)
return err;
}
return PDC_SUCCESS;
}

View File

@@ -136,28 +136,30 @@ static struct mbox_chan *bcm2835_mbox_index_xlate(struct mbox_controller *mbox,
static int bcm2835_mbox_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
void __iomem *regs;
int ret = 0;
int irq;
struct bcm2835_mbox *mbox;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))
return PTR_ERR(regs);
mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
if (mbox == NULL)
return -ENOMEM;
spin_lock_init(&mbox->lock);
mbox->regs = regs;
ret = devm_request_irq(dev, irq_of_parse_and_map(dev->of_node, 0),
bcm2835_mbox_irq, IRQF_NO_SUSPEND, dev_name(dev),
mbox);
if (ret) {
dev_err(dev, "Failed to register a mailbox IRQ handler: %d\n",
ret);
return -ENODEV;
}
mbox->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mbox->regs)) {
ret = PTR_ERR(mbox->regs);
ret = devm_request_irq(dev, irq, bcm2835_mbox_irq,
IRQF_NO_SUSPEND, dev_name(dev), mbox);
if (ret)
return ret;
}
mbox->controller.txdone_poll = true;
mbox->controller.txpoll_period = 5;
@@ -170,14 +172,7 @@ static int bcm2835_mbox_probe(struct platform_device *pdev)
if (!mbox->controller.chans)
return -ENOMEM;
ret = devm_mbox_controller_register(dev, &mbox->controller);
if (ret)
return ret;
platform_set_drvdata(pdev, mbox);
dev_info(dev, "mailbox enabled\n");
return ret;
return devm_mbox_controller_register(dev, &mbox->controller);
}
static const struct of_device_id bcm2835_mbox_of_match[] = {

View File

@@ -601,7 +601,7 @@ static int bcm74110_mbox_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, mbox->rx_irq, bcm74110_mbox_isr,
IRQF_NO_SUSPEND, pdev->name, mbox);
if (ret)
return dev_err_probe(dev, ret, "Failed to request irq\n");
return ret;
mbox->controller.ops = &bcm74110_mbox_chan_ops;
mbox->controller.dev = dev;

View File

@@ -587,19 +587,15 @@ static int cix_mbox_probe(struct platform_device *pdev)
if (priv->irq < 0)
return priv->irq;
if (device_property_read_string(dev, "cix,mbox-dir", &dir_str)) {
dev_err(priv->dev, "cix,mbox_dir property not found\n");
return -EINVAL;
}
if (device_property_read_string(dev, "cix,mbox-dir", &dir_str))
return dev_err_probe(dev, -EINVAL, "cix,mbox-dir property not found\n");
if (!strcmp(dir_str, "tx"))
priv->dir = 0;
else if (!strcmp(dir_str, "rx"))
priv->dir = 1;
else {
dev_err(priv->dev, "cix,mbox_dir=%s is not expected\n", dir_str);
return -EINVAL;
}
else
return dev_err_probe(dev, -EINVAL, "cix,mbox-dir=%s is not expected\n", dir_str);
cix_mbox_init(priv);
@@ -613,9 +609,9 @@ static int cix_mbox_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, priv);
ret = devm_mbox_controller_register(dev, &priv->mbox);
if (ret)
dev_err(dev, "Failed to register mailbox %d\n", ret);
return dev_err_probe(dev, ret, "Failed to register mailbox\n");
return ret;
return 0;
}
static const struct of_device_id cix_mbox_dt_ids[] = {

View File

@@ -189,7 +189,7 @@ static int cv1800_mbox_probe(struct platform_device *pdev)
cv1800_mbox_isr, IRQF_ONESHOT,
dev_name(&pdev->dev), mb);
if (err < 0)
return dev_err_probe(dev, err, "Failed to register irq\n");
return err;
for (idx = 0; idx < MAILBOX_MAX_CHAN; idx++) {
mb->priv[idx].idx = idx;

View File

@@ -24,14 +24,60 @@
#define EXYNOS_MBOX_CHAN_COUNT HWEIGHT32(EXYNOS_MBOX_INTGR1_MASK)
#define EXYNOS850_MBOX_INTGR0 0x8 /* Interrupt Generation Register 0 */
#define EXYNOS850_MBOX_INTMR1 0x24 /* Interrupt Mask Register 1 */
#define EXYNOS850_MBOX_INTMR1_MASK GENMASK(15, 0)
#define EXYNOS850_MBOX_INTGR0_MASK GENMASK(31, 16)
#define EXYNOS850_MBOX_CHAN_COUNT HWEIGHT32(EXYNOS850_MBOX_INTGR0_MASK)
/**
* struct exynos_mbox_driver_data - platform-specific mailbox configuration.
* @intgr: offset to the IRQ generation register, doorbell
* to APM co-processor.
* @intgr_shift: shift to apply to the value written to IRQ generation
* register.
* @intmr: offset to the IRQ mask register.
* @intmr_mask: value to write to the mask register to mask out all
* interrupts.
* @num_chans: number of channels the mailbox can support (hardware
* capability).
*/
struct exynos_mbox_driver_data {
u32 intgr;
u32 intgr_shift;
u32 intmr;
u32 intmr_mask;
int num_chans;
};
/**
* struct exynos_mbox - driver's private data.
* @regs: mailbox registers base address.
* @mbox: pointer to the mailbox controller.
* @data: pointer to driver platform-specific data.
*/
struct exynos_mbox {
void __iomem *regs;
struct mbox_controller *mbox;
const struct exynos_mbox_driver_data *data;
};
static const struct exynos_mbox_driver_data exynos850_mbox_data = {
.intgr = EXYNOS850_MBOX_INTGR0,
.intgr_shift = 16,
.intmr = EXYNOS850_MBOX_INTMR1,
.intmr_mask = EXYNOS850_MBOX_INTMR1_MASK,
.num_chans = EXYNOS850_MBOX_CHAN_COUNT,
};
static const struct exynos_mbox_driver_data exynos_gs101_mbox_data = {
.intgr = EXYNOS_MBOX_INTGR1,
.intgr_shift = 0,
.intmr = EXYNOS_MBOX_INTMR0,
.intmr_mask = EXYNOS_MBOX_INTMR0_MASK,
.num_chans = EXYNOS_MBOX_CHAN_COUNT,
};
static int exynos_mbox_send_data(struct mbox_chan *chan, void *data)
@@ -50,7 +96,9 @@ static int exynos_mbox_send_data(struct mbox_chan *chan, void *data)
return -EINVAL;
}
writel(BIT(msg->chan_id), exynos_mbox->regs + EXYNOS_MBOX_INTGR1);
/* Ring the doorbell */
writel(BIT(msg->chan_id) << exynos_mbox->data->intgr_shift,
exynos_mbox->regs + exynos_mbox->data->intgr);
return 0;
}
@@ -80,19 +128,31 @@ static struct mbox_chan *exynos_mbox_of_xlate(struct mbox_controller *mbox,
}
static const struct of_device_id exynos_mbox_match[] = {
{ .compatible = "google,gs101-mbox" },
{
.compatible = "google,gs101-mbox",
.data = &exynos_gs101_mbox_data
},
{
.compatible = "samsung,exynos850-mbox",
.data = &exynos850_mbox_data
},
{},
};
MODULE_DEVICE_TABLE(of, exynos_mbox_match);
static int exynos_mbox_probe(struct platform_device *pdev)
{
const struct exynos_mbox_driver_data *data;
struct device *dev = &pdev->dev;
struct exynos_mbox *exynos_mbox;
struct mbox_controller *mbox;
struct mbox_chan *chans;
struct clk *pclk;
data = device_get_match_data(&pdev->dev);
if (!data)
return -ENODEV;
exynos_mbox = devm_kzalloc(dev, sizeof(*exynos_mbox), GFP_KERNEL);
if (!exynos_mbox)
return -ENOMEM;
@@ -101,8 +161,7 @@ static int exynos_mbox_probe(struct platform_device *pdev)
if (!mbox)
return -ENOMEM;
chans = devm_kcalloc(dev, EXYNOS_MBOX_CHAN_COUNT, sizeof(*chans),
GFP_KERNEL);
chans = devm_kcalloc(dev, data->num_chans, sizeof(*chans), GFP_KERNEL);
if (!chans)
return -ENOMEM;
@@ -115,7 +174,8 @@ static int exynos_mbox_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(pclk),
"Failed to enable clock.\n");
mbox->num_chans = EXYNOS_MBOX_CHAN_COUNT;
exynos_mbox->data = data;
mbox->num_chans = data->num_chans;
mbox->chans = chans;
mbox->dev = dev;
mbox->ops = &exynos_mbox_chan_ops;
@@ -126,7 +186,7 @@ static int exynos_mbox_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, exynos_mbox);
/* Mask out all interrupts. We support just polling channels for now. */
writel(EXYNOS_MBOX_INTMR0_MASK, exynos_mbox->regs + EXYNOS_MBOX_INTMR0);
writel(data->intmr_mask, exynos_mbox->regs + data->intmr);
return devm_mbox_controller_register(dev, mbox);
}

View File

@@ -297,11 +297,8 @@ static int hi6220_mbox_probe(struct platform_device *pdev)
err = devm_request_irq(dev, mbox->irq, hi6220_mbox_interrupt, 0,
dev_name(dev), mbox);
if (err) {
dev_err(dev, "Failed to register a mailbox IRQ handler: %d\n",
err);
if (err)
return -ENODEV;
}
mbox->controller.dev = dev;
mbox->controller.chans = &mbox->chan[0];

View File

@@ -378,6 +378,8 @@ static int mchp_ipc_get_cluster_aggr_irq(struct mchp_ipc_sbi_mbox *ipc)
for_each_online_cpu(cpuid) {
hartid = cpuid_to_hartid_map(cpuid);
irq_name = devm_kasprintf(ipc->dev, GFP_KERNEL, "hart-%lu", hartid);
if (!irq_name)
return -ENOMEM;
ret = platform_get_irq_byname_optional(pdev, irq_name);
if (ret <= 0)
continue;

View File

@@ -219,8 +219,6 @@ static int mpfs_mbox_startup(struct mbox_chan *chan)
return -EINVAL;
ret = devm_request_irq(mbox->dev, mbox->irq, mpfs_mbox_inbox_isr, 0, "mpfs-mailbox", chan);
if (ret)
dev_err(mbox->dev, "failed to register mailbox interrupt:%d\n", ret);
return ret;
}

View File

@@ -468,10 +468,8 @@ static int sti_mbox_probe(struct platform_device *pdev)
sti_mbox_irq_handler,
sti_mbox_thread_handler,
IRQF_ONESHOT, mdev->name, mdev);
if (ret) {
dev_err(&pdev->dev, "Can't claim IRQ %d\n", irq);
if (ret)
return -EINVAL;
}
dev_info(&pdev->dev, "%s: Registered Tx/Rx Mailbox\n", mdev->name);

View File

@@ -91,12 +91,11 @@ struct pcc_chan_reg {
* @plat_irq: platform interrupt
* @type: PCC subspace type
* @plat_irq_flags: platform interrupt flags
* @chan_in_use: this flag is used just to check if the interrupt needs
* handling when it is shared. Since only one transfer can occur
* at a time and mailbox takes care of locking, this flag can be
* accessed without a lock. Note: the type only support the
* communication from OSPM to Platform, like type3, use it, and
* other types completely ignore it.
* @chan_in_use: lockless flag used by type 3 initiator subspaces to filter
* platform interrupts. Only one transfer can occur at a time, but
* the interrupt handler may sample the flag on another CPU, so all
* accesses must use READ_ONCE() or WRITE_ONCE(). Other subspace
* types do not test it.
*/
struct pcc_chan_info {
struct pcc_mbox_chan chan;
@@ -320,8 +319,13 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)
if (pcc_chan_reg_read_modify_write(&pchan->plat_irq_ack))
return IRQ_NONE;
/*
* Initiator subspaces use this flag to filter shared interrupts. Use
* READ_ONCE() to sample the lockless flag written by pcc_send_data()
* on another CPU.
*/
if (pchan->type == ACPI_PCCT_TYPE_EXT_PCC_MASTER_SUBSPACE &&
!pchan->chan_in_use)
!READ_ONCE(pchan->chan_in_use))
return IRQ_NONE;
if (!pcc_mbox_cmd_complete_check(pchan))
@@ -331,12 +335,12 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)
return IRQ_NONE;
/*
* Clear this flag after updating interrupt ack register and just
* before mbox_chan_received_data() which might call pcc_send_data()
* where the flag is set again to start new transfer. This is
* required to avoid any possible race in updatation of this flag.
* Clear this flag after updating the interrupt ack register and before
* notifying the client and mailbox core. mbox_chan_txdone() may submit
* the next queued transfer and set the flag again. Use WRITE_ONCE() for
* the lockless update observed by the send and interrupt paths.
*/
pchan->chan_in_use = false;
WRITE_ONCE(pchan->chan_in_use, false);
mbox_chan_received_data(chan, NULL);
mbox_chan_txdone(chan, 0);
@@ -345,6 +349,26 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)
return IRQ_HANDLED;
}
static int pcc_mbox_validate_signature(struct pcc_mbox_chan *pcc_mchan,
int subspace_id)
{
u32 expected_signature = PCC_SIGNATURE | subspace_id;
u32 signature;
if (pcc_mchan->shmem_size < sizeof(signature)) {
pr_err("PCC subspace %d shared memory is too small\n",
subspace_id);
return -EINVAL;
}
signature = ioread32(pcc_mchan->shmem);
if (signature != expected_signature)
pr_warn("PCC subspace %d invalid signature %#x expected %#x\n",
subspace_id, signature, expected_signature);
return 0;
}
/**
* pcc_mbox_request_channel - PCC clients call this function to
* request a pointer to their PCC subspace, from which they
@@ -381,14 +405,20 @@ pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)
if (!pcc_mchan->shmem)
return ERR_PTR(-ENXIO);
rc = pcc_mbox_validate_signature(pcc_mchan, subspace_id);
if (rc)
goto err_unmap_shmem;
rc = mbox_bind_client(chan, cl);
if (rc) {
iounmap(pcc_mchan->shmem);
pcc_mchan->shmem = NULL;
return ERR_PTR(rc);
}
if (rc)
goto err_unmap_shmem;
return pcc_mchan;
err_unmap_shmem:
iounmap(pcc_mchan->shmem);
pcc_mchan->shmem = NULL;
return ERR_PTR(rc);
}
EXPORT_SYMBOL_GPL(pcc_mbox_request_channel);
@@ -438,9 +468,18 @@ static int pcc_send_data(struct mbox_chan *chan, void *data)
if (ret)
return ret;
/*
* Set chan_in_use before ringing the doorbell so a fast completion
* interrupt is not mistaken for a shared interrupt from another
* subspace. Use WRITE_ONCE() for the lockless flag update. The
* ordered I/O accessor used to ring the doorbell orders this store
* before the platform is notified.
*/
if (pchan->plat_irq > 0)
WRITE_ONCE(pchan->chan_in_use, true);
ret = pcc_chan_reg_read_modify_write(&pchan->db);
if (!ret && pchan->plat_irq > 0)
pchan->chan_in_use = true;
if (ret && pchan->plat_irq > 0)
WRITE_ONCE(pchan->chan_in_use, false);
return ret;
}
@@ -449,7 +488,15 @@ static bool pcc_last_tx_done(struct mbox_chan *chan)
{
struct pcc_chan_info *pchan = chan->con_priv;
return pcc_mbox_cmd_complete_check(pchan);
if (!(chan->txdone_method & MBOX_TXDONE_BY_POLL))
return false;
if (!pcc_mbox_cmd_complete_check(pchan))
return false;
mbox_chan_received_data(chan, NULL);
return true;
}
/**

View File

@@ -63,14 +63,25 @@ static irqreturn_t qcom_cpucp_mbox_irq_fn(int irq, void *data)
for_each_set_bit(i, (unsigned long *)&status, cpucp->mbox.num_chans) {
u32 val = readl(cpucp->rx_base + APSS_CPUCP_RX_MBOX_CMD(i) + APSS_CPUCP_MBOX_CMD_OFF);
struct mbox_chan *chan = &cpucp->chans[i];
struct mbox_client *cl;
unsigned long flags;
/* Provide mutual exclusion with changes to chan->cl */
/*
* Provide mutual exclusion with changes to chan->cl.
* Save cl locally and clear the HW interrupt inside the lock,
* then invoke mbox_chan_received_data() outside the lock to
* avoid a PREEMPT_RT self-deadlock: mbox_chan_received_data()
* can call back into mbox_send_message() via scmi_rx_callback()
* -> mailbox_clear_channel(), which re-acquires chan->lock
* (converted to an rt_spinlock under PREEMPT_RT).
*/
spin_lock_irqsave(&chan->lock, flags);
if (chan->cl)
mbox_chan_received_data(chan, &val);
cl = chan->cl;
writeq(BIT(i), cpucp->rx_base + APSS_CPUCP_RX_MBOX_CLEAR);
spin_unlock_irqrestore(&chan->lock, flags);
if (cl)
mbox_chan_received_data(chan, &val);
}
return IRQ_HANDLED;
@@ -106,6 +117,14 @@ static int qcom_cpucp_mbox_send_data(struct mbox_chan *chan, void *data)
unsigned long chan_id = channel_number(chan);
u32 *val = data;
/*
* mailbox_clear_channel() calls mbox_send_message() with NULL data to
* signal the remote side that the channel has been cleared. Nothing
* needs to be written to the TX register in that case, so just return.
*/
if (!val)
return 0;
writel(*val, cpucp->tx_base + APSS_CPUCP_TX_MBOX_CMD(chan_id) + APSS_CPUCP_MBOX_CMD_OFF);
return 0;
@@ -156,7 +175,7 @@ static int qcom_cpucp_mbox_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, irq, qcom_cpucp_mbox_irq_fn,
IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND, "apss_cpucp_mbox", cpucp);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to register irq: %d\n", irq);
return ret;
writeq(APSS_CPUCP_RX_MBOX_CMD_MASK, cpucp->rx_base + APSS_CPUCP_RX_MBOX_MAP);

View File

@@ -167,7 +167,7 @@ static struct mbox_chan *qcom_ipcc_mbox_xlate(struct mbox_controller *mbox,
{
struct qcom_ipcc *ipcc = to_qcom_ipcc(mbox);
struct qcom_ipcc_chan_info *mchan;
struct mbox_chan *chan;
struct mbox_chan *chan, *free_chan = NULL;
struct device *dev;
int chan_id;
@@ -180,16 +180,21 @@ static struct mbox_chan *qcom_ipcc_mbox_xlate(struct mbox_controller *mbox,
chan = &ipcc->chans[chan_id];
mchan = chan->con_priv;
if (!mchan)
break;
else if (mchan->client_id == ph->args[0] &&
mchan->signal_id == ph->args[1])
if (!mchan) {
/* Keep scanning past holes to reject duplicate channel requests. */
if (!free_chan)
free_chan = chan;
} else if (mchan->client_id == ph->args[0] &&
mchan->signal_id == ph->args[1]) {
return ERR_PTR(-EBUSY);
}
}
if (chan_id >= mbox->num_chans)
if (!free_chan)
return ERR_PTR(-EBUSY);
chan = free_chan;
mchan = devm_kzalloc(dev, sizeof(*mchan), GFP_KERNEL);
if (!mchan)
return ERR_PTR(-ENOMEM);
@@ -323,10 +328,8 @@ static int qcom_ipcc_probe(struct platform_device *pdev)
ret = devm_request_irq(&pdev->dev, ipcc->irq, qcom_ipcc_irq_fn,
IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND |
IRQF_NO_THREAD, name, ipcc);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register the irq: %d\n", ret);
if (ret < 0)
goto err_req_irq;
}
platform_set_drvdata(pdev, ipcc);

View File

@@ -314,8 +314,13 @@ static int mpxy_get_notifications(u32 channel_id,
channel_id, 0, 0, 0, 0, 0);
if (sret.error)
goto err_put_cpu;
if (sret.value < 0 || mpxy_shmem_size < sizeof(*notif_data) ||
sret.value > mpxy_shmem_size - sizeof(*notif_data)) {
put_cpu();
return -EOVERFLOW;
}
memcpy(notif_data, mpxy->shmem, sret.value + 16);
memcpy(notif_data, mpxy->shmem, sret.value + sizeof(*notif_data));
*events_data_len = sret.value;
err_put_cpu:
@@ -480,11 +485,14 @@ static void mpxy_mbox_peek_rpmi_data(struct mbox_chan *chan,
struct rpmi_mbox_message msg;
unsigned long pos = 0;
while (pos < events_data_len && (events_data_len - pos) <= sizeof(*event)) {
while (events_data_len - pos >= sizeof(*event)) {
event = (struct rpmi_notification_event *)(notif->events_data + pos);
msg.type = RPMI_MBOX_MSG_TYPE_NOTIFICATION_EVENT;
msg.notif.event_datalen = le16_to_cpu(event->event_datalen);
if (msg.notif.event_datalen >
events_data_len - pos - sizeof(*event))
break;
msg.notif.event_id = event->event_id;
msg.notif.event_data = event->event_data;
msg.error = 0;

View File

@@ -40,7 +40,6 @@ struct rockchip_mbox_chan {
struct rockchip_mbox {
struct mbox_controller mbox;
struct clk *pclk;
void __iomem *mbox_base;
/* The maximum size of buf for each channel */
@@ -166,6 +165,7 @@ static int rockchip_mbox_probe(struct platform_device *pdev)
struct rockchip_mbox *mb;
const struct rockchip_mbox_data *drv_data;
struct resource *res;
struct clk *pclk;
int ret, irq, i;
if (!pdev->dev.of_node)
@@ -196,19 +196,10 @@ static int rockchip_mbox_probe(struct platform_device *pdev)
/* Each channel has two buffers for A2B and B2A */
mb->buf_size = (size_t)resource_size(res) / (drv_data->num_chans * 2);
mb->pclk = devm_clk_get(&pdev->dev, "pclk_mailbox");
if (IS_ERR(mb->pclk)) {
ret = PTR_ERR(mb->pclk);
dev_err(&pdev->dev, "failed to get pclk_mailbox clock: %d\n",
ret);
return ret;
}
ret = clk_prepare_enable(mb->pclk);
if (ret) {
dev_err(&pdev->dev, "failed to enable pclk: %d\n", ret);
return ret;
}
pclk = devm_clk_get_enabled(&pdev->dev, "pclk_mailbox");
if (IS_ERR(pclk))
return dev_err_probe(&pdev->dev, PTR_ERR(pclk),
"failed to get and enable pclk_mailbox clock\n");
for (i = 0; i < mb->mbox.num_chans; i++) {
irq = platform_get_irq(pdev, i);

View File

@@ -377,10 +377,8 @@ static int sprd_mbox_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, inbox_irq, sprd_mbox_inbox_isr,
IRQF_NO_SUSPEND, dev_name(dev), priv);
if (ret) {
dev_err(dev, "failed to request inbox IRQ: %d\n", ret);
if (ret)
return ret;
}
outbox_irq = platform_get_irq_byname(pdev, "outbox");
if (outbox_irq < 0)
@@ -388,20 +386,16 @@ static int sprd_mbox_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, outbox_irq, sprd_mbox_outbox_isr,
IRQF_NO_SUSPEND, dev_name(dev), priv);
if (ret) {
dev_err(dev, "failed to request outbox IRQ: %d\n", ret);
if (ret)
return ret;
}
/* Supplementary outbox IRQ is optional */
supp_irq = platform_get_irq_byname(pdev, "supp-outbox");
if (supp_irq > 0) {
ret = devm_request_irq(dev, supp_irq, sprd_mbox_supp_isr,
IRQF_NO_SUSPEND, dev_name(dev), priv);
if (ret) {
dev_err(dev, "failed to request outbox IRQ: %d\n", ret);
if (ret)
return ret;
}
if (!priv->info->supp_id) {
dev_err(dev, "no supplementary outbox specified\n");

View File

@@ -263,10 +263,8 @@ static int stm32_ipcc_probe(struct platform_device *pdev)
ret = devm_request_threaded_irq(dev, ipcc->irqs[i], NULL,
irq_thread[i], IRQF_ONESHOT,
dev_name(dev), ipcc);
if (ret) {
dev_err(dev, "failed to request irq %lu (%d)\n", i, ret);
if (ret)
goto err_clk;
}
}
/* mask and enable rx/tx irq */

View File

@@ -257,10 +257,8 @@ static int sun6i_msgbox_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, irq_of_parse_and_map(dev->of_node, 0),
sun6i_msgbox_irq, 0, dev_name(dev), mbox);
if (ret) {
dev_err(dev, "Failed to register IRQ handler: %d\n", ret);
if (ret)
goto err_disable_unprepare;
}
mbox->controller.dev = dev;
mbox->controller.ops = &sun6i_msgbox_chan_ops;

View File

@@ -708,11 +708,8 @@ static int tegra_hsp_request_shared_irq(struct tegra_hsp *hsp)
err = devm_request_irq(hsp->dev, irq, tegra_hsp_shared_irq, 0,
dev_name(hsp->dev), hsp);
if (err < 0) {
dev_err(hsp->dev, "failed to request interrupt: %d\n",
err);
if (err < 0)
continue;
}
hsp->shared_irq = i;
@@ -856,12 +853,8 @@ static int tegra_hsp_probe(struct platform_device *pdev)
err = devm_request_irq(&pdev->dev, hsp->doorbell_irq,
tegra_hsp_doorbell_irq, IRQF_NO_SUSPEND,
dev_name(&pdev->dev), hsp);
if (err < 0) {
dev_err(&pdev->dev,
"failed to request doorbell IRQ#%u: %d\n",
hsp->doorbell_irq, err);
if (err < 0)
return err;
}
}
if (hsp->shared_irqs) {