diff options
author | José Pekkarinen <jose.pekkarinen@nokia.com> | 2016-04-11 10:41:07 +0300 |
---|---|---|
committer | José Pekkarinen <jose.pekkarinen@nokia.com> | 2016-04-13 08:17:18 +0300 |
commit | e09b41010ba33a20a87472ee821fa407a5b8da36 (patch) | |
tree | d10dc367189862e7ca5c592f033dc3726e1df4e3 /kernel/drivers/mailbox | |
parent | f93b97fd65072de626c074dbe099a1fff05ce060 (diff) |
These changes are the raw update to linux-4.4.6-rt14. Kernel sources
are taken from kernel.org, and rt patch from the rt wiki download page.
During the rebasing, the following patch collided:
Force tick interrupt and get rid of softirq magic(I70131fb85).
Collisions have been removed because its logic was found on the
source already.
Change-Id: I7f57a4081d9deaa0d9ccfc41a6c8daccdee3b769
Signed-off-by: José Pekkarinen <jose.pekkarinen@nokia.com>
Diffstat (limited to 'kernel/drivers/mailbox')
-rw-r--r-- | kernel/drivers/mailbox/Kconfig | 25 | ||||
-rw-r--r-- | kernel/drivers/mailbox/Makefile | 6 | ||||
-rw-r--r-- | kernel/drivers/mailbox/arm_mhu.c | 6 | ||||
-rw-r--r-- | kernel/drivers/mailbox/bcm2835-mailbox.c | 216 | ||||
-rw-r--r-- | kernel/drivers/mailbox/mailbox-altera.c | 2 | ||||
-rw-r--r-- | kernel/drivers/mailbox/mailbox-sti.c | 513 | ||||
-rw-r--r-- | kernel/drivers/mailbox/mailbox-test.c | 361 | ||||
-rw-r--r-- | kernel/drivers/mailbox/mailbox.c | 67 | ||||
-rw-r--r-- | kernel/drivers/mailbox/omap-mailbox.c | 57 | ||||
-rw-r--r-- | kernel/drivers/mailbox/pcc.c | 12 | ||||
-rw-r--r-- | kernel/drivers/mailbox/pl320-ipc.c | 2 |
11 files changed, 1236 insertions, 31 deletions
diff --git a/kernel/drivers/mailbox/Kconfig b/kernel/drivers/mailbox/Kconfig index 84b0a2d74..546d05f43 100644 --- a/kernel/drivers/mailbox/Kconfig +++ b/kernel/drivers/mailbox/Kconfig @@ -46,6 +46,7 @@ config OMAP_MBOX_KFIFO_SIZE config PCC bool "Platform Communication Channel Driver" depends on ACPI + default n help ACPI 5.0+ spec defines a generic mode of communication between the OS and a platform such as the BMC. This medium @@ -56,8 +57,32 @@ 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 STI_MBOX + tristate "STI Mailbox framework support" + depends on ARCH_STI && OF + help + Mailbox implementation for STMicroelectonics family chips with + hardware for interprocessor communication. + +config MAILBOX_TEST + tristate "Mailbox Test Client" + depends on OF + help + Test client to help with testing new Controller driver + implementations. + endif diff --git a/kernel/drivers/mailbox/Makefile b/kernel/drivers/mailbox/Makefile index b18201e97..92435ef11 100644 --- a/kernel/drivers/mailbox/Makefile +++ b/kernel/drivers/mailbox/Makefile @@ -2,6 +2,8 @@ obj-$(CONFIG_MAILBOX) += mailbox.o +obj-$(CONFIG_MAILBOX_TEST) += mailbox-test.o + obj-$(CONFIG_ARM_MHU) += arm_mhu.o obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o @@ -11,3 +13,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_STI_MBOX) += mailbox-sti.o diff --git a/kernel/drivers/mailbox/arm_mhu.c b/kernel/drivers/mailbox/arm_mhu.c index ac693c635..99befa76e 100644 --- a/kernel/drivers/mailbox/arm_mhu.c +++ b/kernel/drivers/mailbox/arm_mhu.c @@ -96,7 +96,7 @@ static int mhu_startup(struct mbox_chan *chan) IRQF_SHARED, "mhu_link", chan); if (ret) { dev_err(chan->mbox->dev, - "Unable to aquire IRQ %d\n", mlink->irq); + "Unable to acquire IRQ %d\n", mlink->irq); return ret; } @@ -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, @@ -148,7 +148,7 @@ static int mhu_probe(struct amba_device *adev, const struct amba_id *id) mhu->mbox.ops = &mhu_ops; mhu->mbox.txdone_irq = false; mhu->mbox.txdone_poll = true; - mhu->mbox.txpoll_period = 10; + mhu->mbox.txpoll_period = 1; amba_set_drvdata(adev, mhu); diff --git a/kernel/drivers/mailbox/bcm2835-mailbox.c b/kernel/drivers/mailbox/bcm2835-mailbox.c new file mode 100644 index 000000000..cfb4b4496 --- /dev/null +++ b/kernel/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) +#define MAIL1_STA (ARM_0_MAIL1 + 0x18) + +/* 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 + MAIL1_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", + .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/kernel/drivers/mailbox/mailbox-altera.c b/kernel/drivers/mailbox/mailbox-altera.c index a26626567..bb682c926 100644 --- a/kernel/drivers/mailbox/mailbox-altera.c +++ b/kernel/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/kernel/drivers/mailbox/mailbox-sti.c b/kernel/drivers/mailbox/mailbox-sti.c new file mode 100644 index 000000000..4835817c5 --- /dev/null +++ b/kernel/drivers/mailbox/mailbox-sti.c @@ -0,0 +1,513 @@ +/* + * STi Mailbox + * + * Copyright (C) 2015 ST Microelectronics + * + * Author: Lee Jones <lee.jones@linaro.org> for ST Microelectronics + * + * Based on the original driver written by; + * Alexandre Torgue, Olivier Lebreton and Loic Pallardy + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mailbox_controller.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "mailbox.h" + +#define STI_MBOX_INST_MAX 4 /* RAM saving: Max supported instances */ +#define STI_MBOX_CHAN_MAX 20 /* RAM saving: Max supported channels */ + +#define STI_IRQ_VAL_OFFSET 0x04 /* Read interrupt status */ +#define STI_IRQ_SET_OFFSET 0x24 /* Generate a Tx channel interrupt */ +#define STI_IRQ_CLR_OFFSET 0x44 /* Clear pending Rx interrupts */ +#define STI_ENA_VAL_OFFSET 0x64 /* Read enable status */ +#define STI_ENA_SET_OFFSET 0x84 /* Enable a channel */ +#define STI_ENA_CLR_OFFSET 0xa4 /* Disable a channel */ + +#define MBOX_BASE(mdev, inst) ((mdev)->base + ((inst) * 4)) + +/** + * STi Mailbox device data + * + * An IP Mailbox is currently composed of 4 instances + * Each instance is currently composed of 32 channels + * This means that we have 128 channels per Mailbox + * A channel an be used for TX or RX + * + * @dev: Device to which it is attached + * @mbox: Representation of a communication channel controller + * @base: Base address of the register mapping region + * @name: Name of the mailbox + * @enabled: Local copy of enabled channels + * @lock: Mutex protecting enabled status + */ +struct sti_mbox_device { + struct device *dev; + struct mbox_controller *mbox; + void __iomem *base; + const char *name; + u32 enabled[STI_MBOX_INST_MAX]; + spinlock_t lock; +}; + +/** + * STi Mailbox platform specific configuration + * + * @num_inst: Maximum number of instances in one HW Mailbox + * @num_chan: Maximum number of channel per instance + */ +struct sti_mbox_pdata { + unsigned int num_inst; + unsigned int num_chan; +}; + +/** + * STi Mailbox allocated channel information + * + * @mdev: Pointer to parent Mailbox device + * @instance: Instance number channel resides in + * @channel: Channel number pertaining to this container + */ +struct sti_channel { + struct sti_mbox_device *mdev; + unsigned int instance; + unsigned int channel; +}; + +static inline bool sti_mbox_channel_is_enabled(struct mbox_chan *chan) +{ + struct sti_channel *chan_info = chan->con_priv; + struct sti_mbox_device *mdev = chan_info->mdev; + unsigned int instance = chan_info->instance; + unsigned int channel = chan_info->channel; + + return mdev->enabled[instance] & BIT(channel); +} + +static inline +struct mbox_chan *sti_mbox_to_channel(struct mbox_controller *mbox, + unsigned int instance, + unsigned int channel) +{ + struct sti_channel *chan_info; + int i; + + for (i = 0; i < mbox->num_chans; i++) { + chan_info = mbox->chans[i].con_priv; + if (chan_info && + chan_info->instance == instance && + chan_info->channel == channel) + return &mbox->chans[i]; + } + + dev_err(mbox->dev, + "Channel not registered: instance: %d channel: %d\n", + instance, channel); + + return NULL; +} + +static void sti_mbox_enable_channel(struct mbox_chan *chan) +{ + struct sti_channel *chan_info = chan->con_priv; + struct sti_mbox_device *mdev = chan_info->mdev; + unsigned int instance = chan_info->instance; + unsigned int channel = chan_info->channel; + unsigned long flags; + void __iomem *base = MBOX_BASE(mdev, instance); + + spin_lock_irqsave(&mdev->lock, flags); + mdev->enabled[instance] |= BIT(channel); + writel_relaxed(BIT(channel), base + STI_ENA_SET_OFFSET); + spin_unlock_irqrestore(&mdev->lock, flags); +} + +static void sti_mbox_disable_channel(struct mbox_chan *chan) +{ + struct sti_channel *chan_info = chan->con_priv; + struct sti_mbox_device *mdev = chan_info->mdev; + unsigned int instance = chan_info->instance; + unsigned int channel = chan_info->channel; + unsigned long flags; + void __iomem *base = MBOX_BASE(mdev, instance); + + spin_lock_irqsave(&mdev->lock, flags); + mdev->enabled[instance] &= ~BIT(channel); + writel_relaxed(BIT(channel), base + STI_ENA_CLR_OFFSET); + spin_unlock_irqrestore(&mdev->lock, flags); +} + +static void sti_mbox_clear_irq(struct mbox_chan *chan) +{ + struct sti_channel *chan_info = chan->con_priv; + struct sti_mbox_device *mdev = chan_info->mdev; + unsigned int instance = chan_info->instance; + unsigned int channel = chan_info->channel; + void __iomem *base = MBOX_BASE(mdev, instance); + + writel_relaxed(BIT(channel), base + STI_IRQ_CLR_OFFSET); +} + +static struct mbox_chan *sti_mbox_irq_to_channel(struct sti_mbox_device *mdev, + unsigned int instance) +{ + struct mbox_controller *mbox = mdev->mbox; + struct mbox_chan *chan = NULL; + unsigned int channel; + unsigned long bits; + void __iomem *base = MBOX_BASE(mdev, instance); + + bits = readl_relaxed(base + STI_IRQ_VAL_OFFSET); + if (!bits) + /* No IRQs fired in specified instance */ + return NULL; + + /* An IRQ has fired, find the associated channel */ + for (channel = 0; bits; channel++) { + if (!test_and_clear_bit(channel, &bits)) + continue; + + chan = sti_mbox_to_channel(mbox, instance, channel); + if (chan) { + dev_dbg(mbox->dev, + "IRQ fired on instance: %d channel: %d\n", + instance, channel); + break; + } + } + + return chan; +} + +static irqreturn_t sti_mbox_thread_handler(int irq, void *data) +{ + struct sti_mbox_device *mdev = data; + struct sti_mbox_pdata *pdata = dev_get_platdata(mdev->dev); + struct mbox_chan *chan; + unsigned int instance; + + for (instance = 0; instance < pdata->num_inst; instance++) { +keep_looking: + chan = sti_mbox_irq_to_channel(mdev, instance); + if (!chan) + continue; + + mbox_chan_received_data(chan, NULL); + sti_mbox_clear_irq(chan); + sti_mbox_enable_channel(chan); + goto keep_looking; + } + + return IRQ_HANDLED; +} + +static irqreturn_t sti_mbox_irq_handler(int irq, void *data) +{ + struct sti_mbox_device *mdev = data; + struct sti_mbox_pdata *pdata = dev_get_platdata(mdev->dev); + struct sti_channel *chan_info; + struct mbox_chan *chan; + unsigned int instance; + int ret = IRQ_NONE; + + for (instance = 0; instance < pdata->num_inst; instance++) { + chan = sti_mbox_irq_to_channel(mdev, instance); + if (!chan) + continue; + chan_info = chan->con_priv; + + if (!sti_mbox_channel_is_enabled(chan)) { + dev_warn(mdev->dev, + "Unexpected IRQ: %s\n" + " instance: %d: channel: %d [enabled: %x]\n", + mdev->name, chan_info->instance, + chan_info->channel, mdev->enabled[instance]); + + /* Only handle IRQ if no other valid IRQs were found */ + if (ret == IRQ_NONE) + ret = IRQ_HANDLED; + continue; + } + + sti_mbox_disable_channel(chan); + ret = IRQ_WAKE_THREAD; + } + + if (ret == IRQ_NONE) + dev_err(mdev->dev, "Spurious IRQ - was a channel requested?\n"); + + return ret; +} + +static bool sti_mbox_tx_is_ready(struct mbox_chan *chan) +{ + struct sti_channel *chan_info = chan->con_priv; + struct sti_mbox_device *mdev = chan_info->mdev; + unsigned int instance = chan_info->instance; + unsigned int channel = chan_info->channel; + void __iomem *base = MBOX_BASE(mdev, instance); + + if (!(readl_relaxed(base + STI_ENA_VAL_OFFSET) & BIT(channel))) { + dev_dbg(mdev->dev, "Mbox: %s: inst: %d, chan: %d disabled\n", + mdev->name, instance, channel); + return false; + } + + if (readl_relaxed(base + STI_IRQ_VAL_OFFSET) & BIT(channel)) { + dev_dbg(mdev->dev, "Mbox: %s: inst: %d, chan: %d not ready\n", + mdev->name, instance, channel); + return false; + } + + return true; +} + +static int sti_mbox_send_data(struct mbox_chan *chan, void *data) +{ + struct sti_channel *chan_info = chan->con_priv; + struct sti_mbox_device *mdev = chan_info->mdev; + unsigned int instance = chan_info->instance; + unsigned int channel = chan_info->channel; + void __iomem *base = MBOX_BASE(mdev, instance); + + /* Send event to co-processor */ + writel_relaxed(BIT(channel), base + STI_IRQ_SET_OFFSET); + + dev_dbg(mdev->dev, + "Sent via Mailbox %s: instance: %d channel: %d\n", + mdev->name, instance, channel); + + return 0; +} + +static int sti_mbox_startup_chan(struct mbox_chan *chan) +{ + sti_mbox_clear_irq(chan); + sti_mbox_enable_channel(chan); + + return 0; +} + +static void sti_mbox_shutdown_chan(struct mbox_chan *chan) +{ + struct sti_channel *chan_info = chan->con_priv; + struct mbox_controller *mbox = chan_info->mdev->mbox; + int i; + + for (i = 0; i < mbox->num_chans; i++) + if (chan == &mbox->chans[i]) + break; + + if (mbox->num_chans == i) { + dev_warn(mbox->dev, "Request to free non-existent channel\n"); + return; + } + + /* Reset channel */ + sti_mbox_disable_channel(chan); + sti_mbox_clear_irq(chan); + chan->con_priv = NULL; +} + +static struct mbox_chan *sti_mbox_xlate(struct mbox_controller *mbox, + const struct of_phandle_args *spec) +{ + struct sti_mbox_device *mdev = dev_get_drvdata(mbox->dev); + struct sti_mbox_pdata *pdata = dev_get_platdata(mdev->dev); + struct sti_channel *chan_info; + struct mbox_chan *chan = NULL; + unsigned int instance = spec->args[0]; + unsigned int channel = spec->args[1]; + int i; + + /* Bounds checking */ + if (instance >= pdata->num_inst || channel >= pdata->num_chan) { + dev_err(mbox->dev, + "Invalid channel requested instance: %d channel: %d\n", + instance, channel); + return ERR_PTR(-EINVAL); + } + + for (i = 0; i < mbox->num_chans; i++) { + chan_info = mbox->chans[i].con_priv; + + /* Is requested channel free? */ + if (chan_info && + mbox->dev == chan_info->mdev->dev && + instance == chan_info->instance && + channel == chan_info->channel) { + + dev_err(mbox->dev, "Channel in use\n"); + return ERR_PTR(-EBUSY); + } + + /* + * Find the first free slot, then continue checking + * to see if requested channel is in use + */ + if (!chan && !chan_info) + chan = &mbox->chans[i]; + } + + if (!chan) { + dev_err(mbox->dev, "No free channels left\n"); + return ERR_PTR(-EBUSY); + } + + chan_info = devm_kzalloc(mbox->dev, sizeof(*chan_info), GFP_KERNEL); + if (!chan_info) + return ERR_PTR(-ENOMEM); + + chan_info->mdev = mdev; + chan_info->instance = instance; + chan_info->channel = channel; + + chan->con_priv = chan_info; + + dev_info(mbox->dev, + "Mbox: %s: Created channel: instance: %d channel: %d\n", + mdev->name, instance, channel); + + return chan; +} + +static struct mbox_chan_ops sti_mbox_ops = { + .startup = sti_mbox_startup_chan, + .shutdown = sti_mbox_shutdown_chan, + .send_data = sti_mbox_send_data, + .last_tx_done = sti_mbox_tx_is_ready, +}; + +static const struct sti_mbox_pdata mbox_stih407_pdata = { + .num_inst = 4, + .num_chan = 32, +}; + +static const struct of_device_id sti_mailbox_match[] = { + { + .compatible = "st,stih407-mailbox", + .data = (void *)&mbox_stih407_pdata + }, + { } +}; + +static int sti_mbox_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + struct mbox_controller *mbox; + struct sti_mbox_device *mdev; + struct device_node *np = pdev->dev.of_node; + struct mbox_chan *chans; + struct resource *res; + int irq; + int ret; + + match = of_match_device(sti_mailbox_match, &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "No configuration found\n"); + return -ENODEV; + } + pdev->dev.platform_data = (struct sti_mbox_pdata *) match->data; + + mdev = devm_kzalloc(&pdev->dev, sizeof(*mdev), GFP_KERNEL); + if (!mdev) + return -ENOMEM; + + platform_set_drvdata(pdev, mdev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mdev->base = devm_ioremap_resource(&pdev->dev, res); + if (!mdev->base) + return -ENOMEM; + + ret = of_property_read_string(np, "mbox-name", &mdev->name); + if (ret) + mdev->name = np->full_name; + + mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox), GFP_KERNEL); + if (!mbox) + return -ENOMEM; + + chans = devm_kzalloc(&pdev->dev, + sizeof(*chans) * STI_MBOX_CHAN_MAX, GFP_KERNEL); + if (!chans) + return -ENOMEM; + + mdev->dev = &pdev->dev; + mdev->mbox = mbox; + + spin_lock_init(&mdev->lock); + + /* STi Mailbox does not have a Tx-Done or Tx-Ready IRQ */ + mbox->txdone_irq = false; + mbox->txdone_poll = true; + mbox->txpoll_period = 100; + mbox->ops = &sti_mbox_ops; + mbox->dev = mdev->dev; + mbox->of_xlate = sti_mbox_xlate; + mbox->chans = chans; + mbox->num_chans = STI_MBOX_CHAN_MAX; + + ret = mbox_controller_register(mbox); + if (ret) + return ret; + + /* It's okay for Tx Mailboxes to not supply IRQs */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_info(&pdev->dev, + "%s: Registered Tx only Mailbox\n", mdev->name); + return 0; + } + + ret = devm_request_threaded_irq(&pdev->dev, irq, + sti_mbox_irq_handler, + sti_mbox_thread_handler, + IRQF_ONESHOT, mdev->name, mdev); + if (ret) { + dev_err(&pdev->dev, "Can't claim IRQ %d\n", irq); + mbox_controller_unregister(mbox); + return -EINVAL; + } + + dev_info(&pdev->dev, "%s: Registered Tx/Rx Mailbox\n", mdev->name); + + return 0; +} + +static int sti_mbox_remove(struct platform_device *pdev) +{ + struct sti_mbox_device *mdev = platform_get_drvdata(pdev); + + mbox_controller_unregister(mdev->mbox); + + return 0; +} + +static struct platform_driver sti_mbox_driver = { + .probe = sti_mbox_probe, + .remove = sti_mbox_remove, + .driver = { + .name = "sti-mailbox", + .of_match_table = sti_mailbox_match, + }, +}; +module_platform_driver(sti_mbox_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("STMicroelectronics Mailbox Controller"); +MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org"); +MODULE_ALIAS("platform:mailbox-sti"); diff --git a/kernel/drivers/mailbox/mailbox-test.c b/kernel/drivers/mailbox/mailbox-test.c new file mode 100644 index 000000000..684ae17dc --- /dev/null +++ b/kernel/drivers/mailbox/mailbox-test.c @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2015 ST Microelectronics + * + * Author: Lee Jones <lee.jones@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; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/debugfs.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mailbox_client.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/uaccess.h> + +#define MBOX_MAX_SIG_LEN 8 +#define MBOX_MAX_MSG_LEN 128 +#define MBOX_BYTES_PER_LINE 16 +#define MBOX_HEXDUMP_LINE_LEN ((MBOX_BYTES_PER_LINE * 4) + 2) +#define MBOX_HEXDUMP_MAX_LEN (MBOX_HEXDUMP_LINE_LEN * \ + (MBOX_MAX_MSG_LEN / MBOX_BYTES_PER_LINE)) + +static struct dentry *root_debugfs_dir; + +struct mbox_test_device { + struct device *dev; + void __iomem *mmio; + struct mbox_chan *tx_channel; + struct mbox_chan *rx_channel; + char *rx_buffer; + char *signal; + char *message; + spinlock_t lock; +}; + +static ssize_t mbox_test_signal_write(struct file *filp, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct mbox_test_device *tdev = filp->private_data; + int ret; + + if (!tdev->tx_channel) { + dev_err(tdev->dev, "Channel cannot do Tx\n"); + return -EINVAL; + } + + if (count > MBOX_MAX_SIG_LEN) { + dev_err(tdev->dev, + "Signal length %zd greater than max allowed %d\n", + count, MBOX_MAX_SIG_LEN); + return -EINVAL; + } + + tdev->signal = kzalloc(MBOX_MAX_SIG_LEN, GFP_KERNEL); + if (!tdev->signal) + return -ENOMEM; + + ret = copy_from_user(tdev->signal, userbuf, count); + if (ret) { + kfree(tdev->signal); + return -EFAULT; + } + + return ret < 0 ? ret : count; +} + +static const struct file_operations mbox_test_signal_ops = { + .write = mbox_test_signal_write, + .open = simple_open, + .llseek = generic_file_llseek, +}; + +static ssize_t mbox_test_message_write(struct file *filp, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct mbox_test_device *tdev = filp->private_data; + void *data; + int ret; + + if (!tdev->tx_channel) { + dev_err(tdev->dev, "Channel cannot do Tx\n"); + return -EINVAL; + } + + if (count > MBOX_MAX_MSG_LEN) { + dev_err(tdev->dev, + "Message length %zd greater than max allowed %d\n", + count, MBOX_MAX_MSG_LEN); + return -EINVAL; + } + + tdev->message = kzalloc(MBOX_MAX_MSG_LEN, GFP_KERNEL); + if (!tdev->message) + return -ENOMEM; + + ret = copy_from_user(tdev->message, userbuf, count); + if (ret) { + ret = -EFAULT; + goto out; + } + + /* + * A separate signal is only of use if there is + * MMIO to subsequently pass the message through + */ + if (tdev->mmio && tdev->signal) { + print_hex_dump(KERN_INFO, "Client: Sending: Signal: ", DUMP_PREFIX_ADDRESS, + MBOX_BYTES_PER_LINE, 1, tdev->signal, MBOX_MAX_SIG_LEN, true); + + data = tdev->signal; + } else + data = tdev->message; + + print_hex_dump(KERN_INFO, "Client: Sending: Message: ", DUMP_PREFIX_ADDRESS, + MBOX_BYTES_PER_LINE, 1, tdev->message, MBOX_MAX_MSG_LEN, true); + + ret = mbox_send_message(tdev->tx_channel, data); + if (ret < 0) + dev_err(tdev->dev, "Failed to send message via mailbox\n"); + +out: + kfree(tdev->signal); + kfree(tdev->message); + + return ret < 0 ? ret : count; +} + +static ssize_t mbox_test_message_read(struct file *filp, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct mbox_test_device *tdev = filp->private_data; + unsigned long flags; + char *touser, *ptr; + int l = 0; + int ret; + + touser = kzalloc(MBOX_HEXDUMP_MAX_LEN + 1, GFP_KERNEL); + if (!touser) + return -ENOMEM; + + if (!tdev->rx_channel) { + ret = snprintf(touser, 20, "<NO RX CAPABILITY>\n"); + ret = simple_read_from_buffer(userbuf, count, ppos, + touser, ret); + goto out; + } + + if (tdev->rx_buffer[0] == '\0') { + ret = snprintf(touser, 9, "<EMPTY>\n"); + ret = simple_read_from_buffer(userbuf, count, ppos, + touser, ret); + goto out; + } + + spin_lock_irqsave(&tdev->lock, flags); + + ptr = tdev->rx_buffer; + while (l < MBOX_HEXDUMP_MAX_LEN) { + hex_dump_to_buffer(ptr, + MBOX_BYTES_PER_LINE, + MBOX_BYTES_PER_LINE, 1, touser + l, + MBOX_HEXDUMP_LINE_LEN, true); + + ptr += MBOX_BYTES_PER_LINE; + l += MBOX_HEXDUMP_LINE_LEN; + *(touser + (l - 1)) = '\n'; + } + *(touser + l) = '\0'; + + memset(tdev->rx_buffer, 0, MBOX_MAX_MSG_LEN); + + spin_unlock_irqrestore(&tdev->lock, flags); + + ret = simple_read_from_buffer(userbuf, count, ppos, touser, MBOX_HEXDUMP_MAX_LEN); +out: + kfree(touser); + return ret; +} + +static const struct file_operations mbox_test_message_ops = { + .write = mbox_test_message_write, + .read = mbox_test_message_read, + .open = simple_open, + .llseek = generic_file_llseek, +}; + +static int mbox_test_add_debugfs(struct platform_device *pdev, + struct mbox_test_device *tdev) +{ + if (!debugfs_initialized()) + return 0; + + root_debugfs_dir = debugfs_create_dir("mailbox", NULL); + if (!root_debugfs_dir) { + dev_err(&pdev->dev, "Failed to create Mailbox debugfs\n"); + return -EINVAL; + } + + debugfs_create_file("message", 0600, root_debugfs_dir, + tdev, &mbox_test_message_ops); + + debugfs_create_file("signal", 0200, root_debugfs_dir, + tdev, &mbox_test_signal_ops); + + return 0; +} + +static void mbox_test_receive_message(struct mbox_client *client, void *message) +{ + struct mbox_test_device *tdev = dev_get_drvdata(client->dev); + unsigned long flags; + + spin_lock_irqsave(&tdev->lock, flags); + if (tdev->mmio) { + memcpy_fromio(tdev->rx_buffer, tdev->mmio, MBOX_MAX_MSG_LEN); + print_hex_dump(KERN_INFO, "Client: Received [MMIO]: ", + DUMP_PREFIX_ADDRESS, MBOX_BYTES_PER_LINE, 1, + tdev->rx_buffer, MBOX_MAX_MSG_LEN, true); + } else if (message) { + print_hex_dump(KERN_INFO, "Client: Received [API]: ", + DUMP_PREFIX_ADDRESS, MBOX_BYTES_PER_LINE, 1, + message, MBOX_MAX_MSG_LEN, true); + memcpy(tdev->rx_buffer, message, MBOX_MAX_MSG_LEN); + } + spin_unlock_irqrestore(&tdev->lock, flags); +} + +static void mbox_test_prepare_message(struct mbox_client *client, void *message) +{ + struct mbox_test_device *tdev = dev_get_drvdata(client->dev); + + if (tdev->mmio) { + if (tdev->signal) + memcpy_toio(tdev->mmio, tdev->message, MBOX_MAX_MSG_LEN); + else + memcpy_toio(tdev->mmio, message, MBOX_MAX_MSG_LEN); + } +} + +static void mbox_test_message_sent(struct mbox_client *client, + void *message, int r) +{ + if (r) + dev_warn(client->dev, + "Client: Message could not be sent: %d\n", r); + else + dev_info(client->dev, + "Client: Message sent\n"); +} + +static struct mbox_chan * +mbox_test_request_channel(struct platform_device *pdev, const char *name) +{ + struct mbox_client *client; + struct mbox_chan *channel; + + client = devm_kzalloc(&pdev->dev, sizeof(*client), GFP_KERNEL); + if (!client) + return ERR_PTR(-ENOMEM); + + client->dev = &pdev->dev; + client->rx_callback = mbox_test_receive_message; + client->tx_prepare = mbox_test_prepare_message; + client->tx_done = mbox_test_message_sent; + client->tx_block = true; + client->knows_txdone = false; + client->tx_tout = 500; + + channel = mbox_request_channel_byname(client, name); + if (IS_ERR(channel)) { + dev_warn(&pdev->dev, "Failed to request %s channel\n", name); + return NULL; + } + + return channel; +} + +static int mbox_test_probe(struct platform_device *pdev) +{ + struct mbox_test_device *tdev; + struct resource *res; + int ret; + + tdev = devm_kzalloc(&pdev->dev, sizeof(*tdev), GFP_KERNEL); + if (!tdev) + return -ENOMEM; + + /* It's okay for MMIO to be NULL */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + tdev->mmio = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(tdev->mmio)) + tdev->mmio = NULL; + + tdev->tx_channel = mbox_test_request_channel(pdev, "tx"); + tdev->rx_channel = mbox_test_request_channel(pdev, "rx"); + + if (!tdev->tx_channel && !tdev->rx_channel) + return -EPROBE_DEFER; + + tdev->dev = &pdev->dev; + platform_set_drvdata(pdev, tdev); + + spin_lock_init(&tdev->lock); + + if (tdev->rx_channel) { + tdev->rx_buffer = devm_kzalloc(&pdev->dev, + MBOX_MAX_MSG_LEN, GFP_KERNEL); + if (!tdev->rx_buffer) + return -ENOMEM; + } + + ret = mbox_test_add_debugfs(pdev, tdev); + if (ret) + return ret; + + dev_info(&pdev->dev, "Successfully registered\n"); + + return 0; +} + +static int mbox_test_remove(struct platform_device *pdev) +{ + struct mbox_test_device *tdev = platform_get_drvdata(pdev); + + debugfs_remove_recursive(root_debugfs_dir); + + if (tdev->tx_channel) + mbox_free_channel(tdev->tx_channel); + if (tdev->rx_channel) + mbox_free_channel(tdev->rx_channel); + + return 0; +} + +static const struct of_device_id mbox_test_match[] = { + { .compatible = "mailbox_test" }, + {}, +}; + +static struct platform_driver mbox_test_driver = { + .driver = { + .name = "mailbox_sti_test", + .of_match_table = mbox_test_match, + }, + .probe = mbox_test_probe, + .remove = mbox_test_remove, +}; +module_platform_driver(mbox_test_driver); + +MODULE_DESCRIPTION("Generic Mailbox Testing Facility"); +MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org"); +MODULE_LICENSE("GPL v2"); diff --git a/kernel/drivers/mailbox/mailbox.c b/kernel/drivers/mailbox/mailbox.c index 19b491d29..6a4811f85 100644 --- a/kernel/drivers/mailbox/mailbox.c +++ b/kernel/drivers/mailbox/mailbox.c @@ -26,8 +26,6 @@ static LIST_HEAD(mbox_cons); static DEFINE_MUTEX(con_mutex); -static void poll_txdone(unsigned long data); - static int add_to_rbuf(struct mbox_chan *chan, void *mssg) { int idx; @@ -88,7 +86,9 @@ exit: spin_unlock_irqrestore(&chan->lock, flags); if (!err && (chan->txdone_method & TXDONE_BY_POLL)) - poll_txdone((unsigned long)chan->mbox); + /* kick start the timer immediately to avoid delays */ + hrtimer_start(&chan->mbox->poll_hrt, ktime_set(0, 0), + HRTIMER_MODE_REL); } static void tx_tick(struct mbox_chan *chan, int r) @@ -112,9 +112,10 @@ static void tx_tick(struct mbox_chan *chan, int r) complete(&chan->tx_complete); } -static void poll_txdone(unsigned long data) +static enum hrtimer_restart txdone_hrtimer(struct hrtimer *hrtimer) { - struct mbox_controller *mbox = (struct mbox_controller *)data; + struct mbox_controller *mbox = + container_of(hrtimer, struct mbox_controller, poll_hrt); bool txdone, resched = false; int i; @@ -130,9 +131,11 @@ static void poll_txdone(unsigned long data) } } - if (resched) - mod_timer(&mbox->poll, jiffies + - msecs_to_jiffies(mbox->txpoll_period)); + if (resched) { + hrtimer_forward_now(hrtimer, ms_to_ktime(mbox->txpoll_period)); + return HRTIMER_RESTART; + } + return HRTIMER_NORESTART; } /** @@ -318,7 +321,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 +330,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); @@ -357,6 +365,35 @@ struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index) } EXPORT_SYMBOL_GPL(mbox_request_channel); +struct mbox_chan *mbox_request_channel_byname(struct mbox_client *cl, + const char *name) +{ + struct device_node *np = cl->dev->of_node; + struct property *prop; + const char *mbox_name; + int index = 0; + + if (!np) { + dev_err(cl->dev, "%s() currently only supports DT\n", __func__); + return ERR_PTR(-ENOSYS); + } + + if (!of_get_property(np, "mbox-names", NULL)) { + dev_err(cl->dev, + "%s() requires an \"mbox-names\" property\n", __func__); + return ERR_PTR(-ENOSYS); + } + + of_property_for_each_string(np, "mbox-names", prop, mbox_name) { + if (!strncmp(name, mbox_name, strlen(name))) + break; + index++; + } + + return mbox_request_channel(cl, index); +} +EXPORT_SYMBOL_GPL(mbox_request_channel_byname); + /** * mbox_free_channel - The client relinquishes control of a mailbox * channel by this call. @@ -390,7 +427,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]; } @@ -417,9 +454,9 @@ int mbox_controller_register(struct mbox_controller *mbox) txdone = TXDONE_BY_ACK; if (txdone == TXDONE_BY_POLL) { - mbox->poll.function = &poll_txdone; - mbox->poll.data = (unsigned long)mbox; - init_timer(&mbox->poll); + hrtimer_init(&mbox->poll_hrt, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + mbox->poll_hrt.function = txdone_hrtimer; } for (i = 0; i < mbox->num_chans; i++) { @@ -461,7 +498,7 @@ void mbox_controller_unregister(struct mbox_controller *mbox) mbox_free_channel(&mbox->chans[i]); if (mbox->txdone_poll) - del_timer_sync(&mbox->poll); + hrtimer_cancel(&mbox->poll_hrt); mutex_unlock(&con_mutex); } diff --git a/kernel/drivers/mailbox/omap-mailbox.c b/kernel/drivers/mailbox/omap-mailbox.c index 0f332c178..b7f636f15 100644 --- a/kernel/drivers/mailbox/omap-mailbox.c +++ b/kernel/drivers/mailbox/omap-mailbox.c @@ -38,6 +38,8 @@ #include <linux/mailbox_controller.h> #include <linux/mailbox_client.h> +#include "mailbox.h" + #define MAILBOX_REVISION 0x000 #define MAILBOX_MESSAGE(m) (0x040 + 4 * (m)) #define MAILBOX_FIFOSTATUS(m) (0x080 + 4 * (m)) @@ -106,6 +108,7 @@ struct omap_mbox_fifo_info { int rx_irq; const char *name; + bool send_no_irq; }; struct omap_mbox { @@ -119,6 +122,7 @@ struct omap_mbox { u32 ctx[OMAP4_MBOX_NR_REGS]; u32 intr_type; struct mbox_chan *chan; + bool send_no_irq; }; /* global variables for the mailbox devices */ @@ -418,6 +422,9 @@ static int omap_mbox_startup(struct omap_mbox *mbox) goto fail_request_irq; } + if (mbox->send_no_irq) + mbox->chan->txdone_method = TXDONE_BY_ACK; + _omap_mbox_enable_irq(mbox, IRQ_RX); return 0; @@ -586,13 +593,27 @@ static void omap_mbox_chan_shutdown(struct mbox_chan *chan) mutex_unlock(&mdev->cfg_lock); } -static int omap_mbox_chan_send_data(struct mbox_chan *chan, void *data) +static int omap_mbox_chan_send_noirq(struct omap_mbox *mbox, void *data) { - struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan); int ret = -EBUSY; - if (!mbox) - return -EINVAL; + if (!mbox_fifo_full(mbox)) { + _omap_mbox_enable_irq(mbox, IRQ_RX); + mbox_fifo_write(mbox, (mbox_msg_t)data); + ret = 0; + _omap_mbox_disable_irq(mbox, IRQ_RX); + + /* we must read and ack the interrupt directly from here */ + mbox_fifo_read(mbox); + ack_mbox_irq(mbox, IRQ_RX); + } + + return ret; +} + +static int omap_mbox_chan_send(struct omap_mbox *mbox, void *data) +{ + int ret = -EBUSY; if (!mbox_fifo_full(mbox)) { mbox_fifo_write(mbox, (mbox_msg_t)data); @@ -604,7 +625,23 @@ 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 int omap_mbox_chan_send_data(struct mbox_chan *chan, void *data) +{ + struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan); + int ret; + + if (!mbox) + return -EINVAL; + + if (mbox->send_no_irq) + ret = omap_mbox_chan_send_noirq(mbox, data); + else + ret = omap_mbox_chan_send(mbox, data); + + return ret; +} + +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 +676,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) @@ -732,6 +769,9 @@ static int omap_mbox_probe(struct platform_device *pdev) finfo->rx_usr = tmp[2]; finfo->name = child->name; + + if (of_find_property(child, "ti,mbox-send-noirq", NULL)) + finfo->send_no_irq = true; } else { finfo->tx_id = info->tx_id; finfo->rx_id = info->rx_id; @@ -791,6 +831,7 @@ static int omap_mbox_probe(struct platform_device *pdev) fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, finfo->rx_usr); fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, finfo->rx_usr); + mbox->send_no_irq = finfo->send_no_irq; mbox->intr_type = intr_type; mbox->parent = mdev; diff --git a/kernel/drivers/mailbox/pcc.c b/kernel/drivers/mailbox/pcc.c index 7e91d68a3..45d85aea9 100644 --- a/kernel/drivers/mailbox/pcc.c +++ b/kernel/drivers/mailbox/pcc.c @@ -122,7 +122,7 @@ struct mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl, */ chan = get_pcc_channel(subspace_id); - if (!chan || chan->cl) { + if (IS_ERR(chan) || chan->cl) { dev_err(dev, "Channel not found for idx: %d\n", subspace_id); return ERR_PTR(-EBUSY); } @@ -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, }; @@ -352,4 +352,10 @@ static int __init pcc_init(void) return 0; } -device_initcall(pcc_init); + +/* + * Make PCC init postcore so that users of this mailbox + * such as the ACPI Processor driver have it available + * at their init. + */ +postcore_initcall(pcc_init); diff --git a/kernel/drivers/mailbox/pl320-ipc.c b/kernel/drivers/mailbox/pl320-ipc.c index f3755e0aa..f80acb36f 100644 --- a/kernel/drivers/mailbox/pl320-ipc.c +++ b/kernel/drivers/mailbox/pl320-ipc.c @@ -195,4 +195,4 @@ static int __init ipc_init(void) { return amba_driver_register(&pl320_driver); } -module_init(ipc_init); +subsys_initcall(ipc_init); |