From: Suman Anna <s-anna@ti.com>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Linus Walleij <linus.walleij@linaro.org>,
Russell King <linux@arm.linux.org.uk>,
Arnd Bergmann <arnd@arndb.de>, Tony Lindgren <tony@atomide.com>,
Ohad Ben-Cohen <ohad@wizery.com>, <linux-kernel@vger.kernel.org>,
<linux-arm-kernel@lists.infradead.org>,
<linux-omap@vger.kernel.org>,
Omar Ramirez Luna <omar.ramirez@copitl.com>,
Loic Pallardy <loic.pallardy@st.com>, Suman Anna <s-anna@ti.com>
Subject: [PATCHv3 11/14] mailbox: create dbx500 mailbox driver
Date: Tue, 12 Mar 2013 22:24:27 -0500 [thread overview]
Message-ID: <1363145067-14715-1-git-send-email-s-anna@ti.com> (raw)
From: Loic Pallardy <loic.pallardy@st.com>
Add STEriccson DBX500 PRCM mailbox support.
Signed-off-by: Loic Pallardy <loic.pallardy@st.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
.../devicetree/bindings/mailbox/dbx500-mailbox.txt | 27 +
drivers/mailbox/Kconfig | 7 +
drivers/mailbox/Makefile | 1 +
drivers/mailbox/mailbox-dbx500.c | 648 +++++++++++++++++++++
include/linux/platform_data/mailbox-dbx500.h | 12 +
5 files changed, 695 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mailbox/dbx500-mailbox.txt
create mode 100644 drivers/mailbox/mailbox-dbx500.c
create mode 100644 include/linux/platform_data/mailbox-dbx500.h
diff --git a/Documentation/devicetree/bindings/mailbox/dbx500-mailbox.txt b/Documentation/devicetree/bindings/mailbox/dbx500-mailbox.txt
new file mode 100644
index 0000000..df0f594
--- /dev/null
+++ b/Documentation/devicetree/bindings/mailbox/dbx500-mailbox.txt
@@ -0,0 +1,27 @@
+ST-Ericsson DBx500 PRCM Mailbox Driver
+
+Required properties:
+- compatible : Should be
+ "stericsson,db8500-mailbox" for db8500 and db9500
+ "stericsson,db9540-mailbox" for db9540
+- reg : Physical base address and length of mailbox's
+ registers and shared memory
+- reg-names : Should contain the reg names "prcm-reg" and
+ "prcmu-tcdm"
+- interrupts : contains the IRQ line for the PRCM mailbox
+- interrupt-names : Should contain the interrupt name "irq"
+- legacy-offset : Memory offset in shared mem for legacy mailboxes
+
+Optional properties:
+- upap-offset : Memory offset in shared mem for upap mailboxes
+
+Examples:
+
+mailbox {
+ compatible = "stericsson,db8500-mailbox";
+ reg = <0x80157000 0x1000>, <0x801B8000 0x2000>;
+ reg-names = "prcm-reg", "prcmu-tcdm";
+ interrupts = <0 47 0x4>;
+ interrupt-names = "irq";
+ legacy-offset = <0xdd4>;
+};
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index d7692a7..438ea21 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -33,6 +33,12 @@ config OMAP2PLUS_MBOX
OMAP2/3; or IPU, IVA HD and DSP in OMAP4. Say Y here if you want
to use OMAP2+ Mailbox framework support.
+config DBX500_MBOX
+ tristate "DBx500 Mailbox driver support"
+ depends on ARCH_U8500
+ help
+ Say Y here if you want to use DBx500 Mailbox driver support for
+ power coprocessor access on Ux500 and Ux540 families
config MBOX_KFIFO_SIZE
int "Mailbox kfifo default buffer size (bytes)"
@@ -44,6 +50,7 @@ config MBOX_KFIFO_SIZE
config MBOX_DATA_SIZE
int "Mailbox associated data max size (bytes)"
+ default 64 if DBX500_MBOX
default 4
help
Specify the default size of mailbox's associated data buffer
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 9ee0fcb..ee5fb1e 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -3,3 +3,4 @@ obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o
obj-$(CONFIG_MAILBOX) += mailbox.o
obj-$(CONFIG_OMAP1_MBOX) += mailbox-omap1.o
obj-$(CONFIG_OMAP2PLUS_MBOX) += mailbox-omap2.o
+obj-$(CONFIG_DBX500_MBOX) += mailbox-dbx500.o
diff --git a/drivers/mailbox/mailbox-dbx500.c b/drivers/mailbox/mailbox-dbx500.c
new file mode 100644
index 0000000..e9d2b0a
--- /dev/null
+++ b/drivers/mailbox/mailbox-dbx500.c
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Loic Pallardy <loic.pallardy@st.com> for ST-Ericsson
+ * DBX500 PRCM Mailbox driver
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/platform_data/mailbox-dbx500.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/irqdomain.h>
+
+#include "mailbox_internal.h"
+
+#define MAILBOX_LEGACY 0
+#define MAILBOX_UPAP 1
+
+enum {
+ MAILBOX_BLOCKING,
+ MAILBOX_ATOMIC,
+};
+
+/* CPU mailbox registers */
+#define PRCM_MBOX_CPU_VAL 0x0fc
+#define PRCM_MBOX_CPU_SET 0x100
+#define PRCM_MBOX_CPU_CLR 0x104
+
+#define PRCM_ARM_IT1_CLR 0x48C
+#define PRCM_ARM_IT1_VAL 0x494
+
+#define NUM_MB 8
+#define MBOX_BIT BIT
+#define ALL_MBOX_BITS (MBOX_BIT(NUM_MB) - 1)
+
+/* CPU mailbox shared memory */
+#define PRCM_MBOX_LEGACY_SIZE 0x220
+#define _PRCM_MBOX_HEADER 0x214 /* 16 bytes */
+#define PRCM_MBOX_HEADER_REQ_MB0 (_PRCM_MBOX_HEADER + 0x0)
+#define PRCM_MBOX_HEADER_REQ_MB1 (_PRCM_MBOX_HEADER + 0x1)
+#define PRCM_MBOX_HEADER_REQ_MB2 (_PRCM_MBOX_HEADER + 0x2)
+#define PRCM_MBOX_HEADER_REQ_MB3 (_PRCM_MBOX_HEADER + 0x3)
+#define PRCM_MBOX_HEADER_REQ_MB4 (_PRCM_MBOX_HEADER + 0x4)
+#define PRCM_MBOX_HEADER_REQ_MB5 (_PRCM_MBOX_HEADER + 0x5)
+#define PRCM_MBOX_HEADER_ACK_MB0 (_PRCM_MBOX_HEADER + 0x8)
+
+/* Req Mailboxes */
+#define PRCM_REQ_MB0 0x208 /* 12 bytes */
+#define PRCM_REQ_MB1 0x1FC /* 12 bytes */
+#define PRCM_REQ_MB2 0x1EC /* 16 bytes */
+#define PRCM_REQ_MB3 0x78 /* 372 bytes */
+#define PRCM_REQ_MB4 0x74 /* 4 bytes */
+#define PRCM_REQ_MB5 0x70 /* 4 bytes */
+#define PRCM_REQ_PASR 0x30 /* 4 bytes */
+
+/* Ack Mailboxes */
+#define PRCM_ACK_MB0 0x34 /* 52 bytes */
+#define PRCM_ACK_MB1 0x30 /* 4 bytes */
+#define PRCM_ACK_MB2 0x2C /* 4 bytes */
+#define PRCM_ACK_MB3 0x28 /* 4 bytes */
+#define PRCM_ACK_MB4 0x24 /* 4 bytes */
+#define PRCM_ACK_MB5 0x20 /* 4 bytes */
+
+/* Ack Mailboxe sizes */
+#define PRCM_ACK_MB0_SIZE 0x24 /* 52 bytes */
+#define PRCM_ACK_MB1_SIZE 0x4 /* 4 bytes */
+#define PRCM_ACK_MB2_SIZE 0x1 /* 1 bytes */
+#define PRCM_ACK_MB3_SIZE 0x2 /* 2 bytes */
+#define PRCM_ACK_MB4_SIZE 0x0 /* 0 bytes */
+#define PRCM_ACK_MB5_SIZE 0x4 /* 4 bytes */
+
+static void __iomem *mbox_base;
+static void __iomem *tcdm_mem_base;
+
+static u8 prcm_mbox_irq_mask; /* masked by default */
+static DEFINE_SPINLOCK(prcm_mbox_irqs_lock);
+
+struct dbx500_mbox_priv {
+ int access_mode;
+ int type;
+ int header_size;
+ unsigned int tx_header_offset;
+ unsigned int rx_header_offset;
+ unsigned int tx_offset;
+ unsigned int rx_offset;
+ unsigned int rx_size;
+ unsigned int sw_irq;
+ bool empty_flag;
+};
+
+static inline unsigned int mbox_read_reg(size_t ofs)
+{
+ return __raw_readl(mbox_base + ofs);
+}
+
+static inline void mbox_write_reg(u32 val, size_t ofs)
+{
+ __raw_writel(val, mbox_base + ofs);
+}
+
+/* Mailbox H/W preparations */
+static struct irq_chip dbx500_mbox_irq_chip;
+static struct irq_domain *dbx500_mbox_irq_domain;
+
+static int dbx500_mbox_startup(struct mailbox *mbox)
+{
+ /* Nothing to do */
+ return 0;
+}
+
+/* Mailbox IRQ handle functions */
+
+static void dbx500_mbox_enable_irq(struct mailbox *mbox, mailbox_irq_t irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&prcm_mbox_irqs_lock, flags);
+
+ prcm_mbox_irq_mask |= MBOX_BIT(mbox->id);
+
+ spin_unlock_irqrestore(&prcm_mbox_irqs_lock, flags);
+
+}
+
+static void dbx500_mbox_disable_irq(struct mailbox *mbox, mailbox_irq_t irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&prcm_mbox_irqs_lock, flags);
+
+ prcm_mbox_irq_mask &= ~MBOX_BIT(mbox->id);
+
+ spin_unlock_irqrestore(&prcm_mbox_irqs_lock, flags);
+
+}
+
+static void dbx500_mbox_ack_irq(struct mailbox *mbox, mailbox_irq_t irq)
+{
+ if (irq == IRQ_RX)
+ mbox_write_reg(MBOX_BIT(mbox->id), PRCM_ARM_IT1_CLR);
+}
+
+static int dbx500_mbox_is_irq(struct mailbox *mbox, mailbox_irq_t irq)
+{
+ struct dbx500_mbox_priv *priv = (struct dbx500_mbox_priv *)mbox->priv;
+
+ if (irq == IRQ_RX) {
+ if (mbox_read_reg(PRCM_ARM_IT1_VAL) & MBOX_BIT(mbox->id)) {
+ priv->empty_flag = true;
+ return 1;
+ } else {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+}
+
+/* message management */
+
+static int dbx500_mbox_is_ready(struct mailbox *mbox)
+{
+ return mbox_read_reg(PRCM_MBOX_CPU_VAL) & MBOX_BIT(mbox->id);
+}
+
+static int dbx500_mbox_write(struct mailbox *mbox,
+ struct mailbox_msg *msg)
+{
+ int j;
+ struct dbx500_mbox_priv *priv = (struct dbx500_mbox_priv *)mbox->priv;
+
+ if (msg->size && !msg->pdata)
+ return -EINVAL;
+
+ while (dbx500_mbox_is_ready(mbox))
+ cpu_relax();
+
+ /* write header */
+ if (priv->header_size)
+ writeb(msg->header, tcdm_mem_base + priv->tx_header_offset);
+
+ /* write data */
+ for (j = 0; j < msg->size; j++)
+ writeb(((unsigned char *)msg->pdata)[j],
+ tcdm_mem_base + priv->tx_offset + j);
+
+ /* send event */
+ mbox_write_reg(MBOX_BIT(mbox->id), PRCM_MBOX_CPU_SET);
+
+ return 0;
+}
+
+static void dbx500_mbox_read(struct mailbox *mbox, struct mailbox_msg *msg)
+{
+ struct dbx500_mbox_priv *priv = (struct dbx500_mbox_priv *)mbox->priv;
+
+ msg->header = readb(tcdm_mem_base + priv->rx_header_offset);
+ msg->pdata = (unsigned char *)(tcdm_mem_base + priv->rx_offset);
+
+ msg->size = priv->rx_size;
+ priv->empty_flag = false;
+}
+
+static int dbx500_mbox_empty(struct mailbox *mbox)
+{
+ struct dbx500_mbox_priv *priv = (struct dbx500_mbox_priv *)mbox->priv;
+ if (priv->empty_flag)
+ return 0;
+ else
+ return 1;
+}
+
+static int dbx500_mbox_poll_for_space(struct mailbox *mbox)
+{
+ return 0;
+}
+/* interrupt management */
+
+/* mask/unmask must be managed by SW */
+
+static void mbox_irq_mask(struct irq_data *d)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&prcm_mbox_irqs_lock, flags);
+
+ prcm_mbox_irq_mask &= ~MBOX_BIT(d->hwirq);
+
+ spin_unlock_irqrestore(&prcm_mbox_irqs_lock, flags);
+}
+
+static void mbox_irq_unmask(struct irq_data *d)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&prcm_mbox_irqs_lock, flags);
+
+ prcm_mbox_irq_mask |= MBOX_BIT(d->hwirq);
+
+ spin_unlock_irqrestore(&prcm_mbox_irqs_lock, flags);
+}
+
+static void mbox_irq_ack(struct irq_data *d)
+{
+ mbox_write_reg(MBOX_BIT(d->hwirq), PRCM_ARM_IT1_CLR);
+}
+
+static struct irq_chip dbx500_mbox_irq_chip = {
+ .name = "dbx500_mbox",
+ .irq_disable = mbox_irq_unmask,
+ .irq_ack = mbox_irq_ack,
+ .irq_mask = mbox_irq_mask,
+ .irq_unmask = mbox_irq_unmask,
+};
+
+static int dbx500_mbox_irq_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_chip_and_handler(irq, &dbx500_mbox_irq_chip,
+ handle_simple_irq);
+ set_irq_flags(irq, IRQF_VALID);
+
+ return 0;
+}
+
+static struct irq_domain_ops dbx500_mbox_irq_ops = {
+ .map = dbx500_mbox_irq_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static irqreturn_t dbx500_mbox_irq_handler(int irq, void *data)
+{
+ u32 bits;
+ u8 n;
+
+ bits = (mbox_read_reg(PRCM_ARM_IT1_VAL) & ALL_MBOX_BITS);
+ if (unlikely(!bits))
+ return IRQ_NONE;
+
+ bits &= prcm_mbox_irq_mask;
+
+ for (n = 0; bits; n++) {
+ if (bits & MBOX_BIT(n)) {
+ bits -= MBOX_BIT(n);
+ generic_handle_irq(
+ irq_find_mapping(dbx500_mbox_irq_domain, n));
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+/* 5 mailboxes AP <--> PRCMU */
+static struct mailbox_ops dbx500_mbox_ops = {
+ .type = MBOX_SHARED_MEM_TYPE,
+ .startup = dbx500_mbox_startup,
+ .enable_irq = dbx500_mbox_enable_irq,
+ .disable_irq = dbx500_mbox_disable_irq,
+ .ack_irq = dbx500_mbox_ack_irq,
+ .is_irq = dbx500_mbox_is_irq,
+ .read = dbx500_mbox_read,
+ .write = dbx500_mbox_write,
+ .empty = dbx500_mbox_empty,
+ .poll_for_space = dbx500_mbox_poll_for_space,
+};
+
+struct dbx500_mbox_priv mbox0_priv = {
+ .access_mode = MAILBOX_ATOMIC,
+ .type = MAILBOX_LEGACY,
+ .tx_header_offset = PRCM_MBOX_HEADER_REQ_MB0,
+ .header_size = 1,
+ .tx_offset = PRCM_REQ_MB0,
+ .rx_header_offset = PRCM_MBOX_HEADER_ACK_MB0,
+ .rx_offset = PRCM_ACK_MB0,
+ .rx_size = PRCM_ACK_MB0_SIZE,
+};
+
+struct mailbox mbox0_info = {
+ .name = "mbox0",
+ .id = 0,
+ .ops = &dbx500_mbox_ops,
+ .priv = &mbox0_priv,
+};
+
+struct dbx500_mbox_priv mbox1_priv = {
+ .access_mode = MAILBOX_BLOCKING,
+ .type = MAILBOX_LEGACY,
+ .tx_header_offset = PRCM_MBOX_HEADER_REQ_MB1,
+ .header_size = 1,
+ .tx_offset = PRCM_REQ_MB1,
+ .rx_header_offset = PRCM_MBOX_HEADER_REQ_MB1,
+ .rx_offset = PRCM_ACK_MB1,
+ .rx_size = PRCM_ACK_MB1_SIZE,
+};
+
+struct mailbox mbox1_info = {
+ .name = "mbox1",
+ .id = 1,
+ .ops = &dbx500_mbox_ops,
+ .priv = &mbox1_priv,
+};
+
+struct dbx500_mbox_priv mbox2_priv = {
+ .access_mode = MAILBOX_BLOCKING,
+ .type = MAILBOX_LEGACY,
+ .tx_header_offset = PRCM_MBOX_HEADER_REQ_MB2,
+ .header_size = 1,
+ .tx_offset = PRCM_REQ_MB2,
+ .rx_header_offset = PRCM_MBOX_HEADER_REQ_MB2,
+ .rx_offset = PRCM_ACK_MB2,
+ .rx_size = PRCM_ACK_MB2_SIZE,
+};
+
+struct mailbox mbox2_info = {
+ .name = "mbox2",
+ .id = 2,
+ .ops = &dbx500_mbox_ops,
+ .priv = &mbox2_priv,
+};
+
+struct dbx500_mbox_priv mbox3_priv = {
+ .access_mode = MAILBOX_BLOCKING,
+ .type = MAILBOX_LEGACY,
+ .tx_header_offset = PRCM_MBOX_HEADER_REQ_MB3,
+ .header_size = 1,
+ .tx_offset = PRCM_REQ_MB3,
+ .rx_header_offset = PRCM_MBOX_HEADER_REQ_MB3,
+ .rx_offset = PRCM_ACK_MB3,
+ .rx_size = PRCM_ACK_MB3_SIZE,
+};
+
+struct mailbox mbox3_info = {
+ .name = "mbox3",
+ .id = 3,
+ .ops = &dbx500_mbox_ops,
+ .priv = &mbox3_priv,
+};
+
+struct dbx500_mbox_priv mbox4_priv = {
+ .access_mode = MAILBOX_BLOCKING,
+ .type = MAILBOX_LEGACY,
+ .tx_header_offset = PRCM_MBOX_HEADER_REQ_MB4,
+ .header_size = 1,
+ .tx_offset = PRCM_REQ_MB4,
+ .rx_header_offset = PRCM_MBOX_HEADER_REQ_MB4,
+ .rx_offset = PRCM_ACK_MB4,
+ .rx_size = PRCM_ACK_MB4_SIZE,
+};
+
+struct mailbox mbox4_info = {
+ .name = "mbox4",
+ .id = 4,
+ .ops = &dbx500_mbox_ops,
+ .priv = &mbox4_priv,
+};
+
+struct dbx500_mbox_priv mbox5_priv = {
+ .access_mode = MAILBOX_BLOCKING,
+ .type = MAILBOX_LEGACY,
+ .tx_header_offset = PRCM_MBOX_HEADER_REQ_MB5,
+ .header_size = 1,
+ .tx_offset = PRCM_REQ_MB5,
+ .rx_header_offset = PRCM_MBOX_HEADER_REQ_MB5,
+ .rx_offset = PRCM_ACK_MB5,
+ .rx_size = PRCM_ACK_MB5_SIZE,
+};
+
+struct mailbox mbox5_info = {
+ .name = "mbox5",
+ .id = 5,
+ .ops = &dbx500_mbox_ops,
+ .priv = &mbox5_priv,
+};
+
+struct mailbox mbox6_info = {
+ .name = "mbox6",
+ .id = 6,
+ .ops = &dbx500_mbox_ops,
+};
+
+struct mailbox mbox7_info = {
+ .name = "mbox7",
+ .id = 7,
+ .ops = &dbx500_mbox_ops,
+};
+
+/* x540 mailbox definition */
+struct dbx500_mbox_priv mbox1_upap_priv = {
+ .access_mode = MAILBOX_BLOCKING,
+ .type = MAILBOX_UPAP,
+ .tx_header_offset = 0,
+ .header_size = 0,
+ .tx_offset = 0,
+ .rx_offset = 0, /* TODO to be replaced by dynamic detection */
+ .rx_size = 0x40,
+};
+
+struct mailbox mbox1_upap_info = {
+ .name = "mbox1_upap",
+ .id = 1,
+ .ops = &dbx500_mbox_ops,
+ .priv = &mbox1_upap_priv,
+};
+
+struct mailbox *db8500_mboxes[] = { &mbox0_info, &mbox1_info, &mbox2_info,
+ &mbox3_info, &mbox4_info, &mbox5_info, &mbox6_info, &mbox7_info,
+ NULL };
+
+struct mailbox *dbx540_mboxes[] = { &mbox0_info, &mbox2_info,
+ &mbox3_info, &mbox4_info, &mbox5_info, &mbox6_info, &mbox7_info,
+ &mbox1_upap_info, NULL };
+
+static const struct of_device_id dbx500_mailbox_match[] = {
+ { .compatible = "stericsson,db8500-mailbox",
+ .data = (void *)db8500_mboxes,
+ },
+ { .compatible = "stericsson,db9540-mailbox",
+ .data = (void *)dbx540_mboxes,
+ },
+ { /* sentinel */}
+};
+
+static int dbx500_mbox_probe(struct platform_device *pdev)
+{
+ const struct platform_device_id *platid = platform_get_device_id(pdev);
+ struct resource *mem;
+ int ret, i, irq;
+ u32 legacy_offset = 0;
+ u32 upap_offset = 0, upap_size = PRCM_MBOX_LEGACY_SIZE;
+ struct mailbox **list;
+ struct dbx500_plat_data *pdata = dev_get_platdata(&pdev->dev);
+ struct device_node *np = pdev->dev.of_node;
+ bool upap_used = false;
+
+ if (!pdata) {
+ if (np) {
+ if (of_property_read_u32(np, "legacy-offset",
+ &legacy_offset)) {
+ /* missing legacy-offset */
+ dev_err(&pdev->dev, "No legacy offset found\n");
+ return -ENODEV;
+ }
+ if (of_property_read_u32(np, "upap-offset",
+ &upap_offset)) {
+ dev_dbg(&pdev->dev, "No upap offset found\n");
+ upap_offset = -1;
+ }
+ list = (struct mailbox **)of_match_device(
+ dbx500_mailbox_match, &pdev->dev)->data;
+ if (!list) {
+ /* No mailbox configuration */
+ dev_err(&pdev->dev, "No configuration found\n");
+ return -ENODEV;
+ }
+ } else {
+ /* No mailbox configuration */
+ dev_err(&pdev->dev, "No configuration found\n");
+ return -ENODEV;
+ }
+ } else {
+ list = (struct mailbox **)platid->driver_data;
+ legacy_offset = pdata->legacy_offset;
+ upap_offset = pdata->upap_offset;
+ }
+
+ mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcm-reg");
+ mbox_base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+ if (!mbox_base) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu-tcdm");
+ tcdm_mem_base = devm_ioremap(&pdev->dev, mem->start,
+ resource_size(mem));
+ if (!tcdm_mem_base) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* check mailbox offset values */
+ if (legacy_offset >= (resource_size(mem) - PRCM_MBOX_LEGACY_SIZE)) {
+ dev_err(&pdev->dev, "Incorrect legacy offset value\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ for (i = 0; list[i]; i++) {
+ struct mailbox *mbox = list[i];
+ struct dbx500_mbox_priv *priv =
+ (struct dbx500_mbox_priv *)mbox->priv;
+ if (!priv)
+ continue;
+ if (priv->type == MAILBOX_UPAP) {
+ upap_used = true;
+ upap_size += priv->rx_size;
+ break;
+ }
+ }
+
+ if (upap_used && (upap_offset >= (resource_size(mem) - upap_size))) {
+ dev_err(&pdev->dev, "Incorrect upap offset value\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ /* clean all mailboxes */
+ mbox_write_reg(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR);
+ ret = request_irq(irq, dbx500_mbox_irq_handler,
+ IRQF_NO_SUSPEND, "db8500_mbox", NULL);
+ if (ret) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ dbx500_mbox_irq_domain = irq_domain_add_linear(
+ np, NUM_MB, &dbx500_mbox_irq_ops, NULL);
+
+ if (!dbx500_mbox_irq_domain) {
+ dev_err(&pdev->dev, "Failed to create irqdomain\n");
+ ret = -ENOSYS;
+ goto irq_free;
+ }
+
+ /*
+ * Update mailbox shared memory buffer offset according to mailbox
+ * type
+ */
+ for (i = 0; list[i]; i++) {
+ struct mailbox *mbox = list[i];
+ struct dbx500_mbox_priv *priv =
+ (struct dbx500_mbox_priv *)mbox->priv;
+ if (!priv)
+ continue;
+ mbox->irq = irq_create_mapping(dbx500_mbox_irq_domain,
+ mbox->id);
+ if (priv->type == MAILBOX_LEGACY) {
+ priv->rx_offset += legacy_offset;
+ priv->rx_header_offset += legacy_offset;
+ priv->tx_offset += legacy_offset;
+ priv->tx_header_offset += legacy_offset;
+ } else if (priv->type == MAILBOX_UPAP) {
+ priv->tx_offset += upap_offset;
+ priv->rx_offset += upap_offset;
+ }
+ }
+
+ ret = mailbox_register(&pdev->dev, list);
+irq_free:
+ if (ret)
+ free_irq(irq, NULL);
+
+out:
+ return ret;
+}
+
+static int dbx500_mbox_remove(struct platform_device *pdev)
+{
+ mailbox_unregister();
+ iounmap(mbox_base);
+ return 0;
+}
+
+static const struct platform_device_id dbx500_mbox_id[] = {
+ {
+ .name = "db8500-mailbox",
+ .driver_data = (unsigned long) db8500_mboxes,
+ }, {
+ .name = "db9540-mailbox",
+ .driver_data = (unsigned long) dbx540_mboxes,
+ }, {
+ /* sentinel */
+ }
+};
+
+static struct platform_driver dbx500_mbox_driver = {
+ .probe = dbx500_mbox_probe,
+ .remove = dbx500_mbox_remove,
+ .driver = {
+ .name = "dbx500-mailbox",
+ .of_match_table = dbx500_mailbox_match,
+ },
+ .id_table = dbx500_mbox_id,
+};
+
+static int __init dbx500_mbox_init(void)
+{
+ return platform_driver_register(&dbx500_mbox_driver);
+}
+
+static void __exit dbx500_mbox_exit(void)
+{
+ platform_driver_unregister(&dbx500_mbox_driver);
+}
+
+postcore_initcall(dbx500_mbox_init);
+module_exit(dbx500_mbox_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ST-Ericsson mailbox: dbx500 architecture specific functions");
+MODULE_AUTHOR("Loic Pallardy <loic.pallardy@st.com>");
+MODULE_ALIAS("platform:dbx500-mailbox");
diff --git a/include/linux/platform_data/mailbox-dbx500.h b/include/linux/platform_data/mailbox-dbx500.h
new file mode 100644
index 0000000..d5aebd9
--- /dev/null
+++ b/include/linux/platform_data/mailbox-dbx500.h
@@ -0,0 +1,12 @@
+/*
+ * mailbox-dbx500.h
+ *
+ * Copyright (C) ST-Ericsson SA 2012
+ * Author: <loic.pallardy@st.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+struct dbx500_plat_data {
+ unsigned int legacy_offset;
+ unsigned int upap_offset;
+};
--
1.8.1.2
WARNING: multiple messages have this Message-ID (diff)
From: Suman Anna <s-anna@ti.com>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Linus Walleij <linus.walleij@linaro.org>,
Russell King <linux@arm.linux.org.uk>,
Arnd Bergmann <arnd@arndb.de>, Tony Lindgren <tony@atomide.com>,
Ohad Ben-Cohen <ohad@wizery.com>,
linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org, linux-omap@vger.kernel.org,
Omar Ramirez Luna <omar.ramirez@copitl.com>,
Loic Pallardy <loic.pallardy@st.com>, Suman Anna <s-anna@ti.com>
Subject: [PATCHv3 11/14] mailbox: create dbx500 mailbox driver
Date: Tue, 12 Mar 2013 22:24:27 -0500 [thread overview]
Message-ID: <1363145067-14715-1-git-send-email-s-anna@ti.com> (raw)
From: Loic Pallardy <loic.pallardy@st.com>
Add STEriccson DBX500 PRCM mailbox support.
Signed-off-by: Loic Pallardy <loic.pallardy@st.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
.../devicetree/bindings/mailbox/dbx500-mailbox.txt | 27 +
drivers/mailbox/Kconfig | 7 +
drivers/mailbox/Makefile | 1 +
drivers/mailbox/mailbox-dbx500.c | 648 +++++++++++++++++++++
include/linux/platform_data/mailbox-dbx500.h | 12 +
5 files changed, 695 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mailbox/dbx500-mailbox.txt
create mode 100644 drivers/mailbox/mailbox-dbx500.c
create mode 100644 include/linux/platform_data/mailbox-dbx500.h
diff --git a/Documentation/devicetree/bindings/mailbox/dbx500-mailbox.txt b/Documentation/devicetree/bindings/mailbox/dbx500-mailbox.txt
new file mode 100644
index 0000000..df0f594
--- /dev/null
+++ b/Documentation/devicetree/bindings/mailbox/dbx500-mailbox.txt
@@ -0,0 +1,27 @@
+ST-Ericsson DBx500 PRCM Mailbox Driver
+
+Required properties:
+- compatible : Should be
+ "stericsson,db8500-mailbox" for db8500 and db9500
+ "stericsson,db9540-mailbox" for db9540
+- reg : Physical base address and length of mailbox's
+ registers and shared memory
+- reg-names : Should contain the reg names "prcm-reg" and
+ "prcmu-tcdm"
+- interrupts : contains the IRQ line for the PRCM mailbox
+- interrupt-names : Should contain the interrupt name "irq"
+- legacy-offset : Memory offset in shared mem for legacy mailboxes
+
+Optional properties:
+- upap-offset : Memory offset in shared mem for upap mailboxes
+
+Examples:
+
+mailbox {
+ compatible = "stericsson,db8500-mailbox";
+ reg = <0x80157000 0x1000>, <0x801B8000 0x2000>;
+ reg-names = "prcm-reg", "prcmu-tcdm";
+ interrupts = <0 47 0x4>;
+ interrupt-names = "irq";
+ legacy-offset = <0xdd4>;
+};
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index d7692a7..438ea21 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -33,6 +33,12 @@ config OMAP2PLUS_MBOX
OMAP2/3; or IPU, IVA HD and DSP in OMAP4. Say Y here if you want
to use OMAP2+ Mailbox framework support.
+config DBX500_MBOX
+ tristate "DBx500 Mailbox driver support"
+ depends on ARCH_U8500
+ help
+ Say Y here if you want to use DBx500 Mailbox driver support for
+ power coprocessor access on Ux500 and Ux540 families
config MBOX_KFIFO_SIZE
int "Mailbox kfifo default buffer size (bytes)"
@@ -44,6 +50,7 @@ config MBOX_KFIFO_SIZE
config MBOX_DATA_SIZE
int "Mailbox associated data max size (bytes)"
+ default 64 if DBX500_MBOX
default 4
help
Specify the default size of mailbox's associated data buffer
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 9ee0fcb..ee5fb1e 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -3,3 +3,4 @@ obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o
obj-$(CONFIG_MAILBOX) += mailbox.o
obj-$(CONFIG_OMAP1_MBOX) += mailbox-omap1.o
obj-$(CONFIG_OMAP2PLUS_MBOX) += mailbox-omap2.o
+obj-$(CONFIG_DBX500_MBOX) += mailbox-dbx500.o
diff --git a/drivers/mailbox/mailbox-dbx500.c b/drivers/mailbox/mailbox-dbx500.c
new file mode 100644
index 0000000..e9d2b0a
--- /dev/null
+++ b/drivers/mailbox/mailbox-dbx500.c
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Loic Pallardy <loic.pallardy@st.com> for ST-Ericsson
+ * DBX500 PRCM Mailbox driver
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/platform_data/mailbox-dbx500.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/irqdomain.h>
+
+#include "mailbox_internal.h"
+
+#define MAILBOX_LEGACY 0
+#define MAILBOX_UPAP 1
+
+enum {
+ MAILBOX_BLOCKING,
+ MAILBOX_ATOMIC,
+};
+
+/* CPU mailbox registers */
+#define PRCM_MBOX_CPU_VAL 0x0fc
+#define PRCM_MBOX_CPU_SET 0x100
+#define PRCM_MBOX_CPU_CLR 0x104
+
+#define PRCM_ARM_IT1_CLR 0x48C
+#define PRCM_ARM_IT1_VAL 0x494
+
+#define NUM_MB 8
+#define MBOX_BIT BIT
+#define ALL_MBOX_BITS (MBOX_BIT(NUM_MB) - 1)
+
+/* CPU mailbox shared memory */
+#define PRCM_MBOX_LEGACY_SIZE 0x220
+#define _PRCM_MBOX_HEADER 0x214 /* 16 bytes */
+#define PRCM_MBOX_HEADER_REQ_MB0 (_PRCM_MBOX_HEADER + 0x0)
+#define PRCM_MBOX_HEADER_REQ_MB1 (_PRCM_MBOX_HEADER + 0x1)
+#define PRCM_MBOX_HEADER_REQ_MB2 (_PRCM_MBOX_HEADER + 0x2)
+#define PRCM_MBOX_HEADER_REQ_MB3 (_PRCM_MBOX_HEADER + 0x3)
+#define PRCM_MBOX_HEADER_REQ_MB4 (_PRCM_MBOX_HEADER + 0x4)
+#define PRCM_MBOX_HEADER_REQ_MB5 (_PRCM_MBOX_HEADER + 0x5)
+#define PRCM_MBOX_HEADER_ACK_MB0 (_PRCM_MBOX_HEADER + 0x8)
+
+/* Req Mailboxes */
+#define PRCM_REQ_MB0 0x208 /* 12 bytes */
+#define PRCM_REQ_MB1 0x1FC /* 12 bytes */
+#define PRCM_REQ_MB2 0x1EC /* 16 bytes */
+#define PRCM_REQ_MB3 0x78 /* 372 bytes */
+#define PRCM_REQ_MB4 0x74 /* 4 bytes */
+#define PRCM_REQ_MB5 0x70 /* 4 bytes */
+#define PRCM_REQ_PASR 0x30 /* 4 bytes */
+
+/* Ack Mailboxes */
+#define PRCM_ACK_MB0 0x34 /* 52 bytes */
+#define PRCM_ACK_MB1 0x30 /* 4 bytes */
+#define PRCM_ACK_MB2 0x2C /* 4 bytes */
+#define PRCM_ACK_MB3 0x28 /* 4 bytes */
+#define PRCM_ACK_MB4 0x24 /* 4 bytes */
+#define PRCM_ACK_MB5 0x20 /* 4 bytes */
+
+/* Ack Mailboxe sizes */
+#define PRCM_ACK_MB0_SIZE 0x24 /* 52 bytes */
+#define PRCM_ACK_MB1_SIZE 0x4 /* 4 bytes */
+#define PRCM_ACK_MB2_SIZE 0x1 /* 1 bytes */
+#define PRCM_ACK_MB3_SIZE 0x2 /* 2 bytes */
+#define PRCM_ACK_MB4_SIZE 0x0 /* 0 bytes */
+#define PRCM_ACK_MB5_SIZE 0x4 /* 4 bytes */
+
+static void __iomem *mbox_base;
+static void __iomem *tcdm_mem_base;
+
+static u8 prcm_mbox_irq_mask; /* masked by default */
+static DEFINE_SPINLOCK(prcm_mbox_irqs_lock);
+
+struct dbx500_mbox_priv {
+ int access_mode;
+ int type;
+ int header_size;
+ unsigned int tx_header_offset;
+ unsigned int rx_header_offset;
+ unsigned int tx_offset;
+ unsigned int rx_offset;
+ unsigned int rx_size;
+ unsigned int sw_irq;
+ bool empty_flag;
+};
+
+static inline unsigned int mbox_read_reg(size_t ofs)
+{
+ return __raw_readl(mbox_base + ofs);
+}
+
+static inline void mbox_write_reg(u32 val, size_t ofs)
+{
+ __raw_writel(val, mbox_base + ofs);
+}
+
+/* Mailbox H/W preparations */
+static struct irq_chip dbx500_mbox_irq_chip;
+static struct irq_domain *dbx500_mbox_irq_domain;
+
+static int dbx500_mbox_startup(struct mailbox *mbox)
+{
+ /* Nothing to do */
+ return 0;
+}
+
+/* Mailbox IRQ handle functions */
+
+static void dbx500_mbox_enable_irq(struct mailbox *mbox, mailbox_irq_t irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&prcm_mbox_irqs_lock, flags);
+
+ prcm_mbox_irq_mask |= MBOX_BIT(mbox->id);
+
+ spin_unlock_irqrestore(&prcm_mbox_irqs_lock, flags);
+
+}
+
+static void dbx500_mbox_disable_irq(struct mailbox *mbox, mailbox_irq_t irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&prcm_mbox_irqs_lock, flags);
+
+ prcm_mbox_irq_mask &= ~MBOX_BIT(mbox->id);
+
+ spin_unlock_irqrestore(&prcm_mbox_irqs_lock, flags);
+
+}
+
+static void dbx500_mbox_ack_irq(struct mailbox *mbox, mailbox_irq_t irq)
+{
+ if (irq == IRQ_RX)
+ mbox_write_reg(MBOX_BIT(mbox->id), PRCM_ARM_IT1_CLR);
+}
+
+static int dbx500_mbox_is_irq(struct mailbox *mbox, mailbox_irq_t irq)
+{
+ struct dbx500_mbox_priv *priv = (struct dbx500_mbox_priv *)mbox->priv;
+
+ if (irq == IRQ_RX) {
+ if (mbox_read_reg(PRCM_ARM_IT1_VAL) & MBOX_BIT(mbox->id)) {
+ priv->empty_flag = true;
+ return 1;
+ } else {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+}
+
+/* message management */
+
+static int dbx500_mbox_is_ready(struct mailbox *mbox)
+{
+ return mbox_read_reg(PRCM_MBOX_CPU_VAL) & MBOX_BIT(mbox->id);
+}
+
+static int dbx500_mbox_write(struct mailbox *mbox,
+ struct mailbox_msg *msg)
+{
+ int j;
+ struct dbx500_mbox_priv *priv = (struct dbx500_mbox_priv *)mbox->priv;
+
+ if (msg->size && !msg->pdata)
+ return -EINVAL;
+
+ while (dbx500_mbox_is_ready(mbox))
+ cpu_relax();
+
+ /* write header */
+ if (priv->header_size)
+ writeb(msg->header, tcdm_mem_base + priv->tx_header_offset);
+
+ /* write data */
+ for (j = 0; j < msg->size; j++)
+ writeb(((unsigned char *)msg->pdata)[j],
+ tcdm_mem_base + priv->tx_offset + j);
+
+ /* send event */
+ mbox_write_reg(MBOX_BIT(mbox->id), PRCM_MBOX_CPU_SET);
+
+ return 0;
+}
+
+static void dbx500_mbox_read(struct mailbox *mbox, struct mailbox_msg *msg)
+{
+ struct dbx500_mbox_priv *priv = (struct dbx500_mbox_priv *)mbox->priv;
+
+ msg->header = readb(tcdm_mem_base + priv->rx_header_offset);
+ msg->pdata = (unsigned char *)(tcdm_mem_base + priv->rx_offset);
+
+ msg->size = priv->rx_size;
+ priv->empty_flag = false;
+}
+
+static int dbx500_mbox_empty(struct mailbox *mbox)
+{
+ struct dbx500_mbox_priv *priv = (struct dbx500_mbox_priv *)mbox->priv;
+ if (priv->empty_flag)
+ return 0;
+ else
+ return 1;
+}
+
+static int dbx500_mbox_poll_for_space(struct mailbox *mbox)
+{
+ return 0;
+}
+/* interrupt management */
+
+/* mask/unmask must be managed by SW */
+
+static void mbox_irq_mask(struct irq_data *d)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&prcm_mbox_irqs_lock, flags);
+
+ prcm_mbox_irq_mask &= ~MBOX_BIT(d->hwirq);
+
+ spin_unlock_irqrestore(&prcm_mbox_irqs_lock, flags);
+}
+
+static void mbox_irq_unmask(struct irq_data *d)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&prcm_mbox_irqs_lock, flags);
+
+ prcm_mbox_irq_mask |= MBOX_BIT(d->hwirq);
+
+ spin_unlock_irqrestore(&prcm_mbox_irqs_lock, flags);
+}
+
+static void mbox_irq_ack(struct irq_data *d)
+{
+ mbox_write_reg(MBOX_BIT(d->hwirq), PRCM_ARM_IT1_CLR);
+}
+
+static struct irq_chip dbx500_mbox_irq_chip = {
+ .name = "dbx500_mbox",
+ .irq_disable = mbox_irq_unmask,
+ .irq_ack = mbox_irq_ack,
+ .irq_mask = mbox_irq_mask,
+ .irq_unmask = mbox_irq_unmask,
+};
+
+static int dbx500_mbox_irq_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_chip_and_handler(irq, &dbx500_mbox_irq_chip,
+ handle_simple_irq);
+ set_irq_flags(irq, IRQF_VALID);
+
+ return 0;
+}
+
+static struct irq_domain_ops dbx500_mbox_irq_ops = {
+ .map = dbx500_mbox_irq_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static irqreturn_t dbx500_mbox_irq_handler(int irq, void *data)
+{
+ u32 bits;
+ u8 n;
+
+ bits = (mbox_read_reg(PRCM_ARM_IT1_VAL) & ALL_MBOX_BITS);
+ if (unlikely(!bits))
+ return IRQ_NONE;
+
+ bits &= prcm_mbox_irq_mask;
+
+ for (n = 0; bits; n++) {
+ if (bits & MBOX_BIT(n)) {
+ bits -= MBOX_BIT(n);
+ generic_handle_irq(
+ irq_find_mapping(dbx500_mbox_irq_domain, n));
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+/* 5 mailboxes AP <--> PRCMU */
+static struct mailbox_ops dbx500_mbox_ops = {
+ .type = MBOX_SHARED_MEM_TYPE,
+ .startup = dbx500_mbox_startup,
+ .enable_irq = dbx500_mbox_enable_irq,
+ .disable_irq = dbx500_mbox_disable_irq,
+ .ack_irq = dbx500_mbox_ack_irq,
+ .is_irq = dbx500_mbox_is_irq,
+ .read = dbx500_mbox_read,
+ .write = dbx500_mbox_write,
+ .empty = dbx500_mbox_empty,
+ .poll_for_space = dbx500_mbox_poll_for_space,
+};
+
+struct dbx500_mbox_priv mbox0_priv = {
+ .access_mode = MAILBOX_ATOMIC,
+ .type = MAILBOX_LEGACY,
+ .tx_header_offset = PRCM_MBOX_HEADER_REQ_MB0,
+ .header_size = 1,
+ .tx_offset = PRCM_REQ_MB0,
+ .rx_header_offset = PRCM_MBOX_HEADER_ACK_MB0,
+ .rx_offset = PRCM_ACK_MB0,
+ .rx_size = PRCM_ACK_MB0_SIZE,
+};
+
+struct mailbox mbox0_info = {
+ .name = "mbox0",
+ .id = 0,
+ .ops = &dbx500_mbox_ops,
+ .priv = &mbox0_priv,
+};
+
+struct dbx500_mbox_priv mbox1_priv = {
+ .access_mode = MAILBOX_BLOCKING,
+ .type = MAILBOX_LEGACY,
+ .tx_header_offset = PRCM_MBOX_HEADER_REQ_MB1,
+ .header_size = 1,
+ .tx_offset = PRCM_REQ_MB1,
+ .rx_header_offset = PRCM_MBOX_HEADER_REQ_MB1,
+ .rx_offset = PRCM_ACK_MB1,
+ .rx_size = PRCM_ACK_MB1_SIZE,
+};
+
+struct mailbox mbox1_info = {
+ .name = "mbox1",
+ .id = 1,
+ .ops = &dbx500_mbox_ops,
+ .priv = &mbox1_priv,
+};
+
+struct dbx500_mbox_priv mbox2_priv = {
+ .access_mode = MAILBOX_BLOCKING,
+ .type = MAILBOX_LEGACY,
+ .tx_header_offset = PRCM_MBOX_HEADER_REQ_MB2,
+ .header_size = 1,
+ .tx_offset = PRCM_REQ_MB2,
+ .rx_header_offset = PRCM_MBOX_HEADER_REQ_MB2,
+ .rx_offset = PRCM_ACK_MB2,
+ .rx_size = PRCM_ACK_MB2_SIZE,
+};
+
+struct mailbox mbox2_info = {
+ .name = "mbox2",
+ .id = 2,
+ .ops = &dbx500_mbox_ops,
+ .priv = &mbox2_priv,
+};
+
+struct dbx500_mbox_priv mbox3_priv = {
+ .access_mode = MAILBOX_BLOCKING,
+ .type = MAILBOX_LEGACY,
+ .tx_header_offset = PRCM_MBOX_HEADER_REQ_MB3,
+ .header_size = 1,
+ .tx_offset = PRCM_REQ_MB3,
+ .rx_header_offset = PRCM_MBOX_HEADER_REQ_MB3,
+ .rx_offset = PRCM_ACK_MB3,
+ .rx_size = PRCM_ACK_MB3_SIZE,
+};
+
+struct mailbox mbox3_info = {
+ .name = "mbox3",
+ .id = 3,
+ .ops = &dbx500_mbox_ops,
+ .priv = &mbox3_priv,
+};
+
+struct dbx500_mbox_priv mbox4_priv = {
+ .access_mode = MAILBOX_BLOCKING,
+ .type = MAILBOX_LEGACY,
+ .tx_header_offset = PRCM_MBOX_HEADER_REQ_MB4,
+ .header_size = 1,
+ .tx_offset = PRCM_REQ_MB4,
+ .rx_header_offset = PRCM_MBOX_HEADER_REQ_MB4,
+ .rx_offset = PRCM_ACK_MB4,
+ .rx_size = PRCM_ACK_MB4_SIZE,
+};
+
+struct mailbox mbox4_info = {
+ .name = "mbox4",
+ .id = 4,
+ .ops = &dbx500_mbox_ops,
+ .priv = &mbox4_priv,
+};
+
+struct dbx500_mbox_priv mbox5_priv = {
+ .access_mode = MAILBOX_BLOCKING,
+ .type = MAILBOX_LEGACY,
+ .tx_header_offset = PRCM_MBOX_HEADER_REQ_MB5,
+ .header_size = 1,
+ .tx_offset = PRCM_REQ_MB5,
+ .rx_header_offset = PRCM_MBOX_HEADER_REQ_MB5,
+ .rx_offset = PRCM_ACK_MB5,
+ .rx_size = PRCM_ACK_MB5_SIZE,
+};
+
+struct mailbox mbox5_info = {
+ .name = "mbox5",
+ .id = 5,
+ .ops = &dbx500_mbox_ops,
+ .priv = &mbox5_priv,
+};
+
+struct mailbox mbox6_info = {
+ .name = "mbox6",
+ .id = 6,
+ .ops = &dbx500_mbox_ops,
+};
+
+struct mailbox mbox7_info = {
+ .name = "mbox7",
+ .id = 7,
+ .ops = &dbx500_mbox_ops,
+};
+
+/* x540 mailbox definition */
+struct dbx500_mbox_priv mbox1_upap_priv = {
+ .access_mode = MAILBOX_BLOCKING,
+ .type = MAILBOX_UPAP,
+ .tx_header_offset = 0,
+ .header_size = 0,
+ .tx_offset = 0,
+ .rx_offset = 0, /* TODO to be replaced by dynamic detection */
+ .rx_size = 0x40,
+};
+
+struct mailbox mbox1_upap_info = {
+ .name = "mbox1_upap",
+ .id = 1,
+ .ops = &dbx500_mbox_ops,
+ .priv = &mbox1_upap_priv,
+};
+
+struct mailbox *db8500_mboxes[] = { &mbox0_info, &mbox1_info, &mbox2_info,
+ &mbox3_info, &mbox4_info, &mbox5_info, &mbox6_info, &mbox7_info,
+ NULL };
+
+struct mailbox *dbx540_mboxes[] = { &mbox0_info, &mbox2_info,
+ &mbox3_info, &mbox4_info, &mbox5_info, &mbox6_info, &mbox7_info,
+ &mbox1_upap_info, NULL };
+
+static const struct of_device_id dbx500_mailbox_match[] = {
+ { .compatible = "stericsson,db8500-mailbox",
+ .data = (void *)db8500_mboxes,
+ },
+ { .compatible = "stericsson,db9540-mailbox",
+ .data = (void *)dbx540_mboxes,
+ },
+ { /* sentinel */}
+};
+
+static int dbx500_mbox_probe(struct platform_device *pdev)
+{
+ const struct platform_device_id *platid = platform_get_device_id(pdev);
+ struct resource *mem;
+ int ret, i, irq;
+ u32 legacy_offset = 0;
+ u32 upap_offset = 0, upap_size = PRCM_MBOX_LEGACY_SIZE;
+ struct mailbox **list;
+ struct dbx500_plat_data *pdata = dev_get_platdata(&pdev->dev);
+ struct device_node *np = pdev->dev.of_node;
+ bool upap_used = false;
+
+ if (!pdata) {
+ if (np) {
+ if (of_property_read_u32(np, "legacy-offset",
+ &legacy_offset)) {
+ /* missing legacy-offset */
+ dev_err(&pdev->dev, "No legacy offset found\n");
+ return -ENODEV;
+ }
+ if (of_property_read_u32(np, "upap-offset",
+ &upap_offset)) {
+ dev_dbg(&pdev->dev, "No upap offset found\n");
+ upap_offset = -1;
+ }
+ list = (struct mailbox **)of_match_device(
+ dbx500_mailbox_match, &pdev->dev)->data;
+ if (!list) {
+ /* No mailbox configuration */
+ dev_err(&pdev->dev, "No configuration found\n");
+ return -ENODEV;
+ }
+ } else {
+ /* No mailbox configuration */
+ dev_err(&pdev->dev, "No configuration found\n");
+ return -ENODEV;
+ }
+ } else {
+ list = (struct mailbox **)platid->driver_data;
+ legacy_offset = pdata->legacy_offset;
+ upap_offset = pdata->upap_offset;
+ }
+
+ mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcm-reg");
+ mbox_base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+ if (!mbox_base) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu-tcdm");
+ tcdm_mem_base = devm_ioremap(&pdev->dev, mem->start,
+ resource_size(mem));
+ if (!tcdm_mem_base) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* check mailbox offset values */
+ if (legacy_offset >= (resource_size(mem) - PRCM_MBOX_LEGACY_SIZE)) {
+ dev_err(&pdev->dev, "Incorrect legacy offset value\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ for (i = 0; list[i]; i++) {
+ struct mailbox *mbox = list[i];
+ struct dbx500_mbox_priv *priv =
+ (struct dbx500_mbox_priv *)mbox->priv;
+ if (!priv)
+ continue;
+ if (priv->type == MAILBOX_UPAP) {
+ upap_used = true;
+ upap_size += priv->rx_size;
+ break;
+ }
+ }
+
+ if (upap_used && (upap_offset >= (resource_size(mem) - upap_size))) {
+ dev_err(&pdev->dev, "Incorrect upap offset value\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ /* clean all mailboxes */
+ mbox_write_reg(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR);
+ ret = request_irq(irq, dbx500_mbox_irq_handler,
+ IRQF_NO_SUSPEND, "db8500_mbox", NULL);
+ if (ret) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ dbx500_mbox_irq_domain = irq_domain_add_linear(
+ np, NUM_MB, &dbx500_mbox_irq_ops, NULL);
+
+ if (!dbx500_mbox_irq_domain) {
+ dev_err(&pdev->dev, "Failed to create irqdomain\n");
+ ret = -ENOSYS;
+ goto irq_free;
+ }
+
+ /*
+ * Update mailbox shared memory buffer offset according to mailbox
+ * type
+ */
+ for (i = 0; list[i]; i++) {
+ struct mailbox *mbox = list[i];
+ struct dbx500_mbox_priv *priv =
+ (struct dbx500_mbox_priv *)mbox->priv;
+ if (!priv)
+ continue;
+ mbox->irq = irq_create_mapping(dbx500_mbox_irq_domain,
+ mbox->id);
+ if (priv->type == MAILBOX_LEGACY) {
+ priv->rx_offset += legacy_offset;
+ priv->rx_header_offset += legacy_offset;
+ priv->tx_offset += legacy_offset;
+ priv->tx_header_offset += legacy_offset;
+ } else if (priv->type == MAILBOX_UPAP) {
+ priv->tx_offset += upap_offset;
+ priv->rx_offset += upap_offset;
+ }
+ }
+
+ ret = mailbox_register(&pdev->dev, list);
+irq_free:
+ if (ret)
+ free_irq(irq, NULL);
+
+out:
+ return ret;
+}
+
+static int dbx500_mbox_remove(struct platform_device *pdev)
+{
+ mailbox_unregister();
+ iounmap(mbox_base);
+ return 0;
+}
+
+static const struct platform_device_id dbx500_mbox_id[] = {
+ {
+ .name = "db8500-mailbox",
+ .driver_data = (unsigned long) db8500_mboxes,
+ }, {
+ .name = "db9540-mailbox",
+ .driver_data = (unsigned long) dbx540_mboxes,
+ }, {
+ /* sentinel */
+ }
+};
+
+static struct platform_driver dbx500_mbox_driver = {
+ .probe = dbx500_mbox_probe,
+ .remove = dbx500_mbox_remove,
+ .driver = {
+ .name = "dbx500-mailbox",
+ .of_match_table = dbx500_mailbox_match,
+ },
+ .id_table = dbx500_mbox_id,
+};
+
+static int __init dbx500_mbox_init(void)
+{
+ return platform_driver_register(&dbx500_mbox_driver);
+}
+
+static void __exit dbx500_mbox_exit(void)
+{
+ platform_driver_unregister(&dbx500_mbox_driver);
+}
+
+postcore_initcall(dbx500_mbox_init);
+module_exit(dbx500_mbox_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ST-Ericsson mailbox: dbx500 architecture specific functions");
+MODULE_AUTHOR("Loic Pallardy <loic.pallardy@st.com>");
+MODULE_ALIAS("platform:dbx500-mailbox");
diff --git a/include/linux/platform_data/mailbox-dbx500.h b/include/linux/platform_data/mailbox-dbx500.h
new file mode 100644
index 0000000..d5aebd9
--- /dev/null
+++ b/include/linux/platform_data/mailbox-dbx500.h
@@ -0,0 +1,12 @@
+/*
+ * mailbox-dbx500.h
+ *
+ * Copyright (C) ST-Ericsson SA 2012
+ * Author: <loic.pallardy@st.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+struct dbx500_plat_data {
+ unsigned int legacy_offset;
+ unsigned int upap_offset;
+};
--
1.8.1.2
WARNING: multiple messages have this Message-ID (diff)
From: s-anna@ti.com (Suman Anna)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCHv3 11/14] mailbox: create dbx500 mailbox driver
Date: Tue, 12 Mar 2013 22:24:27 -0500 [thread overview]
Message-ID: <1363145067-14715-1-git-send-email-s-anna@ti.com> (raw)
From: Loic Pallardy <loic.pallardy@st.com>
Add STEriccson DBX500 PRCM mailbox support.
Signed-off-by: Loic Pallardy <loic.pallardy@st.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
.../devicetree/bindings/mailbox/dbx500-mailbox.txt | 27 +
drivers/mailbox/Kconfig | 7 +
drivers/mailbox/Makefile | 1 +
drivers/mailbox/mailbox-dbx500.c | 648 +++++++++++++++++++++
include/linux/platform_data/mailbox-dbx500.h | 12 +
5 files changed, 695 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mailbox/dbx500-mailbox.txt
create mode 100644 drivers/mailbox/mailbox-dbx500.c
create mode 100644 include/linux/platform_data/mailbox-dbx500.h
diff --git a/Documentation/devicetree/bindings/mailbox/dbx500-mailbox.txt b/Documentation/devicetree/bindings/mailbox/dbx500-mailbox.txt
new file mode 100644
index 0000000..df0f594
--- /dev/null
+++ b/Documentation/devicetree/bindings/mailbox/dbx500-mailbox.txt
@@ -0,0 +1,27 @@
+ST-Ericsson DBx500 PRCM Mailbox Driver
+
+Required properties:
+- compatible : Should be
+ "stericsson,db8500-mailbox" for db8500 and db9500
+ "stericsson,db9540-mailbox" for db9540
+- reg : Physical base address and length of mailbox's
+ registers and shared memory
+- reg-names : Should contain the reg names "prcm-reg" and
+ "prcmu-tcdm"
+- interrupts : contains the IRQ line for the PRCM mailbox
+- interrupt-names : Should contain the interrupt name "irq"
+- legacy-offset : Memory offset in shared mem for legacy mailboxes
+
+Optional properties:
+- upap-offset : Memory offset in shared mem for upap mailboxes
+
+Examples:
+
+mailbox {
+ compatible = "stericsson,db8500-mailbox";
+ reg = <0x80157000 0x1000>, <0x801B8000 0x2000>;
+ reg-names = "prcm-reg", "prcmu-tcdm";
+ interrupts = <0 47 0x4>;
+ interrupt-names = "irq";
+ legacy-offset = <0xdd4>;
+};
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index d7692a7..438ea21 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -33,6 +33,12 @@ config OMAP2PLUS_MBOX
OMAP2/3; or IPU, IVA HD and DSP in OMAP4. Say Y here if you want
to use OMAP2+ Mailbox framework support.
+config DBX500_MBOX
+ tristate "DBx500 Mailbox driver support"
+ depends on ARCH_U8500
+ help
+ Say Y here if you want to use DBx500 Mailbox driver support for
+ power coprocessor access on Ux500 and Ux540 families
config MBOX_KFIFO_SIZE
int "Mailbox kfifo default buffer size (bytes)"
@@ -44,6 +50,7 @@ config MBOX_KFIFO_SIZE
config MBOX_DATA_SIZE
int "Mailbox associated data max size (bytes)"
+ default 64 if DBX500_MBOX
default 4
help
Specify the default size of mailbox's associated data buffer
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 9ee0fcb..ee5fb1e 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -3,3 +3,4 @@ obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o
obj-$(CONFIG_MAILBOX) += mailbox.o
obj-$(CONFIG_OMAP1_MBOX) += mailbox-omap1.o
obj-$(CONFIG_OMAP2PLUS_MBOX) += mailbox-omap2.o
+obj-$(CONFIG_DBX500_MBOX) += mailbox-dbx500.o
diff --git a/drivers/mailbox/mailbox-dbx500.c b/drivers/mailbox/mailbox-dbx500.c
new file mode 100644
index 0000000..e9d2b0a
--- /dev/null
+++ b/drivers/mailbox/mailbox-dbx500.c
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Loic Pallardy <loic.pallardy@st.com> for ST-Ericsson
+ * DBX500 PRCM Mailbox driver
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/platform_data/mailbox-dbx500.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/irqdomain.h>
+
+#include "mailbox_internal.h"
+
+#define MAILBOX_LEGACY 0
+#define MAILBOX_UPAP 1
+
+enum {
+ MAILBOX_BLOCKING,
+ MAILBOX_ATOMIC,
+};
+
+/* CPU mailbox registers */
+#define PRCM_MBOX_CPU_VAL 0x0fc
+#define PRCM_MBOX_CPU_SET 0x100
+#define PRCM_MBOX_CPU_CLR 0x104
+
+#define PRCM_ARM_IT1_CLR 0x48C
+#define PRCM_ARM_IT1_VAL 0x494
+
+#define NUM_MB 8
+#define MBOX_BIT BIT
+#define ALL_MBOX_BITS (MBOX_BIT(NUM_MB) - 1)
+
+/* CPU mailbox shared memory */
+#define PRCM_MBOX_LEGACY_SIZE 0x220
+#define _PRCM_MBOX_HEADER 0x214 /* 16 bytes */
+#define PRCM_MBOX_HEADER_REQ_MB0 (_PRCM_MBOX_HEADER + 0x0)
+#define PRCM_MBOX_HEADER_REQ_MB1 (_PRCM_MBOX_HEADER + 0x1)
+#define PRCM_MBOX_HEADER_REQ_MB2 (_PRCM_MBOX_HEADER + 0x2)
+#define PRCM_MBOX_HEADER_REQ_MB3 (_PRCM_MBOX_HEADER + 0x3)
+#define PRCM_MBOX_HEADER_REQ_MB4 (_PRCM_MBOX_HEADER + 0x4)
+#define PRCM_MBOX_HEADER_REQ_MB5 (_PRCM_MBOX_HEADER + 0x5)
+#define PRCM_MBOX_HEADER_ACK_MB0 (_PRCM_MBOX_HEADER + 0x8)
+
+/* Req Mailboxes */
+#define PRCM_REQ_MB0 0x208 /* 12 bytes */
+#define PRCM_REQ_MB1 0x1FC /* 12 bytes */
+#define PRCM_REQ_MB2 0x1EC /* 16 bytes */
+#define PRCM_REQ_MB3 0x78 /* 372 bytes */
+#define PRCM_REQ_MB4 0x74 /* 4 bytes */
+#define PRCM_REQ_MB5 0x70 /* 4 bytes */
+#define PRCM_REQ_PASR 0x30 /* 4 bytes */
+
+/* Ack Mailboxes */
+#define PRCM_ACK_MB0 0x34 /* 52 bytes */
+#define PRCM_ACK_MB1 0x30 /* 4 bytes */
+#define PRCM_ACK_MB2 0x2C /* 4 bytes */
+#define PRCM_ACK_MB3 0x28 /* 4 bytes */
+#define PRCM_ACK_MB4 0x24 /* 4 bytes */
+#define PRCM_ACK_MB5 0x20 /* 4 bytes */
+
+/* Ack Mailboxe sizes */
+#define PRCM_ACK_MB0_SIZE 0x24 /* 52 bytes */
+#define PRCM_ACK_MB1_SIZE 0x4 /* 4 bytes */
+#define PRCM_ACK_MB2_SIZE 0x1 /* 1 bytes */
+#define PRCM_ACK_MB3_SIZE 0x2 /* 2 bytes */
+#define PRCM_ACK_MB4_SIZE 0x0 /* 0 bytes */
+#define PRCM_ACK_MB5_SIZE 0x4 /* 4 bytes */
+
+static void __iomem *mbox_base;
+static void __iomem *tcdm_mem_base;
+
+static u8 prcm_mbox_irq_mask; /* masked by default */
+static DEFINE_SPINLOCK(prcm_mbox_irqs_lock);
+
+struct dbx500_mbox_priv {
+ int access_mode;
+ int type;
+ int header_size;
+ unsigned int tx_header_offset;
+ unsigned int rx_header_offset;
+ unsigned int tx_offset;
+ unsigned int rx_offset;
+ unsigned int rx_size;
+ unsigned int sw_irq;
+ bool empty_flag;
+};
+
+static inline unsigned int mbox_read_reg(size_t ofs)
+{
+ return __raw_readl(mbox_base + ofs);
+}
+
+static inline void mbox_write_reg(u32 val, size_t ofs)
+{
+ __raw_writel(val, mbox_base + ofs);
+}
+
+/* Mailbox H/W preparations */
+static struct irq_chip dbx500_mbox_irq_chip;
+static struct irq_domain *dbx500_mbox_irq_domain;
+
+static int dbx500_mbox_startup(struct mailbox *mbox)
+{
+ /* Nothing to do */
+ return 0;
+}
+
+/* Mailbox IRQ handle functions */
+
+static void dbx500_mbox_enable_irq(struct mailbox *mbox, mailbox_irq_t irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&prcm_mbox_irqs_lock, flags);
+
+ prcm_mbox_irq_mask |= MBOX_BIT(mbox->id);
+
+ spin_unlock_irqrestore(&prcm_mbox_irqs_lock, flags);
+
+}
+
+static void dbx500_mbox_disable_irq(struct mailbox *mbox, mailbox_irq_t irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&prcm_mbox_irqs_lock, flags);
+
+ prcm_mbox_irq_mask &= ~MBOX_BIT(mbox->id);
+
+ spin_unlock_irqrestore(&prcm_mbox_irqs_lock, flags);
+
+}
+
+static void dbx500_mbox_ack_irq(struct mailbox *mbox, mailbox_irq_t irq)
+{
+ if (irq == IRQ_RX)
+ mbox_write_reg(MBOX_BIT(mbox->id), PRCM_ARM_IT1_CLR);
+}
+
+static int dbx500_mbox_is_irq(struct mailbox *mbox, mailbox_irq_t irq)
+{
+ struct dbx500_mbox_priv *priv = (struct dbx500_mbox_priv *)mbox->priv;
+
+ if (irq == IRQ_RX) {
+ if (mbox_read_reg(PRCM_ARM_IT1_VAL) & MBOX_BIT(mbox->id)) {
+ priv->empty_flag = true;
+ return 1;
+ } else {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+}
+
+/* message management */
+
+static int dbx500_mbox_is_ready(struct mailbox *mbox)
+{
+ return mbox_read_reg(PRCM_MBOX_CPU_VAL) & MBOX_BIT(mbox->id);
+}
+
+static int dbx500_mbox_write(struct mailbox *mbox,
+ struct mailbox_msg *msg)
+{
+ int j;
+ struct dbx500_mbox_priv *priv = (struct dbx500_mbox_priv *)mbox->priv;
+
+ if (msg->size && !msg->pdata)
+ return -EINVAL;
+
+ while (dbx500_mbox_is_ready(mbox))
+ cpu_relax();
+
+ /* write header */
+ if (priv->header_size)
+ writeb(msg->header, tcdm_mem_base + priv->tx_header_offset);
+
+ /* write data */
+ for (j = 0; j < msg->size; j++)
+ writeb(((unsigned char *)msg->pdata)[j],
+ tcdm_mem_base + priv->tx_offset + j);
+
+ /* send event */
+ mbox_write_reg(MBOX_BIT(mbox->id), PRCM_MBOX_CPU_SET);
+
+ return 0;
+}
+
+static void dbx500_mbox_read(struct mailbox *mbox, struct mailbox_msg *msg)
+{
+ struct dbx500_mbox_priv *priv = (struct dbx500_mbox_priv *)mbox->priv;
+
+ msg->header = readb(tcdm_mem_base + priv->rx_header_offset);
+ msg->pdata = (unsigned char *)(tcdm_mem_base + priv->rx_offset);
+
+ msg->size = priv->rx_size;
+ priv->empty_flag = false;
+}
+
+static int dbx500_mbox_empty(struct mailbox *mbox)
+{
+ struct dbx500_mbox_priv *priv = (struct dbx500_mbox_priv *)mbox->priv;
+ if (priv->empty_flag)
+ return 0;
+ else
+ return 1;
+}
+
+static int dbx500_mbox_poll_for_space(struct mailbox *mbox)
+{
+ return 0;
+}
+/* interrupt management */
+
+/* mask/unmask must be managed by SW */
+
+static void mbox_irq_mask(struct irq_data *d)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&prcm_mbox_irqs_lock, flags);
+
+ prcm_mbox_irq_mask &= ~MBOX_BIT(d->hwirq);
+
+ spin_unlock_irqrestore(&prcm_mbox_irqs_lock, flags);
+}
+
+static void mbox_irq_unmask(struct irq_data *d)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&prcm_mbox_irqs_lock, flags);
+
+ prcm_mbox_irq_mask |= MBOX_BIT(d->hwirq);
+
+ spin_unlock_irqrestore(&prcm_mbox_irqs_lock, flags);
+}
+
+static void mbox_irq_ack(struct irq_data *d)
+{
+ mbox_write_reg(MBOX_BIT(d->hwirq), PRCM_ARM_IT1_CLR);
+}
+
+static struct irq_chip dbx500_mbox_irq_chip = {
+ .name = "dbx500_mbox",
+ .irq_disable = mbox_irq_unmask,
+ .irq_ack = mbox_irq_ack,
+ .irq_mask = mbox_irq_mask,
+ .irq_unmask = mbox_irq_unmask,
+};
+
+static int dbx500_mbox_irq_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_chip_and_handler(irq, &dbx500_mbox_irq_chip,
+ handle_simple_irq);
+ set_irq_flags(irq, IRQF_VALID);
+
+ return 0;
+}
+
+static struct irq_domain_ops dbx500_mbox_irq_ops = {
+ .map = dbx500_mbox_irq_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static irqreturn_t dbx500_mbox_irq_handler(int irq, void *data)
+{
+ u32 bits;
+ u8 n;
+
+ bits = (mbox_read_reg(PRCM_ARM_IT1_VAL) & ALL_MBOX_BITS);
+ if (unlikely(!bits))
+ return IRQ_NONE;
+
+ bits &= prcm_mbox_irq_mask;
+
+ for (n = 0; bits; n++) {
+ if (bits & MBOX_BIT(n)) {
+ bits -= MBOX_BIT(n);
+ generic_handle_irq(
+ irq_find_mapping(dbx500_mbox_irq_domain, n));
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+/* 5 mailboxes AP <--> PRCMU */
+static struct mailbox_ops dbx500_mbox_ops = {
+ .type = MBOX_SHARED_MEM_TYPE,
+ .startup = dbx500_mbox_startup,
+ .enable_irq = dbx500_mbox_enable_irq,
+ .disable_irq = dbx500_mbox_disable_irq,
+ .ack_irq = dbx500_mbox_ack_irq,
+ .is_irq = dbx500_mbox_is_irq,
+ .read = dbx500_mbox_read,
+ .write = dbx500_mbox_write,
+ .empty = dbx500_mbox_empty,
+ .poll_for_space = dbx500_mbox_poll_for_space,
+};
+
+struct dbx500_mbox_priv mbox0_priv = {
+ .access_mode = MAILBOX_ATOMIC,
+ .type = MAILBOX_LEGACY,
+ .tx_header_offset = PRCM_MBOX_HEADER_REQ_MB0,
+ .header_size = 1,
+ .tx_offset = PRCM_REQ_MB0,
+ .rx_header_offset = PRCM_MBOX_HEADER_ACK_MB0,
+ .rx_offset = PRCM_ACK_MB0,
+ .rx_size = PRCM_ACK_MB0_SIZE,
+};
+
+struct mailbox mbox0_info = {
+ .name = "mbox0",
+ .id = 0,
+ .ops = &dbx500_mbox_ops,
+ .priv = &mbox0_priv,
+};
+
+struct dbx500_mbox_priv mbox1_priv = {
+ .access_mode = MAILBOX_BLOCKING,
+ .type = MAILBOX_LEGACY,
+ .tx_header_offset = PRCM_MBOX_HEADER_REQ_MB1,
+ .header_size = 1,
+ .tx_offset = PRCM_REQ_MB1,
+ .rx_header_offset = PRCM_MBOX_HEADER_REQ_MB1,
+ .rx_offset = PRCM_ACK_MB1,
+ .rx_size = PRCM_ACK_MB1_SIZE,
+};
+
+struct mailbox mbox1_info = {
+ .name = "mbox1",
+ .id = 1,
+ .ops = &dbx500_mbox_ops,
+ .priv = &mbox1_priv,
+};
+
+struct dbx500_mbox_priv mbox2_priv = {
+ .access_mode = MAILBOX_BLOCKING,
+ .type = MAILBOX_LEGACY,
+ .tx_header_offset = PRCM_MBOX_HEADER_REQ_MB2,
+ .header_size = 1,
+ .tx_offset = PRCM_REQ_MB2,
+ .rx_header_offset = PRCM_MBOX_HEADER_REQ_MB2,
+ .rx_offset = PRCM_ACK_MB2,
+ .rx_size = PRCM_ACK_MB2_SIZE,
+};
+
+struct mailbox mbox2_info = {
+ .name = "mbox2",
+ .id = 2,
+ .ops = &dbx500_mbox_ops,
+ .priv = &mbox2_priv,
+};
+
+struct dbx500_mbox_priv mbox3_priv = {
+ .access_mode = MAILBOX_BLOCKING,
+ .type = MAILBOX_LEGACY,
+ .tx_header_offset = PRCM_MBOX_HEADER_REQ_MB3,
+ .header_size = 1,
+ .tx_offset = PRCM_REQ_MB3,
+ .rx_header_offset = PRCM_MBOX_HEADER_REQ_MB3,
+ .rx_offset = PRCM_ACK_MB3,
+ .rx_size = PRCM_ACK_MB3_SIZE,
+};
+
+struct mailbox mbox3_info = {
+ .name = "mbox3",
+ .id = 3,
+ .ops = &dbx500_mbox_ops,
+ .priv = &mbox3_priv,
+};
+
+struct dbx500_mbox_priv mbox4_priv = {
+ .access_mode = MAILBOX_BLOCKING,
+ .type = MAILBOX_LEGACY,
+ .tx_header_offset = PRCM_MBOX_HEADER_REQ_MB4,
+ .header_size = 1,
+ .tx_offset = PRCM_REQ_MB4,
+ .rx_header_offset = PRCM_MBOX_HEADER_REQ_MB4,
+ .rx_offset = PRCM_ACK_MB4,
+ .rx_size = PRCM_ACK_MB4_SIZE,
+};
+
+struct mailbox mbox4_info = {
+ .name = "mbox4",
+ .id = 4,
+ .ops = &dbx500_mbox_ops,
+ .priv = &mbox4_priv,
+};
+
+struct dbx500_mbox_priv mbox5_priv = {
+ .access_mode = MAILBOX_BLOCKING,
+ .type = MAILBOX_LEGACY,
+ .tx_header_offset = PRCM_MBOX_HEADER_REQ_MB5,
+ .header_size = 1,
+ .tx_offset = PRCM_REQ_MB5,
+ .rx_header_offset = PRCM_MBOX_HEADER_REQ_MB5,
+ .rx_offset = PRCM_ACK_MB5,
+ .rx_size = PRCM_ACK_MB5_SIZE,
+};
+
+struct mailbox mbox5_info = {
+ .name = "mbox5",
+ .id = 5,
+ .ops = &dbx500_mbox_ops,
+ .priv = &mbox5_priv,
+};
+
+struct mailbox mbox6_info = {
+ .name = "mbox6",
+ .id = 6,
+ .ops = &dbx500_mbox_ops,
+};
+
+struct mailbox mbox7_info = {
+ .name = "mbox7",
+ .id = 7,
+ .ops = &dbx500_mbox_ops,
+};
+
+/* x540 mailbox definition */
+struct dbx500_mbox_priv mbox1_upap_priv = {
+ .access_mode = MAILBOX_BLOCKING,
+ .type = MAILBOX_UPAP,
+ .tx_header_offset = 0,
+ .header_size = 0,
+ .tx_offset = 0,
+ .rx_offset = 0, /* TODO to be replaced by dynamic detection */
+ .rx_size = 0x40,
+};
+
+struct mailbox mbox1_upap_info = {
+ .name = "mbox1_upap",
+ .id = 1,
+ .ops = &dbx500_mbox_ops,
+ .priv = &mbox1_upap_priv,
+};
+
+struct mailbox *db8500_mboxes[] = { &mbox0_info, &mbox1_info, &mbox2_info,
+ &mbox3_info, &mbox4_info, &mbox5_info, &mbox6_info, &mbox7_info,
+ NULL };
+
+struct mailbox *dbx540_mboxes[] = { &mbox0_info, &mbox2_info,
+ &mbox3_info, &mbox4_info, &mbox5_info, &mbox6_info, &mbox7_info,
+ &mbox1_upap_info, NULL };
+
+static const struct of_device_id dbx500_mailbox_match[] = {
+ { .compatible = "stericsson,db8500-mailbox",
+ .data = (void *)db8500_mboxes,
+ },
+ { .compatible = "stericsson,db9540-mailbox",
+ .data = (void *)dbx540_mboxes,
+ },
+ { /* sentinel */}
+};
+
+static int dbx500_mbox_probe(struct platform_device *pdev)
+{
+ const struct platform_device_id *platid = platform_get_device_id(pdev);
+ struct resource *mem;
+ int ret, i, irq;
+ u32 legacy_offset = 0;
+ u32 upap_offset = 0, upap_size = PRCM_MBOX_LEGACY_SIZE;
+ struct mailbox **list;
+ struct dbx500_plat_data *pdata = dev_get_platdata(&pdev->dev);
+ struct device_node *np = pdev->dev.of_node;
+ bool upap_used = false;
+
+ if (!pdata) {
+ if (np) {
+ if (of_property_read_u32(np, "legacy-offset",
+ &legacy_offset)) {
+ /* missing legacy-offset */
+ dev_err(&pdev->dev, "No legacy offset found\n");
+ return -ENODEV;
+ }
+ if (of_property_read_u32(np, "upap-offset",
+ &upap_offset)) {
+ dev_dbg(&pdev->dev, "No upap offset found\n");
+ upap_offset = -1;
+ }
+ list = (struct mailbox **)of_match_device(
+ dbx500_mailbox_match, &pdev->dev)->data;
+ if (!list) {
+ /* No mailbox configuration */
+ dev_err(&pdev->dev, "No configuration found\n");
+ return -ENODEV;
+ }
+ } else {
+ /* No mailbox configuration */
+ dev_err(&pdev->dev, "No configuration found\n");
+ return -ENODEV;
+ }
+ } else {
+ list = (struct mailbox **)platid->driver_data;
+ legacy_offset = pdata->legacy_offset;
+ upap_offset = pdata->upap_offset;
+ }
+
+ mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcm-reg");
+ mbox_base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+ if (!mbox_base) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu-tcdm");
+ tcdm_mem_base = devm_ioremap(&pdev->dev, mem->start,
+ resource_size(mem));
+ if (!tcdm_mem_base) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* check mailbox offset values */
+ if (legacy_offset >= (resource_size(mem) - PRCM_MBOX_LEGACY_SIZE)) {
+ dev_err(&pdev->dev, "Incorrect legacy offset value\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ for (i = 0; list[i]; i++) {
+ struct mailbox *mbox = list[i];
+ struct dbx500_mbox_priv *priv =
+ (struct dbx500_mbox_priv *)mbox->priv;
+ if (!priv)
+ continue;
+ if (priv->type == MAILBOX_UPAP) {
+ upap_used = true;
+ upap_size += priv->rx_size;
+ break;
+ }
+ }
+
+ if (upap_used && (upap_offset >= (resource_size(mem) - upap_size))) {
+ dev_err(&pdev->dev, "Incorrect upap offset value\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ /* clean all mailboxes */
+ mbox_write_reg(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR);
+ ret = request_irq(irq, dbx500_mbox_irq_handler,
+ IRQF_NO_SUSPEND, "db8500_mbox", NULL);
+ if (ret) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ dbx500_mbox_irq_domain = irq_domain_add_linear(
+ np, NUM_MB, &dbx500_mbox_irq_ops, NULL);
+
+ if (!dbx500_mbox_irq_domain) {
+ dev_err(&pdev->dev, "Failed to create irqdomain\n");
+ ret = -ENOSYS;
+ goto irq_free;
+ }
+
+ /*
+ * Update mailbox shared memory buffer offset according to mailbox
+ * type
+ */
+ for (i = 0; list[i]; i++) {
+ struct mailbox *mbox = list[i];
+ struct dbx500_mbox_priv *priv =
+ (struct dbx500_mbox_priv *)mbox->priv;
+ if (!priv)
+ continue;
+ mbox->irq = irq_create_mapping(dbx500_mbox_irq_domain,
+ mbox->id);
+ if (priv->type == MAILBOX_LEGACY) {
+ priv->rx_offset += legacy_offset;
+ priv->rx_header_offset += legacy_offset;
+ priv->tx_offset += legacy_offset;
+ priv->tx_header_offset += legacy_offset;
+ } else if (priv->type == MAILBOX_UPAP) {
+ priv->tx_offset += upap_offset;
+ priv->rx_offset += upap_offset;
+ }
+ }
+
+ ret = mailbox_register(&pdev->dev, list);
+irq_free:
+ if (ret)
+ free_irq(irq, NULL);
+
+out:
+ return ret;
+}
+
+static int dbx500_mbox_remove(struct platform_device *pdev)
+{
+ mailbox_unregister();
+ iounmap(mbox_base);
+ return 0;
+}
+
+static const struct platform_device_id dbx500_mbox_id[] = {
+ {
+ .name = "db8500-mailbox",
+ .driver_data = (unsigned long) db8500_mboxes,
+ }, {
+ .name = "db9540-mailbox",
+ .driver_data = (unsigned long) dbx540_mboxes,
+ }, {
+ /* sentinel */
+ }
+};
+
+static struct platform_driver dbx500_mbox_driver = {
+ .probe = dbx500_mbox_probe,
+ .remove = dbx500_mbox_remove,
+ .driver = {
+ .name = "dbx500-mailbox",
+ .of_match_table = dbx500_mailbox_match,
+ },
+ .id_table = dbx500_mbox_id,
+};
+
+static int __init dbx500_mbox_init(void)
+{
+ return platform_driver_register(&dbx500_mbox_driver);
+}
+
+static void __exit dbx500_mbox_exit(void)
+{
+ platform_driver_unregister(&dbx500_mbox_driver);
+}
+
+postcore_initcall(dbx500_mbox_init);
+module_exit(dbx500_mbox_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ST-Ericsson mailbox: dbx500 architecture specific functions");
+MODULE_AUTHOR("Loic Pallardy <loic.pallardy@st.com>");
+MODULE_ALIAS("platform:dbx500-mailbox");
diff --git a/include/linux/platform_data/mailbox-dbx500.h b/include/linux/platform_data/mailbox-dbx500.h
new file mode 100644
index 0000000..d5aebd9
--- /dev/null
+++ b/include/linux/platform_data/mailbox-dbx500.h
@@ -0,0 +1,12 @@
+/*
+ * mailbox-dbx500.h
+ *
+ * Copyright (C) ST-Ericsson SA 2012
+ * Author: <loic.pallardy@st.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+struct dbx500_plat_data {
+ unsigned int legacy_offset;
+ unsigned int upap_offset;
+};
--
1.8.1.2
next reply other threads:[~2013-03-13 3:29 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-03-13 3:24 Suman Anna [this message]
2013-03-13 3:24 ` [PATCHv3 11/14] mailbox: create dbx500 mailbox driver Suman Anna
2013-03-13 3:24 ` Suman Anna
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1363145067-14715-1-git-send-email-s-anna@ti.com \
--to=s-anna@ti.com \
--cc=arnd@arndb.de \
--cc=gregkh@linuxfoundation.org \
--cc=linus.walleij@linaro.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-omap@vger.kernel.org \
--cc=linux@arm.linux.org.uk \
--cc=loic.pallardy@st.com \
--cc=ohad@wizery.com \
--cc=omar.ramirez@copitl.com \
--cc=tony@atomide.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.