mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 14:04:27 -04:00
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:
@@ -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>;
|
||||
};
|
||||
};
|
||||
@@ -20,7 +20,9 @@ description:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: google,gs101-mbox
|
||||
enum:
|
||||
- google,gs101-mbox
|
||||
- samsung,exynos850-mbox
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>;
|
||||
};
|
||||
@@ -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>;
|
||||
[...]
|
||||
};
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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);
|
||||
|
||||
395
drivers/mailbox/axiado-mailbox.c
Normal file
395
drivers/mailbox/axiado-mailbox.c
Normal 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");
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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[] = {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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[] = {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user