Commit 247ee3e7 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull mailbox updates from Jassi Brar:
 "qcom:
   - add support for qcm2290
   - consolidate msm8994 type apcs_data

  mtk:
   - fix clock id usage

  apple:
   - add driver for ASC/M3 controllers

  pcc:
   - reorganise PCC pcc_mbox_request_channel
   - add support for PCCT extended PCC subspaces

  misc:
   - make use of devm_platform_ioremap_resource()
   - change Altera, PCC and Apple mailbox maintainers"

* tag 'mailbox-v5.16' of git://git.linaro.org/landing-teams/working/fujitsu/integration: (38 commits)
  mailbox: imx: support i.MX8ULP S4 MU
  dt-bindings: mailbox: imx-mu: add i.MX8ULP S400 MU support
  ACPI/PCC: Add maintainer for PCC mailbox driver
  mailbox: pcc: Move bulk of PCCT parsing into pcc_mbox_probe
  mailbox: pcc: Add support for PCCT extended PCC subspaces(type 3/4)
  mailbox: pcc: Drop handling invalid bit-width in {read,write}_register
  mailbox: pcc: Avoid accessing PCCT table in pcc_send_data and pcc_mbox_irq
  mailbox: pcc: Add PCC register bundle and associated accessor functions
  mailbox: pcc: Rename doorbell ack to platform interrupt ack register
  mailbox: pcc: Use PCC mailbox channel pointer instead of standard
  mailbox: pcc: Add pcc_mbox_chan structure to hold shared memory region info
  mailbox: pcc: Consolidate subspace doorbell register parsing
  mailbox: pcc: Consolidate subspace interrupt information parsing
  mailbox: pcc: Refactor all PCC channel information into a structure
  mailbox: pcc: Fix kernel doc warnings
  mailbox: apple: Add driver for Apple mailboxes
  dt-bindings: mailbox: Add Apple mailbox bindings
  MAINTAINERS: Add Apple mailbox files
  mailbox: mtk-cmdq: Fix local clock ID usage
  mailbox: mtk-cmdq: Validate alias_id on probe
  ...
