From f700e84f417b98f867c5db0555e6f01724d53cd2 Mon Sep 17 00:00:00 2001 From: Duc Dang Date: Fri, 12 Feb 2016 19:39:26 -0800 Subject: mailbox: Add support for APM X-Gene platform mailbox driver X-Gene mailbox controller provides 8 mailbox channels, with each channel has a dedicated interrupt line. Signed-off-by: Feng Kan Signed-off-by: Duc Dang Signed-off-by: Jassi Brar --- drivers/mailbox/Kconfig | 9 + drivers/mailbox/Makefile | 2 + drivers/mailbox/mailbox-xgene-slimpro.c | 284 ++++++++++++++++++++++++++++++++ 3 files changed, 295 insertions(+) create mode 100644 drivers/mailbox/mailbox-xgene-slimpro.c (limited to 'drivers/mailbox') diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index b2bbe8659bed..1107666110af 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -86,4 +86,13 @@ config MAILBOX_TEST Test client to help with testing new Controller driver implementations. +config XGENE_SLIMPRO_MBOX + tristate "APM SoC X-Gene SLIMpro Mailbox Controller" + depends on ARCH_XGENE + help + An implementation of the APM X-Gene Interprocessor Communication + Mailbox (IPCM) between the ARM 64-bit cores and SLIMpro controller. + It is used to send short messages between ARM64-bit cores and + the SLIMpro Management Engine, primarily for PM. Say Y here if you + want to use the APM X-Gene SLIMpro IPCM support. endif diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 92435ef11f26..b602ef85ba48 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -17,3 +17,5 @@ obj-$(CONFIG_ALTERA_MBOX) += mailbox-altera.o obj-$(CONFIG_BCM2835_MBOX) += bcm2835-mailbox.o obj-$(CONFIG_STI_MBOX) += mailbox-sti.o + +obj-$(CONFIG_XGENE_SLIMPRO_MBOX) += mailbox-xgene-slimpro.o diff --git a/drivers/mailbox/mailbox-xgene-slimpro.c b/drivers/mailbox/mailbox-xgene-slimpro.c new file mode 100644 index 000000000000..b5f51062b1f7 --- /dev/null +++ b/drivers/mailbox/mailbox-xgene-slimpro.c @@ -0,0 +1,284 @@ +/* + * APM X-Gene SLIMpro MailBox Driver + * + * Copyright (c) 2015, Applied Micro Circuits Corporation + * Author: Feng Kan fkan@apm.com + * + * 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. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MBOX_CON_NAME "slimpro-mbox" +#define MBOX_REG_SET_OFFSET 0x1000 +#define MBOX_CNT 8 +#define MBOX_STATUS_AVAIL_MASK BIT(16) +#define MBOX_STATUS_ACK_MASK BIT(0) + +/* Configuration and Status Registers */ +#define REG_DB_IN 0x00 +#define REG_DB_DIN0 0x04 +#define REG_DB_DIN1 0x08 +#define REG_DB_OUT 0x10 +#define REG_DB_DOUT0 0x14 +#define REG_DB_DOUT1 0x18 +#define REG_DB_STAT 0x20 +#define REG_DB_STATMASK 0x24 + +/** + * X-Gene SlimPRO mailbox channel information + * + * @dev: Device to which it is attached + * @chan: Pointer to mailbox communication channel + * @reg: Base address to access channel registers + * @irq: Interrupt number of the channel + * @rx_msg: Received message storage + */ +struct slimpro_mbox_chan { + struct device *dev; + struct mbox_chan *chan; + void __iomem *reg; + int irq; + u32 rx_msg[3]; +}; + +/** + * X-Gene SlimPRO Mailbox controller data + * + * X-Gene SlimPRO Mailbox controller has 8 commnunication channels. + * Each channel has a separate IRQ number assgined to it. + * + * @mb_ctrl: Representation of the commnunication channel controller + * @mc: Array of SlimPRO mailbox channels of the controller + * @chans: Array of mailbox communication channels + * + */ +struct slimpro_mbox { + struct mbox_controller mb_ctrl; + struct slimpro_mbox_chan mc[MBOX_CNT]; + struct mbox_chan chans[MBOX_CNT]; +}; + +static void mb_chan_send_msg(struct slimpro_mbox_chan *mb_chan, u32 *msg) +{ + writel(msg[1], mb_chan->reg + REG_DB_DOUT0); + writel(msg[2], mb_chan->reg + REG_DB_DOUT1); + writel(msg[0], mb_chan->reg + REG_DB_OUT); +} + +static void mb_chan_recv_msg(struct slimpro_mbox_chan *mb_chan) +{ + mb_chan->rx_msg[1] = readl(mb_chan->reg + REG_DB_DIN0); + mb_chan->rx_msg[2] = readl(mb_chan->reg + REG_DB_DIN1); + mb_chan->rx_msg[0] = readl(mb_chan->reg + REG_DB_IN); +} + +static int mb_chan_status_ack(struct slimpro_mbox_chan *mb_chan) +{ + u32 val = readl(mb_chan->reg + REG_DB_STAT); + + if (val & MBOX_STATUS_ACK_MASK) { + writel(MBOX_STATUS_ACK_MASK, mb_chan->reg + REG_DB_STAT); + return 1; + } + return 0; +} + +static int mb_chan_status_avail(struct slimpro_mbox_chan *mb_chan) +{ + u32 val = readl(mb_chan->reg + REG_DB_STAT); + + if (val & MBOX_STATUS_AVAIL_MASK) { + mb_chan_recv_msg(mb_chan); + writel(MBOX_STATUS_AVAIL_MASK, mb_chan->reg + REG_DB_STAT); + return 1; + } + return 0; +} + +static irqreturn_t slimpro_mbox_irq(int irq, void *id) +{ + struct slimpro_mbox_chan *mb_chan = id; + + if (mb_chan_status_ack(mb_chan)) + mbox_chan_txdone(mb_chan->chan, 0); + + if (mb_chan_status_avail(mb_chan)) + mbox_chan_received_data(mb_chan->chan, mb_chan->rx_msg); + + return IRQ_HANDLED; +} + +static int slimpro_mbox_send_data(struct mbox_chan *chan, void *msg) +{ + struct slimpro_mbox_chan *mb_chan = chan->con_priv; + + mb_chan_send_msg(mb_chan, msg); + return 0; +} + +static int slimpro_mbox_startup(struct mbox_chan *chan) +{ + struct slimpro_mbox_chan *mb_chan = chan->con_priv; + int rc; + u32 val; + + rc = devm_request_irq(mb_chan->dev, mb_chan->irq, slimpro_mbox_irq, 0, + MBOX_CON_NAME, mb_chan); + if (unlikely(rc)) { + dev_err(mb_chan->dev, "failed to register mailbox interrupt %d\n", + mb_chan->irq); + return rc; + } + + /* Enable HW interrupt */ + writel(MBOX_STATUS_ACK_MASK | MBOX_STATUS_AVAIL_MASK, + mb_chan->reg + REG_DB_STAT); + /* Unmask doorbell status interrupt */ + val = readl(mb_chan->reg + REG_DB_STATMASK); + val &= ~(MBOX_STATUS_ACK_MASK | MBOX_STATUS_AVAIL_MASK); + writel(val, mb_chan->reg + REG_DB_STATMASK); + + return 0; +} + +static void slimpro_mbox_shutdown(struct mbox_chan *chan) +{ + struct slimpro_mbox_chan *mb_chan = chan->con_priv; + u32 val; + + /* Mask doorbell status interrupt */ + val = readl(mb_chan->reg + REG_DB_STATMASK); + val |= (MBOX_STATUS_ACK_MASK | MBOX_STATUS_AVAIL_MASK); + writel(val, mb_chan->reg + REG_DB_STATMASK); + + devm_free_irq(mb_chan->dev, mb_chan->irq, mb_chan); +} + +static struct mbox_chan_ops slimpro_mbox_ops = { + .send_data = slimpro_mbox_send_data, + .startup = slimpro_mbox_startup, + .shutdown = slimpro_mbox_shutdown, +}; + +static int slimpro_mbox_probe(struct platform_device *pdev) +{ + struct slimpro_mbox *ctx; + struct resource *regs; + void __iomem *mb_base; + int rc; + int i; + + ctx = devm_kzalloc(&pdev->dev, sizeof(struct slimpro_mbox), GFP_KERNEL); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + platform_set_drvdata(pdev, ctx); + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mb_base = devm_ioremap(&pdev->dev, regs->start, resource_size(regs)); + if (IS_ERR(mb_base)) + return PTR_ERR(mb_base); + + /* Setup mailbox links */ + for (i = 0; i < MBOX_CNT; i++) { + ctx->mc[i].irq = platform_get_irq(pdev, i); + if (ctx->mc[i].irq < 0) { + if (i == 0) { + dev_err(&pdev->dev, "no available IRQ\n"); + return -EINVAL; + } + dev_info(&pdev->dev, "no IRQ for channel %d\n", i); + break; + } + + ctx->mc[i].dev = &pdev->dev; + ctx->mc[i].reg = mb_base + i * MBOX_REG_SET_OFFSET; + ctx->mc[i].chan = &ctx->chans[i]; + ctx->chans[i].con_priv = &ctx->mc[i]; + } + + /* Setup mailbox controller */ + ctx->mb_ctrl.dev = &pdev->dev; + ctx->mb_ctrl.chans = ctx->chans; + ctx->mb_ctrl.txdone_irq = true; + ctx->mb_ctrl.ops = &slimpro_mbox_ops; + ctx->mb_ctrl.num_chans = i; + + rc = mbox_controller_register(&ctx->mb_ctrl); + if (rc) { + dev_err(&pdev->dev, + "APM X-Gene SLIMpro MailBox register failed:%d\n", rc); + return rc; + } + + dev_info(&pdev->dev, "APM X-Gene SLIMpro MailBox registered\n"); + return 0; +} + +static int slimpro_mbox_remove(struct platform_device *pdev) +{ + struct slimpro_mbox *smb = platform_get_drvdata(pdev); + + mbox_controller_unregister(&smb->mb_ctrl); + return 0; +} + +static const struct of_device_id slimpro_of_match[] = { + {.compatible = "apm,xgene-slimpro-mbox" }, + { }, +}; +MODULE_DEVICE_TABLE(of, slimpro_of_match); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id slimpro_acpi_ids[] = { + {"APMC0D01", 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, slimpro_acpi_ids); +#endif + +static struct platform_driver slimpro_mbox_driver = { + .probe = slimpro_mbox_probe, + .remove = slimpro_mbox_remove, + .driver = { + .name = "xgene-slimpro-mbox", + .of_match_table = of_match_ptr(slimpro_of_match), + .acpi_match_table = ACPI_PTR(slimpro_acpi_ids) + }, +}; + +static int __init slimpro_mbox_init(void) +{ + return platform_driver_register(&slimpro_mbox_driver); +} + +static void __exit slimpro_mbox_exit(void) +{ + platform_driver_unregister(&slimpro_mbox_driver); +} + +subsys_initcall(slimpro_mbox_init); +module_exit(slimpro_mbox_exit); + +MODULE_DESCRIPTION("APM X-Gene SLIMpro Mailbox Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From adf06ba9b363c05eaf4da8afb02f5e99dd5caac9 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Fri, 19 Feb 2016 16:01:15 +0000 Subject: mailbox: mailbox-test: rename driver as generic test driver This mailbox-test driver was designed to be generic, so let's remove ST tag on it and make it generic. Acked-by: Lee Jones Signed-off-by: Sudeep Holla Signed-off-by: Jassi Brar --- drivers/mailbox/mailbox-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mailbox') diff --git a/drivers/mailbox/mailbox-test.c b/drivers/mailbox/mailbox-test.c index 684ae17dcf39..3813f6d9eba9 100644 --- a/drivers/mailbox/mailbox-test.c +++ b/drivers/mailbox/mailbox-test.c @@ -348,7 +348,7 @@ static const struct of_device_id mbox_test_match[] = { static struct platform_driver mbox_test_driver = { .driver = { - .name = "mailbox_sti_test", + .name = "mailbox_test", .of_match_table = mbox_test_match, }, .probe = mbox_test_probe, -- cgit v1.2.3 From c428013783dc969d9d298e81b2d276a9dc0003ab Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Fri, 19 Feb 2016 16:01:16 +0000 Subject: mailbox: mailbox-test: fix the compatible string Underscores are usually forbidden in the compatible strings. So lets remove it before the first users of this is seen. Acked-by: Rob Herring Acked-by: Lee Jones Signed-off-by: Sudeep Holla Signed-off-by: Jassi Brar --- drivers/mailbox/mailbox-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mailbox') diff --git a/drivers/mailbox/mailbox-test.c b/drivers/mailbox/mailbox-test.c index 3813f6d9eba9..036a852b5fa1 100644 --- a/drivers/mailbox/mailbox-test.c +++ b/drivers/mailbox/mailbox-test.c @@ -342,7 +342,7 @@ static int mbox_test_remove(struct platform_device *pdev) } static const struct of_device_id mbox_test_match[] = { - { .compatible = "mailbox_test" }, + { .compatible = "mailbox-test" }, {}, }; -- cgit v1.2.3 From 27fa680f7f4143f1d04945dbcded0f52e26e5d56 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Fri, 19 Feb 2016 16:01:17 +0000 Subject: mailbox: mailbox-test: use print_hex_dump_bytes to allow dynamic printk Reduce the logging from info to debug. Also use print_hex_dump_bytes instead as it has support for dynamic printk providing options to conditionally enable/disable these logs. Cc: Lee Jones Signed-off-by: Sudeep Holla Signed-off-by: Jassi Brar --- drivers/mailbox/mailbox-test.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'drivers/mailbox') diff --git a/drivers/mailbox/mailbox-test.c b/drivers/mailbox/mailbox-test.c index 036a852b5fa1..f690f11969a1 100644 --- a/drivers/mailbox/mailbox-test.c +++ b/drivers/mailbox/mailbox-test.c @@ -113,15 +113,15 @@ static ssize_t mbox_test_message_write(struct file *filp, * 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); + print_hex_dump_bytes("Client: Sending: Signal: ", DUMP_PREFIX_ADDRESS, + tdev->signal, MBOX_MAX_SIG_LEN); 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); + print_hex_dump_bytes("Client: Sending: Message: ", DUMP_PREFIX_ADDRESS, + tdev->message, MBOX_MAX_MSG_LEN); ret = mbox_send_message(tdev->tx_channel, data); if (ret < 0) @@ -222,13 +222,11 @@ static void mbox_test_receive_message(struct mbox_client *client, void *message) 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); + print_hex_dump_bytes("Client: Received [MMIO]: ", DUMP_PREFIX_ADDRESS, + tdev->rx_buffer, MBOX_MAX_MSG_LEN); } 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); + print_hex_dump_bytes("Client: Received [API]: ", DUMP_PREFIX_ADDRESS, + message, MBOX_MAX_MSG_LEN); memcpy(tdev->rx_buffer, message, MBOX_MAX_MSG_LEN); } spin_unlock_irqrestore(&tdev->lock, flags); -- cgit v1.2.3 From 2d74ffdc910e06c407f0a3ace9795f77cc07d05b Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Fri, 19 Feb 2016 16:01:18 +0000 Subject: mailbox: mailbox-test: add support for separate tx/rx buffer with single channel This patch adds support for different MMIO region for Tx and Rx paths. If only one region is specified, it's assumed to be shared between Rx and Tx, thereby retaining backward compatibility. Also in order to support single channel dealing with both Tx and Rx with dedicated MMIO regions, Tx channel itself is assigned to Rx if MMIO regions are different and Rx is not specified. Acked-by: Lee Jones Signed-off-by: Sudeep Holla Signed-off-by: Jassi Brar --- drivers/mailbox/mailbox-test.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'drivers/mailbox') diff --git a/drivers/mailbox/mailbox-test.c b/drivers/mailbox/mailbox-test.c index f690f11969a1..dc11bbf27274 100644 --- a/drivers/mailbox/mailbox-test.c +++ b/drivers/mailbox/mailbox-test.c @@ -31,7 +31,8 @@ static struct dentry *root_debugfs_dir; struct mbox_test_device { struct device *dev; - void __iomem *mmio; + void __iomem *tx_mmio; + void __iomem *rx_mmio; struct mbox_chan *tx_channel; struct mbox_chan *rx_channel; char *rx_buffer; @@ -112,7 +113,7 @@ static ssize_t mbox_test_message_write(struct file *filp, * A separate signal is only of use if there is * MMIO to subsequently pass the message through */ - if (tdev->mmio && tdev->signal) { + if (tdev->tx_mmio && tdev->signal) { print_hex_dump_bytes("Client: Sending: Signal: ", DUMP_PREFIX_ADDRESS, tdev->signal, MBOX_MAX_SIG_LEN); @@ -220,8 +221,8 @@ static void mbox_test_receive_message(struct mbox_client *client, void *message) unsigned long flags; spin_lock_irqsave(&tdev->lock, flags); - if (tdev->mmio) { - memcpy_fromio(tdev->rx_buffer, tdev->mmio, MBOX_MAX_MSG_LEN); + if (tdev->rx_mmio) { + memcpy_fromio(tdev->rx_buffer, tdev->rx_mmio, MBOX_MAX_MSG_LEN); print_hex_dump_bytes("Client: Received [MMIO]: ", DUMP_PREFIX_ADDRESS, tdev->rx_buffer, MBOX_MAX_MSG_LEN); } else if (message) { @@ -236,11 +237,11 @@ 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->tx_mmio) { if (tdev->signal) - memcpy_toio(tdev->mmio, tdev->message, MBOX_MAX_MSG_LEN); + memcpy_toio(tdev->tx_mmio, tdev->message, MBOX_MAX_MSG_LEN); else - memcpy_toio(tdev->mmio, message, MBOX_MAX_MSG_LEN); + memcpy_toio(tdev->tx_mmio, message, MBOX_MAX_MSG_LEN); } } @@ -294,9 +295,15 @@ static int mbox_test_probe(struct platform_device *pdev) /* 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_mmio = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(tdev->tx_mmio)) + tdev->tx_mmio = NULL; + + /* If specified, second reg entry is Rx MMIO */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + tdev->rx_mmio = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(tdev->rx_mmio)) + tdev->rx_mmio = tdev->tx_mmio; tdev->tx_channel = mbox_test_request_channel(pdev, "tx"); tdev->rx_channel = mbox_test_request_channel(pdev, "rx"); @@ -304,6 +311,10 @@ static int mbox_test_probe(struct platform_device *pdev) if (!tdev->tx_channel && !tdev->rx_channel) return -EPROBE_DEFER; + /* If Rx is not specified but has Rx MMIO, then Rx = Tx */ + if (!tdev->rx_channel && (tdev->rx_mmio != tdev->tx_mmio)) + tdev->rx_channel = tdev->tx_channel; + tdev->dev = &pdev->dev; platform_set_drvdata(pdev, tdev); -- cgit v1.2.3 From 9c384189f5407565141f1a950d8350e56d86b4dd Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Mon, 15 Feb 2016 21:50:24 +0800 Subject: mailbox: Hi6220: add mailbox driver Add driver for Hi6220 mailbox, the mailbox communicates with MCU; for sending data, it can support two methods for low level implementation: one is to use interrupt as acknowledge, another is automatic mode which without any acknowledge. These two methods have been supported in the driver. For receiving data, it will depend on the interrupt to notify the channel has incoming message. Now mailbox driver is used to send message to MCU to control dynamic voltage and frequency scaling for CPU, GPU and DDR. Signed-off-by: Leo Yan Signed-off-by: Jassi Brar --- drivers/mailbox/Kconfig | 8 + drivers/mailbox/Makefile | 2 + drivers/mailbox/hi6220-mailbox.c | 395 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 405 insertions(+) create mode 100644 drivers/mailbox/hi6220-mailbox.c (limited to 'drivers/mailbox') diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 1107666110af..642da1c812bf 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -78,6 +78,14 @@ config STI_MBOX Mailbox implementation for STMicroelectonics family chips with hardware for interprocessor communication. +config HI6220_MBOX + tristate "Hi6220 Mailbox" + depends on ARCH_HISI + help + An implementation of the hi6220 mailbox. It is used to send message + between application processors and MCU. Say Y here if you want to + build Hi6220 mailbox controller driver. + config MAILBOX_TEST tristate "Mailbox Test Client" depends on OF diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index b602ef85ba48..2310e991ff55 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -19,3 +19,5 @@ obj-$(CONFIG_BCM2835_MBOX) += bcm2835-mailbox.o obj-$(CONFIG_STI_MBOX) += mailbox-sti.o obj-$(CONFIG_XGENE_SLIMPRO_MBOX) += mailbox-xgene-slimpro.o + +obj-$(CONFIG_HI6220_MBOX) += hi6220-mailbox.o diff --git a/drivers/mailbox/hi6220-mailbox.c b/drivers/mailbox/hi6220-mailbox.c new file mode 100644 index 000000000000..613722db5daf --- /dev/null +++ b/drivers/mailbox/hi6220-mailbox.c @@ -0,0 +1,395 @@ +/* + * Hisilicon's Hi6220 mailbox driver + * + * Copyright (c) 2015 Hisilicon Limited. + * Copyright (c) 2015 Linaro Limited. + * + * Author: Leo Yan + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define MBOX_CHAN_MAX 32 + +#define MBOX_TX 0x1 + +/* Mailbox message length: 8 words */ +#define MBOX_MSG_LEN 8 + +/* Mailbox Registers */ +#define MBOX_OFF(m) (0x40 * (m)) +#define MBOX_MODE_REG(m) (MBOX_OFF(m) + 0x0) +#define MBOX_DATA_REG(m) (MBOX_OFF(m) + 0x4) + +#define MBOX_STATE_MASK (0xF << 4) +#define MBOX_STATE_IDLE (0x1 << 4) +#define MBOX_STATE_TX (0x2 << 4) +#define MBOX_STATE_RX (0x4 << 4) +#define MBOX_STATE_ACK (0x8 << 4) +#define MBOX_ACK_CONFIG_MASK (0x1 << 0) +#define MBOX_ACK_AUTOMATIC (0x1 << 0) +#define MBOX_ACK_IRQ (0x0 << 0) + +/* IPC registers */ +#define ACK_INT_RAW_REG(i) ((i) + 0x400) +#define ACK_INT_MSK_REG(i) ((i) + 0x404) +#define ACK_INT_STAT_REG(i) ((i) + 0x408) +#define ACK_INT_CLR_REG(i) ((i) + 0x40c) +#define ACK_INT_ENA_REG(i) ((i) + 0x500) +#define ACK_INT_DIS_REG(i) ((i) + 0x504) +#define DST_INT_RAW_REG(i) ((i) + 0x420) + + +struct hi6220_mbox_chan { + + /* + * Description for channel's hardware info: + * - direction: tx or rx + * - dst irq: peer core's irq number + * - ack irq: local irq number + * - slot number + */ + unsigned int dir, dst_irq, ack_irq; + unsigned int slot; + + struct hi6220_mbox *parent; +}; + +struct hi6220_mbox { + struct device *dev; + + int irq; + + /* flag of enabling tx's irq mode */ + bool tx_irq_mode; + + /* region for ipc event */ + void __iomem *ipc; + + /* region for mailbox */ + void __iomem *base; + + unsigned int chan_num; + struct hi6220_mbox_chan *mchan; + + void *irq_map_chan[MBOX_CHAN_MAX]; + struct mbox_chan *chan; + struct mbox_controller controller; +}; + +static void mbox_set_state(struct hi6220_mbox *mbox, + unsigned int slot, u32 val) +{ + u32 status; + + status = readl(mbox->base + MBOX_MODE_REG(slot)); + status = (status & ~MBOX_STATE_MASK) | val; + writel(status, mbox->base + MBOX_MODE_REG(slot)); +} + +static void mbox_set_mode(struct hi6220_mbox *mbox, + unsigned int slot, u32 val) +{ + u32 mode; + + mode = readl(mbox->base + MBOX_MODE_REG(slot)); + mode = (mode & ~MBOX_ACK_CONFIG_MASK) | val; + writel(mode, mbox->base + MBOX_MODE_REG(slot)); +} + +static bool hi6220_mbox_last_tx_done(struct mbox_chan *chan) +{ + struct hi6220_mbox_chan *mchan = chan->con_priv; + struct hi6220_mbox *mbox = mchan->parent; + u32 state; + + /* Only set idle state for polling mode */ + BUG_ON(mbox->tx_irq_mode); + + state = readl(mbox->base + MBOX_MODE_REG(mchan->slot)); + return ((state & MBOX_STATE_MASK) == MBOX_STATE_IDLE); +} + +static int hi6220_mbox_send_data(struct mbox_chan *chan, void *msg) +{ + struct hi6220_mbox_chan *mchan = chan->con_priv; + struct hi6220_mbox *mbox = mchan->parent; + unsigned int slot = mchan->slot; + u32 *buf = msg; + int i; + + /* indicate as a TX channel */ + mchan->dir = MBOX_TX; + + mbox_set_state(mbox, slot, MBOX_STATE_TX); + + if (mbox->tx_irq_mode) + mbox_set_mode(mbox, slot, MBOX_ACK_IRQ); + else + mbox_set_mode(mbox, slot, MBOX_ACK_AUTOMATIC); + + for (i = 0; i < MBOX_MSG_LEN; i++) + writel(buf[i], mbox->base + MBOX_DATA_REG(slot) + i * 4); + + /* trigger remote request */ + writel(BIT(mchan->dst_irq), DST_INT_RAW_REG(mbox->ipc)); + return 0; +} + +static irqreturn_t hi6220_mbox_interrupt(int irq, void *p) +{ + struct hi6220_mbox *mbox = p; + struct hi6220_mbox_chan *mchan; + struct mbox_chan *chan; + unsigned int state, intr_bit, i; + u32 msg[MBOX_MSG_LEN]; + + state = readl(ACK_INT_STAT_REG(mbox->ipc)); + if (!state) { + dev_warn(mbox->dev, "%s: spurious interrupt\n", + __func__); + return IRQ_HANDLED; + } + + while (state) { + intr_bit = __ffs(state); + state &= (state - 1); + + chan = mbox->irq_map_chan[intr_bit]; + if (!chan) { + dev_warn(mbox->dev, "%s: unexpected irq vector %d\n", + __func__, intr_bit); + continue; + } + + mchan = chan->con_priv; + if (mchan->dir == MBOX_TX) + mbox_chan_txdone(chan, 0); + else { + for (i = 0; i < MBOX_MSG_LEN; i++) + msg[i] = readl(mbox->base + + MBOX_DATA_REG(mchan->slot) + i * 4); + + mbox_chan_received_data(chan, (void *)msg); + } + + /* clear IRQ source */ + writel(BIT(mchan->ack_irq), ACK_INT_CLR_REG(mbox->ipc)); + mbox_set_state(mbox, mchan->slot, MBOX_STATE_IDLE); + } + + return IRQ_HANDLED; +} + +static int hi6220_mbox_startup(struct mbox_chan *chan) +{ + struct hi6220_mbox_chan *mchan = chan->con_priv; + struct hi6220_mbox *mbox = mchan->parent; + + mchan->dir = 0; + + /* enable interrupt */ + writel(BIT(mchan->ack_irq), ACK_INT_ENA_REG(mbox->ipc)); + return 0; +} + +static void hi6220_mbox_shutdown(struct mbox_chan *chan) +{ + struct hi6220_mbox_chan *mchan = chan->con_priv; + struct hi6220_mbox *mbox = mchan->parent; + + /* disable interrupt */ + writel(BIT(mchan->ack_irq), ACK_INT_DIS_REG(mbox->ipc)); + mbox->irq_map_chan[mchan->ack_irq] = NULL; +} + +static struct mbox_chan_ops hi6220_mbox_ops = { + .send_data = hi6220_mbox_send_data, + .startup = hi6220_mbox_startup, + .shutdown = hi6220_mbox_shutdown, + .last_tx_done = hi6220_mbox_last_tx_done, +}; + +static struct mbox_chan *hi6220_mbox_xlate(struct mbox_controller *controller, + const struct of_phandle_args *spec) +{ + struct hi6220_mbox *mbox = dev_get_drvdata(controller->dev); + struct hi6220_mbox_chan *mchan; + struct mbox_chan *chan; + unsigned int i = spec->args[0]; + unsigned int dst_irq = spec->args[1]; + unsigned int ack_irq = spec->args[2]; + + /* Bounds checking */ + if (i >= mbox->chan_num || dst_irq >= mbox->chan_num || + ack_irq >= mbox->chan_num) { + dev_err(mbox->dev, + "Invalid channel idx %d dst_irq %d ack_irq %d\n", + i, dst_irq, ack_irq); + return ERR_PTR(-EINVAL); + } + + /* Is requested channel free? */ + chan = &mbox->chan[i]; + if (mbox->irq_map_chan[ack_irq] == (void *)chan) { + dev_err(mbox->dev, "Channel in use\n"); + return ERR_PTR(-EBUSY); + } + + mchan = chan->con_priv; + mchan->dst_irq = dst_irq; + mchan->ack_irq = ack_irq; + + mbox->irq_map_chan[ack_irq] = (void *)chan; + return chan; +} + +static const struct of_device_id hi6220_mbox_of_match[] = { + { .compatible = "hisilicon,hi6220-mbox", }, + {}, +}; +MODULE_DEVICE_TABLE(of, hi6220_mbox_of_match); + +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); + if (!mbox) + return -ENOMEM; + + mbox->dev = dev; + mbox->chan_num = MBOX_CHAN_MAX; + mbox->mchan = devm_kzalloc(dev, + mbox->chan_num * sizeof(*mbox->mchan), GFP_KERNEL); + if (!mbox->mchan) + return -ENOMEM; + + mbox->chan = devm_kzalloc(dev, + mbox->chan_num * sizeof(*mbox->chan), GFP_KERNEL); + if (!mbox->chan) + return -ENOMEM; + + mbox->irq = platform_get_irq(pdev, 0); + if (mbox->irq < 0) + return mbox->irq; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mbox->ipc = devm_ioremap_resource(dev, res); + 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); + if (IS_ERR(mbox->base)) { + dev_err(dev, "ioremap buffer failed\n"); + return PTR_ERR(mbox->base); + } + + err = devm_request_irq(dev, mbox->irq, hi6220_mbox_interrupt, 0, + dev_name(dev), mbox); + if (err) { + dev_err(dev, "Failed to register a mailbox IRQ handler: %d\n", + err); + return -ENODEV; + } + + mbox->controller.dev = dev; + mbox->controller.chans = &mbox->chan[0]; + mbox->controller.num_chans = mbox->chan_num; + mbox->controller.ops = &hi6220_mbox_ops; + mbox->controller.of_xlate = hi6220_mbox_xlate; + + for (i = 0; i < mbox->chan_num; i++) { + mbox->chan[i].con_priv = &mbox->mchan[i]; + mbox->irq_map_chan[i] = NULL; + + mbox->mchan[i].parent = mbox; + mbox->mchan[i].slot = i; + } + + /* mask and clear all interrupt vectors */ + writel(0x0, ACK_INT_MSK_REG(mbox->ipc)); + writel(~0x0, ACK_INT_CLR_REG(mbox->ipc)); + + /* use interrupt for tx's ack */ + if (of_find_property(node, "hi6220,mbox-tx-noirq", NULL)) + mbox->tx_irq_mode = false; + else + mbox->tx_irq_mode = true; + + if (mbox->tx_irq_mode) + mbox->controller.txdone_irq = true; + else { + mbox->controller.txdone_poll = true; + mbox->controller.txpoll_period = 5; + } + + err = mbox_controller_register(&mbox->controller); + if (err) { + dev_err(dev, "Failed to register mailbox %d\n", err); + return err; + } + + platform_set_drvdata(pdev, mbox); + dev_info(dev, "Mailbox enabled\n"); + return 0; +} + +static int hi6220_mbox_remove(struct platform_device *pdev) +{ + struct hi6220_mbox *mbox = platform_get_drvdata(pdev); + + mbox_controller_unregister(&mbox->controller); + return 0; +} + +static struct platform_driver hi6220_mbox_driver = { + .driver = { + .name = "hi6220-mbox", + .owner = THIS_MODULE, + .of_match_table = hi6220_mbox_of_match, + }, + .probe = hi6220_mbox_probe, + .remove = hi6220_mbox_remove, +}; + +static int __init hi6220_mbox_init(void) +{ + return platform_driver_register(&hi6220_mbox_driver); +} +core_initcall(hi6220_mbox_init); + +static void __exit hi6220_mbox_exit(void) +{ + platform_driver_unregister(&hi6220_mbox_driver); +} +module_exit(hi6220_mbox_exit); + +MODULE_AUTHOR("Leo Yan "); +MODULE_DESCRIPTION("Hi6220 mailbox driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 14d653af4e32b1ae2964e5b2847ef6be191fee64 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 26 Feb 2016 00:32:21 +0300 Subject: mailbox/xgene-slimpro: Checking for IS_ERR instead of NULL devm_ioremap() returns NULL, it never returns an ERR_PTR. Fixes: f700e84f417b ('mailbox: Add support for APM X-Gene platform mailbox driver') Signed-off-by: Dan Carpenter Signed-off-by: Jassi Brar --- drivers/mailbox/mailbox-xgene-slimpro.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mailbox') diff --git a/drivers/mailbox/mailbox-xgene-slimpro.c b/drivers/mailbox/mailbox-xgene-slimpro.c index b5f51062b1f7..bd07f39f0692 100644 --- a/drivers/mailbox/mailbox-xgene-slimpro.c +++ b/drivers/mailbox/mailbox-xgene-slimpro.c @@ -196,8 +196,8 @@ static int slimpro_mbox_probe(struct platform_device *pdev) regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); mb_base = devm_ioremap(&pdev->dev, regs->start, resource_size(regs)); - if (IS_ERR(mb_base)) - return PTR_ERR(mb_base); + if (!mb_base) + return -ENOMEM; /* Setup mailbox links */ for (i = 0; i < MBOX_CNT; i++) { -- cgit v1.2.3 From f70ed3b5dc8b0810a01a24b198eca3f562aedc14 Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Tue, 27 Oct 2015 15:31:45 +0800 Subject: mailbox: rockchip: Add Rockchip mailbox driver This driver is found on RK3368 SoCs. The Mailbox module is a simple APB peripheral that allows both the Cortex-A53 MCU system to communicate by writing operation to generate interrupt. The registers are accessible by both CPU via APB interface. The Mailbox has the following main features: 1) Support dual-core system: Cortex-A53 and MCU. 2) Support APB interface. 3) Support four mailbox elements, each element includes one data word, one command word register and one flag bit that can represent one interrupt. 4) Four interrupts to Cortex-A53. 5) Four interrupts to MCU. 6) Provide 32 lock registers for software to use to indicate whether mailbox is occupied. [Jassi: Removed unused variable buf_base] Signed-off-by: Caesar Wang Signed-off-by: Jassi Brar --- drivers/mailbox/Kconfig | 9 ++ drivers/mailbox/Makefile | 2 + drivers/mailbox/rockchip-mailbox.c | 283 +++++++++++++++++++++++++++++++++++++ 3 files changed, 294 insertions(+) create mode 100644 drivers/mailbox/rockchip-mailbox.c (limited to 'drivers/mailbox') diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 642da1c812bf..09719843ad4a 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -43,6 +43,15 @@ config OMAP_MBOX_KFIFO_SIZE This can also be changed at runtime (via the mbox_kfifo_size module parameter). +config ROCKCHIP_MBOX + bool "Rockchip Soc Intergrated Mailbox Support" + depends on ARCH_ROCKCHIP || COMPILE_TEST + help + This driver provides support for inter-processor communication + between CPU cores and MCU processor on Some Rockchip SOCs. + Please check it that the Soc you use have Mailbox hardware. + Say Y here if you want to use the Rockchip Mailbox support. + config PCC bool "Platform Communication Channel Driver" depends on ACPI diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 2310e991ff55..948671733aa7 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -10,6 +10,8 @@ obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o obj-$(CONFIG_OMAP2PLUS_MBOX) += omap-mailbox.o +obj-$(CONFIG_ROCKCHIP_MBOX) += rockchip-mailbox.o + obj-$(CONFIG_PCC) += pcc.o obj-$(CONFIG_ALTERA_MBOX) += mailbox-altera.o diff --git a/drivers/mailbox/rockchip-mailbox.c b/drivers/mailbox/rockchip-mailbox.c new file mode 100644 index 000000000000..b79a2871227c --- /dev/null +++ b/drivers/mailbox/rockchip-mailbox.c @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2015, Fuzhou Rockchip Electronics Co., Ltd + * + * 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. + * + * This program is distributed in the hope 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 +#include +#include +#include +#include +#include +#include +#include + +#define MAILBOX_A2B_INTEN 0x00 +#define MAILBOX_A2B_STATUS 0x04 +#define MAILBOX_A2B_CMD(x) (0x08 + (x) * 8) +#define MAILBOX_A2B_DAT(x) (0x0c + (x) * 8) + +#define MAILBOX_B2A_INTEN 0x28 +#define MAILBOX_B2A_STATUS 0x2C +#define MAILBOX_B2A_CMD(x) (0x30 + (x) * 8) +#define MAILBOX_B2A_DAT(x) (0x34 + (x) * 8) + +struct rockchip_mbox_msg { + u32 cmd; + int rx_size; +}; + +struct rockchip_mbox_data { + int num_chans; +}; + +struct rockchip_mbox_chan { + int idx; + int irq; + struct rockchip_mbox_msg *msg; + struct rockchip_mbox *mb; +}; + +struct rockchip_mbox { + struct mbox_controller mbox; + struct clk *pclk; + void __iomem *mbox_base; + + /* The maximum size of buf for each channel */ + u32 buf_size; + + struct rockchip_mbox_chan *chans; +}; + +static int rockchip_mbox_send_data(struct mbox_chan *chan, void *data) +{ + struct rockchip_mbox *mb = dev_get_drvdata(chan->mbox->dev); + struct rockchip_mbox_msg *msg = data; + struct rockchip_mbox_chan *chans = mb->chans; + + if (!msg) + return -EINVAL; + + if (msg->rx_size > mb->buf_size) { + dev_err(mb->mbox.dev, "Transmit size over buf size(%d)\n", + mb->buf_size); + return -EINVAL; + } + + dev_dbg(mb->mbox.dev, "Chan[%d]: A2B message, cmd 0x%08x\n", + chans->idx, msg->cmd); + + mb->chans[chans->idx].msg = msg; + + writel_relaxed(msg->cmd, mb->mbox_base + MAILBOX_A2B_CMD(chans->idx)); + writel_relaxed(msg->rx_size, mb->mbox_base + + MAILBOX_A2B_DAT(chans->idx)); + + return 0; +} + +static int rockchip_mbox_startup(struct mbox_chan *chan) +{ + struct rockchip_mbox *mb = dev_get_drvdata(chan->mbox->dev); + + /* Enable all B2A interrupts */ + writel_relaxed((1 << mb->mbox.num_chans) - 1, + mb->mbox_base + MAILBOX_B2A_INTEN); + + return 0; +} + +static void rockchip_mbox_shutdown(struct mbox_chan *chan) +{ + struct rockchip_mbox *mb = dev_get_drvdata(chan->mbox->dev); + struct rockchip_mbox_chan *chans = mb->chans; + + /* Disable all B2A interrupts */ + writel_relaxed(0, mb->mbox_base + MAILBOX_B2A_INTEN); + + mb->chans[chans->idx].msg = NULL; +} + +static const struct mbox_chan_ops rockchip_mbox_chan_ops = { + .send_data = rockchip_mbox_send_data, + .startup = rockchip_mbox_startup, + .shutdown = rockchip_mbox_shutdown, +}; + +static irqreturn_t rockchip_mbox_irq(int irq, void *dev_id) +{ + int idx; + struct rockchip_mbox *mb = (struct rockchip_mbox *)dev_id; + u32 status = readl_relaxed(mb->mbox_base + MAILBOX_B2A_STATUS); + + for (idx = 0; idx < mb->mbox.num_chans; idx++) { + if ((status & (1 << idx)) && (irq == mb->chans[idx].irq)) { + /* Clear mbox interrupt */ + writel_relaxed(1 << idx, + mb->mbox_base + MAILBOX_B2A_STATUS); + return IRQ_WAKE_THREAD; + } + } + + return IRQ_NONE; +} + +static irqreturn_t rockchip_mbox_isr(int irq, void *dev_id) +{ + int idx; + struct rockchip_mbox_msg *msg = NULL; + struct rockchip_mbox *mb = (struct rockchip_mbox *)dev_id; + + for (idx = 0; idx < mb->mbox.num_chans; idx++) { + if (irq != mb->chans[idx].irq) + continue; + + msg = mb->chans[idx].msg; + if (!msg) { + dev_err(mb->mbox.dev, + "Chan[%d]: B2A message is NULL\n", idx); + break; /* spurious */ + } + + mbox_chan_received_data(&mb->mbox.chans[idx], msg); + mb->chans[idx].msg = NULL; + + dev_dbg(mb->mbox.dev, "Chan[%d]: B2A message, cmd 0x%08x\n", + idx, msg->cmd); + + break; + } + + return IRQ_HANDLED; +} + +static const struct rockchip_mbox_data rk3368_drv_data = { + .num_chans = 4, +}; + +static const struct of_device_id rockchip_mbox_of_match[] = { + { .compatible = "rockchip,rk3368-mailbox", .data = &rk3368_drv_data}, + { }, +}; +MODULE_DEVICE_TABLE(of, rockchp_mbox_of_match); + +static int rockchip_mbox_probe(struct platform_device *pdev) +{ + struct rockchip_mbox *mb; + const struct of_device_id *match; + const struct rockchip_mbox_data *drv_data; + struct resource *res; + int ret, irq, i; + + if (!pdev->dev.of_node) + return -ENODEV; + + match = of_match_node(rockchip_mbox_of_match, pdev->dev.of_node); + drv_data = (const struct rockchip_mbox_data *)match->data; + + mb = devm_kzalloc(&pdev->dev, sizeof(*mb), GFP_KERNEL); + if (!mb) + return -ENOMEM; + + mb->chans = devm_kcalloc(&pdev->dev, drv_data->num_chans, + sizeof(*mb->chans), GFP_KERNEL); + if (!mb->chans) + return -ENOMEM; + + mb->mbox.chans = devm_kcalloc(&pdev->dev, drv_data->num_chans, + sizeof(*mb->mbox.chans), GFP_KERNEL); + if (!mb->mbox.chans) + return -ENOMEM; + + platform_set_drvdata(pdev, mb); + + mb->mbox.dev = &pdev->dev; + mb->mbox.num_chans = drv_data->num_chans; + mb->mbox.ops = &rockchip_mbox_chan_ops; + mb->mbox.txdone_irq = true; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + mb->mbox_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mb->mbox_base)) + return PTR_ERR(mb->mbox_base); + + /* Each channel has two buffers for A2B and B2A */ + mb->buf_size = resource_size(res) / (drv_data->num_chans * 2); + + mb->pclk = devm_clk_get(&pdev->dev, "pclk_mailbox"); + if (IS_ERR(mb->pclk)) { + ret = PTR_ERR(mb->pclk); + dev_err(&pdev->dev, "failed to get pclk_mailbox clock: %d\n", + ret); + return ret; + } + + ret = clk_prepare_enable(mb->pclk); + if (ret) { + dev_err(&pdev->dev, "failed to enable pclk: %d\n", ret); + return ret; + } + + for (i = 0; i < mb->mbox.num_chans; i++) { + irq = platform_get_irq(pdev, i); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(&pdev->dev, irq, + rockchip_mbox_irq, + rockchip_mbox_isr, IRQF_ONESHOT, + dev_name(&pdev->dev), mb); + if (ret < 0) + return ret; + + mb->chans[i].idx = i; + mb->chans[i].irq = irq; + mb->chans[i].mb = mb; + mb->chans[i].msg = NULL; + } + + ret = mbox_controller_register(&mb->mbox); + if (ret < 0) + dev_err(&pdev->dev, "Failed to register mailbox: %d\n", ret); + + return ret; +} + +static int rockchip_mbox_remove(struct platform_device *pdev) +{ + struct rockchip_mbox *mb = platform_get_drvdata(pdev); + + if (!mb) + return -EINVAL; + + mbox_controller_unregister(&mb->mbox); + + return 0; +} + +static struct platform_driver rockchip_mbox_driver = { + .probe = rockchip_mbox_probe, + .remove = rockchip_mbox_remove, + .driver = { + .name = "rockchip-mailbox", + .of_match_table = of_match_ptr(rockchip_mbox_of_match), + }, +}; + +module_platform_driver(rockchip_mbox_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Rockchip mailbox: communicate between CPU cores and MCU"); +MODULE_AUTHOR("Addy Ke "); +MODULE_AUTHOR("Caesar Wang "); -- cgit v1.2.3 From c5a9d1f30c066b7922a5f66b8c03a263d2f06a8c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 14 Mar 2016 23:46:55 +0100 Subject: mailbox: rockchip: avoid 64-bit division The newly added rockchip mailbox driver causes a bug in the ARM allyesconfig build because of a division of a resource_size_t variable that may be 64 bit wide: drivers/mailbox/built-in.o: In function `rockchip_mbox_probe': :(.text+0x6614): undefined reference to `__aeabi_uldivmod' This adds a cast to size_t, which turns it into a 32-bit division in this case. This is safe because we know that we cannot possibly map a resource that is longer than what a pointer contains, and in practice it will be very short instead. Signed-off-by: Arnd Bergmann Signed-off-by: Jassi Brar --- drivers/mailbox/rockchip-mailbox.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mailbox') diff --git a/drivers/mailbox/rockchip-mailbox.c b/drivers/mailbox/rockchip-mailbox.c index b79a2871227c..d702a204f5c1 100644 --- a/drivers/mailbox/rockchip-mailbox.c +++ b/drivers/mailbox/rockchip-mailbox.c @@ -213,7 +213,7 @@ static int rockchip_mbox_probe(struct platform_device *pdev) return PTR_ERR(mb->mbox_base); /* Each channel has two buffers for A2B and B2A */ - mb->buf_size = resource_size(res) / (drv_data->num_chans * 2); + mb->buf_size = (size_t)resource_size(res) / (drv_data->num_chans * 2); mb->pclk = devm_clk_get(&pdev->dev, "pclk_mailbox"); if (IS_ERR(mb->pclk)) { -- cgit v1.2.3