diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-04-16 19:00:31 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-04-16 19:00:31 -0400 |
commit | 8fa6f4974d55ad1fb0a9522c5a5cfd050a517048 (patch) | |
tree | c0f4729057649ae3a24830e0636313e418eba2ab | |
parent | eabbfdecdae4413687a36fd17c3e98de59ba492b (diff) | |
parent | ee23d66af9219dfe2407a9b08ef9a165dbe6f994 (diff) | |
download | linux-8fa6f4974d55ad1fb0a9522c5a5cfd050a517048.tar.bz2 |
Merge branch 'mailbox-for-next' of git://git.linaro.org/landing-teams/working/fujitsu/integration
Pull mailbox updates from Jassi Brar.
* 'mailbox-for-next' of git://git.linaro.org/landing-teams/working/fujitsu/integration:
mailbox: arm_mhu: add driver for ARM MHU controller
Mailbox: Restructure and simplify PCC mailbox code
-rw-r--r-- | Documentation/devicetree/bindings/mailbox/arm-mhu.txt | 43 | ||||
-rw-r--r-- | drivers/mailbox/Kconfig | 9 | ||||
-rw-r--r-- | drivers/mailbox/Makefile | 2 | ||||
-rw-r--r-- | drivers/mailbox/arm_mhu.c | 195 | ||||
-rw-r--r-- | drivers/mailbox/pcc.c | 122 |
5 files changed, 286 insertions, 85 deletions
diff --git a/Documentation/devicetree/bindings/mailbox/arm-mhu.txt b/Documentation/devicetree/bindings/mailbox/arm-mhu.txt new file mode 100644 index 000000000000..4971f03f0b33 --- /dev/null +++ b/Documentation/devicetree/bindings/mailbox/arm-mhu.txt @@ -0,0 +1,43 @@ +ARM MHU Mailbox Driver +====================== + +The ARM's Message-Handling-Unit (MHU) is a mailbox controller that has +3 independent channels/links to communicate with remote processor(s). + MHU links are hardwired on a platform. A link raises interrupt for any +received data. However, there is no specified way of knowing if the sent +data has been read by the remote. This driver assumes the sender polls +STAT register and the remote clears it after having read the data. +The last channel is specified to be a 'Secure' resource, hence can't be +used by Linux running NS. + +Mailbox Device Node: +==================== + +Required properties: +-------------------- +- compatible: Shall be "arm,mhu" & "arm,primecell" +- reg: Contains the mailbox register address range (base + address and length) +- #mbox-cells Shall be 1 - the index of the channel needed. +- interrupts: Contains the interrupt information corresponding to + each of the 3 links of MHU. + +Example: +-------- + + mhu: mailbox@2b1f0000 { + #mbox-cells = <1>; + compatible = "arm,mhu", "arm,primecell"; + reg = <0 0x2b1f0000 0x1000>; + interrupts = <0 36 4>, /* LP-NonSecure */ + <0 35 4>, /* HP-NonSecure */ + <0 37 4>; /* Secure */ + clocks = <&clock 0 2 1>; + clock-names = "apb_pclk"; + }; + + mhu_client: scb@2e000000 { + compatible = "fujitsu,mb86s70-scb-1.0"; + reg = <0 0x2e000000 0x4000>; + mboxes = <&mhu 1>; /* HP-NonSecure */ + }; diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 84325f267acf..84b0a2d74d60 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -6,6 +6,15 @@ menuconfig MAILBOX signals. Say Y if your platform supports hardware mailboxes. if MAILBOX + +config ARM_MHU + tristate "ARM MHU Mailbox" + depends on ARM_AMBA + help + Say Y here if you want to build the ARM MHU controller driver. + The controller has 3 mailbox channels, the last of which can be + used in Secure mode only. + config PL320_MBOX bool "ARM PL320 Mailbox" depends on ARM_AMBA diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 2e79231154cf..b18201e97e29 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -2,6 +2,8 @@ obj-$(CONFIG_MAILBOX) += mailbox.o +obj-$(CONFIG_ARM_MHU) += arm_mhu.o + obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o obj-$(CONFIG_OMAP2PLUS_MBOX) += omap-mailbox.o diff --git a/drivers/mailbox/arm_mhu.c b/drivers/mailbox/arm_mhu.c new file mode 100644 index 000000000000..ac693c635357 --- /dev/null +++ b/drivers/mailbox/arm_mhu.c @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2013-2015 Fujitsu Semiconductor Ltd. + * Copyright (C) 2015 Linaro Ltd. + * Author: Jassi Brar <jaswinder.singh@linaro.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/amba/bus.h> +#include <linux/mailbox_controller.h> + +#define INTR_STAT_OFS 0x0 +#define INTR_SET_OFS 0x8 +#define INTR_CLR_OFS 0x10 + +#define MHU_LP_OFFSET 0x0 +#define MHU_HP_OFFSET 0x20 +#define MHU_SEC_OFFSET 0x200 +#define TX_REG_OFFSET 0x100 + +#define MHU_CHANS 3 + +struct mhu_link { + unsigned irq; + void __iomem *tx_reg; + void __iomem *rx_reg; +}; + +struct arm_mhu { + void __iomem *base; + struct mhu_link mlink[MHU_CHANS]; + struct mbox_chan chan[MHU_CHANS]; + struct mbox_controller mbox; +}; + +static irqreturn_t mhu_rx_interrupt(int irq, void *p) +{ + struct mbox_chan *chan = p; + struct mhu_link *mlink = chan->con_priv; + u32 val; + + val = readl_relaxed(mlink->rx_reg + INTR_STAT_OFS); + if (!val) + return IRQ_NONE; + + mbox_chan_received_data(chan, (void *)&val); + + writel_relaxed(val, mlink->rx_reg + INTR_CLR_OFS); + + return IRQ_HANDLED; +} + +static bool mhu_last_tx_done(struct mbox_chan *chan) +{ + struct mhu_link *mlink = chan->con_priv; + u32 val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS); + + return (val == 0); +} + +static int mhu_send_data(struct mbox_chan *chan, void *data) +{ + struct mhu_link *mlink = chan->con_priv; + u32 *arg = data; + + writel_relaxed(*arg, mlink->tx_reg + INTR_SET_OFS); + + return 0; +} + +static int mhu_startup(struct mbox_chan *chan) +{ + struct mhu_link *mlink = chan->con_priv; + u32 val; + int ret; + + val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS); + writel_relaxed(val, mlink->tx_reg + INTR_CLR_OFS); + + ret = request_irq(mlink->irq, mhu_rx_interrupt, + IRQF_SHARED, "mhu_link", chan); + if (ret) { + dev_err(chan->mbox->dev, + "Unable to aquire IRQ %d\n", mlink->irq); + return ret; + } + + return 0; +} + +static void mhu_shutdown(struct mbox_chan *chan) +{ + struct mhu_link *mlink = chan->con_priv; + + free_irq(mlink->irq, chan); +} + +static struct mbox_chan_ops mhu_ops = { + .send_data = mhu_send_data, + .startup = mhu_startup, + .shutdown = mhu_shutdown, + .last_tx_done = mhu_last_tx_done, +}; + +static int mhu_probe(struct amba_device *adev, const struct amba_id *id) +{ + int i, err; + struct arm_mhu *mhu; + struct device *dev = &adev->dev; + int mhu_reg[MHU_CHANS] = {MHU_LP_OFFSET, MHU_HP_OFFSET, MHU_SEC_OFFSET}; + + /* Allocate memory for device */ + mhu = devm_kzalloc(dev, sizeof(*mhu), GFP_KERNEL); + if (!mhu) + return -ENOMEM; + + mhu->base = devm_ioremap_resource(dev, &adev->res); + if (IS_ERR(mhu->base)) { + dev_err(dev, "ioremap failed\n"); + return PTR_ERR(mhu->base); + } + + for (i = 0; i < MHU_CHANS; i++) { + mhu->chan[i].con_priv = &mhu->mlink[i]; + mhu->mlink[i].irq = adev->irq[i]; + mhu->mlink[i].rx_reg = mhu->base + mhu_reg[i]; + mhu->mlink[i].tx_reg = mhu->mlink[i].rx_reg + TX_REG_OFFSET; + } + + mhu->mbox.dev = dev; + mhu->mbox.chans = &mhu->chan[0]; + mhu->mbox.num_chans = MHU_CHANS; + mhu->mbox.ops = &mhu_ops; + mhu->mbox.txdone_irq = false; + mhu->mbox.txdone_poll = true; + mhu->mbox.txpoll_period = 10; + + amba_set_drvdata(adev, mhu); + + err = mbox_controller_register(&mhu->mbox); + if (err) { + dev_err(dev, "Failed to register mailboxes %d\n", err); + return err; + } + + dev_info(dev, "ARM MHU Mailbox registered\n"); + return 0; +} + +static int mhu_remove(struct amba_device *adev) +{ + struct arm_mhu *mhu = amba_get_drvdata(adev); + + mbox_controller_unregister(&mhu->mbox); + + return 0; +} + +static struct amba_id mhu_ids[] = { + { + .id = 0x1bb098, + .mask = 0xffffff, + }, + { 0, 0 }, +}; +MODULE_DEVICE_TABLE(amba, mhu_ids); + +static struct amba_driver arm_mhu_driver = { + .drv = { + .name = "mhu", + }, + .id_table = mhu_ids, + .probe = mhu_probe, + .remove = mhu_remove, +}; +module_amba_driver(arm_mhu_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ARM MHU Driver"); +MODULE_AUTHOR("Jassi Brar <jassisinghbrar@gmail.com>"); diff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c index 977c814cdf6f..7e91d68a3ac3 100644 --- a/drivers/mailbox/pcc.c +++ b/drivers/mailbox/pcc.c @@ -20,10 +20,35 @@ * shared memory regions as defined in the PCC table entries. The PCC * specification supports a Doorbell mechanism for the PCC clients * to notify the platform about new data. This Doorbell information - * is also specified in each PCC table entry. See pcc_send_data() - * and pcc_tx_done() for basic mode of operation. + * is also specified in each PCC table entry. * - * For more details about PCC, please see the ACPI specification from + * Typical high level flow of operation is: + * + * PCC Reads: + * * Client tries to acquire a channel lock. + * * After it is acquired it writes READ cmd in communication region cmd + * address. + * * Client issues mbox_send_message() which rings the PCC doorbell + * for its PCC channel. + * * If command completes, then client has control over channel and + * it can proceed with its reads. + * * Client releases lock. + * + * PCC Writes: + * * Client tries to acquire channel lock. + * * Client writes to its communication region after it acquires a + * channel lock. + * * Client writes WRITE cmd in communication region cmd address. + * * Client issues mbox_send_message() which rings the PCC doorbell + * for its PCC channel. + * * If command completes, then writes have succeded and it can release + * the channel lock. + * + * There is a Nominal latency defined for each channel which indicates + * how long to wait until a command completes. If command is not complete + * the client needs to retry or assume failure. + * + * For more details about PCC, please see the ACPI specification from * http://www.uefi.org/ACPIv5.1 Section 14. * * This file implements PCC as a Mailbox controller and allows for PCC @@ -42,8 +67,6 @@ #include "mailbox.h" #define MAX_PCC_SUBSPACES 256 -#define PCCS_SS_SIG_MAGIC 0x50434300 -#define PCC_CMD_COMPLETE 0x1 static struct mbox_chan *pcc_mbox_channels; @@ -71,23 +94,6 @@ static struct mbox_chan *get_pcc_channel(int id) } /** - * get_subspace_id - Given a Mailbox channel, find out the - * PCC subspace id. - * @chan: Pointer to Mailbox Channel from which we want - * the index. - * Return: Errno if not found, else positive index number. - */ -static int get_subspace_id(struct mbox_chan *chan) -{ - unsigned int id = chan - pcc_mbox_channels; - - if (id < 0 || id > pcc_mbox_ctrl.num_chans) - return -ENOENT; - - return id; -} - -/** * pcc_mbox_request_channel - PCC clients call this function to * request a pointer to their PCC subspace, from which they * can get the details of communicating with the remote. @@ -117,7 +123,7 @@ struct mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl, chan = get_pcc_channel(subspace_id); if (!chan || chan->cl) { - dev_err(dev, "%s: PCC mailbox not free\n", __func__); + dev_err(dev, "Channel not found for idx: %d\n", subspace_id); return ERR_PTR(-EBUSY); } @@ -161,81 +167,30 @@ void pcc_mbox_free_channel(struct mbox_chan *chan) EXPORT_SYMBOL_GPL(pcc_mbox_free_channel); /** - * pcc_tx_done - Callback from Mailbox controller code to - * check if PCC message transmission completed. - * @chan: Pointer to Mailbox channel on which previous - * transmission occurred. - * - * Return: TRUE if succeeded. - */ -static bool pcc_tx_done(struct mbox_chan *chan) -{ - struct acpi_pcct_hw_reduced *pcct_ss = chan->con_priv; - struct acpi_pcct_shared_memory *generic_comm_base = - (struct acpi_pcct_shared_memory *) pcct_ss->base_address; - u16 cmd_delay = pcct_ss->latency; - unsigned int retries = 0; - - /* Try a few times while waiting for platform to consume */ - while (!(readw_relaxed(&generic_comm_base->status) - & PCC_CMD_COMPLETE)) { - - if (retries++ < 5) - udelay(cmd_delay); - else { - /* - * If the remote is dead, this will cause the Mbox - * controller to timeout after mbox client.tx_tout - * msecs. - */ - pr_err("PCC platform did not respond.\n"); - return false; - } - } - return true; -} - -/** - * pcc_send_data - Called from Mailbox Controller code to finally - * transmit data over channel. + * pcc_send_data - Called from Mailbox Controller code. Used + * here only to ring the channel doorbell. The PCC client + * specific read/write is done in the client driver in + * order to maintain atomicity over PCC channel once + * OS has control over it. See above for flow of operations. * @chan: Pointer to Mailbox channel over which to send data. - * @data: Actual data to be written over channel. + * @data: Client specific data written over channel. Used here + * only for debug after PCC transaction completes. * * Return: Err if something failed else 0 for success. */ static int pcc_send_data(struct mbox_chan *chan, void *data) { struct acpi_pcct_hw_reduced *pcct_ss = chan->con_priv; - struct acpi_pcct_shared_memory *generic_comm_base = - (struct acpi_pcct_shared_memory *) pcct_ss->base_address; struct acpi_generic_address doorbell; u64 doorbell_preserve; u64 doorbell_val; u64 doorbell_write; - u16 cmd = *(u16 *) data; - u16 ss_idx = -1; - - ss_idx = get_subspace_id(chan); - - if (ss_idx < 0) { - pr_err("Invalid Subspace ID from PCC client\n"); - return -EINVAL; - } doorbell = pcct_ss->doorbell_register; doorbell_preserve = pcct_ss->preserve_mask; doorbell_write = pcct_ss->write_mask; - /* Write to the shared comm region. */ - writew(cmd, &generic_comm_base->command); - - /* Write Subspace MAGIC value so platform can identify destination. */ - writel((PCCS_SS_SIG_MAGIC | ss_idx), &generic_comm_base->signature); - - /* Flip CMD COMPLETE bit */ - writew(0, &generic_comm_base->status); - - /* Sync notification from OSPM to Platform. */ + /* Sync notification from OS to Platform. */ acpi_read(&doorbell_val, &doorbell); acpi_write((doorbell_val & doorbell_preserve) | doorbell_write, &doorbell); @@ -245,7 +200,6 @@ static int pcc_send_data(struct mbox_chan *chan, void *data) static struct mbox_chan_ops pcc_chan_ops = { .send_data = pcc_send_data, - .last_tx_done = pcc_tx_done, }; /** @@ -351,8 +305,6 @@ static int pcc_mbox_probe(struct platform_device *pdev) pcc_mbox_ctrl.chans = pcc_mbox_channels; pcc_mbox_ctrl.ops = &pcc_chan_ops; - pcc_mbox_ctrl.txdone_poll = true; - pcc_mbox_ctrl.txpoll_period = 10; pcc_mbox_ctrl.dev = &pdev->dev; pr_info("Registering PCC driver as Mailbox controller\n"); |