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: ...@@ -28,6 +28,7 @@ properties:
- const: fsl,imx7ulp-mu - const: fsl,imx7ulp-mu
- const: fsl,imx8ulp-mu - const: fsl,imx8ulp-mu
- const: fsl,imx8-mu-scu - const: fsl,imx8-mu-scu
- const: fsl,imx8ulp-mu-s4
- items: - items:
- enum: - enum:
- fsl,imx7s-mu - fsl,imx7s-mu
......
...@@ -11,7 +11,7 @@ description: ...@@ -11,7 +11,7 @@ description:
platforms. platforms.
maintainers: maintainers:
- Sivaprakash Murugesan <sivaprak@codeaurora.org> - Jassi Brar <jassisinghbrar@gmail.com>
properties: properties:
compatible: compatible:
...@@ -24,6 +24,7 @@ properties: ...@@ -24,6 +24,7 @@ properties:
- qcom,msm8994-apcs-kpss-global - qcom,msm8994-apcs-kpss-global
- qcom,msm8996-apcs-hmss-global - qcom,msm8996-apcs-hmss-global
- qcom,msm8998-apcs-hmss-global - qcom,msm8998-apcs-hmss-global
- qcom,qcm2290-apcs-hmss-global
- qcom,qcs404-apcs-apps-global - qcom,qcs404-apcs-apps-global
- qcom,sc7180-apss-shared - qcom,sc7180-apss-shared
- qcom,sc8180x-apss-shared - qcom,sc8180x-apss-shared
......
...@@ -401,6 +401,12 @@ L: platform-driver-x86@vger.kernel.org ...@@ -401,6 +401,12 @@ L: platform-driver-x86@vger.kernel.org
S: Maintained S: Maintained
F: drivers/platform/x86/i2c-multi-instantiate.c 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 ACPI PMIC DRIVERS
M: "Rafael J. Wysocki" <rafael@kernel.org> M: "Rafael J. Wysocki" <rafael@kernel.org>
M: Len Brown <lenb@kernel.org> M: Len Brown <lenb@kernel.org>
...@@ -798,7 +804,7 @@ F: Documentation/devicetree/bindings/i2c/i2c-altera.txt ...@@ -798,7 +804,7 @@ F: Documentation/devicetree/bindings/i2c/i2c-altera.txt
F: drivers/i2c/busses/i2c-altera.c F: drivers/i2c/busses/i2c-altera.c
ALTERA MAILBOX DRIVER ALTERA MAILBOX DRIVER
M: Joyce Ooi <joyce.ooi@intel.com> M: Mun Yew Tham <mun.yew.tham@intel.com>
S: Maintained S: Maintained
F: drivers/mailbox/mailbox-altera.c F: drivers/mailbox/mailbox-altera.c
...@@ -1723,11 +1729,14 @@ C: irc://irc.oftc.net/asahi-dev ...@@ -1723,11 +1729,14 @@ C: irc://irc.oftc.net/asahi-dev
T: git https://github.com/AsahiLinux/linux.git T: git https://github.com/AsahiLinux/linux.git
F: Documentation/devicetree/bindings/arm/apple.yaml F: Documentation/devicetree/bindings/arm/apple.yaml
F: Documentation/devicetree/bindings/interrupt-controller/apple,aic.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: Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml
F: arch/arm64/boot/dts/apple/ F: arch/arm64/boot/dts/apple/
F: drivers/irqchip/irq-apple-aic.c 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/interrupt-controller/apple-aic.h
F: include/dt-bindings/pinctrl/apple.h F: include/dt-bindings/pinctrl/apple.h
F: include/linux/apple-mailbox.h
ARM/ARTPEC MACHINE SUPPORT ARM/ARTPEC MACHINE SUPPORT
M: Jesper Nilsson <jesper.nilsson@axis.com> M: Jesper Nilsson <jesper.nilsson@axis.com>
......
...@@ -43,7 +43,7 @@ ...@@ -43,7 +43,7 @@
#include <acpi/cppc_acpi.h> #include <acpi/cppc_acpi.h>
struct cppc_pcc_data { struct cppc_pcc_data {
struct mbox_chan *pcc_channel; struct pcc_mbox_chan *pcc_channel;
void __iomem *pcc_comm_addr; void __iomem *pcc_comm_addr;
bool pcc_channel_acquired; bool pcc_channel_acquired;
unsigned int deadline_us; unsigned int deadline_us;
...@@ -295,7 +295,7 @@ static int send_pcc_cmd(int pcc_ss_id, u16 cmd) ...@@ -295,7 +295,7 @@ static int send_pcc_cmd(int pcc_ss_id, u16 cmd)
pcc_ss_data->platform_owns_pcc = true; pcc_ss_data->platform_owns_pcc = true;
/* Ring doorbell */ /* 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) { if (ret < 0) {
pr_err("Err sending PCC mbox message. ss: %d cmd:%d, ret:%d\n", pr_err("Err sending PCC mbox message. ss: %d cmd:%d, ret:%d\n",
pcc_ss_id, cmd, ret); pcc_ss_id, cmd, ret);
...@@ -308,10 +308,10 @@ static int send_pcc_cmd(int pcc_ss_id, u16 cmd) ...@@ -308,10 +308,10 @@ static int send_pcc_cmd(int pcc_ss_id, u16 cmd)
if (pcc_ss_data->pcc_mrtt) if (pcc_ss_data->pcc_mrtt)
pcc_ss_data->last_cmd_cmpl_time = ktime_get(); pcc_ss_data->last_cmd_cmpl_time = ktime_get();
if (pcc_ss_data->pcc_channel->mbox->txdone_irq) if (pcc_ss_data->pcc_channel->mchan->mbox->txdone_irq)
mbox_chan_txdone(pcc_ss_data->pcc_channel, ret); mbox_chan_txdone(pcc_ss_data->pcc_channel->mchan, ret);
else else
mbox_client_txdone(pcc_ss_data->pcc_channel, ret); mbox_client_txdone(pcc_ss_data->pcc_channel->mchan, ret);
end: end:
if (cmd == CMD_WRITE) { if (cmd == CMD_WRITE) {
...@@ -493,46 +493,33 @@ EXPORT_SYMBOL_GPL(acpi_get_psd_map); ...@@ -493,46 +493,33 @@ EXPORT_SYMBOL_GPL(acpi_get_psd_map);
static int register_pcc_channel(int pcc_ss_idx) 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; u64 usecs_lat;
if (pcc_ss_idx >= 0) { if (pcc_ss_idx >= 0) {
pcc_data[pcc_ss_idx]->pcc_channel = pcc_chan = pcc_mbox_request_channel(&cppc_mbox_cl, pcc_ss_idx);
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", pr_err("Failed to find PCC channel for subspace %d\n",
pcc_ss_idx); pcc_ss_idx);
return -ENODEV; return -ENODEV;
} }
/* pcc_data[pcc_ss_idx]->pcc_channel = 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 = (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;
}
/* /*
* cppc_ss->latency is just a Nominal value. In reality * cppc_ss->latency is just a Nominal value. In reality
* the remote processor could be much slower to reply. * the remote processor could be much slower to reply.
* So add an arbitrary amount of wait on top of Nominal. * 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]->deadline_us = usecs_lat;
pcc_data[pcc_ss_idx]->pcc_mrtt = cppc_ss->min_turnaround_time; pcc_data[pcc_ss_idx]->pcc_mrtt = pcc_chan->min_turnaround_time;
pcc_data[pcc_ss_idx]->pcc_mpar = cppc_ss->max_access_rate; pcc_data[pcc_ss_idx]->pcc_mpar = pcc_chan->max_access_rate;
pcc_data[pcc_ss_idx]->pcc_nominal = cppc_ss->latency; pcc_data[pcc_ss_idx]->pcc_nominal = pcc_chan->latency;
pcc_data[pcc_ss_idx]->pcc_comm_addr = 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) { if (!pcc_data[pcc_ss_idx]->pcc_comm_addr) {
pr_err("Failed to ioremap PCC comm region mem for %d\n", pr_err("Failed to ioremap PCC comm region mem for %d\n",
pcc_ss_idx); pcc_ss_idx);
......
...@@ -93,6 +93,7 @@ struct slimpro_resp_msg { ...@@ -93,6 +93,7 @@ struct slimpro_resp_msg {
struct xgene_hwmon_dev { struct xgene_hwmon_dev {
struct device *dev; struct device *dev;
struct mbox_chan *mbox_chan; struct mbox_chan *mbox_chan;
struct pcc_mbox_chan *pcc_chan;
struct mbox_client mbox_client; struct mbox_client mbox_client;
int mbox_idx; int mbox_idx;
...@@ -652,7 +653,7 @@ static int xgene_hwmon_probe(struct platform_device *pdev) ...@@ -652,7 +653,7 @@ static int xgene_hwmon_probe(struct platform_device *pdev)
goto out_mbox_free; goto out_mbox_free;
} }
} else { } else {
struct acpi_pcct_hw_reduced *cppc_ss; struct pcc_mbox_chan *pcc_chan;
const struct acpi_device_id *acpi_id; const struct acpi_device_id *acpi_id;
int version; int version;
...@@ -671,26 +672,16 @@ static int xgene_hwmon_probe(struct platform_device *pdev) ...@@ -671,26 +672,16 @@ static int xgene_hwmon_probe(struct platform_device *pdev)
} }
cl->rx_callback = xgene_hwmon_pcc_rx_cb; cl->rx_callback = xgene_hwmon_pcc_rx_cb;
ctx->mbox_chan = pcc_mbox_request_channel(cl, ctx->mbox_idx); pcc_chan = pcc_mbox_request_channel(cl, ctx->mbox_idx);
if (IS_ERR(ctx->mbox_chan)) { if (IS_ERR(pcc_chan)) {
dev_err(&pdev->dev, dev_err(&pdev->dev,
"PPC channel request failed\n"); "PPC channel request failed\n");
rc = -ENODEV; rc = -ENODEV;
goto out_mbox_free; goto out_mbox_free;
} }
/* ctx->pcc_chan = pcc_chan;
* The PCC mailbox controller driver should ctx->mbox_chan = pcc_chan->mchan;
* 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;
}
if (!ctx->mbox_chan->mbox->txdone_irq) { if (!ctx->mbox_chan->mbox->txdone_irq) {
dev_err(&pdev->dev, "PCC IRQ not supported\n"); dev_err(&pdev->dev, "PCC IRQ not supported\n");
...@@ -702,16 +693,16 @@ static int xgene_hwmon_probe(struct platform_device *pdev) ...@@ -702,16 +693,16 @@ static int xgene_hwmon_probe(struct platform_device *pdev)
* This is the shared communication region * This is the shared communication region
* for the OS and Platform to communicate over. * 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 (ctx->comm_base_addr) {
if (version == XGENE_HWMON_V2) if (version == XGENE_HWMON_V2)
ctx->pcc_comm_addr = (void __force *)ioremap( ctx->pcc_comm_addr = (void __force *)ioremap(
ctx->comm_base_addr, ctx->comm_base_addr,
cppc_ss->length); pcc_chan->shmem_size);
else else
ctx->pcc_comm_addr = memremap( ctx->pcc_comm_addr = memremap(
ctx->comm_base_addr, ctx->comm_base_addr,
cppc_ss->length, pcc_chan->shmem_size,
MEMREMAP_WB); MEMREMAP_WB);
} else { } else {
dev_err(&pdev->dev, "Failed to get PCC comm region\n"); dev_err(&pdev->dev, "Failed to get PCC comm region\n");
...@@ -727,11 +718,11 @@ static int xgene_hwmon_probe(struct platform_device *pdev) ...@@ -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. * the remote processor could be much slower to reply.
* So add an arbitrary amount of wait on top of Nominal. * 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, ctx->hwmon_dev = hwmon_device_register_with_groups(ctx->dev,
...@@ -757,7 +748,7 @@ static int xgene_hwmon_probe(struct platform_device *pdev) ...@@ -757,7 +748,7 @@ static int xgene_hwmon_probe(struct platform_device *pdev)
if (acpi_disabled) if (acpi_disabled)
mbox_free_channel(ctx->mbox_chan); mbox_free_channel(ctx->mbox_chan);
else else
pcc_mbox_free_channel(ctx->mbox_chan); pcc_mbox_free_channel(ctx->pcc_chan);
out_mbox_free: out_mbox_free:
kfifo_free(&ctx->async_msg_fifo); kfifo_free(&ctx->async_msg_fifo);
...@@ -773,7 +764,7 @@ static int xgene_hwmon_remove(struct platform_device *pdev) ...@@ -773,7 +764,7 @@ static int xgene_hwmon_remove(struct platform_device *pdev)
if (acpi_disabled) if (acpi_disabled)
mbox_free_channel(ctx->mbox_chan); mbox_free_channel(ctx->mbox_chan);
else else
pcc_mbox_free_channel(ctx->mbox_chan); pcc_mbox_free_channel(ctx->pcc_chan);
return 0; return 0;
} }
......
...@@ -103,6 +103,7 @@ struct slimpro_i2c_dev { ...@@ -103,6 +103,7 @@ struct slimpro_i2c_dev {
struct i2c_adapter adapter; struct i2c_adapter adapter;
struct device *dev; struct device *dev;
struct mbox_chan *mbox_chan; struct mbox_chan *mbox_chan;
struct pcc_mbox_chan *pcc_chan;
struct mbox_client mbox_client; struct mbox_client mbox_client;
int mbox_idx; int mbox_idx;
struct completion rd_complete; struct completion rd_complete;
...@@ -466,7 +467,7 @@ static int xgene_slimpro_i2c_probe(struct platform_device *pdev) ...@@ -466,7 +467,7 @@ static int xgene_slimpro_i2c_probe(struct platform_device *pdev)
return PTR_ERR(ctx->mbox_chan); return PTR_ERR(ctx->mbox_chan);
} }
} else { } else {
struct acpi_pcct_hw_reduced *cppc_ss; struct pcc_mbox_chan *pcc_chan;
const struct acpi_device_id *acpi_id; const struct acpi_device_id *acpi_id;
int version = XGENE_SLIMPRO_I2C_V1; int version = XGENE_SLIMPRO_I2C_V1;
...@@ -483,24 +484,14 @@ static int xgene_slimpro_i2c_probe(struct platform_device *pdev) ...@@ -483,24 +484,14 @@ static int xgene_slimpro_i2c_probe(struct platform_device *pdev)
cl->tx_block = false; cl->tx_block = false;
cl->rx_callback = slimpro_i2c_pcc_rx_cb; cl->rx_callback = slimpro_i2c_pcc_rx_cb;
ctx->mbox_chan = pcc_mbox_request_channel(cl, ctx->mbox_idx); pcc_chan = pcc_mbox_request_channel(cl, ctx->mbox_idx);
if (IS_ERR(ctx->mbox_chan)) { if (IS_ERR(pcc_chan)) {
dev_err(&pdev->dev, "PCC mailbox channel request failed\n"); dev_err(&pdev->dev, "PCC mailbox channel request failed\n");
return PTR_ERR(ctx->mbox_chan); return PTR_ERR(ctx->pcc_chan);
} }
/* ctx->pcc_chan = pcc_chan;
* The PCC mailbox controller driver should ctx->mbox_chan = pcc_chan->mchan;
* 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;
}
if (!ctx->mbox_chan->mbox->txdone_irq) { if (!ctx->mbox_chan->mbox->txdone_irq) {
dev_err(&pdev->dev, "PCC IRQ not supported\n"); dev_err(&pdev->dev, "PCC IRQ not supported\n");
...@@ -512,17 +503,17 @@ static int xgene_slimpro_i2c_probe(struct platform_device *pdev) ...@@ -512,17 +503,17 @@ static int xgene_slimpro_i2c_probe(struct platform_device *pdev)
* This is the shared communication region * This is the shared communication region
* for the OS and Platform to communicate over. * 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 (ctx->comm_base_addr) {
if (version == XGENE_SLIMPRO_I2C_V2) if (version == XGENE_SLIMPRO_I2C_V2)
ctx->pcc_comm_addr = memremap( ctx->pcc_comm_addr = memremap(
ctx->comm_base_addr, ctx->comm_base_addr,
cppc_ss->length, pcc_chan->shmem_size,
MEMREMAP_WT); MEMREMAP_WT);
else else
ctx->pcc_comm_addr = memremap( ctx->pcc_comm_addr = memremap(
ctx->comm_base_addr, ctx->comm_base_addr,
cppc_ss->length, pcc_chan->shmem_size,
MEMREMAP_WB); MEMREMAP_WB);
} else { } else {
dev_err(&pdev->dev, "Failed to get PCC comm region\n"); 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) ...@@ -561,7 +552,7 @@ static int xgene_slimpro_i2c_probe(struct platform_device *pdev)
if (acpi_disabled) if (acpi_disabled)
mbox_free_channel(ctx->mbox_chan); mbox_free_channel(ctx->mbox_chan);
else else
pcc_mbox_free_channel(ctx->mbox_chan); pcc_mbox_free_channel(ctx->pcc_chan);
return rc; return rc;
} }
...@@ -575,7 +566,7 @@ static int xgene_slimpro_i2c_remove(struct platform_device *pdev) ...@@ -575,7 +566,7 @@ static int xgene_slimpro_i2c_remove(struct platform_device *pdev)
if (acpi_disabled) if (acpi_disabled)
mbox_free_channel(ctx->mbox_chan); mbox_free_channel(ctx->mbox_chan);
else else
pcc_mbox_free_channel(ctx->mbox_chan); pcc_mbox_free_channel(ctx->pcc_chan);
return 0; return 0;
} }
......
...@@ -8,6 +8,18 @@ menuconfig MAILBOX ...@@ -8,6 +8,18 @@ menuconfig MAILBOX
if 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 config ARM_MHU
tristate "ARM MHU Mailbox" tristate "ARM MHU Mailbox"
depends on ARM_AMBA depends on ARM_AMBA
......
...@@ -58,3 +58,5 @@ obj-$(CONFIG_SUN6I_MSGBOX) += sun6i-msgbox.o ...@@ -58,3 +58,5 @@ obj-$(CONFIG_SUN6I_MSGBOX) += sun6i-msgbox.o
obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox.o obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox.o
obj-$(CONFIG_QCOM_IPCC) += qcom-ipcc.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) ...@@ -137,7 +137,6 @@ static int bcm2835_mbox_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
int ret = 0; int ret = 0;
struct resource *iomem;
struct bcm2835_mbox *mbox; struct bcm2835_mbox *mbox;
mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
...@@ -153,8 +152,7 @@ static int bcm2835_mbox_probe(struct platform_device *pdev) ...@@ -153,8 +152,7 @@ static int bcm2835_mbox_probe(struct platform_device *pdev)
return -ENODEV; return -ENODEV;
} }
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); mbox->regs = devm_platform_ioremap_resource(pdev, 0);
mbox->regs = devm_ioremap_resource(&pdev->dev, iomem);
if (IS_ERR(mbox->regs)) { if (IS_ERR(mbox->regs)) {
ret = PTR_ERR(mbox->regs); ret = PTR_ERR(mbox->regs);
return ret; return ret;
......
...@@ -240,7 +240,6 @@ static int hi3660_mbox_probe(struct platform_device *pdev) ...@@ -240,7 +240,6 @@ static int hi3660_mbox_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct hi3660_mbox *mbox; struct hi3660_mbox *mbox;
struct mbox_chan *chan; struct mbox_chan *chan;
struct resource *res;
unsigned long ch; unsigned long ch;
int err; int err;
...@@ -248,8 +247,7 @@ static int hi3660_mbox_probe(struct platform_device *pdev) ...@@ -248,8 +247,7 @@ static int hi3660_mbox_probe(struct platform_device *pdev)
if (!mbox) if (!mbox)
return -ENOMEM; return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); mbox->base = devm_platform_ioremap_resource(pdev, 0);
mbox->base = devm_ioremap_resource(dev, res);
if (IS_ERR(mbox->base)) if (IS_ERR(mbox->base))
return PTR_ERR(mbox->base); return PTR_ERR(mbox->base);
......
...@@ -264,7 +264,6 @@ static int hi6220_mbox_probe(struct platform_device *pdev) ...@@ -264,7 +264,6 @@ static int hi6220_mbox_probe(struct platform_device *pdev)
struct device_node *node = pdev->dev.of_node; struct device_node *node = pdev->dev.of_node;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct hi6220_mbox *mbox; struct hi6220_mbox *mbox;
struct resource *res;
int i, err; int i, err;
mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
...@@ -287,15 +286,13 @@ static int hi6220_mbox_probe(struct platform_device *pdev) ...@@ -287,15 +286,13 @@ static int hi6220_mbox_probe(struct platform_device *pdev)
if (mbox->irq < 0) if (mbox->irq < 0)
return mbox->irq; return mbox->irq;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); mbox->ipc = devm_platform_ioremap_resource(pdev, 0);
mbox->ipc = devm_ioremap_resource(dev, res);
if (IS_ERR(mbox->ipc)) { if (IS_ERR(mbox->ipc)) {
dev_err(dev, "ioremap ipc failed\n"); dev_err(dev, "ioremap ipc failed\n");
return PTR_ERR(mbox->ipc); return PTR_ERR(mbox->ipc);
} }
res = platform_get_resource(pdev, IORESOURCE_MEM, 1); mbox->base = devm_platform_ioremap_resource(pdev, 1);
mbox->base = devm_ioremap_resource(dev, res);
if (IS_ERR(mbox->base)) { if (IS_ERR(mbox->base)) {
dev_err(dev, "ioremap buffer failed\n"); dev_err(dev, "ioremap buffer failed\n");
return PTR_ERR(mbox->base); return PTR_ERR(mbox->base);
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/firmware/imx/ipc.h> #include <linux/firmware/imx/ipc.h>
#include <linux/firmware/imx/s4.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
...@@ -18,6 +19,8 @@ ...@@ -18,6 +19,8 @@
#define IMX_MU_CHANS 16 #define IMX_MU_CHANS 16
/* TX0/RX0/RXDB[0-3] */ /* TX0/RX0/RXDB[0-3] */
#define IMX_MU_SCU_CHANS 6 #define IMX_MU_SCU_CHANS 6
/* TX0/RX0 */
#define IMX_MU_S4_CHANS 2
#define IMX_MU_CHAN_NAME_SIZE 20 #define IMX_MU_CHAN_NAME_SIZE 20
enum imx_mu_chan_type { enum imx_mu_chan_type {
...@@ -47,6 +50,11 @@ struct imx_sc_rpc_msg_max { ...@@ -47,6 +50,11 @@ struct imx_sc_rpc_msg_max {
u32 data[7]; u32 data[7];
}; };
struct imx_s4_rpc_msg_max {
struct imx_s4_rpc_msg hdr;
u32 data[254];
};
struct imx_mu_con_priv { struct imx_mu_con_priv {
unsigned int idx; unsigned int idx;
char irq_desc[IMX_MU_CHAN_NAME_SIZE]; char irq_desc[IMX_MU_CHAN_NAME_SIZE];
...@@ -58,6 +66,7 @@ struct imx_mu_con_priv { ...@@ -58,6 +66,7 @@ struct imx_mu_con_priv {
struct imx_mu_priv { struct imx_mu_priv {
struct device *dev; struct device *dev;
void __iomem *base; void __iomem *base;
void *msg;
spinlock_t xcr_lock; /* control register lock */ spinlock_t xcr_lock; /* control register lock */
struct mbox_controller mbox; struct mbox_controller mbox;
...@@ -75,7 +84,8 @@ struct imx_mu_priv { ...@@ -75,7 +84,8 @@ struct imx_mu_priv {
enum imx_mu_type { enum imx_mu_type {
IMX_MU_V1, IMX_MU_V1,
IMX_MU_V2, IMX_MU_V2 = BIT(1),
IMX_MU_V2_S4 = BIT(15),
}; };
struct imx_mu_dcfg { struct imx_mu_dcfg {
...@@ -89,18 +99,18 @@ struct imx_mu_dcfg { ...@@ -89,18 +99,18 @@ struct imx_mu_dcfg {
u32 xCR[4]; /* Control Registers */ 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_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_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_TEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x))))
/* General Purpose Interrupt Enable */ /* 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 */ /* 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 */ /* 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 */ /* 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) 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, ...@@ -167,14 +177,22 @@ static int imx_mu_generic_rx(struct imx_mu_priv *priv,
return 0; return 0;
} }
static int imx_mu_scu_tx(struct imx_mu_priv *priv, static int imx_mu_specific_tx(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, void *data)
struct imx_mu_con_priv *cp,
void *data)
{ {
struct imx_sc_rpc_msg_max *msg = data;
u32 *arg = data; u32 *arg = data;
int i, ret; int i, ret;
u32 xsr; 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) { switch (cp->type) {
case IMX_MU_TYPE_TX: case IMX_MU_TYPE_TX:
...@@ -183,27 +201,27 @@ static int imx_mu_scu_tx(struct imx_mu_priv *priv, ...@@ -183,27 +201,27 @@ static int imx_mu_scu_tx(struct imx_mu_priv *priv,
* sizeof yields bytes. * sizeof yields bytes.
*/ */
if (msg->hdr.size > sizeof(*msg) / 4) { if (size > max_size / 4) {
/* /*
* The real message size can be different to * 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; return -EINVAL;
} }
for (i = 0; i < 4 && i < msg->hdr.size; i++) for (i = 0; i < num_tr && i < size; i++)
imx_mu_write(priv, *arg++, priv->dcfg->xTR + (i % 4) * 4); imx_mu_write(priv, *arg++, priv->dcfg->xTR + (i % num_tr) * 4);
for (; i < msg->hdr.size; i++) { for (; i < size; i++) {
ret = readl_poll_timeout(priv->base + priv->dcfg->xSR[IMX_MU_TSR], ret = readl_poll_timeout(priv->base + priv->dcfg->xSR[IMX_MU_TSR],
xsr, xsr,
xsr & IMX_MU_xSR_TEn(priv->dcfg->type, i % 4), xsr & IMX_MU_xSR_TEn(priv->dcfg->type, i % num_tr),
0, 100); 0, 100);
if (ret) { if (ret) {
dev_err(priv->dev, "Send data index: %d timeout\n", i); dev_err(priv->dev, "Send data index: %d timeout\n", i);
return ret; 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); 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, ...@@ -216,23 +234,32 @@ static int imx_mu_scu_tx(struct imx_mu_priv *priv,
return 0; return 0;
} }
static int imx_mu_scu_rx(struct imx_mu_priv *priv, static int imx_mu_specific_rx(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp)
struct imx_mu_con_priv *cp)
{ {
struct imx_sc_rpc_msg_max msg; u32 *data;
u32 *data = (u32 *)&msg;
int i, ret; int i, ret;
u32 xsr; 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)); 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); *data++ = imx_mu_read(priv, priv->dcfg->xRR);
if (msg.hdr.size > sizeof(msg) / 4) { if (priv->dcfg->type & IMX_MU_V2_S4) {
dev_err(priv->dev, "Maximal message size (%zu bytes) exceeded on RX; got: %i bytes\n", sizeof(msg), msg.hdr.size << 2); 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; 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, 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); xsr & IMX_MU_xSR_RFn(priv->dcfg->type, i % 4), 0, 100);
if (ret) { if (ret) {
...@@ -243,7 +270,7 @@ static int imx_mu_scu_rx(struct imx_mu_priv *priv, ...@@ -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); 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; return 0;
} }
...@@ -394,7 +421,7 @@ static const struct mbox_chan_ops imx_mu_ops = { ...@@ -394,7 +421,7 @@ static const struct mbox_chan_ops imx_mu_ops = {
.shutdown = imx_mu_shutdown, .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) const struct of_phandle_args *sp)
{ {
u32 type, idx, chan; u32 type, idx, chan;
...@@ -478,11 +505,12 @@ static void imx_mu_init_generic(struct imx_mu_priv *priv) ...@@ -478,11 +505,12 @@ static void imx_mu_init_generic(struct imx_mu_priv *priv)
imx_mu_write(priv, 0, priv->dcfg->xCR[i]); 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; 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]; struct imx_mu_con_priv *cp = &priv->con_priv[i];
cp->idx = i < 2 ? 0 : i - 2; cp->idx = i < 2 ? 0 : i - 2;
...@@ -493,8 +521,8 @@ static void imx_mu_init_scu(struct imx_mu_priv *priv) ...@@ -493,8 +521,8 @@ static void imx_mu_init_scu(struct imx_mu_priv *priv)
"imx_mu_chan[%i-%i]", cp->type, cp->idx); "imx_mu_chan[%i-%i]", cp->type, cp->idx);
} }
priv->mbox.num_chans = IMX_MU_SCU_CHANS; priv->mbox.num_chans = num_chans;
priv->mbox.of_xlate = imx_mu_scu_xlate; priv->mbox.of_xlate = imx_mu_specific_xlate;
/* Set default MU configuration */ /* Set default MU configuration */
for (i = 0; i < IMX_MU_xCR_MAX; i++) for (i = 0; i < IMX_MU_xCR_MAX; i++)
...@@ -508,6 +536,7 @@ static int imx_mu_probe(struct platform_device *pdev) ...@@ -508,6 +536,7 @@ static int imx_mu_probe(struct platform_device *pdev)
struct imx_mu_priv *priv; struct imx_mu_priv *priv;
const struct imx_mu_dcfg *dcfg; const struct imx_mu_dcfg *dcfg;
int ret; int ret;
u32 size;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv) if (!priv)
...@@ -528,6 +557,15 @@ static int imx_mu_probe(struct platform_device *pdev) ...@@ -528,6 +557,15 @@ static int imx_mu_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
priv->dcfg = dcfg; 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); priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk)) { if (IS_ERR(priv->clk)) {
if (PTR_ERR(priv->clk) != -ENOENT) if (PTR_ERR(priv->clk) != -ENOENT)
...@@ -623,10 +661,21 @@ static const struct imx_mu_dcfg imx_mu_cfg_imx8ulp = { ...@@ -623,10 +661,21 @@ static const struct imx_mu_dcfg imx_mu_cfg_imx8ulp = {
.xCR = {0x110, 0x114, 0x120, 0x128}, .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 = { static const struct imx_mu_dcfg imx_mu_cfg_imx8_scu = {
.tx = imx_mu_scu_tx, .tx = imx_mu_specific_tx,
.rx = imx_mu_scu_rx, .rx = imx_mu_specific_rx,
.init = imx_mu_init_scu, .init = imx_mu_init_specific,
.xTR = 0x0, .xTR = 0x0,
.xRR = 0x10, .xRR = 0x10,
.xSR = {0x20, 0x20, 0x20, 0x20}, .xSR = {0x20, 0x20, 0x20, 0x20},
...@@ -637,6 +686,7 @@ static const struct of_device_id imx_mu_dt_ids[] = { ...@@ -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,imx7ulp-mu", .data = &imx_mu_cfg_imx7ulp },
{ .compatible = "fsl,imx6sx-mu", .data = &imx_mu_cfg_imx6sx }, { .compatible = "fsl,imx6sx-mu", .data = &imx_mu_cfg_imx6sx },
{ .compatible = "fsl,imx8ulp-mu", .data = &imx_mu_cfg_imx8ulp }, { .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 }, { .compatible = "fsl,imx8-mu-scu", .data = &imx_mu_cfg_imx8_scu },
{ }, { },
}; };
......
...@@ -285,7 +285,6 @@ static const struct mbox_chan_ops altera_mbox_ops = { ...@@ -285,7 +285,6 @@ static const struct mbox_chan_ops altera_mbox_ops = {
static int altera_mbox_probe(struct platform_device *pdev) static int altera_mbox_probe(struct platform_device *pdev)
{ {
struct altera_mbox *mbox; struct altera_mbox *mbox;
struct resource *regs;
struct mbox_chan *chans; struct mbox_chan *chans;
int ret; int ret;
...@@ -299,9 +298,7 @@ static int altera_mbox_probe(struct platform_device *pdev) ...@@ -299,9 +298,7 @@ static int altera_mbox_probe(struct platform_device *pdev)
if (!chans) if (!chans)
return -ENOMEM; return -ENOMEM;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); mbox->mbox_base = devm_platform_ioremap_resource(pdev, 0);
mbox->mbox_base = devm_ioremap_resource(&pdev->dev, regs);
if (IS_ERR(mbox->mbox_base)) if (IS_ERR(mbox->mbox_base))
return PTR_ERR(mbox->mbox_base); return PTR_ERR(mbox->mbox_base);
......
...@@ -408,7 +408,6 @@ static int sti_mbox_probe(struct platform_device *pdev) ...@@ -408,7 +408,6 @@ static int sti_mbox_probe(struct platform_device *pdev)
struct sti_mbox_device *mdev; struct sti_mbox_device *mdev;
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
struct mbox_chan *chans; struct mbox_chan *chans;
struct resource *res;
int irq; int irq;
int ret; int ret;
...@@ -425,8 +424,7 @@ static int sti_mbox_probe(struct platform_device *pdev) ...@@ -425,8 +424,7 @@ static int sti_mbox_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, mdev); platform_set_drvdata(pdev, mdev);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); mdev->base = devm_platform_ioremap_resource(pdev, 0);
mdev->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(mdev->base)) if (IS_ERR(mdev->base))
return PTR_ERR(mdev->base); return PTR_ERR(mdev->base);
......
...@@ -170,7 +170,6 @@ static const struct mbox_chan_ops slimpro_mbox_ops = { ...@@ -170,7 +170,6 @@ static const struct mbox_chan_ops slimpro_mbox_ops = {
static int slimpro_mbox_probe(struct platform_device *pdev) static int slimpro_mbox_probe(struct platform_device *pdev)
{ {
struct slimpro_mbox *ctx; struct slimpro_mbox *ctx;
struct resource *regs;
void __iomem *mb_base; void __iomem *mb_base;
int rc; int rc;
int i; int i;
...@@ -181,8 +180,7 @@ static int slimpro_mbox_probe(struct platform_device *pdev) ...@@ -181,8 +180,7 @@ static int slimpro_mbox_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ctx); platform_set_drvdata(pdev, ctx);
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); mb_base = devm_platform_ioremap_resource(pdev, 0);
mb_base = devm_ioremap_resource(&pdev->dev, regs);
if (IS_ERR(mb_base)) if (IS_ERR(mb_base))
return PTR_ERR(mb_base); return PTR_ERR(mb_base);
......
...@@ -195,7 +195,6 @@ static void cmdq_task_exec_done(struct cmdq_task *task, int sta) ...@@ -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_task_cb *cb = &task->pkt->async_cb;
struct cmdq_cb_data data; struct cmdq_cb_data data;
WARN_ON(cb->cb == (cmdq_async_flush_cb)NULL);
data.sta = sta; data.sta = sta;
data.data = cb->data; data.data = cb->data;
data.pkt = task->pkt; data.pkt = task->pkt;
...@@ -525,21 +524,20 @@ static struct mbox_chan *cmdq_xlate(struct mbox_controller *mbox, ...@@ -525,21 +524,20 @@ static struct mbox_chan *cmdq_xlate(struct mbox_controller *mbox,
static int cmdq_probe(struct platform_device *pdev) static int cmdq_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct resource *res;
struct cmdq *cmdq; struct cmdq *cmdq;
int err, i; int err, i;
struct gce_plat *plat_data; struct gce_plat *plat_data;
struct device_node *phandle = dev->of_node; struct device_node *phandle = dev->of_node;
struct device_node *node; struct device_node *node;
int alias_id = 0; 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); cmdq = devm_kzalloc(dev, sizeof(*cmdq), GFP_KERNEL);
if (!cmdq) if (!cmdq)
return -ENOMEM; return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); cmdq->base = devm_platform_ioremap_resource(pdev, 0);
cmdq->base = devm_ioremap_resource(dev, res);
if (IS_ERR(cmdq->base)) if (IS_ERR(cmdq->base))
return PTR_ERR(cmdq->base); return PTR_ERR(cmdq->base);
...@@ -570,12 +568,9 @@ static int cmdq_probe(struct platform_device *pdev) ...@@ -570,12 +568,9 @@ static int cmdq_probe(struct platform_device *pdev)
if (cmdq->gce_num > 1) { if (cmdq->gce_num > 1) {
for_each_child_of_node(phandle->parent, node) { for_each_child_of_node(phandle->parent, node) {
char clk_id[8];
alias_id = of_alias_get_id(node, clk_name); alias_id = of_alias_get_id(node, clk_name);
if (alias_id < cmdq->gce_num) { if (alias_id >= 0 && alias_id < cmdq->gce_num) {
snprintf(clk_id, sizeof(clk_id), "%s%d", clk_name, alias_id); cmdq->clocks[alias_id].id = clk_names[alias_id];
cmdq->clocks[alias_id].id = clk_id;
cmdq->clocks[alias_id].clk = of_clk_get(node, 0); cmdq->clocks[alias_id].clk = of_clk_get(node, 0);
if (IS_ERR(cmdq->clocks[alias_id].clk)) { if (IS_ERR(cmdq->clocks[alias_id].clk)) {
dev_err(dev, "failed to get gce clk: %d\n", alias_id); 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, ...@@ -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) static int omap_mbox_probe(struct platform_device *pdev)
{ {
struct resource *mem;
int ret; int ret;
struct mbox_chan *chnls; struct mbox_chan *chnls;
struct omap_mbox **list, *mbox, *mboxblk; struct omap_mbox **list, *mbox, *mboxblk;
...@@ -776,8 +775,7 @@ static int omap_mbox_probe(struct platform_device *pdev) ...@@ -776,8 +775,7 @@ static int omap_mbox_probe(struct platform_device *pdev)
if (!mdev) if (!mdev)
return -ENOMEM; return -ENOMEM;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); mdev->mbox_base = devm_platform_ioremap_resource(pdev, 0);
mdev->mbox_base = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(mdev->mbox_base)) if (IS_ERR(mdev->mbox_base))
return PTR_ERR(mdev->mbox_base); return PTR_ERR(mdev->mbox_base);
......
...@@ -52,6 +52,7 @@ ...@@ -52,6 +52,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/log2.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/mailbox_controller.h> #include <linux/mailbox_controller.h>
#include <linux/mailbox_client.h> #include <linux/mailbox_client.h>
...@@ -62,31 +63,48 @@ ...@@ -62,31 +63,48 @@
#define MBOX_IRQ_NAME "pcc-mbox" #define MBOX_IRQ_NAME "pcc-mbox"
static struct mbox_chan *pcc_mbox_channels; /**
* struct pcc_chan_reg - PCC register bundle
/* Array of cached virtual address for doorbell registers */ *
static void __iomem **pcc_doorbell_vaddr; * @vaddr: cached virtual address for this register
/* Array of cached virtual address for doorbell ack registers */ * @gas: pointer to the generic address structure for this register
static void __iomem **pcc_doorbell_ack_vaddr; * @preserve_mask: bitmask to preserve when writing to this register
/* Array of doorbell interrupts */ * @set_mask: bitmask to set when writing to this register
static int *pcc_doorbell_irq; * @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 * struct pcc_chan_info - PCC channel specific information
* the respective mbox_channel.
* @id: PCC subspace index.
* *
* Return: ERR_PTR(errno) if error, else pointer * @chan: PCC channel information with Shared Memory Region info
* to mbox channel. * @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) struct pcc_chan_info {
{ struct pcc_mbox_chan chan;
if (id < 0 || id >= pcc_mbox_ctrl.num_chans) struct pcc_chan_reg db;
return ERR_PTR(-ENOENT); 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 * PCC can be used with perf critical drivers such as CPPC
...@@ -96,10 +114,8 @@ static struct mbox_chan *get_pcc_channel(int id) ...@@ -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 * The below read_register and write_registers are used to read and
* write from perf critical registers such as PCC doorbell register * 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) { switch (bit_width) {
case 8: case 8:
*val = readb(vaddr); *val = readb(vaddr);
...@@ -113,19 +129,11 @@ static int read_register(void __iomem *vaddr, u64 *val, unsigned int bit_width) ...@@ -113,19 +129,11 @@ static int read_register(void __iomem *vaddr, u64 *val, unsigned int bit_width)
case 64: case 64:
*val = readq(vaddr); *val = readq(vaddr);
break; 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) { switch (bit_width) {
case 8: case 8:
writeb(val, vaddr); writeb(val, vaddr);
...@@ -139,13 +147,54 @@ static int write_register(void __iomem *vaddr, u64 val, unsigned int bit_width) ...@@ -139,13 +147,54 @@ static int write_register(void __iomem *vaddr, u64 val, unsigned int bit_width)
case 64: case 64:
writeq(val, vaddr); writeq(val, vaddr);
break; 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) ...@@ -174,43 +223,43 @@ static int pcc_map_interrupt(u32 interrupt, u32 flags)
/** /**
* pcc_mbox_irq - PCC mailbox interrupt handler * 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) static irqreturn_t pcc_mbox_irq(int irq, void *p)
{ {
struct acpi_generic_address *doorbell_ack; struct pcc_chan_info *pchan;
struct acpi_pcct_hw_reduced *pcct_ss;
struct mbox_chan *chan = p; struct mbox_chan *chan = p;
u64 doorbell_ack_preserve; u64 val;
u64 doorbell_ack_write;
u64 doorbell_ack_val;
int ret; int ret;
pcct_ss = chan->con_priv; pchan = chan->con_priv;
mbox_chan_received_data(chan, NULL); ret = pcc_chan_reg_read(&pchan->cmd_complete, &val);
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);
if (ret) if (ret)
return IRQ_NONE; return IRQ_NONE;
ret = write_register(pcc_doorbell_ack_vaddr[id], val &= pchan->cmd_complete.status_mask;
(doorbell_ack_val & doorbell_ack_preserve) if (!val)
| doorbell_ack_write, return IRQ_NONE;
doorbell_ack->bit_width);
ret = pcc_chan_reg_read(&pchan->error, &val);
if (ret) if (ret)
return IRQ_NONE; 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; return IRQ_HANDLED;
} }
...@@ -224,29 +273,26 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p) ...@@ -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 * ACPI package. This is used to lookup the array of PCC
* subspaces as parsed by the PCC Mailbox controller. * subspaces as parsed by the PCC Mailbox controller.
* *
* Return: Pointer to the Mailbox Channel if successful or * Return: Pointer to the PCC Mailbox Channel if successful or ERR_PTR.
* ERR_PTR.
*/ */
struct mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl, struct pcc_mbox_chan *
int subspace_id) 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 mbox_chan *chan;
struct device *dev;
unsigned long flags; unsigned long flags;
/* if (subspace_id < 0 || subspace_id >= pcc_chan_count)
* Each PCC Subspace is a Mailbox Channel. return ERR_PTR(-ENOENT);
* 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);
pchan = chan_info + subspace_id;
chan = pchan->chan.mchan;
if (IS_ERR(chan) || chan->cl) { if (IS_ERR(chan) || chan->cl) {
dev_err(dev, "Channel not found for idx: %d\n", subspace_id); dev_err(dev, "Channel not found for idx: %d\n", subspace_id);
return ERR_PTR(-EBUSY); return ERR_PTR(-EBUSY);
} }
dev = chan->mbox->dev;
spin_lock_irqsave(&chan->lock, flags); spin_lock_irqsave(&chan->lock, flags);
chan->msg_free = 0; chan->msg_free = 0;
...@@ -260,44 +306,40 @@ struct mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl, ...@@ -260,44 +306,40 @@ struct mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl,
spin_unlock_irqrestore(&chan->lock, flags); spin_unlock_irqrestore(&chan->lock, flags);
if (pcc_doorbell_irq[subspace_id] > 0) { if (pchan->plat_irq > 0) {
int rc; int rc;
rc = devm_request_irq(dev, pcc_doorbell_irq[subspace_id], rc = devm_request_irq(dev, pchan->plat_irq, pcc_mbox_irq, 0,
pcc_mbox_irq, 0, MBOX_IRQ_NAME, chan); MBOX_IRQ_NAME, chan);
if (unlikely(rc)) { if (unlikely(rc)) {
dev_err(dev, "failed to register PCC interrupt %d\n", dev_err(dev, "failed to register PCC interrupt %d\n",
pcc_doorbell_irq[subspace_id]); pchan->plat_irq);
pcc_mbox_free_channel(chan); pcc_mbox_free_channel(&pchan->chan);
chan = ERR_PTR(rc); return ERR_PTR(rc);
} }
} }
return chan; return &pchan->chan;
} }
EXPORT_SYMBOL_GPL(pcc_mbox_request_channel); EXPORT_SYMBOL_GPL(pcc_mbox_request_channel);
/** /**
* pcc_mbox_free_channel - Clients call this to free their 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() * 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; unsigned long flags;
if (!chan || !chan->cl) if (!chan || !chan->cl)
return; return;
if (id >= pcc_mbox_ctrl.num_chans) { if (pchan_info->plat_irq > 0)
pr_debug("pcc_mbox_free_channel: Invalid mbox_chan passed\n"); devm_free_irq(chan->mbox->dev, pchan_info->plat_irq, chan);
return;
}
if (pcc_doorbell_irq[id] > 0)
devm_free_irq(chan->mbox->dev, pcc_doorbell_irq[id], chan);
spin_lock_irqsave(&chan->lock, flags); spin_lock_irqsave(&chan->lock, flags);
chan->cl = NULL; chan->cl = NULL;
...@@ -323,40 +365,14 @@ EXPORT_SYMBOL_GPL(pcc_mbox_free_channel); ...@@ -323,40 +365,14 @@ EXPORT_SYMBOL_GPL(pcc_mbox_free_channel);
*/ */
static int pcc_send_data(struct mbox_chan *chan, void *data) static int pcc_send_data(struct mbox_chan *chan, void *data)
{ {
struct acpi_pcct_hw_reduced *pcct_ss = chan->con_priv; int ret;
struct acpi_generic_address *doorbell; struct pcc_chan_info *pchan = chan->con_priv;
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;
/* Sync notification from OS to Platform. */ ret = pcc_chan_reg_read_modify_write(&pchan->cmd_update);
if (pcc_doorbell_vaddr[id]) {
ret = read_register(pcc_doorbell_vaddr[id], &doorbell_val,
doorbell->bit_width);
if (ret) if (ret)
return ret; return ret;
ret = write_register(pcc_doorbell_vaddr[id],
(doorbell_val & doorbell_preserve) | doorbell_write, return pcc_chan_reg_read_modify_write(&pchan->db);
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;
} }
static const struct mbox_chan_ops pcc_chan_ops = { static const struct mbox_chan_ops pcc_chan_ops = {
...@@ -364,7 +380,7 @@ 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. * @header: Pointer to the ACPI subtable header under the PCCT.
* @end: End of subtable entry. * @end: End of subtable entry.
* *
...@@ -384,41 +400,172 @@ static int parse_pcc_subspace(union acpi_subtable_headers *header, ...@@ -384,41 +400,172 @@ static int parse_pcc_subspace(union acpi_subtable_headers *header,
return -EINVAL; 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 * pcc_parse_subspace_irq - Parse the PCC IRQ and PCC ACK register
* There should be one entry per PCC client. *
* @id: PCC subspace index. * @pchan: Pointer to the PCC channel info structure.
* @pcct_ss: Pointer to the ACPI subtable header under the PCCT. * @pcct_entry: Pointer to the ACPI subtable header.
* *
* Return: 0 for Success, else errno. * 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, static int pcc_parse_subspace_irq(struct pcc_chan_info *pchan,
struct acpi_pcct_hw_reduced *pcct_ss) 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); (u32)pcct_ss->flags);
if (pcc_doorbell_irq[id] <= 0) { if (pchan->plat_irq <= 0) {
pr_err("PCC GSI %d not registered\n", pr_err("PCC GSI %d not registered\n",
pcct_ss->platform_interrupt); pcct_ss->platform_interrupt);
return -EINVAL; return -EINVAL;
} }
if (pcct_ss->header.type if (pcct_ss->header.type == ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) {
== ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) {
struct acpi_pcct_hw_reduced_type2 *pcct2_ss = (void *)pcct_ss; struct acpi_pcct_hw_reduced_type2 *pcct2_ss = (void *)pcct_ss;
pcc_doorbell_ack_vaddr[id] = acpi_os_ioremap( ret = pcc_chan_reg_init(&pchan->plat_irq_ack,
pcct2_ss->platform_ack_register.address, &pcct2_ss->platform_ack_register,
pcct2_ss->platform_ack_register.bit_width / 8); pcct2_ss->ack_preserve_mask,
if (!pcc_doorbell_ack_vaddr[id]) { pcct2_ss->ack_write_mask, 0,
pr_err("Failed to ioremap PCC ACK register\n"); "PLAT IRQ ACK");
return -ENOMEM;
} 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, ...@@ -428,16 +575,12 @@ static int pcc_parse_subspace_irq(int id,
*/ */
static int __init acpi_pcc_probe(void) static int __init acpi_pcc_probe(void)
{ {
int count, i, rc = 0;
acpi_status status;
struct acpi_table_header *pcct_tbl; 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]; 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); status = acpi_get_table(ACPI_SIG_PCCT, 0, &pcct_tbl);
if (ACPI_FAILURE(status) || !pcct_tbl) if (ACPI_FAILURE(status) || !pcct_tbl)
return -ENODEV; return -ENODEV;
...@@ -459,33 +602,60 @@ static int __init acpi_pcc_probe(void) ...@@ -459,33 +602,60 @@ static int __init acpi_pcc_probe(void)
pr_warn("Invalid PCCT: %d PCC subspaces\n", count); pr_warn("Invalid PCCT: %d PCC subspaces\n", count);
rc = -EINVAL; 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); GFP_KERNEL);
if (!pcc_mbox_channels) { 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; rc = -ENOMEM;
goto err_free_mbox; goto err;
} }
pcc_doorbell_ack_vaddr = kcalloc(count, sizeof(void *), GFP_KERNEL); chan_info = devm_kcalloc(dev, count, sizeof(*chan_info), GFP_KERNEL);
if (!pcc_doorbell_ack_vaddr) { if (!chan_info) {
rc = -ENOMEM; rc = -ENOMEM;
goto err_free_db_vaddr; goto err;
} }
pcc_doorbell_irq = kcalloc(count, sizeof(int), GFP_KERNEL); pcc_mbox_ctrl = devm_kmalloc(dev, sizeof(*pcc_mbox_ctrl), GFP_KERNEL);
if (!pcc_doorbell_irq) { if (!pcc_mbox_ctrl) {
rc = -ENOMEM; rc = -ENOMEM;
goto err_free_db_ack_vaddr; goto err;
} }
/* Point to the first PCC subspace entry */ /* Point to the first PCC subspace entry */
...@@ -494,85 +664,55 @@ static int __init acpi_pcc_probe(void) ...@@ -494,85 +664,55 @@ static int __init acpi_pcc_probe(void)
acpi_pcct_tbl = (struct acpi_table_pcct *) pcct_tbl; acpi_pcct_tbl = (struct acpi_table_pcct *) pcct_tbl;
if (acpi_pcct_tbl->flags & ACPI_PCCT_DOORBELL) 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++) { for (i = 0; i < count; i++) {
struct acpi_generic_address *db_reg; struct pcc_chan_info *pchan = chan_info + i;
struct acpi_pcct_subspace *pcct_ss;
pcc_mbox_channels[i].con_priv = pcct_entry;
if (pcct_entry->type == ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE || pcc_mbox_channels[i].con_priv = pchan;
pcct_entry->type == ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) { pchan->chan.mchan = &pcc_mbox_channels[i];
struct acpi_pcct_hw_reduced *pcct_hrss;
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) { if (pcc_mbox_ctrl->txdone_irq) {
rc = pcc_parse_subspace_irq(i, pcct_hrss); rc = pcc_parse_subspace_irq(pchan, pcct_entry);
if (rc < 0) if (rc < 0)
goto err; goto err;
} }
} rc = pcc_parse_subspace_db_reg(pchan, pcct_entry);
pcct_ss = (struct acpi_pcct_subspace *) 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 *) pcct_entry = (struct acpi_subtable_header *)
((unsigned long) pcct_entry + pcct_entry->length); ((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: 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); acpi_put_table(pcct_tbl);
return rc; 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 = { static struct platform_driver pcc_mbox_driver = {
.probe = pcc_mbox_probe, .probe = pcc_mbox_probe,
.driver = { .driver = {
......
...@@ -117,7 +117,6 @@ static int platform_mhu_probe(struct platform_device *pdev) ...@@ -117,7 +117,6 @@ static int platform_mhu_probe(struct platform_device *pdev)
int i, err; int i, err;
struct platform_mhu *mhu; struct platform_mhu *mhu;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct resource *res;
int platform_mhu_reg[MHU_CHANS] = { int platform_mhu_reg[MHU_CHANS] = {
MHU_SEC_OFFSET, MHU_LP_OFFSET, MHU_HP_OFFSET MHU_SEC_OFFSET, MHU_LP_OFFSET, MHU_HP_OFFSET
}; };
...@@ -127,8 +126,7 @@ static int platform_mhu_probe(struct platform_device *pdev) ...@@ -127,8 +126,7 @@ static int platform_mhu_probe(struct platform_device *pdev)
if (!mhu) if (!mhu)
return -ENOMEM; return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); mhu->base = devm_platform_ioremap_resource(pdev, 0);
mhu->base = devm_ioremap_resource(dev, res);
if (IS_ERR(mhu->base)) { if (IS_ERR(mhu->base)) {
dev_err(dev, "ioremap failed\n"); dev_err(dev, "ioremap failed\n");
return PTR_ERR(mhu->base); return PTR_ERR(mhu->base);
......
...@@ -33,10 +33,6 @@ static const struct qcom_apcs_ipc_data ipq6018_apcs_data = { ...@@ -33,10 +33,6 @@ static const struct qcom_apcs_ipc_data ipq6018_apcs_data = {
.offset = 8, .clk_name = "qcom,apss-ipq6018-clk" .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 = { static const struct qcom_apcs_ipc_data msm8916_apcs_data = {
.offset = 8, .clk_name = "qcom-apcs-msm8916-clk" .offset = 8, .clk_name = "qcom-apcs-msm8916-clk"
}; };
...@@ -49,18 +45,6 @@ static const struct qcom_apcs_ipc_data msm8996_apcs_data = { ...@@ -49,18 +45,6 @@ static const struct qcom_apcs_ipc_data msm8996_apcs_data = {
.offset = 16, .clk_name = NULL .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 = { static const struct qcom_apcs_ipc_data apps_shared_apcs_data = {
.offset = 12, .clk_name = NULL .offset = 12, .clk_name = NULL
}; };
...@@ -95,7 +79,6 @@ static int qcom_apcs_ipc_probe(struct platform_device *pdev) ...@@ -95,7 +79,6 @@ static int qcom_apcs_ipc_probe(struct platform_device *pdev)
struct qcom_apcs_ipc *apcs; struct qcom_apcs_ipc *apcs;
const struct qcom_apcs_ipc_data *apcs_data; const struct qcom_apcs_ipc_data *apcs_data;
struct regmap *regmap; struct regmap *regmap;
struct resource *res;
void __iomem *base; void __iomem *base;
unsigned long i; unsigned long i;
int ret; int ret;
...@@ -104,8 +87,7 @@ static int qcom_apcs_ipc_probe(struct platform_device *pdev) ...@@ -104,8 +87,7 @@ static int qcom_apcs_ipc_probe(struct platform_device *pdev)
if (!apcs) if (!apcs)
return -ENOMEM; return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_platform_ioremap_resource(pdev, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base)) if (IS_ERR(base))
return PTR_ERR(base); return PTR_ERR(base);
...@@ -160,21 +142,22 @@ static int qcom_apcs_ipc_remove(struct platform_device *pdev) ...@@ -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 */ /* .data is the offset of the ipc register within the global block */
static const struct of_device_id qcom_apcs_ipc_of_match[] = { static const struct of_device_id qcom_apcs_ipc_of_match[] = {
{ .compatible = "qcom,ipq6018-apcs-apps-global", .data = &ipq6018_apcs_data }, { .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,msm8916-apcs-kpss-global", .data = &msm8916_apcs_data },
{ .compatible = "qcom,msm8939-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,msm8953-apcs-kpss-global", .data = &msm8994_apcs_data },
{ .compatible = "qcom,msm8994-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,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,qcs404-apcs-apps-global", .data = &msm8916_apcs_data },
{ .compatible = "qcom,sc7180-apss-shared", .data = &apps_shared_apcs_data }, { .compatible = "qcom,sc7180-apss-shared", .data = &apps_shared_apcs_data },
{ .compatible = "qcom,sc8180x-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,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,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 }, { .compatible = "qcom,sdx55-apcs-gcc", .data = &sdx55_apcs_data },
{} {}
}; };
......
...@@ -205,7 +205,6 @@ static int stm32_ipcc_probe(struct platform_device *pdev) ...@@ -205,7 +205,6 @@ static int stm32_ipcc_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node; struct device_node *np = dev->of_node;
struct stm32_ipcc *ipcc; struct stm32_ipcc *ipcc;
struct resource *res;
unsigned long i; unsigned long i;
int ret; int ret;
u32 ip_ver; u32 ip_ver;
...@@ -235,8 +234,7 @@ static int stm32_ipcc_probe(struct platform_device *pdev) ...@@ -235,8 +234,7 @@ static int stm32_ipcc_probe(struct platform_device *pdev)
} }
/* regs */ /* regs */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ipcc->reg_base = devm_platform_ioremap_resource(pdev, 0);
ipcc->reg_base = devm_ioremap_resource(dev, res);
if (IS_ERR(ipcc->reg_base)) if (IS_ERR(ipcc->reg_base))
return PTR_ERR(ipcc->reg_base); return PTR_ERR(ipcc->reg_base);
......
...@@ -197,7 +197,6 @@ static int sun6i_msgbox_probe(struct platform_device *pdev) ...@@ -197,7 +197,6 @@ static int sun6i_msgbox_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct mbox_chan *chans; struct mbox_chan *chans;
struct reset_control *reset; struct reset_control *reset;
struct resource *res;
struct sun6i_msgbox *mbox; struct sun6i_msgbox *mbox;
int i, ret; int i, ret;
...@@ -246,13 +245,7 @@ static int sun6i_msgbox_probe(struct platform_device *pdev) ...@@ -246,13 +245,7 @@ static int sun6i_msgbox_probe(struct platform_device *pdev)
goto err_disable_unprepare; goto err_disable_unprepare;
} }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); mbox->regs = devm_platform_ioremap_resource(pdev, 0);
if (!res) {
ret = -ENODEV;
goto err_disable_unprepare;
}
mbox->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(mbox->regs)) { if (IS_ERR(mbox->regs)) {
ret = PTR_ERR(mbox->regs); ret = PTR_ERR(mbox->regs);
dev_err(dev, "Failed to map MMIO resource: %d\n", ret); dev_err(dev, "Failed to map MMIO resource: %d\n", ret);
......
...@@ -9,18 +9,27 @@ ...@@ -9,18 +9,27 @@
#include <linux/mailbox_controller.h> #include <linux/mailbox_controller.h>
#include <linux/mailbox_client.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 #define MAX_PCC_SUBSPACES 256
#ifdef CONFIG_PCC #ifdef CONFIG_PCC
extern struct mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl, extern struct pcc_mbox_chan *
int subspace_id); pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id);
extern void pcc_mbox_free_channel(struct mbox_chan *chan); extern void pcc_mbox_free_channel(struct pcc_mbox_chan *chan);
#else #else
static inline struct mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl, static inline struct pcc_mbox_chan *
int subspace_id) pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)
{ {
return ERR_PTR(-ENODEV); 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
#endif /* _PCC_H */ #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