aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Ellerman <mpe@ellerman.id.au>2015-06-05 18:03:57 +1000
committerMichael Ellerman <mpe@ellerman.id.au>2015-06-05 18:03:57 +1000
commit571d3820fb5bf246883846076a41f721e890c53a (patch)
tree50fae91c0601edda7f94bf6b61ce9d44e09372e4
parent26052da7cfba68ee2972f39e002a4b40e242102b (diff)
parent4c0f74bf82e8295ce711da81774178769f6f8215 (diff)
downloadlinux-next-571d3820fb5bf246883846076a41f721e890c53a.tar.gz
Merge remote-tracking branch 'mailbox/mailbox-for-next'
-rw-r--r--Documentation/devicetree/bindings/mailbox/brcm,bcm2835-mbox.txt26
-rw-r--r--Documentation/devicetree/bindings/mailbox/nvidia,tegra124-xusb-mbox.txt30
-rw-r--r--drivers/mailbox/Kconfig17
-rw-r--r--drivers/mailbox/Makefile4
-rw-r--r--drivers/mailbox/arm_mhu.c2
-rw-r--r--drivers/mailbox/bcm2835-mailbox.c216
-rw-r--r--drivers/mailbox/mailbox-altera.c2
-rw-r--r--drivers/mailbox/mailbox.c11
-rw-r--r--drivers/mailbox/omap-mailbox.c8
-rw-r--r--drivers/mailbox/pcc.c2
-rw-r--r--drivers/mailbox/tegra-xusb-mailbox.c290
-rw-r--r--include/linux/mailbox_controller.h2
-rw-r--r--include/soc/tegra/xusb.h43
13 files changed, 642 insertions, 11 deletions
diff --git a/Documentation/devicetree/bindings/mailbox/brcm,bcm2835-mbox.txt b/Documentation/devicetree/bindings/mailbox/brcm,bcm2835-mbox.txt
new file mode 100644
index 00000000000000..e893615ef63570
--- /dev/null
+++ b/Documentation/devicetree/bindings/mailbox/brcm,bcm2835-mbox.txt
@@ -0,0 +1,26 @@
+Broadcom BCM2835 VideoCore mailbox IPC
+
+Required properties:
+
+- compatible: Should be "brcm,bcm2835-mbox"
+- reg: Specifies base physical address and size of the registers
+- interrupts: The interrupt number
+ See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
+- #mbox-cells: Specifies the number of cells needed to encode a mailbox
+ channel. The value shall be 0, since there is only one
+ mailbox channel implemented by the device.
+
+Example:
+
+mailbox: mailbox@7e00b800 {
+ compatible = "brcm,bcm2835-mbox";
+ reg = <0x7e00b880 0x40>;
+ interrupts = <0 1>;
+ #mbox-cells = <0>;
+};
+
+firmware: firmware {
+ compatible = "raspberrypi,firmware";
+ mboxes = <&mailbox>;
+ #power-domain-cells = <1>;
+};
diff --git a/Documentation/devicetree/bindings/mailbox/nvidia,tegra124-xusb-mbox.txt b/Documentation/devicetree/bindings/mailbox/nvidia,tegra124-xusb-mbox.txt
new file mode 100644
index 00000000000000..9d89afadc07066
--- /dev/null
+++ b/Documentation/devicetree/bindings/mailbox/nvidia,tegra124-xusb-mbox.txt
@@ -0,0 +1,30 @@
+NVIDIA Tegra XUSB mailbox
+=========================
+
+The Tegra XUSB mailbox is used by the Tegra xHCI controller's firmware to
+communicate requests to the host and PHY drivers.
+
+Refer to ./mailbox.txt for generic information about mailbox device-tree
+bindings.
+
+Required properties:
+--------------------
+ - compatible: For Tegra124, must contain "nvidia,tegra124-xusb-mbox".
+ Otherwise, must contain '"nvidia,<chip>-xusb-mbox",
+ "nvidia,tegra124-xusb-mbox"' where <chip> is tegra132.
+ - #mbox-cells: Should be 0. There is only one physical channel.
+
+Example:
+--------
+ mailbox {
+ compatible = "nvidia,tegra124-xusb-mbox";
+
+ #mbox-cells = <0>;
+ };
+
+ usb-host {
+ ...
+ mboxes = <&xusb_mbox>;
+ mbox-names = "xusb";
+ ...
+ };
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index 84b0a2d74d60b7..76c11942ea2378 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -56,8 +56,25 @@ config PCC
config ALTERA_MBOX
tristate "Altera Mailbox"
+ depends on HAS_IOMEM
help
An implementation of the Altera Mailbox soft core. It is used
to send message between processors. Say Y here if you want to use the
Altera mailbox support.
+
+config BCM2835_MBOX
+ tristate "BCM2835 Mailbox"
+ depends on ARCH_BCM2835
+ help
+ An implementation of the BCM2385 Mailbox. It is used to invoke
+ the services of the Videocore. Say Y here if you want to use the
+ BCM2835 Mailbox.
+
+config TEGRA_XUSB_MBOX
+ tristate "NVIDIA Tegra XUSB Mailbox"
+ depends on MFD_TEGRA_XUSB
+ help
+ Mailbox driver for the XUSB complex found on NVIDIA Tegra124 and
+ later SoCs. The XUSB mailbox is used to communicate between the
+ XUSB microcontroller and the host processor.
endif
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index b18201e97e2988..1dc9f9acafdbda 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -11,3 +11,7 @@ obj-$(CONFIG_OMAP2PLUS_MBOX) += omap-mailbox.o
obj-$(CONFIG_PCC) += pcc.o
obj-$(CONFIG_ALTERA_MBOX) += mailbox-altera.o
+
+obj-$(CONFIG_BCM2835_MBOX) += bcm2835-mailbox.o
+
+obj-$(CONFIG_TEGRA_XUSB_MBOX) += tegra-xusb-mailbox.o
diff --git a/drivers/mailbox/arm_mhu.c b/drivers/mailbox/arm_mhu.c
index ac693c63535769..d9e99f981aa98e 100644
--- a/drivers/mailbox/arm_mhu.c
+++ b/drivers/mailbox/arm_mhu.c
@@ -110,7 +110,7 @@ static void mhu_shutdown(struct mbox_chan *chan)
free_irq(mlink->irq, chan);
}
-static struct mbox_chan_ops mhu_ops = {
+static const struct mbox_chan_ops mhu_ops = {
.send_data = mhu_send_data,
.startup = mhu_startup,
.shutdown = mhu_shutdown,
diff --git a/drivers/mailbox/bcm2835-mailbox.c b/drivers/mailbox/bcm2835-mailbox.c
new file mode 100644
index 00000000000000..4b13268529f94e
--- /dev/null
+++ b/drivers/mailbox/bcm2835-mailbox.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2010,2015 Broadcom
+ * Copyright (C) 2013-2014 Lubomir Rintel
+ * Copyright (C) 2013 Craig McGeachie
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This device provides a mechanism for writing to the mailboxes,
+ * that are shared between the ARM and the VideoCore processor
+ *
+ * Parts of the driver are based on:
+ * - arch/arm/mach-bcm2708/vcio.c file written by Gray Girling that was
+ * obtained from branch "rpi-3.6.y" of git://github.com/raspberrypi/
+ * linux.git
+ * - drivers/mailbox/bcm2835-ipc.c by Lubomir Rintel at
+ * https://github.com/hackerspace/rpi-linux/blob/lr-raspberry-pi/drivers/
+ * mailbox/bcm2835-ipc.c
+ * - documentation available on the following web site:
+ * https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
+ */
+
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_controller.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+/* Mailboxes */
+#define ARM_0_MAIL0 0x00
+#define ARM_0_MAIL1 0x20
+
+/*
+ * Mailbox registers. We basically only support mailbox 0 & 1. We
+ * deliver to the VC in mailbox 1, it delivers to us in mailbox 0. See
+ * BCM2835-ARM-Peripherals.pdf section 1.3 for an explanation about
+ * the placement of memory barriers.
+ */
+#define MAIL0_RD (ARM_0_MAIL0 + 0x00)
+#define MAIL0_POL (ARM_0_MAIL0 + 0x10)
+#define MAIL0_STA (ARM_0_MAIL0 + 0x18)
+#define MAIL0_CNF (ARM_0_MAIL0 + 0x1C)
+#define MAIL1_WRT (ARM_0_MAIL1 + 0x00)
+
+/* Status register: FIFO state. */
+#define ARM_MS_FULL BIT(31)
+#define ARM_MS_EMPTY BIT(30)
+
+/* Configuration register: Enable interrupts. */
+#define ARM_MC_IHAVEDATAIRQEN BIT(0)
+
+struct bcm2835_mbox {
+ void __iomem *regs;
+ spinlock_t lock;
+ struct mbox_controller controller;
+};
+
+static struct bcm2835_mbox *bcm2835_link_mbox(struct mbox_chan *link)
+{
+ return container_of(link->mbox, struct bcm2835_mbox, controller);
+}
+
+static irqreturn_t bcm2835_mbox_irq(int irq, void *dev_id)
+{
+ struct bcm2835_mbox *mbox = dev_id;
+ struct device *dev = mbox->controller.dev;
+ struct mbox_chan *link = &mbox->controller.chans[0];
+
+ while (!(readl(mbox->regs + MAIL0_STA) & ARM_MS_EMPTY)) {
+ u32 msg = readl(mbox->regs + MAIL0_RD);
+ dev_dbg(dev, "Reply 0x%08X\n", msg);
+ mbox_chan_received_data(link, &msg);
+ }
+ return IRQ_HANDLED;
+}
+
+static int bcm2835_send_data(struct mbox_chan *link, void *data)
+{
+ struct bcm2835_mbox *mbox = bcm2835_link_mbox(link);
+ u32 msg = *(u32 *)data;
+
+ spin_lock(&mbox->lock);
+ writel(msg, mbox->regs + MAIL1_WRT);
+ dev_dbg(mbox->controller.dev, "Request 0x%08X\n", msg);
+ spin_unlock(&mbox->lock);
+ return 0;
+}
+
+static int bcm2835_startup(struct mbox_chan *link)
+{
+ struct bcm2835_mbox *mbox = bcm2835_link_mbox(link);
+
+ /* Enable the interrupt on data reception */
+ writel(ARM_MC_IHAVEDATAIRQEN, mbox->regs + MAIL0_CNF);
+
+ return 0;
+}
+
+static void bcm2835_shutdown(struct mbox_chan *link)
+{
+ struct bcm2835_mbox *mbox = bcm2835_link_mbox(link);
+
+ writel(0, mbox->regs + MAIL0_CNF);
+}
+
+static bool bcm2835_last_tx_done(struct mbox_chan *link)
+{
+ struct bcm2835_mbox *mbox = bcm2835_link_mbox(link);
+ bool ret;
+
+ spin_lock(&mbox->lock);
+ ret = !(readl(mbox->regs + MAIL0_STA) & ARM_MS_FULL);
+ spin_unlock(&mbox->lock);
+ return ret;
+}
+
+static const struct mbox_chan_ops bcm2835_mbox_chan_ops = {
+ .send_data = bcm2835_send_data,
+ .startup = bcm2835_startup,
+ .shutdown = bcm2835_shutdown,
+ .last_tx_done = bcm2835_last_tx_done
+};
+
+static struct mbox_chan *bcm2835_mbox_index_xlate(struct mbox_controller *mbox,
+ const struct of_phandle_args *sp)
+{
+ if (sp->args_count != 0)
+ return NULL;
+
+ return &mbox->chans[0];
+}
+
+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);
+ if (mbox == NULL)
+ return -ENOMEM;
+ spin_lock_init(&mbox->lock);
+
+ ret = devm_request_irq(dev, irq_of_parse_and_map(dev->of_node, 0),
+ bcm2835_mbox_irq, 0, dev_name(dev), mbox);
+ if (ret) {
+ dev_err(dev, "Failed to register a mailbox IRQ handler: %d\n",
+ ret);
+ return -ENODEV;
+ }
+
+ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ mbox->regs = devm_ioremap_resource(&pdev->dev, iomem);
+ if (IS_ERR(mbox->regs)) {
+ ret = PTR_ERR(mbox->regs);
+ dev_err(&pdev->dev, "Failed to remap mailbox regs: %d\n", ret);
+ return ret;
+ }
+
+ mbox->controller.txdone_poll = true;
+ mbox->controller.txpoll_period = 5;
+ mbox->controller.ops = &bcm2835_mbox_chan_ops;
+ mbox->controller.of_xlate = &bcm2835_mbox_index_xlate;
+ mbox->controller.dev = dev;
+ mbox->controller.num_chans = 1;
+ mbox->controller.chans = devm_kzalloc(dev,
+ sizeof(*mbox->controller.chans), GFP_KERNEL);
+ if (!mbox->controller.chans)
+ return -ENOMEM;
+
+ ret = mbox_controller_register(&mbox->controller);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, mbox);
+ dev_info(dev, "mailbox enabled\n");
+
+ return ret;
+}
+
+static int bcm2835_mbox_remove(struct platform_device *pdev)
+{
+ struct bcm2835_mbox *mbox = platform_get_drvdata(pdev);
+ mbox_controller_unregister(&mbox->controller);
+ return 0;
+}
+
+static const struct of_device_id bcm2835_mbox_of_match[] = {
+ { .compatible = "brcm,bcm2835-mbox", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bcm2835_mbox_of_match);
+
+static struct platform_driver bcm2835_mbox_driver = {
+ .driver = {
+ .name = "bcm2835-mbox",
+ .owner = THIS_MODULE,
+ .of_match_table = bcm2835_mbox_of_match,
+ },
+ .probe = bcm2835_mbox_probe,
+ .remove = bcm2835_mbox_remove,
+};
+module_platform_driver(bcm2835_mbox_driver);
+
+MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
+MODULE_DESCRIPTION("BCM2835 mailbox IPC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mailbox/mailbox-altera.c b/drivers/mailbox/mailbox-altera.c
index a266265677d3eb..bb682c926b0a04 100644
--- a/drivers/mailbox/mailbox-altera.c
+++ b/drivers/mailbox/mailbox-altera.c
@@ -285,7 +285,7 @@ static void altera_mbox_shutdown(struct mbox_chan *chan)
}
}
-static struct mbox_chan_ops altera_mbox_ops = {
+static const struct mbox_chan_ops altera_mbox_ops = {
.send_data = altera_mbox_send_data,
.startup = altera_mbox_startup,
.shutdown = altera_mbox_shutdown,
diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
index 19b491d2964f31..c3c42d42d017c6 100644
--- a/drivers/mailbox/mailbox.c
+++ b/drivers/mailbox/mailbox.c
@@ -318,7 +318,7 @@ struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index)
return ERR_PTR(-ENODEV);
}
- chan = NULL;
+ chan = ERR_PTR(-EPROBE_DEFER);
list_for_each_entry(mbox, &mbox_cons, node)
if (mbox->dev->of_node == spec.np) {
chan = mbox->of_xlate(mbox, &spec);
@@ -327,7 +327,12 @@ struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index)
of_node_put(spec.np);
- if (!chan || chan->cl || !try_module_get(mbox->dev->driver->owner)) {
+ if (IS_ERR(chan)) {
+ mutex_unlock(&con_mutex);
+ return chan;
+ }
+
+ if (chan->cl || !try_module_get(mbox->dev->driver->owner)) {
dev_dbg(dev, "%s: mailbox not free\n", __func__);
mutex_unlock(&con_mutex);
return ERR_PTR(-EBUSY);
@@ -390,7 +395,7 @@ of_mbox_index_xlate(struct mbox_controller *mbox,
int ind = sp->args[0];
if (ind >= mbox->num_chans)
- return NULL;
+ return ERR_PTR(-EINVAL);
return &mbox->chans[ind];
}
diff --git a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c
index 0f332c178b07a0..a3dbfd9c647994 100644
--- a/drivers/mailbox/omap-mailbox.c
+++ b/drivers/mailbox/omap-mailbox.c
@@ -604,7 +604,7 @@ static int omap_mbox_chan_send_data(struct mbox_chan *chan, void *data)
return ret;
}
-static struct mbox_chan_ops omap_mbox_chan_ops = {
+static const struct mbox_chan_ops omap_mbox_chan_ops = {
.startup = omap_mbox_chan_startup,
.send_data = omap_mbox_chan_send_data,
.shutdown = omap_mbox_chan_shutdown,
@@ -639,18 +639,18 @@ static struct mbox_chan *omap_mbox_of_xlate(struct mbox_controller *controller,
mdev = container_of(controller, struct omap_mbox_device, controller);
if (WARN_ON(!mdev))
- return NULL;
+ return ERR_PTR(-EINVAL);
node = of_find_node_by_phandle(phandle);
if (!node) {
pr_err("%s: could not find node phandle 0x%x\n",
__func__, phandle);
- return NULL;
+ return ERR_PTR(-ENODEV);
}
mbox = omap_mbox_device_find(mdev, node->name);
of_node_put(node);
- return mbox ? mbox->chan : NULL;
+ return mbox ? mbox->chan : ERR_PTR(-ENOENT);
}
static int omap_mbox_probe(struct platform_device *pdev)
diff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c
index 7e91d68a3ac3d0..26d121d1d501b7 100644
--- a/drivers/mailbox/pcc.c
+++ b/drivers/mailbox/pcc.c
@@ -198,7 +198,7 @@ static int pcc_send_data(struct mbox_chan *chan, void *data)
return 0;
}
-static struct mbox_chan_ops pcc_chan_ops = {
+static const struct mbox_chan_ops pcc_chan_ops = {
.send_data = pcc_send_data,
};
diff --git a/drivers/mailbox/tegra-xusb-mailbox.c b/drivers/mailbox/tegra-xusb-mailbox.c
new file mode 100644
index 00000000000000..4e2477dd78819d
--- /dev/null
+++ b/drivers/mailbox/tegra-xusb-mailbox.c
@@ -0,0 +1,290 @@
+/*
+ * NVIDIA Tegra XUSB mailbox driver
+ *
+ * Copyright (C) 2014 NVIDIA Corporation
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_controller.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <soc/tegra/xusb.h>
+
+#define XUSB_MBOX_NUM_CHANS 2 /* Host + PHY */
+
+#define XUSB_CFG_ARU_MBOX_CMD 0xe4
+#define MBOX_DEST_FALC BIT(27)
+#define MBOX_DEST_PME BIT(28)
+#define MBOX_DEST_SMI BIT(29)
+#define MBOX_DEST_XHCI BIT(30)
+#define MBOX_INT_EN BIT(31)
+#define XUSB_CFG_ARU_MBOX_DATA_IN 0xe8
+#define CMD_DATA_SHIFT 0
+#define CMD_DATA_MASK 0xffffff
+#define CMD_TYPE_SHIFT 24
+#define CMD_TYPE_MASK 0xff
+#define XUSB_CFG_ARU_MBOX_DATA_OUT 0xec
+#define XUSB_CFG_ARU_MBOX_OWNER 0xf0
+#define MBOX_OWNER_NONE 0
+#define MBOX_OWNER_FW 1
+#define MBOX_OWNER_SW 2
+#define XUSB_CFG_ARU_SMI_INTR 0x428
+#define MBOX_SMI_INTR_FW_HANG BIT(1)
+#define MBOX_SMI_INTR_EN BIT(3)
+
+struct tegra_xusb_mbox {
+ struct mbox_controller mbox;
+ struct regmap *fpci_regs;
+ spinlock_t lock;
+ int irq;
+};
+
+static inline u32 mbox_readl(struct tegra_xusb_mbox *mbox, unsigned long offset)
+{
+ u32 val;
+
+ regmap_read(mbox->fpci_regs, offset, &val);
+
+ return val;
+}
+
+static inline void mbox_writel(struct tegra_xusb_mbox *mbox, u32 val,
+ unsigned long offset)
+{
+ regmap_write(mbox->fpci_regs, offset, val);
+}
+
+static inline struct tegra_xusb_mbox *to_tegra_mbox(struct mbox_controller *c)
+{
+ return container_of(c, struct tegra_xusb_mbox, mbox);
+}
+
+static inline u32 mbox_pack_msg(struct tegra_xusb_mbox_msg *msg)
+{
+ u32 val;
+
+ val = (msg->cmd & CMD_TYPE_MASK) << CMD_TYPE_SHIFT;
+ val |= (msg->data & CMD_DATA_MASK) << CMD_DATA_SHIFT;
+
+ return val;
+}
+
+static inline void mbox_unpack_msg(u32 val, struct tegra_xusb_mbox_msg *msg)
+{
+ msg->cmd = (val >> CMD_TYPE_SHIFT) & CMD_TYPE_MASK;
+ msg->data = (val >> CMD_DATA_SHIFT) & CMD_DATA_MASK;
+}
+
+static bool mbox_cmd_requires_ack(enum tegra_xusb_mbox_cmd cmd)
+{
+ switch (cmd) {
+ case MBOX_CMD_SET_BW:
+ case MBOX_CMD_ACK:
+ case MBOX_CMD_NAK:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static int tegra_xusb_mbox_send_data(struct mbox_chan *chan, void *data)
+{
+ struct tegra_xusb_mbox *mbox = to_tegra_mbox(chan->mbox);
+ struct tegra_xusb_mbox_msg *msg = data;
+ unsigned long flags;
+ u32 reg;
+
+ dev_dbg(mbox->mbox.dev, "TX message %#x:%#x\n", msg->cmd, msg->data);
+
+ spin_lock_irqsave(&mbox->lock, flags);
+ /*
+ * Acquire the mailbox. The firmware still owns the mailbox for
+ * ACK/NAK messages.
+ */
+ if (!(msg->cmd == MBOX_CMD_ACK || msg->cmd == MBOX_CMD_NAK)) {
+ if (mbox_readl(mbox, XUSB_CFG_ARU_MBOX_OWNER) !=
+ MBOX_OWNER_NONE) {
+ dev_err(mbox->mbox.dev, "Mailbox not idle\n");
+ goto busy;
+ }
+
+ mbox_writel(mbox, MBOX_OWNER_SW, XUSB_CFG_ARU_MBOX_OWNER);
+ if (mbox_readl(mbox, XUSB_CFG_ARU_MBOX_OWNER) !=
+ MBOX_OWNER_SW) {
+ dev_err(mbox->mbox.dev, "Failed to acquire mailbox");
+ goto busy;
+ }
+ }
+
+ mbox_writel(mbox, mbox_pack_msg(msg), XUSB_CFG_ARU_MBOX_DATA_IN);
+ reg = mbox_readl(mbox, XUSB_CFG_ARU_MBOX_CMD);
+ reg |= MBOX_INT_EN | MBOX_DEST_FALC;
+ mbox_writel(mbox, reg, XUSB_CFG_ARU_MBOX_CMD);
+
+ spin_unlock_irqrestore(&mbox->lock, flags);
+
+ return 0;
+busy:
+ spin_unlock_irqrestore(&mbox->lock, flags);
+ return -EBUSY;
+}
+
+static int tegra_xusb_mbox_startup(struct mbox_chan *chan)
+{
+ return 0;
+}
+
+static void tegra_xusb_mbox_shutdown(struct mbox_chan *chan)
+{
+}
+
+static bool tegra_xusb_mbox_last_tx_done(struct mbox_chan *chan)
+{
+ struct tegra_xusb_mbox *mbox = to_tegra_mbox(chan->mbox);
+
+ return mbox_readl(mbox, XUSB_CFG_ARU_MBOX_OWNER) == MBOX_OWNER_NONE;
+}
+
+static const struct mbox_chan_ops tegra_xusb_mbox_chan_ops = {
+ .send_data = tegra_xusb_mbox_send_data,
+ .startup = tegra_xusb_mbox_startup,
+ .shutdown = tegra_xusb_mbox_shutdown,
+ .last_tx_done = tegra_xusb_mbox_last_tx_done,
+};
+
+static irqreturn_t tegra_xusb_mbox_irq(int irq, void *p)
+{
+ struct tegra_xusb_mbox *mbox = p;
+ struct tegra_xusb_mbox_msg msg;
+ unsigned int i;
+ u32 reg;
+
+ spin_lock(&mbox->lock);
+
+ /* Clear mbox interrupts */
+ reg = mbox_readl(mbox, XUSB_CFG_ARU_SMI_INTR);
+ if (reg & MBOX_SMI_INTR_FW_HANG)
+ dev_err(mbox->mbox.dev, "Controller firmware hang\n");
+ mbox_writel(mbox, reg, XUSB_CFG_ARU_SMI_INTR);
+
+ reg = mbox_readl(mbox, XUSB_CFG_ARU_MBOX_DATA_OUT);
+ mbox_unpack_msg(reg, &msg);
+
+ reg = mbox_readl(mbox, XUSB_CFG_ARU_MBOX_CMD);
+ reg &= ~MBOX_DEST_SMI;
+ mbox_writel(mbox, reg, XUSB_CFG_ARU_MBOX_CMD);
+
+ /* Clear mailbox owner if no ACK/NAK is required. */
+ if (!mbox_cmd_requires_ack(msg.cmd))
+ mbox_writel(mbox, MBOX_OWNER_NONE, XUSB_CFG_ARU_MBOX_OWNER);
+
+ dev_dbg(mbox->mbox.dev, "RX message %#x:%#x\n", msg.cmd, msg.data);
+ for (i = 0; i < XUSB_MBOX_NUM_CHANS; i++) {
+ if (mbox->mbox.chans[i].cl)
+ mbox_chan_received_data(&mbox->mbox.chans[i], &msg);
+ }
+
+ spin_unlock(&mbox->lock);
+
+ return IRQ_HANDLED;
+}
+
+static struct mbox_chan *tegra_xusb_mbox_of_xlate(struct mbox_controller *ctlr,
+ const struct of_phandle_args *sp)
+{
+ struct tegra_xusb_mbox *mbox = to_tegra_mbox(ctlr);
+ struct mbox_chan *chan = ERR_PTR(-EINVAL);
+ unsigned long flags;
+ unsigned int i;
+
+ /* Pick the first available (virtual) channel. */
+ spin_lock_irqsave(&mbox->lock, flags);
+ for (i = 0; XUSB_MBOX_NUM_CHANS; i++) {
+ if (!ctlr->chans[i].cl) {
+ chan = &ctlr->chans[i];
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&mbox->lock, flags);
+
+ return chan;
+}
+
+static const struct of_device_id tegra_xusb_mbox_of_match[] = {
+ { .compatible = "nvidia,tegra124-xusb-mbox" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tegra_xusb_mbox_of_match);
+
+static int tegra_xusb_mbox_probe(struct platform_device *pdev)
+{
+ struct tegra_xusb_mbox *mbox;
+ int ret;
+
+ mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox), GFP_KERNEL);
+ if (!mbox)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, mbox);
+ spin_lock_init(&mbox->lock);
+ mbox->fpci_regs = dev_get_drvdata(pdev->dev.parent);
+
+ mbox->mbox.dev = &pdev->dev;
+ mbox->mbox.chans = devm_kcalloc(&pdev->dev, XUSB_MBOX_NUM_CHANS,
+ sizeof(*mbox->mbox.chans), GFP_KERNEL);
+ if (!mbox->mbox.chans)
+ return -ENOMEM;
+ mbox->mbox.num_chans = XUSB_MBOX_NUM_CHANS;
+ mbox->mbox.ops = &tegra_xusb_mbox_chan_ops;
+ mbox->mbox.txdone_poll = true;
+ mbox->mbox.txpoll_period = 1;
+ mbox->mbox.of_xlate = tegra_xusb_mbox_of_xlate;
+
+ mbox->irq = platform_get_irq(pdev, 0);
+ if (mbox->irq < 0)
+ return mbox->irq;
+ ret = devm_request_irq(&pdev->dev, mbox->irq, tegra_xusb_mbox_irq, 0,
+ dev_name(&pdev->dev), mbox);
+ if (ret < 0)
+ return ret;
+
+ ret = mbox_controller_register(&mbox->mbox);
+ if (ret < 0)
+ dev_err(&pdev->dev, "failed to register mailbox: %d\n", ret);
+
+ return ret;
+}
+
+static int tegra_xusb_mbox_remove(struct platform_device *pdev)
+{
+ struct tegra_xusb_mbox *mbox = platform_get_drvdata(pdev);
+
+ synchronize_irq(mbox->irq);
+ devm_free_irq(&pdev->dev, mbox->irq, mbox);
+ mbox_controller_unregister(&mbox->mbox);
+
+ return 0;
+}
+
+static struct platform_driver tegra_xusb_mbox_driver = {
+ .probe = tegra_xusb_mbox_probe,
+ .remove = tegra_xusb_mbox_remove,
+ .driver = {
+ .name = "tegra-xusb-mbox",
+ .of_match_table = tegra_xusb_mbox_of_match,
+ },
+};
+module_platform_driver(tegra_xusb_mbox_driver);
+
+MODULE_AUTHOR("Andrew Bresticker <abrestic@chromium.org>");
+MODULE_DESCRIPTION("NVIDIA Tegra XUSB mailbox driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mailbox_controller.h b/include/linux/mailbox_controller.h
index d4cf96f07cfc42..68c42454439be3 100644
--- a/include/linux/mailbox_controller.h
+++ b/include/linux/mailbox_controller.h
@@ -72,7 +72,7 @@ struct mbox_chan_ops {
*/
struct mbox_controller {
struct device *dev;
- struct mbox_chan_ops *ops;
+ const struct mbox_chan_ops *ops;
struct mbox_chan *chans;
int num_chans;
bool txdone_irq;
diff --git a/include/soc/tegra/xusb.h b/include/soc/tegra/xusb.h
new file mode 100644
index 00000000000000..5ce5e12e99fc32
--- /dev/null
+++ b/include/soc/tegra/xusb.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 NVIDIA Corporation
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#ifndef __SOC_TEGRA_XUSB_H__
+#define __SOC_TEGRA_XUSB_H__
+
+/* Command requests from the firmware */
+enum tegra_xusb_mbox_cmd {
+ MBOX_CMD_MSG_ENABLED = 1,
+ MBOX_CMD_INC_FALC_CLOCK,
+ MBOX_CMD_DEC_FALC_CLOCK,
+ MBOX_CMD_INC_SSPI_CLOCK,
+ MBOX_CMD_DEC_SSPI_CLOCK,
+ MBOX_CMD_SET_BW, /* no ACK/NAK required */
+ MBOX_CMD_SET_SS_PWR_GATING,
+ MBOX_CMD_SET_SS_PWR_UNGATING,
+ MBOX_CMD_SAVE_DFE_CTLE_CTX,
+ MBOX_CMD_AIRPLANE_MODE_ENABLED, /* unused */
+ MBOX_CMD_AIRPLANE_MODE_DISABLED, /* unused */
+ MBOX_CMD_START_HSIC_IDLE,
+ MBOX_CMD_STOP_HSIC_IDLE,
+ MBOX_CMD_DBC_WAKE_STACK, /* unused */
+ MBOX_CMD_HSIC_PRETEND_CONNECT,
+
+ MBOX_CMD_MAX,
+
+ /* Response message to above commands */
+ MBOX_CMD_ACK = 128,
+ MBOX_CMD_NAK
+};
+
+struct tegra_xusb_mbox_msg {
+ u32 cmd;
+ u32 data;
+};
+
+#endif /* __SOC_TEGRA_XUSB_H__ */