parents 8a73c77c 97961f78
# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/mailbox/apple,mailbox.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Apple Mailbox Controller
maintainers:
- Hector Martin <marcan@marcan.st>
- Sven Peter <sven@svenpeter.dev>
description:
The Apple mailbox consists of two FIFOs used to exchange 64+32 bit
messages between the main CPU and a co-processor. Multiple instances
of this mailbox can be found on Apple SoCs.
One of the two FIFOs is used to send data to a co-processor while the other
FIFO is used for the other direction.
Various clients implement different IPC protocols based on these simple
messages and shared memory buffers.
properties:
compatible:
oneOf:
- description:
ASC mailboxes are the most common variant found on the M1 used
for example for the display controller, the system management
controller and the NVMe coprocessor.
items:
- const: apple,t8103-asc-mailbox
- description:
M3 mailboxes are an older variant with a slightly different MMIO
interface still found on the M1. It is used for the Thunderbolt
co-processors.
items:
- const: apple,t8103-m3-mailbox
reg:
maxItems: 1
interrupts:
items:
- description: send fifo is empty interrupt
- description: send fifo is not empty interrupt
- description: receive fifo is empty interrupt
- description: receive fifo is not empty interrupt
interrupt-names:
items:
- const: send-empty
- const: send-not-empty
- const: recv-empty
- const: recv-not-empty
"#mbox-cells":
const: 0
required:
- compatible
- reg
- interrupts
- interrupt-names
- "#mbox-cells"
additionalProperties: false
examples:
- |
mailbox@77408000 {
compatible = "apple,t8103-asc-mailbox";
reg = <0x77408000 0x4000>;
interrupts = <1 583 4>, <1 584 4>, <1 585 4>, <1 586 4>;
interrupt-names = "send-empty", "send-not-empty",
"recv-empty", "recv-not-empty";
#mbox-cells = <0>;
};
......@@ -28,6 +28,7 @@ properties:
- const: fsl,imx7ulp-mu
- const: fsl,imx8ulp-mu
- const: fsl,imx8-mu-scu
- const: fsl,imx8ulp-mu-s4
- items:
- enum:
- fsl,imx7s-mu
......
......@@ -11,7 +11,7 @@ description:
platforms.
maintainers:
- Sivaprakash Murugesan <sivaprak@codeaurora.org>
- Jassi Brar <jassisinghbrar@gmail.com>
properties:
compatible:
......@@ -24,6 +24,7 @@ properties:
- qcom,msm8994-apcs-kpss-global
- qcom,msm8996-apcs-hmss-global
- qcom,msm8998-apcs-hmss-global
- qcom,qcm2290-apcs-hmss-global
- qcom,qcs404-apcs-apps-global
- qcom,sc7180-apss-shared
- qcom,sc8180x-apss-shared
......
......@@ -401,6 +401,12 @@ L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/i2c-multi-instantiate.c
ACPI PCC(Platform Communication Channel) MAILBOX DRIVER
M: Sudeep Holla <sudeep.holla@arm.com>
L: linux-acpi@vger.kernel.org
S: Supported
F: drivers/mailbox/pcc.c
ACPI PMIC DRIVERS
M: "Rafael J. Wysocki" <rafael@kernel.org>
M: Len Brown <lenb@kernel.org>
......@@ -798,7 +804,7 @@ F: Documentation/devicetree/bindings/i2c/i2c-altera.txt
F: drivers/i2c/busses/i2c-altera.c
ALTERA MAILBOX DRIVER
M: Joyce Ooi <joyce.ooi@intel.com>
M: Mun Yew Tham <mun.yew.tham@intel.com>
S: Maintained
F: drivers/mailbox/mailbox-altera.c
......@@ -1723,11 +1729,14 @@ C: irc://irc.oftc.net/asahi-dev
T: git https://github.com/AsahiLinux/linux.git
F: Documentation/devicetree/bindings/arm/apple.yaml
F: Documentation/devicetree/bindings/interrupt-controller/apple,aic.yaml
F: Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml
F: Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml
F: arch/arm64/boot/dts/apple/
F: drivers/irqchip/irq-apple-aic.c
F: drivers/mailbox/apple-mailbox.c
F: include/dt-bindings/interrupt-controller/apple-aic.h
F: include/dt-bindings/pinctrl/apple.h
F: include/linux/apple-mailbox.h
ARM/ARTPEC MACHINE SUPPORT
M: Jesper Nilsson <jesper.nilsson@axis.com>
......
......@@ -43,7 +43,7 @@
#include <acpi/cppc_acpi.h>
struct cppc_pcc_data {
struct mbox_chan *pcc_channel;
struct pcc_mbox_chan *pcc_channel;
void __iomem *pcc_comm_addr;
bool pcc_channel_acquired;
unsigned int deadline_us;
......@@ -295,7 +295,7 @@ static int send_pcc_cmd(int pcc_ss_id, u16 cmd)
pcc_ss_data->platform_owns_pcc = true;
/* Ring doorbell */
ret = mbox_send_message(pcc_ss_data->pcc_channel, &cmd);
ret = mbox_send_message(pcc_ss_data->pcc_channel->mchan, &cmd);
if (ret < 0) {
pr_err("Err sending PCC mbox message. ss: %d cmd:%d, ret:%d\n",
pcc_ss_id, cmd, ret);
......@@ -308,10 +308,10 @@ static int send_pcc_cmd(int pcc_ss_id, u16 cmd)
if (pcc_ss_data->pcc_mrtt)
pcc_ss_data->last_cmd_cmpl_time = ktime_get();
if (pcc_ss_data->pcc_channel->mbox->txdone_irq)
mbox_chan_txdone(pcc_ss_data->pcc_channel, ret);
if (pcc_ss_data->pcc_channel->mchan->mbox->txdone_irq)
mbox_chan_txdone(pcc_ss_data->pcc_channel->mchan, ret);
else
mbox_client_txdone(pcc_ss_data->pcc_channel, ret);
mbox_client_txdone(pcc_ss_data->pcc_channel->mchan, ret);
end:
if (cmd == CMD_WRITE) {
......@@ -493,46 +493,33 @@ EXPORT_SYMBOL_GPL(acpi_get_psd_map);
static int register_pcc_channel(int pcc_ss_idx)
{
struct acpi_pcct_hw_reduced *cppc_ss;
struct pcc_mbox_chan *pcc_chan;
u64 usecs_lat;
if (pcc_ss_idx >= 0) {
pcc_data[pcc_ss_idx]->pcc_channel =
pcc_mbox_request_channel(&cppc_mbox_cl, pcc_ss_idx);
pcc_chan = pcc_mbox_request_channel(&cppc_mbox_cl, pcc_ss_idx);
if (IS_ERR(pcc_data[pcc_ss_idx]->pcc_channel)) {
if (IS_ERR(pcc_chan)) {
pr_err("Failed to find PCC channel for subspace %d\n",
pcc_ss_idx);
return -ENODEV;
}
/*
* The PCC mailbox controller driver should
* have parsed the PCCT (global table of all
* PCC channels) and stored pointers to the
* subspace communication region in con_priv.
*/
cppc_ss = (pcc_data[pcc_ss_idx]->pcc_channel)->con_priv;
if (!cppc_ss) {
pr_err("No PCC subspace found for %d CPPC\n",
pcc_ss_idx);
return -ENODEV;
}
pcc_data[pcc_ss_idx]->pcc_channel = pcc_chan;
/*
* cppc_ss->latency is just a Nominal value. In reality
* the remote processor could be much slower to reply.
* So add an arbitrary amount of wait on top of Nominal.
*/
usecs_lat = NUM_RETRIES * cppc_ss->latency;
usecs_lat = NUM_RETRIES * pcc_chan->latency;
pcc_data[pcc_ss_idx]->deadline_us = usecs_lat;
pcc_data[pcc_ss_idx]->pcc_mrtt = cppc_ss->min_turnaround_time;
pcc_data[pcc_ss_idx]->pcc_mpar = cppc_ss->max_access_rate;
pcc_data[pcc_ss_idx]->pcc_nominal = cppc_ss->latency;
pcc_data[pcc_ss_idx]->pcc_mrtt = pcc_chan->min_turnaround_time;
pcc_data[pcc_ss_idx]->pcc_mpar = pcc_chan->max_access_rate;
pcc_data[pcc_ss_idx]->pcc_nominal = pcc_chan->latency;
pcc_data[pcc_ss_idx]->pcc_comm_addr =
acpi_os_ioremap(cppc_ss->base_address, cppc_ss->length);
acpi_os_ioremap(pcc_chan->shmem_base_addr,
pcc_chan->shmem_size);
if (!pcc_data[pcc_ss_idx]->pcc_comm_addr) {
pr_err("Failed to ioremap PCC comm region mem for %d\n",
pcc_ss_idx);
......
......@@ -93,6 +93,7 @@ struct slimpro_resp_msg {
struct xgene_hwmon_dev {
struct device *dev;
struct mbox_chan *mbox_chan;
struct pcc_mbox_chan *pcc_chan;
struct mbox_client mbox_client;
int mbox_idx;
......@@ -652,7 +653,7 @@ static int xgene_hwmon_probe(struct platform_device *pdev)
goto out_mbox_free;
}
} else {
struct acpi_pcct_hw_reduced *cppc_ss;
struct pcc_mbox_chan *pcc_chan;
const struct acpi_device_id *acpi_id;
int version;
......@@ -671,26 +672,16 @@ static int xgene_hwmon_probe(struct platform_device *pdev)
}
cl->rx_callback = xgene_hwmon_pcc_rx_cb;
ctx->mbox_chan = pcc_mbox_request_channel(cl, ctx->mbox_idx);
if (IS_ERR(ctx->mbox_chan)) {
pcc_chan = pcc_mbox_request_channel(cl, ctx->mbox_idx);
if (IS_ERR(pcc_chan)) {
dev_err(&pdev->dev,
"PPC channel request failed\n");
rc = -ENODEV;
goto out_mbox_free;
}
/*
* The PCC mailbox controller driver should
* have parsed the PCCT (global table of all
* PCC channels) and stored pointers to the
* subspace communication region in con_priv.
*/
cppc_ss = ctx->mbox_chan->con_priv;
if (!cppc_ss) {
dev_err(&pdev->dev, "PPC subspace not found\n");
rc = -ENODEV;
goto out;
}
ctx->pcc_chan = pcc_chan;
ctx->mbox_chan = pcc_chan->mchan;
if (!ctx->mbox_chan->mbox->txdone_irq) {
dev_err(&pdev->dev, "PCC IRQ not supported\n");
......@@ -702,16 +693,16 @@ static int xgene_hwmon_probe(struct platform_device *pdev)
* This is the shared communication region
* for the OS and Platform to communicate over.
*/
ctx->comm_base_addr = cppc_ss->base_address;
ctx->comm_base_addr = pcc_chan->shmem_base_addr;
if (ctx->comm_base_addr) {
if (version == XGENE_HWMON_V2)
ctx->pcc_comm_addr = (void __force *)ioremap(
ctx->comm_base_addr,
cppc_ss->length);
pcc_chan->shmem_size);
else
ctx->pcc_comm_addr = memremap(
ctx->comm_base_addr,
cppc_ss->length,
pcc_chan->shmem_size,
MEMREMAP_WB);
} else {
dev_err(&pdev->dev, "Failed to get PCC comm region\n");
......@@ -727,11 +718,11 @@ static int xgene_hwmon_probe(struct platform_device *pdev)
}
/*
* cppc_ss->latency is just a Nominal value. In reality
* pcc_chan->latency is just a Nominal value. In reality
* the remote processor could be much slower to reply.
* So add an arbitrary amount of wait on top of Nominal.
*/
ctx->usecs_lat = PCC_NUM_RETRIES * cppc_ss->latency;
ctx->usecs_lat = PCC_NUM_RETRIES * pcc_chan->latency;
}
ctx->hwmon_dev = hwmon_device_register_with_groups(ctx->dev,
......@@ -757,7 +748,7 @@ static int xgene_hwmon_probe(struct platform_device *pdev)
if (acpi_disabled)
mbox_free_channel(ctx->mbox_chan);
else
pcc_mbox_free_channel(ctx->mbox_chan);
pcc_mbox_free_channel(ctx->pcc_chan);
out_mbox_free:
kfifo_free(&ctx->async_msg_fifo);
......@@ -773,7 +764,7 @@ static int xgene_hwmon_remove(struct platform_device *pdev)
if (acpi_disabled)
mbox_free_channel(ctx->mbox_chan);
else
pcc_mbox_free_channel(ctx->mbox_chan);
pcc_mbox_free_channel(ctx->pcc_chan);
return 0;
}
......
......@@ -103,6 +103,7 @@ struct slimpro_i2c_dev {
struct i2c_adapter adapter;
struct device *dev;
struct mbox_chan *mbox_chan;
struct pcc_mbox_chan *pcc_chan;
struct mbox_client mbox_client;
int mbox_idx;
struct completion rd_complete;
......@@ -466,7 +467,7 @@ static int xgene_slimpro_i2c_probe(struct platform_device *pdev)
return PTR_ERR(ctx->mbox_chan);
}
} else {
struct acpi_pcct_hw_reduced *cppc_ss;
struct pcc_mbox_chan *pcc_chan;
const struct acpi_device_id *acpi_id;
int version = XGENE_SLIMPRO_I2C_V1;
......@@ -483,24 +484,14 @@ static int xgene_slimpro_i2c_probe(struct platform_device *pdev)
cl->tx_block = false;
cl->rx_callback = slimpro_i2c_pcc_rx_cb;
ctx->mbox_chan = pcc_mbox_request_channel(cl, ctx->mbox_idx);
if (IS_ERR(ctx->mbox_chan)) {
pcc_chan = pcc_mbox_request_channel(cl, ctx->mbox_idx);
if (IS_ERR(pcc_chan)) {
dev_err(&pdev->dev, "PCC mailbox channel request failed\n");
return PTR_ERR(ctx->mbox_chan);
return PTR_ERR(ctx->pcc_chan);
}
/*
* The PCC mailbox controller driver should
* have parsed the PCCT (global table of all
* PCC channels) and stored pointers to the
* subspace communication region in con_priv.
*/
cppc_ss = ctx->mbox_chan->con_priv;
if (!cppc_ss) {
dev_err(&pdev->dev, "PPC subspace not found\n");
rc = -ENOENT;
goto mbox_err;
}
ctx->pcc_chan = pcc_chan;
ctx->mbox_chan = pcc_chan->mchan;
if (!ctx->mbox_chan->mbox->txdone_irq) {
dev_err(&pdev->dev, "PCC IRQ not supported\n");
......@@ -512,17 +503,17 @@ static int xgene_slimpro_i2c_probe(struct platform_device *pdev)
* This is the shared communication region
* for the OS and Platform to communicate over.
*/
ctx->comm_base_addr = cppc_ss->base_address;
ctx->comm_base_addr = pcc_chan->shmem_base_addr;
if (ctx->comm_base_addr) {
if (version == XGENE_SLIMPRO_I2C_V2)
ctx->pcc_comm_addr = memremap(
ctx->comm_base_addr,
cppc_ss->length,
pcc_chan->shmem_size,
MEMREMAP_WT);
else
ctx->pcc_comm_addr = memremap(
ctx->comm_base_addr,
cppc_ss->length,
pcc_chan->shmem_size,
MEMREMAP_WB);
} else {
dev_err(&pdev->dev, "Failed to get PCC comm region\n");
......@@ -561,7 +552,7 @@ static int xgene_slimpro_i2c_probe(struct platform_device *pdev)
if (acpi_disabled)
mbox_free_channel(ctx->mbox_chan);
else
pcc_mbox_free_channel(ctx->mbox_chan);
pcc_mbox_free_channel(ctx->pcc_chan);
return rc;
}
......@@ -575,7 +566,7 @@ static int xgene_slimpro_i2c_remove(struct platform_device *pdev)
if (acpi_disabled)
mbox_free_channel(ctx->mbox_chan);
else
pcc_mbox_free_channel(ctx->mbox_chan);
pcc_mbox_free_channel(ctx->pcc_chan);
return 0;
}
......
......@@ -8,6 +8,18 @@ menuconfig MAILBOX
if MAILBOX
config APPLE_MAILBOX
tristate "Apple Mailbox driver"
depends on ARCH_APPLE || (ARM64 && COMPILE_TEST)
default ARCH_APPLE
help
Apple SoCs have various co-processors required for certain
peripherals to work (NVMe, display controller, etc.). This
driver adds support for the mailbox controller used to
communicate with those.
Say Y here if you have a Apple SoC.
config ARM_MHU
tristate "ARM MHU Mailbox"
depends on ARM_AMBA
......
......@@ -58,3 +58,5 @@ obj-$(CONFIG_SUN6I_MSGBOX) += sun6i-msgbox.o
obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox.o
obj-$(CONFIG_QCOM_IPCC) += qcom-ipcc.o
obj-$(CONFIG_APPLE_MAILBOX) += apple-mailbox.o
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/*
* Apple mailbox driver
*
* Copyright (C) 2021 The Asahi Linux Contributors
*
* This driver adds support for two mailbox variants (called ASC and M3 by
* Apple) found in Apple SoCs such as the M1. It consists of two FIFOs used to
* exchange 64+32 bit messages between the main CPU and a co-processor.
* Various coprocessors implement different IPC protocols based on these simple
* messages and shared memory buffers.
*
* Both the main CPU and the co-processor see the same set of registers but
* the first FIFO (A2I) is always used to transfer messages from the application
* processor (us) to the I/O processor and the second one (I2A) for the
* other direction.
*/
#include <linux/apple-mailbox.h>
#include <linux/device.h>
#include <linux/gfp.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/mailbox_controller.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#define APPLE_ASC_MBOX_CONTROL_FULL BIT(16)
#define APPLE_ASC_MBOX_CONTROL_EMPTY BIT(17)
#define APPLE_ASC_MBOX_A2I_CONTROL 0x110
#define APPLE_ASC_MBOX_A2I_SEND0 0x800
#define APPLE_ASC_MBOX_A2I_SEND1 0x808
#define APPLE_ASC_MBOX_A2I_RECV0 0x810
#define APPLE_ASC_MBOX_A2I_RECV1 0x818
#define APPLE_ASC_MBOX_I2A_CONTROL 0x114
#define APPLE_ASC_MBOX_I2A_SEND0 0x820
#define APPLE_ASC_MBOX_I2A_SEND1 0x828
#define APPLE_ASC_MBOX_I2A_RECV0 0x830
#define APPLE_ASC_MBOX_I2A_RECV1 0x838
#define APPLE_M3_MBOX_CONTROL_FULL BIT(16)
#define APPLE_M3_MBOX_CONTROL_EMPTY BIT(17)
#define APPLE_M3_MBOX_A2I_CONTROL 0x50
#define APPLE_M3_MBOX_A2I_SEND0 0x60
#define APPLE_M3_MBOX_A2I_SEND1 0x68
#define APPLE_M3_MBOX_A2I_RECV0 0x70
#define APPLE_M3_MBOX_A2I_RECV1 0x78
#define APPLE_M3_MBOX_I2A_CONTROL 0x80
#define APPLE_M3_MBOX_I2A_SEND0 0x90
#define APPLE_M3_MBOX_I2A_SEND1 0x98
#define APPLE_M3_MBOX_I2A_RECV0 0xa0
#define APPLE_M3_MBOX_I2A_RECV1 0xa8
#define APPLE_M3_MBOX_IRQ_ENABLE 0x48
#define APPLE_M3_MBOX_IRQ_ACK 0x4c
#define APPLE_M3_MBOX_IRQ_A2I_EMPTY BIT(0)
#define APPLE_M3_MBOX_IRQ_A2I_NOT_EMPTY BIT(1)
#define APPLE_M3_MBOX_IRQ_I2A_EMPTY BIT(2)
#define APPLE_M3_MBOX_IRQ_I2A_NOT_EMPTY BIT(3)
#define APPLE_MBOX_MSG1_OUTCNT GENMASK(56, 52)
#define APPLE_MBOX_MSG1_INCNT GENMASK(51, 48)
#define APPLE_MBOX_MSG1_OUTPTR GENMASK(47, 44)
#define APPLE_MBOX_MSG1_INPTR GENMASK(43, 40)
#define APPLE_MBOX_MSG1_MSG GENMASK(31, 0)
struct apple_mbox_hw {
unsigned int control_full;
unsigned int control_empty;
unsigned int a2i_control;
unsigned int a2i_send0;
unsigned int a2i_send1;
unsigned int i2a_control;
unsigned int i2a_recv0;
unsigned int i2a_recv1;
bool has_irq_controls;
unsigned int irq_enable;
unsigned int irq_ack;
unsigned int irq_bit_recv_not_empty;
unsigned int irq_bit_send_empty;
};
struct apple_mbox {
void __iomem *regs;
const struct apple_mbox_hw *hw;
int irq_recv_not_empty;
int irq_send_empty;
struct mbox_chan chan;
struct device *dev;
struct mbox_controller controller;
};
static const struct of_device_id apple_mbox_of_match[];
static bool apple_mbox_hw_can_send(struct apple_mbox *apple_mbox)
{
u32 mbox_ctrl =
readl_relaxed(apple_mbox->regs + apple_mbox->hw->a2i_control);
return !(mbox_ctrl & apple_mbox->hw->control_full);
}
static int apple_mbox_hw_send(struct apple_mbox *apple_mbox,
struct apple_mbox_msg *msg)
{
if (!apple_mbox_hw_can_send(apple_mbox))
return -EBUSY;
dev_dbg(apple_mbox->dev, "> TX %016llx %08x\n", msg->msg0, msg->msg1);
writeq_relaxed(msg->msg0, apple_mbox->regs + apple_mbox->hw->a2i_send0);
writeq_relaxed(FIELD_PREP(APPLE_MBOX_MSG1_MSG, msg->msg1),
apple_mbox->regs + apple_mbox->hw->a2i_send1);
return 0;
}
static bool apple_mbox_hw_can_recv(struct apple_mbox *apple_mbox)
{
u32 mbox_ctrl =
readl_relaxed(apple_mbox->regs + apple_mbox->hw->i2a_control);
return !(mbox_ctrl & apple_mbox->hw->control_empty);
}
static int apple_mbox_hw_recv(struct apple_mbox *apple_mbox,
struct apple_mbox_msg *msg)
{
if (!apple_mbox_hw_can_recv(apple_mbox))
return -ENOMSG;
msg->msg0 = readq_relaxed(apple_mbox->regs + apple_mbox->hw->i2a_recv0);
msg->msg1 = FIELD_GET(
APPLE_MBOX_MSG1_MSG,
readq_relaxed(apple_mbox->regs + apple_mbox->hw->i2a_recv1));
dev_dbg(apple_mbox->dev, "< RX %016llx %08x\n", msg->msg0, msg->msg1);
return 0;
}
static int apple_mbox_chan_send_data(struct mbox_chan *chan, void *data)
{
struct apple_mbox *apple_mbox = chan->con_priv;
struct apple_mbox_msg *msg = data;
int ret;
ret = apple_mbox_hw_send(apple_mbox, msg);
if (ret)
return ret;
/*
* The interrupt is level triggered and will keep firing as long as the
* FIFO is empty. It will also keep firing if the FIFO was empty
* at any point in the past until it has been acknowledged at the
* mailbox level. By acknowledging it here we can ensure that we will
* only get the interrupt once the FIFO has been cleared again.
* If the FIFO is already empty before the ack it will fire again
* immediately after the ack.
*/
if (apple_mbox->hw->has_irq_controls) {
writel_relaxed(apple_mbox->hw->irq_bit_send_empty,
apple_mbox->regs + apple_mbox->hw->irq_ack);
}
enable_irq(apple_mbox->irq_send_empty);
return 0;
}
static irqreturn_t apple_mbox_send_empty_irq(int irq, void *data)
{
struct apple_mbox *apple_mbox = data;
/*
* We don't need to acknowledge the interrupt at the mailbox level
* here even if supported by the hardware. It will keep firing but that
* doesn't matter since it's disabled at the main interrupt controller.
* apple_mbox_chan_send_data will acknowledge it before enabling
* it at the main controller again.
*/
disable_irq_nosync(apple_mbox->irq_send_empty);
mbox_chan_txdone(&apple_mbox->chan, 0);
return IRQ_HANDLED;
}
static irqreturn_t apple_mbox_recv_irq(int irq, void *data)
{
struct apple_mbox *apple_mbox = data;
struct apple_mbox_msg msg;
while (apple_mbox_hw_recv(apple_mbox, &msg) == 0)
mbox_chan_received_data(&apple_mbox->chan, (void *)&msg);
/*
* The interrupt will keep firing even if there are no more messages
* unless we also acknowledge it at the mailbox level here.
* There's no race if a message comes in between the check in the while
* loop above and the ack below: If a new messages arrives inbetween
* those two the interrupt will just fire again immediately after the
* ack since it's level triggered.
*/
if (apple_mbox->hw->has_irq_controls) {
writel_relaxed(apple_mbox->hw->irq_bit_recv_not_empty,
apple_mbox->regs + apple_mbox->hw->irq_ack);
}
return IRQ_HANDLED;
}
static int apple_mbox_chan_startup(struct mbox_chan *chan)
{
struct apple_mbox *apple_mbox = chan->con_priv;
/*
* Only some variants of this mailbox HW provide interrupt control
* at the mailbox level. We therefore need to handle enabling/disabling
* interrupts at the main interrupt controller anyway for hardware that
* doesn't. Just always keep the interrupts we care about enabled at
* the mailbox level so that both hardware revisions behave almost
* the same.
*/
if (apple_mbox->hw->has_irq_controls) {
writel_relaxed(apple_mbox->hw->irq_bit_recv_not_empty |
apple_mbox->hw->irq_bit_send_empty,
apple_mbox->regs + apple_mbox->hw->irq_enable);
}
enable_irq(apple_mbox->irq_recv_not_empty);
return 0;
}
static void apple_mbox_chan_shutdown(struct mbox_chan *chan)
{
struct apple_mbox *apple_mbox = chan->con_priv;
disable_irq(apple_mbox->irq_recv_not_empty);
}
static const struct mbox_chan_ops apple_mbox_ops = {
.send_data = apple_mbox_chan_send_data,
.startup = apple_mbox_chan_startup,
.shutdown = apple_mbox_chan_shutdown,
};
static struct mbox_chan *apple_mbox_of_xlate(struct mbox_controller *mbox,
const struct of_phandle_args *args)
{
if (args->args_count != 0)
return ERR_PTR(-EINVAL);
return &mbox->chans[0];
}
static int apple_mbox_probe(struct platform_device *pdev)
{
int ret;
const struct of_device_id *match;
char *irqname;
struct apple_mbox *mbox;
struct device *dev = &pdev->dev;
match = of_match_node(apple_mbox_of_match, pdev->dev.of_node);
if (!match)
return -EINVAL;
if (!match->data)
return -EINVAL;
mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
if (!mbox)
return -ENOMEM;
platform_set_drvdata(pdev, mbox);
mbox->dev = dev;
mbox->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mbox->regs))
return PTR_ERR(mbox->regs);
mbox->hw = match->data;
mbox->irq_recv_not_empty =
platform_get_irq_byname(pdev, "recv-not-empty");
if (mbox->irq_recv_not_empty < 0)
return -ENODEV;
mbox->irq_send_empty = platform_get_irq_byname(pdev, "send-empty");
if (mbox->irq_send_empty < 0)
return -ENODEV;
mbox->controller.dev = mbox->dev;
mbox->controller.num_chans = 1;
mbox->controller.chans = &mbox->chan;
mbox->controller.ops = &apple_mbox_ops;
mbox->controller.txdone_irq = true;
mbox->controller.of_xlate = apple_mbox_of_xlate;
mbox->chan.con_priv = mbox;
irqname = devm_kasprintf(dev, GFP_KERNEL, "%s-recv", dev_name(dev));
if (!irqname)
return -ENOMEM;
ret = devm_request_threaded_irq(dev, mbox->irq_recv_not_empty, NULL,
apple_mbox_recv_irq,
IRQF_NO_AUTOEN | IRQF_ONESHOT, irqname,
mbox);
if (ret)
return ret;
irqname = devm_kasprintf(dev, GFP_KERNEL, "%s-send", dev_name(dev));
if (!irqname)
return -ENOMEM;
ret = devm_request_irq(dev, mbox->irq_send_empty,
apple_mbox_send_empty_irq, IRQF_NO_AUTOEN,
irqname, mbox);
if (ret)
return ret;
return devm_mbox_controller_register(dev, &mbox->controller);
}
static const struct apple_mbox_hw apple_mbox_asc_hw = {
.control_full = APPLE_ASC_MBOX_CONTROL_FULL,
.control_empty = APPLE_ASC_MBOX_CONTROL_EMPTY,
.a2i_control = APPLE_ASC_MBOX_A2I_CONTROL,
.a2i_send0 = APPLE_ASC_MBOX_A2I_SEND0,
.a2i_send1 = APPLE_ASC_MBOX_A2I_SEND1,
.i2a_control = APPLE_ASC_MBOX_I2A_CONTROL,
.i2a_recv0 = APPLE_ASC_MBOX_I2A_RECV0,
.i2a_recv1 = APPLE_ASC_MBOX_I2A_RECV1,
.has_irq_controls = false,
};
static const struct apple_mbox_hw apple_mbox_m3_hw = {
.control_full = APPLE_M3_MBOX_CONTROL_FULL,
.control_empty = APPLE_M3_MBOX_CONTROL_EMPTY,
.a2i_control = APPLE_M3_MBOX_A2I_CONTROL,
.a2i_send0 = APPLE_M3_MBOX_A2I_SEND0,
.a2i_send1 = APPLE_M3_MBOX_A2I_SEND1,
.i2a_control = APPLE_M3_MBOX_I2A_CONTROL,
.i2a_recv0 = APPLE_M3_MBOX_I2A_RECV0,
.i2a_recv1 = APPLE_M3_MBOX_I2A_RECV1,
.has_irq_controls = true,
.irq_enable = APPLE_M3_MBOX_IRQ_ENABLE,
.irq_ack = APPLE_M3_MBOX_IRQ_ACK,
.irq_bit_recv_not_empty = APPLE_M3_MBOX_IRQ_I2A_NOT_EMPTY,
.irq_bit_send_empty = APPLE_M3_MBOX_IRQ_A2I_EMPTY,
};
static const struct of_device_id apple_mbox_of_match[] = {
{ .compatible = "apple,t8103-asc-mailbox", .data = &apple_mbox_asc_hw },
{ .compatible = "apple,t8103-m3-mailbox", .data = &apple_mbox_m3_hw },
{}
};
MODULE_DEVICE_TABLE(of, apple_mbox_of_match);
static struct platform_driver apple_mbox_driver = {
.driver = {
.name = "apple-mailbox",
.of_match_table = apple_mbox_of_match,
},
.probe = apple_mbox_probe,
};
module_platform_driver(apple_mbox_driver);
MODULE_LICENSE("Dual MIT/GPL");
MODULE_AUTHOR("Sven Peter <sven@svenpeter.dev>");
MODULE_DESCRIPTION("Apple Mailbox driver");
......@@ -137,7 +137,6 @@ static int bcm2835_mbox_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
int ret = 0;
struct resource *iomem;
struct bcm2835_mbox *mbox;
mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
......@@ -153,8 +152,7 @@ static int bcm2835_mbox_probe(struct platform_device *pdev)
return -ENODEV;
}
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mbox->regs = devm_ioremap_resource(&pdev->dev, iomem);
mbox->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mbox->regs)) {
ret = PTR_ERR(mbox->regs);
return ret;
......
......@@ -240,7 +240,6 @@ static int hi3660_mbox_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct hi3660_mbox *mbox;
struct mbox_chan *chan;
struct resource *res;
unsigned long ch;
int err;
......@@ -248,8 +247,7 @@ static int hi3660_mbox_probe(struct platform_device *pdev)
if (!mbox)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mbox->base = devm_ioremap_resource(dev, res);
mbox->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mbox->base))
return PTR_ERR(mbox->base);
......
......@@ -264,7 +264,6 @@ static int hi6220_mbox_probe(struct platform_device *pdev)
struct device_node *node = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct hi6220_mbox *mbox;
struct resource *res;
int i, err;
mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
......@@ -287,15 +286,13 @@ static int hi6220_mbox_probe(struct platform_device *pdev)
if (mbox->irq < 0)
return mbox->irq;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mbox->ipc = devm_ioremap_resource(dev, res);
mbox->ipc = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mbox->ipc)) {
dev_err(dev, "ioremap ipc failed\n");
return PTR_ERR(mbox->ipc);
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
mbox->base = devm_ioremap_resource(dev, res);
mbox->base = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(mbox->base)) {
dev_err(dev, "ioremap buffer failed\n");
return PTR_ERR(mbox->base);
......
......@@ -5,6 +5,7 @@
#include <linux/clk.h>
#include <linux/firmware/imx/ipc.h>
#include <linux/firmware/imx/s4.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
......@@ -18,6 +19,8 @@
#define IMX_MU_CHANS 16
/* TX0/RX0/RXDB[0-3] */
#define IMX_MU_SCU_CHANS 6
/* TX0/RX0 */
#define IMX_MU_S4_CHANS 2
#define IMX_MU_CHAN_NAME_SIZE 20
enum imx_mu_chan_type {
......@@ -47,6 +50,11 @@ struct imx_sc_rpc_msg_max {
u32 data[7];
};
struct imx_s4_rpc_msg_max {
struct imx_s4_rpc_msg hdr;
u32 data[254];
};
struct imx_mu_con_priv {
unsigned int idx;
char irq_desc[IMX_MU_CHAN_NAME_SIZE];
......@@ -58,6 +66,7 @@ struct imx_mu_con_priv {
struct imx_mu_priv {
struct device *dev;
void __iomem *base;
void *msg;
spinlock_t xcr_lock; /* control register lock */
struct mbox_controller mbox;
......@@ -75,7 +84,8 @@ struct imx_mu_priv {
enum imx_mu_type {
IMX_MU_V1,
IMX_MU_V2,
IMX_MU_V2 = BIT(1),
IMX_MU_V2_S4 = BIT(15),
};
struct imx_mu_dcfg {
......@@ -89,18 +99,18 @@ struct imx_mu_dcfg {
u32 xCR[4]; /* Control Registers */
};
#define IMX_MU_xSR_GIPn(type, x) (type == IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x))))
#define IMX_MU_xSR_RFn(type, x) (type == IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x))))
#define IMX_MU_xSR_TEn(type, x) (type == IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x))))
#define IMX_MU_xSR_GIPn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x))))
#define IMX_MU_xSR_RFn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x))))
#define IMX_MU_xSR_TEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x))))
/* General Purpose Interrupt Enable */
#define IMX_MU_xCR_GIEn(type, x) (type == IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x))))
#define IMX_MU_xCR_GIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x))))
/* Receive Interrupt Enable */
#define IMX_MU_xCR_RIEn(type, x) (type == IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x))))
#define IMX_MU_xCR_RIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x))))
/* Transmit Interrupt Enable */
#define IMX_MU_xCR_TIEn(type, x) (type == IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x))))
#define IMX_MU_xCR_TIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x))))
/* General Purpose Interrupt Request */
#define IMX_MU_xCR_GIRn(type, x) (type == IMX_MU_V2 ? BIT(x) : BIT(16 + (3 - (x))))
#define IMX_MU_xCR_GIRn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(16 + (3 - (x))))
static struct imx_mu_priv *to_imx_mu_priv(struct mbox_controller *mbox)
......@@ -167,14 +177,22 @@ static int imx_mu_generic_rx(struct imx_mu_priv *priv,
return 0;
}
static int imx_mu_scu_tx(struct imx_mu_priv *priv,
struct imx_mu_con_priv *cp,
void *data)
static int imx_mu_specific_tx(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, void *data)
{
struct imx_sc_rpc_msg_max *msg = data;
u32 *arg = data;
int i, ret;
u32 xsr;
u32 size, max_size, num_tr;
if (priv->dcfg->type & IMX_MU_V2_S4) {
size = ((struct imx_s4_rpc_msg_max *)data)->hdr.size;
max_size = sizeof(struct imx_s4_rpc_msg_max);
num_tr = 8;
} else {
size = ((struct imx_sc_rpc_msg_max *)data)->hdr.size;
max_size = sizeof(struct imx_sc_rpc_msg_max);
num_tr = 4;
}
switch (cp->type) {
case IMX_MU_TYPE_TX:
......@@ -183,27 +201,27 @@ static int imx_mu_scu_tx(struct imx_mu_priv *priv,
* sizeof yields bytes.
*/
if (msg->hdr.size > sizeof(*msg) / 4) {
if (size > max_size / 4) {
/*
* The real message size can be different to
* struct imx_sc_rpc_msg_max size
* struct imx_sc_rpc_msg_max/imx_s4_rpc_msg_max size
*/
dev_err(priv->dev, "Maximal message size (%zu bytes) exceeded on TX; got: %i bytes\n", sizeof(*msg), msg->hdr.size << 2);
dev_err(priv->dev, "Maximal message size (%u bytes) exceeded on TX; got: %i bytes\n", max_size, size << 2);
return -EINVAL;
}
for (i = 0; i < 4 && i < msg->hdr.size; i++)
imx_mu_write(priv, *arg++, priv->dcfg->xTR + (i % 4) * 4);
for (; i < msg->hdr.size; i++) {
for (i = 0; i < num_tr && i < size; i++)
imx_mu_write(priv, *arg++, priv->dcfg->xTR + (i % num_tr) * 4);
for (; i < size; i++) {
ret = readl_poll_timeout(priv->base + priv->dcfg->xSR[IMX_MU_TSR],
xsr,
xsr & IMX_MU_xSR_TEn(priv->dcfg->type, i % 4),
xsr & IMX_MU_xSR_TEn(priv->dcfg->type, i % num_tr),
0, 100);
if (ret) {
dev_err(priv->dev, "Send data index: %d timeout\n", i);
return ret;
}
imx_mu_write(priv, *arg++, priv->dcfg->xTR + (i % 4) * 4);
imx_mu_write(priv, *arg++, priv->dcfg->xTR + (i % num_tr) * 4);
}
imx_mu_xcr_rmw(priv, IMX_MU_TCR, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx), 0);
......@@ -216,23 +234,32 @@ static int imx_mu_scu_tx(struct imx_mu_priv *priv,
return 0;
}
static int imx_mu_scu_rx(struct imx_mu_priv *priv,
struct imx_mu_con_priv *cp)
static int imx_mu_specific_rx(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp)
{
struct imx_sc_rpc_msg_max msg;
u32 *data = (u32 *)&msg;
u32 *data;
int i, ret;
u32 xsr;
u32 size, max_size;
data = (u32 *)priv->msg;
imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, 0));
*data++ = imx_mu_read(priv, priv->dcfg->xRR);
if (msg.hdr.size > sizeof(msg) / 4) {
dev_err(priv->dev, "Maximal message size (%zu bytes) exceeded on RX; got: %i bytes\n", sizeof(msg), msg.hdr.size << 2);
if (priv->dcfg->type & IMX_MU_V2_S4) {
size = ((struct imx_s4_rpc_msg_max *)priv->msg)->hdr.size;
max_size = sizeof(struct imx_s4_rpc_msg_max);
} else {
size = ((struct imx_sc_rpc_msg_max *)priv->msg)->hdr.size;
max_size = sizeof(struct imx_sc_rpc_msg_max);
}
if (size > max_size / 4) {
dev_err(priv->dev, "Maximal message size (%u bytes) exceeded on RX; got: %i bytes\n", max_size, size << 2);
return -EINVAL;
}
for (i = 1; i < msg.hdr.size; i++) {
for (i = 1; i < size; i++) {
ret = readl_poll_timeout(priv->base + priv->dcfg->xSR[IMX_MU_RSR], xsr,
xsr & IMX_MU_xSR_RFn(priv->dcfg->type, i % 4), 0, 100);
if (ret) {
......@@ -243,7 +270,7 @@ static int imx_mu_scu_rx(struct imx_mu_priv *priv,
}
imx_mu_xcr_rmw(priv, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, 0), 0);
mbox_chan_received_data(cp->chan, (void *)&msg);
mbox_chan_received_data(cp->chan, (void *)priv->msg);
return 0;
}
......@@ -394,7 +421,7 @@ static const struct mbox_chan_ops imx_mu_ops = {
.shutdown = imx_mu_shutdown,
};
static struct mbox_chan *imx_mu_scu_xlate(struct mbox_controller *mbox,
static struct mbox_chan *imx_mu_specific_xlate(struct mbox_controller *mbox,
const struct of_phandle_args *sp)
{
u32 type, idx, chan;
......@@ -478,11 +505,12 @@ static void imx_mu_init_generic(struct imx_mu_priv *priv)
imx_mu_write(priv, 0, priv->dcfg->xCR[i]);
}
static void imx_mu_init_scu(struct imx_mu_priv *priv)
static void imx_mu_init_specific(struct imx_mu_priv *priv)
{
unsigned int i;
int num_chans = priv->dcfg->type & IMX_MU_V2_S4 ? IMX_MU_S4_CHANS : IMX_MU_SCU_CHANS;
for (i = 0; i < IMX_MU_SCU_CHANS; i++) {
for (i = 0; i < num_chans; i++) {
struct imx_mu_con_priv *cp = &priv->con_priv[i];
cp->idx = i < 2 ? 0 : i - 2;
......@@ -493,8 +521,8 @@ static void imx_mu_init_scu(struct imx_mu_priv *priv)
"imx_mu_chan[%i-%i]", cp->type, cp->idx);
}
priv->mbox.num_chans = IMX_MU_SCU_CHANS;
priv->mbox.of_xlate = imx_mu_scu_xlate;
priv->mbox.num_chans = num_chans;
priv->mbox.of_xlate = imx_mu_specific_xlate;
/* Set default MU configuration */
for (i = 0; i < IMX_MU_xCR_MAX; i++)
......@@ -508,6 +536,7 @@ static int imx_mu_probe(struct platform_device *pdev)
struct imx_mu_priv *priv;
const struct imx_mu_dcfg *dcfg;
int ret;
u32 size;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
......@@ -528,6 +557,15 @@ static int imx_mu_probe(struct platform_device *pdev)
return -EINVAL;
priv->dcfg = dcfg;
if (priv->dcfg->type & IMX_MU_V2_S4)
size = sizeof(struct imx_s4_rpc_msg_max);
else
size = sizeof(struct imx_sc_rpc_msg_max);
priv->msg = devm_kzalloc(dev, size, GFP_KERNEL);
if (IS_ERR(priv->msg))
return PTR_ERR(priv->msg);
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk)) {
if (PTR_ERR(priv->clk) != -ENOENT)
......@@ -623,10 +661,21 @@ static const struct imx_mu_dcfg imx_mu_cfg_imx8ulp = {
.xCR = {0x110, 0x114, 0x120, 0x128},
};
static const struct imx_mu_dcfg imx_mu_cfg_imx8ulp_s4 = {
.tx = imx_mu_specific_tx,
.rx = imx_mu_specific_rx,
.init = imx_mu_init_specific,
.type = IMX_MU_V2 | IMX_MU_V2_S4,
.xTR = 0x200,
.xRR = 0x280,
.xSR = {0xC, 0x118, 0x124, 0x12C},
.xCR = {0x110, 0x114, 0x120, 0x128},
};
static const struct imx_mu_dcfg imx_mu_cfg_imx8_scu = {
.tx = imx_mu_scu_tx,
.rx = imx_mu_scu_rx,
.init = imx_mu_init_scu,
.tx = imx_mu_specific_tx,
.rx = imx_mu_specific_rx,
.init = imx_mu_init_specific,
.xTR = 0x0,
.xRR = 0x10,
.xSR = {0x20, 0x20, 0x20, 0x20},
......@@ -637,6 +686,7 @@ static const struct of_device_id imx_mu_dt_ids[] = {
{ .compatible = "fsl,imx7ulp-mu", .data = &imx_mu_cfg_imx7ulp },
{ .compatible = "fsl,imx6sx-mu", .data = &imx_mu_cfg_imx6sx },
{ .compatible = "fsl,imx8ulp-mu", .data = &imx_mu_cfg_imx8ulp },
{ .compatible = "fsl,imx8ulp-mu-s4", .data = &imx_mu_cfg_imx8ulp_s4 },
{ .compatible = "fsl,imx8-mu-scu", .data = &imx_mu_cfg_imx8_scu },
{ },
};
......
......@@ -285,7 +285,6 @@ static const struct mbox_chan_ops altera_mbox_ops = {
static int altera_mbox_probe(struct platform_device *pdev)
{
struct altera_mbox *mbox;
struct resource *regs;
struct mbox_chan *chans;
int ret;
......@@ -299,9 +298,7 @@ static int altera_mbox_probe(struct platform_device *pdev)
if (!chans)
return -ENOMEM;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mbox->mbox_base = devm_ioremap_resource(&pdev->dev, regs);
mbox->mbox_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mbox->mbox_base))
return PTR_ERR(mbox->mbox_base);
......
......@@ -408,7 +408,6 @@ static int sti_mbox_probe(struct platform_device *pdev)
struct sti_mbox_device *mdev;
struct device_node *np = pdev->dev.of_node;
struct mbox_chan *chans;
struct resource *res;
int irq;
int ret;
......@@ -425,8 +424,7 @@ static int sti_mbox_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, mdev);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mdev->base = devm_ioremap_resource(&pdev->dev, res);
mdev->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mdev->base))
return PTR_ERR(mdev->base);
......
......@@ -170,7 +170,6 @@ static const struct mbox_chan_ops slimpro_mbox_ops = {
static int slimpro_mbox_probe(struct platform_device *pdev)
{
struct slimpro_mbox *ctx;
struct resource *regs;
void __iomem *mb_base;
int rc;
int i;
......@@ -181,8 +180,7 @@ static int slimpro_mbox_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ctx);
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mb_base = devm_ioremap_resource(&pdev->dev, regs);
mb_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mb_base))
return PTR_ERR(mb_base);
......
......@@ -195,7 +195,6 @@ static void cmdq_task_exec_done(struct cmdq_task *task, int sta)
struct cmdq_task_cb *cb = &task->pkt->async_cb;
struct cmdq_cb_data data;
WARN_ON(cb->cb == (cmdq_async_flush_cb)NULL);
data.sta = sta;
data.data = cb->data;
data.pkt = task->pkt;
......@@ -525,21 +524,20 @@ static struct mbox_chan *cmdq_xlate(struct mbox_controller *mbox,
static int cmdq_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
struct cmdq *cmdq;
int err, i;
struct gce_plat *plat_data;
struct device_node *phandle = dev->of_node;
struct device_node *node;
int alias_id = 0;
char clk_name[4] = "gce";
static const char * const clk_name = "gce";
static const char * const clk_names[] = { "gce0", "gce1" };
cmdq = devm_kzalloc(dev, sizeof(*cmdq), GFP_KERNEL);
if (!cmdq)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
cmdq->base = devm_ioremap_resource(dev, res);
cmdq->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(cmdq->base))
return PTR_ERR(cmdq->base);
......@@ -570,12 +568,9 @@ static int cmdq_probe(struct platform_device *pdev)
if (cmdq->gce_num > 1) {
for_each_child_of_node(phandle->parent, node) {
char clk_id[8];
alias_id = of_alias_get_id(node, clk_name);
if (alias_id < cmdq->gce_num) {
snprintf(clk_id, sizeof(clk_id), "%s%d", clk_name, alias_id);
cmdq->clocks[alias_id].id = clk_id;
if (alias_id >= 0 && alias_id < cmdq->gce_num) {
cmdq->clocks[alias_id].id = clk_names[alias_id];
cmdq->clocks[alias_id].clk = of_clk_get(node, 0);
if (IS_ERR(cmdq->clocks[alias_id].clk)) {
dev_err(dev, "failed to get gce clk: %d\n", alias_id);
......
......@@ -699,7 +699,6 @@ static struct mbox_chan *omap_mbox_of_xlate(struct mbox_controller *controller,
static int omap_mbox_probe(struct platform_device *pdev)
{
struct resource *mem;
int ret;
struct mbox_chan *chnls;
struct omap_mbox **list, *mbox, *mboxblk;
......@@ -776,8 +775,7 @@ static int omap_mbox_probe(struct platform_device *pdev)
if (!mdev)
return -ENOMEM;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mdev->mbox_base = devm_ioremap_resource(&pdev->dev, mem);
mdev->mbox_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mdev->mbox_base))
return PTR_ERR(mdev->mbox_base);
......
......@@ -52,6 +52,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/log2.h>
#include <linux/platform_device.h>
#include <linux/mailbox_controller.h>
#include <linux/mailbox_client.h>
......@@ -62,31 +63,48 @@
#define MBOX_IRQ_NAME "pcc-mbox"
static struct mbox_chan *pcc_mbox_channels;
/* Array of cached virtual address for doorbell registers */
static void __iomem **pcc_doorbell_vaddr;
/* Array of cached virtual address for doorbell ack registers */
static void __iomem **pcc_doorbell_ack_vaddr;
/* Array of doorbell interrupts */
static int *pcc_doorbell_irq;
/**
* struct pcc_chan_reg - PCC register bundle
*
* @vaddr: cached virtual address for this register
* @gas: pointer to the generic address structure for this register
* @preserve_mask: bitmask to preserve when writing to this register
* @set_mask: bitmask to set when writing to this register
* @status_mask: bitmask to determine and/or update the status for this register
*/
struct pcc_chan_reg {
void __iomem *vaddr;
struct acpi_generic_address *gas;
u64 preserve_mask;
u64 set_mask;
u64 status_mask;
};
static struct mbox_controller pcc_mbox_ctrl = {};
/**
* get_pcc_channel - Given a PCC subspace idx, get
* the respective mbox_channel.
* @id: PCC subspace index.
* struct pcc_chan_info - PCC channel specific information
*
* Return: ERR_PTR(errno) if error, else pointer
* to mbox channel.
* @chan: PCC channel information with Shared Memory Region info
* @db: PCC register bundle for the doorbell register
* @plat_irq_ack: PCC register bundle for the platform interrupt acknowledge
* register
* @cmd_complete: PCC register bundle for the command complete check register
* @cmd_update: PCC register bundle for the command complete update register
* @error: PCC register bundle for the error status register
* @plat_irq: platform interrupt
*/
static struct mbox_chan *get_pcc_channel(int id)
{
if (id < 0 || id >= pcc_mbox_ctrl.num_chans)
return ERR_PTR(-ENOENT);
struct pcc_chan_info {
struct pcc_mbox_chan chan;
struct pcc_chan_reg db;
struct pcc_chan_reg plat_irq_ack;
struct pcc_chan_reg cmd_complete;
struct pcc_chan_reg cmd_update;
struct pcc_chan_reg error;
int plat_irq;
};
return &pcc_mbox_channels[id];
}
#define to_pcc_chan_info(c) container_of(c, struct pcc_chan_info, chan)
static struct pcc_chan_info *chan_info;
static int pcc_chan_count;
/*
* PCC can be used with perf critical drivers such as CPPC
......@@ -96,10 +114,8 @@ static struct mbox_chan *get_pcc_channel(int id)
* The below read_register and write_registers are used to read and
* write from perf critical registers such as PCC doorbell register
*/
static int read_register(void __iomem *vaddr, u64 *val, unsigned int bit_width)
static void read_register(void __iomem *vaddr, u64 *val, unsigned int bit_width)
{
int ret_val = 0;
switch (bit_width) {
case 8:
*val = readb(vaddr);
......@@ -113,19 +129,11 @@ static int read_register(void __iomem *vaddr, u64 *val, unsigned int bit_width)
case 64:
*val = readq(vaddr);
break;
default:
pr_debug("Error: Cannot read register of %u bit width",
bit_width);
ret_val = -EFAULT;
break;
}
return ret_val;
}
static int write_register(void __iomem *vaddr, u64 val, unsigned int bit_width)
static void write_register(void __iomem *vaddr, u64 val, unsigned int bit_width)
{
int ret_val = 0;
switch (bit_width) {
case 8:
writeb(val, vaddr);
......@@ -139,13 +147,54 @@ static int write_register(void __iomem *vaddr, u64 val, unsigned int bit_width)
case 64:
writeq(val, vaddr);
break;
default:
pr_debug("Error: Cannot write register of %u bit width",
bit_width);
ret_val = -EFAULT;
break;
}
return ret_val;
}
static int pcc_chan_reg_read(struct pcc_chan_reg *reg, u64 *val)
{
int ret = 0;
if (!reg->gas) {
*val = 0;
return 0;
}
if (reg->vaddr)
read_register(reg->vaddr, val, reg->gas->bit_width);
else
ret = acpi_read(val, reg->gas);
return ret;
}
static int pcc_chan_reg_write(struct pcc_chan_reg *reg, u64 val)
{
int ret = 0;
if (!reg->gas)
return 0;
if (reg->vaddr)
write_register(reg->vaddr, val, reg->gas->bit_width);
else
ret = acpi_write(val, reg->gas);
return ret;
}
static int pcc_chan_reg_read_modify_write(struct pcc_chan_reg *reg)
{
int ret = 0;
u64 val;
ret = pcc_chan_reg_read(reg, &val);
if (ret)
return ret;
val &= reg->preserve_mask;
val |= reg->set_mask;
return pcc_chan_reg_write(reg, val);
}
/**
......@@ -174,43 +223,43 @@ static int pcc_map_interrupt(u32 interrupt, u32 flags)
/**
* pcc_mbox_irq - PCC mailbox interrupt handler
* @irq: interrupt number
* @p: data/cookie passed from the caller to identify the channel
*
* Returns: IRQ_HANDLED if interrupt is handled or IRQ_NONE if not
*/
static irqreturn_t pcc_mbox_irq(int irq, void *p)
{
struct acpi_generic_address *doorbell_ack;
struct acpi_pcct_hw_reduced *pcct_ss;
struct pcc_chan_info *pchan;
struct mbox_chan *chan = p;
u64 doorbell_ack_preserve;
u64 doorbell_ack_write;
u64 doorbell_ack_val;
u64 val;
int ret;
pcct_ss = chan->con_priv;
pchan = chan->con_priv;
mbox_chan_received_data(chan, NULL);
if (pcct_ss->header.type == ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) {
struct acpi_pcct_hw_reduced_type2 *pcct2_ss = chan->con_priv;
u32 id = chan - pcc_mbox_channels;
doorbell_ack = &pcct2_ss->platform_ack_register;
doorbell_ack_preserve = pcct2_ss->ack_preserve_mask;
doorbell_ack_write = pcct2_ss->ack_write_mask;
ret = read_register(pcc_doorbell_ack_vaddr[id],
&doorbell_ack_val,
doorbell_ack->bit_width);
ret = pcc_chan_reg_read(&pchan->cmd_complete, &val);
if (ret)
return IRQ_NONE;
ret = write_register(pcc_doorbell_ack_vaddr[id],
(doorbell_ack_val & doorbell_ack_preserve)
| doorbell_ack_write,
doorbell_ack->bit_width);
val &= pchan->cmd_complete.status_mask;
if (!val)
return IRQ_NONE;
ret = pcc_chan_reg_read(&pchan->error, &val);
if (ret)
return IRQ_NONE;
val &= pchan->error.status_mask;
if (val) {
val &= ~pchan->error.status_mask;
pcc_chan_reg_write(&pchan->error, val);
return IRQ_NONE;
}
if (pcc_chan_reg_read_modify_write(&pchan->plat_irq_ack))
return IRQ_NONE;
mbox_chan_received_data(chan, NULL);
return IRQ_HANDLED;
}
......@@ -224,29 +273,26 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)
* ACPI package. This is used to lookup the array of PCC
* subspaces as parsed by the PCC Mailbox controller.
*
* Return: Pointer to the Mailbox Channel if successful or
* ERR_PTR.
* Return: Pointer to the PCC Mailbox Channel if successful or ERR_PTR.
*/
struct mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl,
int subspace_id)
struct pcc_mbox_chan *
pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)
{
struct device *dev = pcc_mbox_ctrl.dev;
struct pcc_chan_info *pchan;
struct mbox_chan *chan;
struct device *dev;
unsigned long flags;
/*
* Each PCC Subspace is a Mailbox Channel.
* The PCC Clients get their PCC Subspace ID
* from their own tables and pass it here.
* This returns a pointer to the PCC subspace
* for the Client to operate on.
*/
chan = get_pcc_channel(subspace_id);
if (subspace_id < 0 || subspace_id >= pcc_chan_count)
return ERR_PTR(-ENOENT);
pchan = chan_info + subspace_id;
chan = pchan->chan.mchan;
if (IS_ERR(chan) || chan->cl) {
dev_err(dev, "Channel not found for idx: %d\n", subspace_id);
return ERR_PTR(-EBUSY);
}
dev = chan->mbox->dev;
spin_lock_irqsave(&chan->lock, flags);
chan->msg_free = 0;
......@@ -260,44 +306,40 @@ struct mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl,
spin_unlock_irqrestore(&chan->lock, flags);
if (pcc_doorbell_irq[subspace_id] > 0) {
if (pchan->plat_irq > 0) {
int rc;
rc = devm_request_irq(dev, pcc_doorbell_irq[subspace_id],
pcc_mbox_irq, 0, MBOX_IRQ_NAME, chan);
rc = devm_request_irq(dev, pchan->plat_irq, pcc_mbox_irq, 0,
MBOX_IRQ_NAME, chan);
if (unlikely(rc)) {
dev_err(dev, "failed to register PCC interrupt %d\n",
pcc_doorbell_irq[subspace_id]);
pcc_mbox_free_channel(chan);
chan = ERR_PTR(rc);
pchan->plat_irq);
pcc_mbox_free_channel(&pchan->chan);
return ERR_PTR(rc);
}
}
return chan;
return &pchan->chan;
}
EXPORT_SYMBOL_GPL(pcc_mbox_request_channel);
/**
* pcc_mbox_free_channel - Clients call this to free their Channel.
*
* @chan: Pointer to the mailbox channel as returned by
* @pchan: Pointer to the PCC mailbox channel as returned by
* pcc_mbox_request_channel()
*/
void pcc_mbox_free_channel(struct mbox_chan *chan)
void pcc_mbox_free_channel(struct pcc_mbox_chan *pchan)
{
u32 id = chan - pcc_mbox_channels;
struct pcc_chan_info *pchan_info = to_pcc_chan_info(pchan);
struct mbox_chan *chan = pchan->mchan;
unsigned long flags;
if (!chan || !chan->cl)
return;
if (id >= pcc_mbox_ctrl.num_chans) {
pr_debug("pcc_mbox_free_channel: Invalid mbox_chan passed\n");
return;
}
if (pcc_doorbell_irq[id] > 0)
devm_free_irq(chan->mbox->dev, pcc_doorbell_irq[id], chan);
if (pchan_info->plat_irq > 0)
devm_free_irq(chan->mbox->dev, pchan_info->plat_irq, chan);
spin_lock_irqsave(&chan->lock, flags);
chan->cl = NULL;
......@@ -323,40 +365,14 @@ EXPORT_SYMBOL_GPL(pcc_mbox_free_channel);
*/
static int pcc_send_data(struct mbox_chan *chan, void *data)
{
struct acpi_pcct_hw_reduced *pcct_ss = chan->con_priv;
struct acpi_generic_address *doorbell;
u64 doorbell_preserve;
u64 doorbell_val;
u64 doorbell_write;
u32 id = chan - pcc_mbox_channels;
int ret = 0;
if (id >= pcc_mbox_ctrl.num_chans) {
pr_debug("pcc_send_data: Invalid mbox_chan passed\n");
return -ENOENT;
}
doorbell = &pcct_ss->doorbell_register;
doorbell_preserve = pcct_ss->preserve_mask;
doorbell_write = pcct_ss->write_mask;
int ret;
struct pcc_chan_info *pchan = chan->con_priv;
/* Sync notification from OS to Platform. */
if (pcc_doorbell_vaddr[id]) {
ret = read_register(pcc_doorbell_vaddr[id], &doorbell_val,
doorbell->bit_width);
ret = pcc_chan_reg_read_modify_write(&pchan->cmd_update);
if (ret)
return ret;
ret = write_register(pcc_doorbell_vaddr[id],
(doorbell_val & doorbell_preserve) | doorbell_write,
doorbell->bit_width);
} else {
ret = acpi_read(&doorbell_val, doorbell);
if (ret)
return ret;
ret = acpi_write((doorbell_val & doorbell_preserve) | doorbell_write,
doorbell);
}
return ret;
return pcc_chan_reg_read_modify_write(&pchan->db);
}
static const struct mbox_chan_ops pcc_chan_ops = {
......@@ -364,7 +380,7 @@ static const struct mbox_chan_ops pcc_chan_ops = {
};
/**
* parse_pcc_subspaces -- Count PCC subspaces defined
* parse_pcc_subspace - Count PCC subspaces defined
* @header: Pointer to the ACPI subtable header under the PCCT.
* @end: End of subtable entry.
*
......@@ -384,41 +400,172 @@ static int parse_pcc_subspace(union acpi_subtable_headers *header,
return -EINVAL;
}
static int
pcc_chan_reg_init(struct pcc_chan_reg *reg, struct acpi_generic_address *gas,
u64 preserve_mask, u64 set_mask, u64 status_mask, char *name)
{
if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
if (!(gas->bit_width >= 8 && gas->bit_width <= 64 &&
is_power_of_2(gas->bit_width))) {
pr_err("Error: Cannot access register of %u bit width",
gas->bit_width);
return -EFAULT;
}
reg->vaddr = acpi_os_ioremap(gas->address, gas->bit_width / 8);
if (!reg->vaddr) {
pr_err("Failed to ioremap PCC %s register\n", name);
return -ENOMEM;
}
}
reg->gas = gas;
reg->preserve_mask = preserve_mask;
reg->set_mask = set_mask;
reg->status_mask = status_mask;
return 0;
}
/**
* pcc_parse_subspace_irq - Parse the PCC IRQ and PCC ACK register
* There should be one entry per PCC client.
* @id: PCC subspace index.
* @pcct_ss: Pointer to the ACPI subtable header under the PCCT.
*
* @pchan: Pointer to the PCC channel info structure.
* @pcct_entry: Pointer to the ACPI subtable header.
*
* Return: 0 for Success, else errno.
*
* This gets called for each entry in the PCC table.
* There should be one entry per PCC channel. This gets called for each
* entry in the PCC table. This uses PCCY Type1 structure for all applicable
* types(Type 1-4) to fetch irq
*/
static int pcc_parse_subspace_irq(int id,
struct acpi_pcct_hw_reduced *pcct_ss)
static int pcc_parse_subspace_irq(struct pcc_chan_info *pchan,
struct acpi_subtable_header *pcct_entry)
{
pcc_doorbell_irq[id] = pcc_map_interrupt(pcct_ss->platform_interrupt,
int ret = 0;
struct acpi_pcct_hw_reduced *pcct_ss;
if (pcct_entry->type < ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE ||
pcct_entry->type > ACPI_PCCT_TYPE_EXT_PCC_SLAVE_SUBSPACE)
return 0;
pcct_ss = (struct acpi_pcct_hw_reduced *)pcct_entry;
pchan->plat_irq = pcc_map_interrupt(pcct_ss->platform_interrupt,
(u32)pcct_ss->flags);
if (pcc_doorbell_irq[id] <= 0) {
if (pchan->plat_irq <= 0) {
pr_err("PCC GSI %d not registered\n",
pcct_ss->platform_interrupt);
return -EINVAL;
}
if (pcct_ss->header.type
== ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) {
if (pcct_ss->header.type == ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) {
struct acpi_pcct_hw_reduced_type2 *pcct2_ss = (void *)pcct_ss;
pcc_doorbell_ack_vaddr[id] = acpi_os_ioremap(
pcct2_ss->platform_ack_register.address,
pcct2_ss->platform_ack_register.bit_width / 8);
if (!pcc_doorbell_ack_vaddr[id]) {
pr_err("Failed to ioremap PCC ACK register\n");
return -ENOMEM;
ret = pcc_chan_reg_init(&pchan->plat_irq_ack,
&pcct2_ss->platform_ack_register,
pcct2_ss->ack_preserve_mask,
pcct2_ss->ack_write_mask, 0,
"PLAT IRQ ACK");
} else if (pcct_ss->header.type == ACPI_PCCT_TYPE_EXT_PCC_MASTER_SUBSPACE ||
pcct_ss->header.type == ACPI_PCCT_TYPE_EXT_PCC_SLAVE_SUBSPACE) {
struct acpi_pcct_ext_pcc_master *pcct_ext = (void *)pcct_ss;
ret = pcc_chan_reg_init(&pchan->plat_irq_ack,
&pcct_ext->platform_ack_register,
pcct_ext->ack_preserve_mask,
pcct_ext->ack_set_mask, 0,
"PLAT IRQ ACK");
}
return ret;
}
/**
* pcc_parse_subspace_db_reg - Parse the PCC doorbell register
*
* @pchan: Pointer to the PCC channel info structure.
* @pcct_entry: Pointer to the ACPI subtable header.
*
* Return: 0 for Success, else errno.
*/
static int pcc_parse_subspace_db_reg(struct pcc_chan_info *pchan,
struct acpi_subtable_header *pcct_entry)
{
int ret = 0;
if (pcct_entry->type <= ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) {
struct acpi_pcct_subspace *pcct_ss;
pcct_ss = (struct acpi_pcct_subspace *)pcct_entry;
ret = pcc_chan_reg_init(&pchan->db,
&pcct_ss->doorbell_register,
pcct_ss->preserve_mask,
pcct_ss->write_mask, 0, "Doorbell");
} else {
struct acpi_pcct_ext_pcc_master *pcct_ext;
pcct_ext = (struct acpi_pcct_ext_pcc_master *)pcct_entry;
ret = pcc_chan_reg_init(&pchan->db,
&pcct_ext->doorbell_register,
pcct_ext->preserve_mask,
pcct_ext->write_mask, 0, "Doorbell");
if (ret)
return ret;
ret = pcc_chan_reg_init(&pchan->cmd_complete,
&pcct_ext->cmd_complete_register,
0, 0, pcct_ext->cmd_complete_mask,
"Command Complete Check");
if (ret)
return ret;
ret = pcc_chan_reg_init(&pchan->cmd_update,
&pcct_ext->cmd_update_register,
pcct_ext->cmd_update_preserve_mask,
pcct_ext->cmd_update_set_mask, 0,
"Command Complete Update");
if (ret)
return ret;
ret = pcc_chan_reg_init(&pchan->error,
&pcct_ext->error_status_register,
0, 0, pcct_ext->error_status_mask,
"Error Status");
}
return ret;
}
return 0;
/**
* pcc_parse_subspace_shmem - Parse the PCC Shared Memory Region information
*
* @pchan: Pointer to the PCC channel info structure.
* @pcct_entry: Pointer to the ACPI subtable header.
*
*/
static void pcc_parse_subspace_shmem(struct pcc_chan_info *pchan,
struct acpi_subtable_header *pcct_entry)
{
if (pcct_entry->type <= ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) {
struct acpi_pcct_subspace *pcct_ss =
(struct acpi_pcct_subspace *)pcct_entry;
pchan->chan.shmem_base_addr = pcct_ss->base_address;
pchan->chan.shmem_size = pcct_ss->length;
pchan->chan.latency = pcct_ss->latency;
pchan->chan.max_access_rate = pcct_ss->max_access_rate;
pchan->chan.min_turnaround_time = pcct_ss->min_turnaround_time;
} else {
struct acpi_pcct_ext_pcc_master *pcct_ext =
(struct acpi_pcct_ext_pcc_master *)pcct_entry;
pchan->chan.shmem_base_addr = pcct_ext->base_address;
pchan->chan.shmem_size = pcct_ext->length;
pchan->chan.latency = pcct_ext->latency;
pchan->chan.max_access_rate = pcct_ext->max_access_rate;
pchan->chan.min_turnaround_time = pcct_ext->min_turnaround_time;
}
}
/**
......@@ -428,16 +575,12 @@ static int pcc_parse_subspace_irq(int id,
*/
static int __init acpi_pcc_probe(void)
{
int count, i, rc = 0;
acpi_status status;
struct acpi_table_header *pcct_tbl;
struct acpi_subtable_header *pcct_entry;
struct acpi_table_pcct *acpi_pcct_tbl;
struct acpi_subtable_proc proc[ACPI_PCCT_TYPE_RESERVED];
int count, i, rc;
acpi_status status = AE_OK;
/* Search for PCCT */
status = acpi_get_table(ACPI_SIG_PCCT, 0, &pcct_tbl);
if (ACPI_FAILURE(status) || !pcct_tbl)
return -ENODEV;
......@@ -459,33 +602,60 @@ static int __init acpi_pcc_probe(void)
pr_warn("Invalid PCCT: %d PCC subspaces\n", count);
rc = -EINVAL;
goto err_put_pcct;
} else {
pcc_chan_count = count;
}
pcc_mbox_channels = kcalloc(count, sizeof(struct mbox_chan),
acpi_put_table(pcct_tbl);
return rc;
}
/**
* pcc_mbox_probe - Called when we find a match for the
* PCCT platform device. This is purely used to represent
* the PCCT as a virtual device for registering with the
* generic Mailbox framework.
*
* @pdev: Pointer to platform device returned when a match
* is found.
*
* Return: 0 for Success, else errno.
*/
static int pcc_mbox_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mbox_controller *pcc_mbox_ctrl;
struct mbox_chan *pcc_mbox_channels;
struct acpi_table_header *pcct_tbl;
struct acpi_subtable_header *pcct_entry;
struct acpi_table_pcct *acpi_pcct_tbl;
acpi_status status = AE_OK;
int i, rc, count = pcc_chan_count;
/* Search for PCCT */
status = acpi_get_table(ACPI_SIG_PCCT, 0, &pcct_tbl);
if (ACPI_FAILURE(status) || !pcct_tbl)
return -ENODEV;
pcc_mbox_channels = devm_kcalloc(dev, count, sizeof(*pcc_mbox_channels),
GFP_KERNEL);
if (!pcc_mbox_channels) {
pr_err("Could not allocate space for PCC mbox channels\n");
rc = -ENOMEM;
goto err_put_pcct;
}
pcc_doorbell_vaddr = kcalloc(count, sizeof(void *), GFP_KERNEL);
if (!pcc_doorbell_vaddr) {
rc = -ENOMEM;
goto err_free_mbox;
goto err;
}
pcc_doorbell_ack_vaddr = kcalloc(count, sizeof(void *), GFP_KERNEL);
if (!pcc_doorbell_ack_vaddr) {
chan_info = devm_kcalloc(dev, count, sizeof(*chan_info), GFP_KERNEL);
if (!chan_info) {
rc = -ENOMEM;
goto err_free_db_vaddr;
goto err;
}
pcc_doorbell_irq = kcalloc(count, sizeof(int), GFP_KERNEL);
if (!pcc_doorbell_irq) {
pcc_mbox_ctrl = devm_kmalloc(dev, sizeof(*pcc_mbox_ctrl), GFP_KERNEL);
if (!pcc_mbox_ctrl) {
rc = -ENOMEM;
goto err_free_db_ack_vaddr;
goto err;
}
/* Point to the first PCC subspace entry */
......@@ -494,85 +664,55 @@ static int __init acpi_pcc_probe(void)
acpi_pcct_tbl = (struct acpi_table_pcct *) pcct_tbl;
if (acpi_pcct_tbl->flags & ACPI_PCCT_DOORBELL)
pcc_mbox_ctrl.txdone_irq = true;
pcc_mbox_ctrl->txdone_irq = true;
for (i = 0; i < count; i++) {
struct acpi_generic_address *db_reg;
struct acpi_pcct_subspace *pcct_ss;
pcc_mbox_channels[i].con_priv = pcct_entry;
struct pcc_chan_info *pchan = chan_info + i;
if (pcct_entry->type == ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE ||
pcct_entry->type == ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) {
struct acpi_pcct_hw_reduced *pcct_hrss;
pcc_mbox_channels[i].con_priv = pchan;
pchan->chan.mchan = &pcc_mbox_channels[i];
pcct_hrss = (struct acpi_pcct_hw_reduced *) pcct_entry;
if (pcct_entry->type == ACPI_PCCT_TYPE_EXT_PCC_SLAVE_SUBSPACE &&
!pcc_mbox_ctrl->txdone_irq) {
pr_err("Plaform Interrupt flag must be set to 1");
rc = -EINVAL;
goto err;
}
if (pcc_mbox_ctrl.txdone_irq) {
rc = pcc_parse_subspace_irq(i, pcct_hrss);
if (pcc_mbox_ctrl->txdone_irq) {
rc = pcc_parse_subspace_irq(pchan, pcct_entry);
if (rc < 0)
goto err;
}
}
pcct_ss = (struct acpi_pcct_subspace *) pcct_entry;
rc = pcc_parse_subspace_db_reg(pchan, pcct_entry);
if (rc < 0)
goto err;
pcc_parse_subspace_shmem(pchan, pcct_entry);
/* If doorbell is in system memory cache the virt address */
db_reg = &pcct_ss->doorbell_register;
if (db_reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
pcc_doorbell_vaddr[i] = acpi_os_ioremap(db_reg->address,
db_reg->bit_width/8);
pcct_entry = (struct acpi_subtable_header *)
((unsigned long) pcct_entry + pcct_entry->length);
}
pcc_mbox_ctrl.num_chans = count;
pcc_mbox_ctrl->num_chans = count;
pr_info("Detected %d PCC Subspaces\n", pcc_mbox_ctrl.num_chans);
pr_info("Detected %d PCC Subspaces\n", pcc_mbox_ctrl->num_chans);
return 0;
pcc_mbox_ctrl->chans = pcc_mbox_channels;
pcc_mbox_ctrl->ops = &pcc_chan_ops;
pcc_mbox_ctrl->dev = dev;
pr_info("Registering PCC driver as Mailbox controller\n");
rc = mbox_controller_register(pcc_mbox_ctrl);
if (rc)
pr_err("Err registering PCC as Mailbox controller: %d\n", rc);
else
return 0;
err:
kfree(pcc_doorbell_irq);
err_free_db_ack_vaddr:
kfree(pcc_doorbell_ack_vaddr);
err_free_db_vaddr:
kfree(pcc_doorbell_vaddr);
err_free_mbox:
kfree(pcc_mbox_channels);
err_put_pcct:
acpi_put_table(pcct_tbl);
return rc;
}
/**
* pcc_mbox_probe - Called when we find a match for the
* PCCT platform device. This is purely used to represent
* the PCCT as a virtual device for registering with the
* generic Mailbox framework.
*
* @pdev: Pointer to platform device returned when a match
* is found.
*
* Return: 0 for Success, else errno.
*/
static int pcc_mbox_probe(struct platform_device *pdev)
{
int ret = 0;
pcc_mbox_ctrl.chans = pcc_mbox_channels;
pcc_mbox_ctrl.ops = &pcc_chan_ops;
pcc_mbox_ctrl.dev = &pdev->dev;
pr_info("Registering PCC driver as Mailbox controller\n");
ret = mbox_controller_register(&pcc_mbox_ctrl);
if (ret) {
pr_err("Err registering PCC as Mailbox controller: %d\n", ret);
ret = -ENODEV;
}
return ret;
}
static struct platform_driver pcc_mbox_driver = {
.probe = pcc_mbox_probe,
.driver = {
......
......@@ -117,7 +117,6 @@ static int platform_mhu_probe(struct platform_device *pdev)
int i, err;
struct platform_mhu *mhu;
struct device *dev = &pdev->dev;
struct resource *res;
int platform_mhu_reg[MHU_CHANS] = {
MHU_SEC_OFFSET, MHU_LP_OFFSET, MHU_HP_OFFSET
};
......@@ -127,8 +126,7 @@ static int platform_mhu_probe(struct platform_device *pdev)
if (!mhu)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mhu->base = devm_ioremap_resource(dev, res);
mhu->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mhu->base)) {
dev_err(dev, "ioremap failed\n");
return PTR_ERR(mhu->base);
......
......@@ -33,10 +33,6 @@ static const struct qcom_apcs_ipc_data ipq6018_apcs_data = {
.offset = 8, .clk_name = "qcom,apss-ipq6018-clk"
};
static const struct qcom_apcs_ipc_data ipq8074_apcs_data = {
.offset = 8, .clk_name = NULL
};
static const struct qcom_apcs_ipc_data msm8916_apcs_data = {
.offset = 8, .clk_name = "qcom-apcs-msm8916-clk"
};
......@@ -49,18 +45,6 @@ static const struct qcom_apcs_ipc_data msm8996_apcs_data = {
.offset = 16, .clk_name = NULL
};
static const struct qcom_apcs_ipc_data msm8998_apcs_data = {
.offset = 8, .clk_name = NULL
};
static const struct qcom_apcs_ipc_data sdm660_apcs_data = {
.offset = 8, .clk_name = NULL
};
static const struct qcom_apcs_ipc_data sm6125_apcs_data = {
.offset = 8, .clk_name = NULL
};
static const struct qcom_apcs_ipc_data apps_shared_apcs_data = {
.offset = 12, .clk_name = NULL
};
......@@ -95,7 +79,6 @@ static int qcom_apcs_ipc_probe(struct platform_device *pdev)
struct qcom_apcs_ipc *apcs;
const struct qcom_apcs_ipc_data *apcs_data;
struct regmap *regmap;
struct resource *res;
void __iomem *base;
unsigned long i;
int ret;
......@@ -104,8 +87,7 @@ static int qcom_apcs_ipc_probe(struct platform_device *pdev)
if (!apcs)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
......@@ -160,21 +142,22 @@ static int qcom_apcs_ipc_remove(struct platform_device *pdev)
/* .data is the offset of the ipc register within the global block */
static const struct of_device_id qcom_apcs_ipc_of_match[] = {
{ .compatible = "qcom,ipq6018-apcs-apps-global", .data = &ipq6018_apcs_data },
{ .compatible = "qcom,ipq8074-apcs-apps-global", .data = &ipq8074_apcs_data },
{ .compatible = "qcom,ipq8074-apcs-apps-global", .data = &msm8994_apcs_data },
{ .compatible = "qcom,msm8916-apcs-kpss-global", .data = &msm8916_apcs_data },
{ .compatible = "qcom,msm8939-apcs-kpss-global", .data = &msm8916_apcs_data },
{ .compatible = "qcom,msm8953-apcs-kpss-global", .data = &msm8994_apcs_data },
{ .compatible = "qcom,msm8994-apcs-kpss-global", .data = &msm8994_apcs_data },
{ .compatible = "qcom,msm8996-apcs-hmss-global", .data = &msm8996_apcs_data },
{ .compatible = "qcom,msm8998-apcs-hmss-global", .data = &msm8998_apcs_data },
{ .compatible = "qcom,msm8998-apcs-hmss-global", .data = &msm8994_apcs_data },
{ .compatible = "qcom,qcm2290-apcs-hmss-global", .data = &msm8994_apcs_data },
{ .compatible = "qcom,qcs404-apcs-apps-global", .data = &msm8916_apcs_data },
{ .compatible = "qcom,sc7180-apss-shared", .data = &apps_shared_apcs_data },
{ .compatible = "qcom,sc8180x-apss-shared", .data = &apps_shared_apcs_data },
{ .compatible = "qcom,sdm660-apcs-hmss-global", .data = &sdm660_apcs_data },
{ .compatible = "qcom,sdm660-apcs-hmss-global", .data = &msm8994_apcs_data },
{ .compatible = "qcom,sdm845-apss-shared", .data = &apps_shared_apcs_data },
{ .compatible = "qcom,sm6125-apcs-hmss-global", .data = &sm6125_apcs_data },
{ .compatible = "qcom,sm6125-apcs-hmss-global", .data = &msm8994_apcs_data },
{ .compatible = "qcom,sm8150-apss-shared", .data = &apps_shared_apcs_data },
{ .compatible = "qcom,sm6115-apcs-hmss-global", .data = &sdm660_apcs_data },
{ .compatible = "qcom,sm6115-apcs-hmss-global", .data = &msm8994_apcs_data },
{ .compatible = "qcom,sdx55-apcs-gcc", .data = &sdx55_apcs_data },
{}
};
......
......@@ -205,7 +205,6 @@ static int stm32_ipcc_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct stm32_ipcc *ipcc;
struct resource *res;
unsigned long i;
int ret;
u32 ip_ver;
......@@ -235,8 +234,7 @@ static int stm32_ipcc_probe(struct platform_device *pdev)
}
/* regs */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ipcc->reg_base = devm_ioremap_resource(dev, res);
ipcc->reg_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(ipcc->reg_base))
return PTR_ERR(ipcc->reg_base);
......
......@@ -197,7 +197,6 @@ static int sun6i_msgbox_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct mbox_chan *chans;
struct reset_control *reset;
struct resource *res;
struct sun6i_msgbox *mbox;
int i, ret;
......@@ -246,13 +245,7 @@ static int sun6i_msgbox_probe(struct platform_device *pdev)
goto err_disable_unprepare;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
ret = -ENODEV;
goto err_disable_unprepare;
}
mbox->regs = devm_ioremap_resource(&pdev->dev, res);
mbox->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mbox->regs)) {
ret = PTR_ERR(mbox->regs);
dev_err(dev, "Failed to map MMIO resource: %d\n", ret);
......
......@@ -9,18 +9,27 @@
#include <linux/mailbox_controller.h>
#include <linux/mailbox_client.h>
struct pcc_mbox_chan {
struct mbox_chan *mchan;
u64 shmem_base_addr;
u64 shmem_size;
u32 latency;
u32 max_access_rate;
u16 min_turnaround_time;
};
#define MAX_PCC_SUBSPACES 256
#ifdef CONFIG_PCC
extern struct mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl,
int subspace_id);
extern void pcc_mbox_free_channel(struct mbox_chan *chan);
extern struct pcc_mbox_chan *
pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id);
extern void pcc_mbox_free_channel(struct pcc_mbox_chan *chan);
#else
static inline struct mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl,
int subspace_id)
static inline struct pcc_mbox_chan *
pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)
{
return ERR_PTR(-ENODEV);
}
static inline void pcc_mbox_free_channel(struct mbox_chan *chan) { }
static inline void pcc_mbox_free_channel(struct pcc_mbox_chan *chan) { }
#endif
#endif /* _PCC_H */
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/*
* Apple mailbox message format
*
* Copyright (C) 2021 The Asahi Linux Contributors
*/
#ifndef _LINUX_APPLE_MAILBOX_H_
#define _LINUX_APPLE_MAILBOX_H_
#include <linux/types.h>
/* encodes a single 96bit message sent over the single channel */
struct apple_mbox_msg {
u64 msg0;
u32 msg1;
};
#endif
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright 2021 NXP
*
* Header file for the IPC implementation.
*/
#ifndef _S4_IPC_H
#define _S4_IPC_H
struct imx_s4_ipc;
struct imx_s4_rpc_msg {
uint8_t ver;
uint8_t size;
uint8_t cmd;
uint8_t tag;
} __packed;
#endif /* _S4_IPC_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment