- 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 -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE6EwehDt/SOnwFyTyf9lkf8eYP5UFAmGAAOoACgkQf9lkf8eY P5XvrQ//SH28voXosZmzyrEV8h7JUPZytHwHTkMpC8fsqB9rcNiWZYXPxHa56X0M alfV+BzxnP6lIZ6ApxTyLGaNTTFkRCI4VGgsY9ioHyb41GhHqaPQBTwG/EJBaEua /44iztVesNl0kP8nTB6u/sRVnRFtEb82hCFUbw38YUWRPsKSOVnQjbs6t58iWh2a 9jTOV9Rg+r6x9kcKiTgTNKceK8VAYghoaVosq0Ob4Ps+RPqsz4edxYbQ4NiiCD0z OaaduH2nBOTJ8PYfTUrdz71fX+mxuo2lpLam8lr0OW+1Dgp1039hSsyfP+ooJfSC xTLWx9HDVvm7PI4GdNQwecmqd+tuyfcAwrPfc0kbn6ksnbqwCcn9btyvaKn3ZfKk pL3whL+VVKJ4VqInISDVGJ1O1Y+LH9ow4VTw8hiQqBIEaow7rQr/ZCIpZW+r6/pS Glcp3eQKdvhxYJLNrfctW2/vxrHJdc8qBE6zSD+RqudZyORsyEPF+TP09oKIivfI Xi6gyGwBWigwYRecr4cPB+22dpU3Zip5c8dD1LaT6eCt+GayPUBpXjhqx4Hm7bs8 y5d+WNu3xbCTEgfO5JyT/QdO75B3CPmhgsM9oUWot+v1DEFELr4atee2cpzJYH7i zjGDhx8d03B2HG6rg72XRub20EtZ5Um84h559bPiXQbxSfEIzio= =pnD9 -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
247ee3e7b7
77
Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml
Normal file
77
Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml
Normal file
@ -0,0 +1,77 @@
|
||||
# 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
|
||||
|
11
MAINTAINERS
11
MAINTAINERS
@ -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 @@ out:
|
||||
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 @@ mbox_err:
|
||||
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
|
||||
|
384
drivers/mailbox/apple-mailbox.c
Normal file
384
drivers/mailbox/apple-mailbox.c
Normal file
@ -0,0 +1,384 @@
|
||||
// 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,8 +421,8 @@ 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,
|
||||
const struct of_phandle_args *sp)
|
||||
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;
|
||||
|
||||
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_reg - PCC register bundle
|
||||
*
|
||||
* Return: ERR_PTR(errno) if error, else pointer
|
||||
* to mbox channel.
|
||||
* @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
|
||||
*/
|
||||
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_reg {
|
||||
void __iomem *vaddr;
|
||||
struct acpi_generic_address *gas;
|
||||
u64 preserve_mask;
|
||||
u64 set_mask;
|
||||
u64 status_mask;
|
||||
};
|
||||
|
||||
return &pcc_mbox_channels[id];
|
||||
}
|
||||
/**
|
||||
* struct pcc_chan_info - PCC channel specific information
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
#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;
|
||||
|
||||
ret = pcc_chan_reg_read(&pchan->cmd_complete, &val);
|
||||
if (ret)
|
||||
return IRQ_NONE;
|
||||
|
||||
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);
|
||||
|
||||
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)
|
||||
return IRQ_NONE;
|
||||
|
||||
ret = write_register(pcc_doorbell_ack_vaddr[id],
|
||||
(doorbell_ack_val & doorbell_ack_preserve)
|
||||
| doorbell_ack_write,
|
||||
doorbell_ack->bit_width);
|
||||
if (ret)
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
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
|
||||
* pcc_mbox_request_channel()
|
||||
* @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;
|
||||
int ret;
|
||||
struct pcc_chan_info *pchan = chan->con_priv;
|
||||
|
||||
if (id >= pcc_mbox_ctrl.num_chans) {
|
||||
pr_debug("pcc_send_data: Invalid mbox_chan passed\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
ret = pcc_chan_reg_read_modify_write(&pchan->cmd_update);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
doorbell = &pcct_ss->doorbell_register;
|
||||
doorbell_preserve = pcct_ss->preserve_mask;
|
||||
doorbell_write = pcct_ss->write_mask;
|
||||
|
||||
/* Sync notification from OS to Platform. */
|
||||
if (pcc_doorbell_vaddr[id]) {
|
||||
ret = read_register(pcc_doorbell_vaddr[id], &doorbell_val,
|
||||
doorbell->bit_width);
|
||||
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,
|
||||
(u32)pcct_ss->flags);
|
||||
if (pcc_doorbell_irq[id] <= 0) {
|
||||
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 (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 0;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,87 +602,12 @@ 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),
|
||||
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;
|
||||
}
|
||||
|
||||
pcc_doorbell_ack_vaddr = kcalloc(count, sizeof(void *), GFP_KERNEL);
|
||||
if (!pcc_doorbell_ack_vaddr) {
|
||||
rc = -ENOMEM;
|
||||
goto err_free_db_vaddr;
|
||||
}
|
||||
|
||||
pcc_doorbell_irq = kcalloc(count, sizeof(int), GFP_KERNEL);
|
||||
if (!pcc_doorbell_irq) {
|
||||
rc = -ENOMEM;
|
||||
goto err_free_db_ack_vaddr;
|
||||
}
|
||||
|
||||
/* Point to the first PCC subspace entry */
|
||||
pcct_entry = (struct acpi_subtable_header *) (
|
||||
(unsigned long) pcct_tbl + sizeof(struct acpi_table_pcct));
|
||||
|
||||
acpi_pcct_tbl = (struct acpi_table_pcct *) pcct_tbl;
|
||||
if (acpi_pcct_tbl->flags & ACPI_PCCT_DOORBELL)
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
pcct_hrss = (struct acpi_pcct_hw_reduced *) pcct_entry;
|
||||
|
||||
if (pcc_mbox_ctrl.txdone_irq) {
|
||||
rc = pcc_parse_subspace_irq(i, pcct_hrss);
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
pcct_ss = (struct acpi_pcct_subspace *) 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;
|
||||
|
||||
pr_info("Detected %d PCC Subspaces\n", pcc_mbox_ctrl.num_chans);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -556,21 +624,93 @@ err_put_pcct:
|
||||
*/
|
||||
static int pcc_mbox_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
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;
|
||||
|
||||
pcc_mbox_ctrl.chans = pcc_mbox_channels;
|
||||
pcc_mbox_ctrl.ops = &pcc_chan_ops;
|
||||
pcc_mbox_ctrl.dev = &pdev->dev;
|
||||
/* Search for PCCT */
|
||||
status = acpi_get_table(ACPI_SIG_PCCT, 0, &pcct_tbl);
|
||||
|
||||
pr_info("Registering PCC driver as Mailbox controller\n");
|
||||
ret = mbox_controller_register(&pcc_mbox_ctrl);
|
||||
if (ACPI_FAILURE(status) || !pcct_tbl)
|
||||
return -ENODEV;
|
||||
|
||||
if (ret) {
|
||||
pr_err("Err registering PCC as Mailbox controller: %d\n", ret);
|
||||
ret = -ENODEV;
|
||||
pcc_mbox_channels = devm_kcalloc(dev, count, sizeof(*pcc_mbox_channels),
|
||||
GFP_KERNEL);
|
||||
if (!pcc_mbox_channels) {
|
||||
rc = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return ret;
|
||||
chan_info = devm_kcalloc(dev, count, sizeof(*chan_info), GFP_KERNEL);
|
||||
if (!chan_info) {
|
||||
rc = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
pcc_mbox_ctrl = devm_kmalloc(dev, sizeof(*pcc_mbox_ctrl), GFP_KERNEL);
|
||||
if (!pcc_mbox_ctrl) {
|
||||
rc = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Point to the first PCC subspace entry */
|
||||
pcct_entry = (struct acpi_subtable_header *) (
|
||||
(unsigned long) pcct_tbl + sizeof(struct acpi_table_pcct));
|
||||
|
||||
acpi_pcct_tbl = (struct acpi_table_pcct *) pcct_tbl;
|
||||
if (acpi_pcct_tbl->flags & ACPI_PCCT_DOORBELL)
|
||||
pcc_mbox_ctrl->txdone_irq = true;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
struct pcc_chan_info *pchan = chan_info + i;
|
||||
|
||||
pcc_mbox_channels[i].con_priv = pchan;
|
||||
pchan->chan.mchan = &pcc_mbox_channels[i];
|
||||
|
||||
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(pchan, pcct_entry);
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
}
|
||||
rc = pcc_parse_subspace_db_reg(pchan, pcct_entry);
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
|
||||
pcc_parse_subspace_shmem(pchan, pcct_entry);
|
||||
|
||||
pcct_entry = (struct acpi_subtable_header *)
|
||||
((unsigned long) pcct_entry + pcct_entry->length);
|
||||
}
|
||||
|
||||
pcc_mbox_ctrl->num_chans = count;
|
||||
|
||||
pr_info("Detected %d PCC Subspaces\n", pcc_mbox_ctrl->num_chans);
|
||||
|
||||
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:
|
||||
acpi_put_table(pcct_tbl);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct platform_driver pcc_mbox_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 */
|
||||
|
19
include/linux/apple-mailbox.h
Normal file
19
include/linux/apple-mailbox.h
Normal file
@ -0,0 +1,19 @@
|
||||
/* 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
|
20
include/linux/firmware/imx/s4.h
Normal file
20
include/linux/firmware/imx/s4.h
Normal file
@ -0,0 +1,20 @@
|
||||
/* 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 */
|
Loading…
Reference in New Issue
Block a user