* Re: [Patch v5 1/2] dmaengine: add Qualcomm BAM dma driver
From: Stephen Boyd @ 2014-02-08 2:42 UTC (permalink / raw)
To: Andy Gross
Cc: devicetree, Vinod Koul, linux-arm-msm, linux-kernel, dmaengine,
Dan Williams, linux-arm-kernel
In-Reply-To: <1391546556-27702-2-git-send-email-agross@codeaurora.org>
On 02/04, Andy Gross wrote:
> diff --git a/drivers/dma/qcom_bam_dma.c b/drivers/dma/qcom_bam_dma.c
> new file mode 100644
> index 0000000..214250c
> --- /dev/null
> +++ b/drivers/dma/qcom_bam_dma.c
> @@ -0,0 +1,1066 @@
> +/*
> + * QCOM BAM DMA engine driver
Can you please move this down into the comment below?
> + *
> + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * 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.
> + *
And split this out into its own comment block? I think this will
make the lawyers happier.
> + *
> + * QCOM BAM DMA blocks are distributed amongst a number of the on-chip
> + * peripherals on the MSM 8x74. The configuration of the channels are dependent
> + * on the way they are hard wired to that specific peripheral. The peripheral
> + * device tree entries specify the configuration of each channel.
> + *
> + * The DMA controller requires the use of external memory for storage of the
> + * hardware descriptors for each channel. The descriptor FIFO is accessed as a
> + * circular buffer and operations are managed according to the offset within the
> + * FIFO. After pipe/channel reset, all of the pipe registers and internal state
> + * are back to defaults.
> + *
> + * During DMA operations, we write descriptors to the FIFO, being careful to
> + * handle wrapping and then write the last FIFO offset to that channel's
> + * P_EVNT_REG register to kick off the transaction. The P_SW_OFSTS register
> + * indicates the current FIFO offset that is being processed, so there is some
> + * indication of where the hardware is currently working.
> + */
[...]
> +
> +/* PIPE CTRL */
> +#define P_EN BIT(1)
^^^^
Nitpick: Weird tab here?
> +
> +/**
> + * bam_start_dma - start next transaction
> + * @bchan - bam dma channel
> + *
> + * Note: must hold bam dma channel vc.lock
You can use lockdep_assert_held() here to document this
requirement and test for it at runtime.
> + */
> +static void bam_start_dma(struct bam_chan *bchan)
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
^ permalink raw reply
* [RFC 6/6] mailbox: omap2+: move to common mbox framework
From: Courtney Cavin @ 2014-02-08 0:50 UTC (permalink / raw)
To: s-anna, rob.herring, rafael.j.wysocki, mark.langsdorf, tony,
omar.ramirez
Cc: gregkh, pawel.moll, mark.rutland, ijc+devicetree, galak, rob,
linux-doc, devicetree, linux-kernel
In-Reply-To: <1391820619-25487-1-git-send-email-courtney.cavin@sonymobile.com>
Signed-off-by: Courtney Cavin <courtney.cavin@sonymobile.com>
---
drivers/mailbox/Kconfig | 1 -
drivers/mailbox/mailbox-omap2.c | 315 +++++++++++++++++-----------------------
2 files changed, 132 insertions(+), 184 deletions(-)
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index ae6b09b..a592a5a 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -27,7 +27,6 @@ config OMAP1_MBOX
config OMAP2PLUS_MBOX
tristate "OMAP2+ Mailbox framework support"
depends on ARCH_OMAP2PLUS
- depends on BROKEN
help
Mailbox implementation for OMAP family chips with hardware for
interprocessor communication involving DSP, IVA1.0 and IVA2 in
diff --git a/drivers/mailbox/mailbox-omap2.c b/drivers/mailbox/mailbox-omap2.c
index 42d2b89..7ddde19 100644
--- a/drivers/mailbox/mailbox-omap2.c
+++ b/drivers/mailbox/mailbox-omap2.c
@@ -18,8 +18,8 @@
#include <linux/io.h>
#include <linux/pm_runtime.h>
#include <linux/platform_data/mailbox-omap.h>
-
-#include "omap-mbox.h"
+#include <linux/interrupt.h>
+#include <linux/mbox.h>
#define MAILBOX_REVISION 0x000
#define MAILBOX_MESSAGE(m) (0x040 + 4 * (m))
@@ -42,192 +42,165 @@
#define MBOX_NR_REGS (MBOX_REG_SIZE / sizeof(u32))
#define OMAP4_MBOX_NR_REGS (OMAP4_MBOX_REG_SIZE / sizeof(u32))
-static void __iomem *mbox_base;
-
struct omap_mbox2_fifo {
unsigned long msg;
unsigned long fifo_stat;
unsigned long msg_stat;
};
+struct omap2_mbox;
+
struct omap_mbox2_priv {
+ struct omap2_mbox *mbox;
+ int irq;
+
struct omap_mbox2_fifo tx_fifo;
struct omap_mbox2_fifo rx_fifo;
unsigned long irqenable;
unsigned long irqstatus;
u32 newmsg_bit;
u32 notfull_bit;
- u32 ctx[OMAP4_MBOX_NR_REGS];
unsigned long irqdisable;
u32 intr_type;
};
-static inline unsigned int mbox_read_reg(size_t ofs)
-{
- return __raw_readl(mbox_base + ofs);
-}
+struct omap2_mbox {
+ struct mbox_adapter adapter;
+ struct completion completion;
+ void __iomem *base;
+ atomic_t active;
+ struct omap_mbox2_priv *priv;
+};
-static inline void mbox_write_reg(u32 val, size_t ofs)
+static inline unsigned int mbox_read_reg(void __iomem *base, size_t ofs)
{
- __raw_writel(val, mbox_base + ofs);
+ return __raw_readl(base + ofs);
}
-/* Mailbox H/W preparations */
-static int omap2_mbox_startup(struct omap_mbox *mbox)
+static inline void mbox_write_reg(void __iomem *base, u32 val, size_t ofs)
{
- u32 l;
-
- pm_runtime_enable(mbox->dev->parent);
- pm_runtime_get_sync(mbox->dev->parent);
-
- l = mbox_read_reg(MAILBOX_REVISION);
- pr_debug("omap mailbox rev %d.%d\n", (l & 0xf0) >> 4, (l & 0x0f));
-
- return 0;
+ __raw_writel(val, base + ofs);
}
-static void omap2_mbox_shutdown(struct omap_mbox *mbox)
+static int omap2_mbox_request(struct mbox_adapter *adap,
+ struct mbox_channel *chan)
{
- pm_runtime_put_sync(mbox->dev->parent);
- pm_runtime_disable(mbox->dev->parent);
-}
+ struct omap_mbox2_priv *p;
+ struct omap2_mbox *mbox;
+ u32 enable;
-/* Mailbox FIFO handle functions */
-static mbox_msg_t omap2_mbox_fifo_read(struct omap_mbox *mbox)
-{
- struct omap_mbox2_fifo *fifo =
- &((struct omap_mbox2_priv *)mbox->priv)->rx_fifo;
- return (mbox_msg_t) mbox_read_reg(fifo->msg);
-}
+ mbox = container_of(adap, struct omap2_mbox, adapter);
+ p = &mbox->priv[chan->id];
-static void omap2_mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg)
-{
- struct omap_mbox2_fifo *fifo =
- &((struct omap_mbox2_priv *)mbox->priv)->tx_fifo;
- mbox_write_reg(msg, fifo->msg);
-}
+ if (atomic_inc_return(&mbox->active) == 1) {
+ pm_runtime_enable(adap->dev);
+ pm_runtime_get_sync(adap->dev);
+ }
-static int omap2_mbox_fifo_empty(struct omap_mbox *mbox)
-{
- struct omap_mbox2_fifo *fifo =
- &((struct omap_mbox2_priv *)mbox->priv)->rx_fifo;
- return (mbox_read_reg(fifo->msg_stat) == 0);
-}
+ enable = mbox_read_reg(mbox->base, p->irqenable);
+ enable |= p->notfull_bit | p->newmsg_bit;
+ mbox_write_reg(mbox->base, enable, p->irqenable);
-static int omap2_mbox_fifo_full(struct omap_mbox *mbox)
-{
- struct omap_mbox2_fifo *fifo =
- &((struct omap_mbox2_priv *)mbox->priv)->tx_fifo;
- return mbox_read_reg(fifo->fifo_stat);
+ return 0;
}
-/* Mailbox IRQ handle functions */
-static void omap2_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
+static int omap2_mbox_release(struct mbox_adapter *adap,
+ struct mbox_channel *chan)
{
- struct omap_mbox2_priv *p = mbox->priv;
- u32 l, bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit;
+ struct omap_mbox2_priv *p;
+ struct omap2_mbox *mbox;
+ u32 disable;
- l = mbox_read_reg(p->irqenable);
- l |= bit;
- mbox_write_reg(l, p->irqenable);
-}
+ mbox = container_of(adap, struct omap2_mbox, adapter);
+ p = &mbox->priv[chan->id];
-static void omap2_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
-{
- struct omap_mbox2_priv *p = mbox->priv;
- u32 bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit;
+ disable = p->notfull_bit | p->newmsg_bit;
/*
* Read and update the interrupt configuration register for pre-OMAP4.
* OMAP4 and later SoCs have a dedicated interrupt disabling register.
*/
if (!p->intr_type)
- bit = mbox_read_reg(p->irqdisable) & ~bit;
+ disable = mbox_read_reg(mbox->base, p->irqdisable) & ~disable;
+ mbox_write_reg(mbox->base, disable, p->irqdisable);
- mbox_write_reg(bit, p->irqdisable);
+ if (atomic_dec_return(&mbox->active) == 0) {
+ pm_runtime_put_sync(adap->dev);
+ pm_runtime_disable(adap->dev);
+ }
+ return 0;
}
-static void omap2_mbox_ack_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
+static int omap2_mbox_put_message(struct mbox_adapter *adap,
+ struct mbox_channel *chan, const void *data, unsigned int len)
{
- struct omap_mbox2_priv *p = mbox->priv;
- u32 bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit;
+ struct omap_mbox2_fifo *fifo;
+ struct omap2_mbox *mbox;
+ int ret;
+ u32 msg;
- mbox_write_reg(bit, p->irqstatus);
+ if (len != sizeof(msg))
+ return -EINVAL;
- /* Flush posted write for irq status to avoid spurious interrupts */
- mbox_read_reg(p->irqstatus);
-}
+ msg = ((u32 *)data)[0];
+ mbox = container_of(adap, struct omap2_mbox, adapter);
+ fifo = &mbox->priv[chan->id].tx_fifo;
-static int omap2_mbox_is_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
-{
- struct omap_mbox2_priv *p = mbox->priv;
- u32 bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit;
- u32 enable = mbox_read_reg(p->irqenable);
- u32 status = mbox_read_reg(p->irqstatus);
+ while (mbox_read_reg(mbox->base, fifo->fifo_stat)) {
+ ret = wait_for_completion_timeout(&mbox->completion,
+ msecs_to_jiffies(2));
+ if (!ret)
+ return -ETIMEDOUT;
+ }
- return (int)(enable & status & bit);
-}
+ mbox_write_reg(mbox->base, msg, fifo->msg);
-static void omap2_mbox_save_ctx(struct omap_mbox *mbox)
-{
- int i;
- struct omap_mbox2_priv *p = mbox->priv;
- int nr_regs;
-
- if (p->intr_type)
- nr_regs = OMAP4_MBOX_NR_REGS;
- else
- nr_regs = MBOX_NR_REGS;
- for (i = 0; i < nr_regs; i++) {
- p->ctx[i] = mbox_read_reg(i * sizeof(u32));
-
- dev_dbg(mbox->dev, "%s: [%02x] %08x\n", __func__,
- i, p->ctx[i]);
- }
+ return 0;
}
-static void omap2_mbox_restore_ctx(struct omap_mbox *mbox)
+static irqreturn_t omap2_mbox_irq(int irq, void *dev)
{
- int i;
- struct omap_mbox2_priv *p = mbox->priv;
- int nr_regs;
-
- if (p->intr_type)
- nr_regs = OMAP4_MBOX_NR_REGS;
- else
- nr_regs = MBOX_NR_REGS;
- for (i = 0; i < nr_regs; i++) {
- mbox_write_reg(p->ctx[i], i * sizeof(u32));
-
- dev_dbg(mbox->dev, "%s: [%02x] %08x\n", __func__,
- i, p->ctx[i]);
+ struct mbox_channel *chan;
+ struct omap_mbox2_priv *p = dev;
+ struct omap2_mbox *mbox;
+ u32 status;
+
+ mbox = p->mbox;
+ status = mbox_read_reg(mbox->base, p->irqstatus);
+ status &= mbox_read_reg(mbox->base, p->irqenable);
+
+ chan = &mbox->adapter.channels[p - mbox->priv];
+
+ if (status & p->notfull_bit) {
+ complete(&mbox->completion);
+ mbox_write_reg(mbox->base, p->newmsg_bit, p->notfull_bit);
+ } else if (status & p->newmsg_bit) {
+ u32 msg = mbox_read_reg(mbox->base, p->rx_fifo.msg);
+ mbox_channel_notify(chan, &msg, sizeof(msg));
+ mbox_write_reg(mbox->base, p->newmsg_bit, p->irqstatus);
}
+
+ /* Flush posted write for irq status to avoid spurious interrupts */
+ mbox_read_reg(mbox->base, p->irqstatus);
+
+ return IRQ_HANDLED;
}
-static struct omap_mbox_ops omap2_mbox_ops = {
- .type = OMAP_MBOX_TYPE2,
- .startup = omap2_mbox_startup,
- .shutdown = omap2_mbox_shutdown,
- .fifo_read = omap2_mbox_fifo_read,
- .fifo_write = omap2_mbox_fifo_write,
- .fifo_empty = omap2_mbox_fifo_empty,
- .fifo_full = omap2_mbox_fifo_full,
- .enable_irq = omap2_mbox_enable_irq,
- .disable_irq = omap2_mbox_disable_irq,
- .ack_irq = omap2_mbox_ack_irq,
- .is_irq = omap2_mbox_is_irq,
- .save_ctx = omap2_mbox_save_ctx,
- .restore_ctx = omap2_mbox_restore_ctx,
+static const struct mbox_adapter_ops omap2_mbox_ops = {
+ .owner = THIS_MODULE,
+ .request = omap2_mbox_request,
+ .release = omap2_mbox_release,
+ .put_message = omap2_mbox_put_message,
};
static int omap2_mbox_probe(struct platform_device *pdev)
{
- struct resource *mem;
- int ret;
- struct omap_mbox **list, *mbox, *mboxblk;
- struct omap_mbox2_priv *priv, *privblk;
struct omap_mbox_pdata *pdata = pdev->dev.platform_data;
+ struct omap_mbox2_priv *priv, *privblk;
struct omap_mbox_dev_info *info;
+ struct omap2_mbox *mbox;
+ struct resource *mem;
+ int ret;
int i;
if (!pdata || !pdata->info_cnt || !pdata->info) {
@@ -235,25 +208,22 @@ static int omap2_mbox_probe(struct platform_device *pdev)
return -ENODEV;
}
- /* allocate one extra for marking end of list */
- list = kzalloc((pdata->info_cnt + 1) * sizeof(*list), GFP_KERNEL);
- if (!list)
+ mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox), GFP_KERNEL);
+ if (!mbox)
return -ENOMEM;
- mboxblk = mbox = kzalloc(pdata->info_cnt * sizeof(*mbox), GFP_KERNEL);
- if (!mboxblk) {
- ret = -ENOMEM;
- goto free_list;
- }
+ atomic_set(&mbox->active, 0);
+ init_completion(&mbox->completion);
- privblk = priv = kzalloc(pdata->info_cnt * sizeof(*priv), GFP_KERNEL);
- if (!privblk) {
- ret = -ENOMEM;
- goto free_mboxblk;
- }
+ privblk = devm_kzalloc(&pdev->dev,
+ pdata->info_cnt * sizeof(*priv), GFP_KERNEL);
+ if (!privblk)
+ return -ENOMEM;
+ priv = privblk;
info = pdata->info;
for (i = 0; i < pdata->info_cnt; i++, info++, priv++) {
+ priv->mbox = mbox;
priv->tx_fifo.msg = MAILBOX_MESSAGE(info->tx_id);
priv->tx_fifo.fifo_stat = MAILBOX_FIFOSTATUS(info->tx_id);
priv->rx_fifo.msg = MAILBOX_MESSAGE(info->rx_id);
@@ -272,61 +242,40 @@ static int omap2_mbox_probe(struct platform_device *pdev)
}
priv->intr_type = pdata->intr_type;
- mbox->priv = priv;
- mbox->name = info->name;
- mbox->ops = &omap2_mbox_ops;
- mbox->irq = platform_get_irq(pdev, info->irq_id);
- if (mbox->irq < 0) {
- ret = mbox->irq;
- goto free_privblk;
- }
- list[i] = mbox++;
+ priv->irq = platform_get_irq(pdev, info->irq_id);
+ if (priv->irq < 0)
+ return priv->irq;
+
+ ret = devm_request_irq(&pdev->dev, priv->irq,
+ omap2_mbox_irq, IRQF_SHARED, info->name, priv);
+ if (ret < 0)
+ return ret;
}
+ mbox->priv = privblk;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem) {
- ret = -ENOENT;
- goto free_privblk;
- }
+ if (!mem)
+ return -ENOENT;
- mbox_base = ioremap(mem->start, resource_size(mem));
- if (!mbox_base) {
- ret = -ENOMEM;
- goto free_privblk;
- }
+ mbox->base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+ if (!mbox->base)
+ return -ENOMEM;
- ret = omap_mbox_register(&pdev->dev, list);
+ mbox->adapter.nchannels = pdata->info_cnt;
+ mbox->adapter.ops = &omap2_mbox_ops;
+ mbox->adapter.dev = &pdev->dev;
+ ret = mbox_adapter_add(&mbox->adapter);
if (ret)
- goto unmap_mbox;
- platform_set_drvdata(pdev, list);
+ return ret;
+ platform_set_drvdata(pdev, mbox);
return 0;
-
-unmap_mbox:
- iounmap(mbox_base);
-free_privblk:
- kfree(privblk);
-free_mboxblk:
- kfree(mboxblk);
-free_list:
- kfree(list);
- return ret;
}
static int omap2_mbox_remove(struct platform_device *pdev)
{
- struct omap_mbox2_priv *privblk;
- struct omap_mbox **list = platform_get_drvdata(pdev);
- struct omap_mbox *mboxblk = list[0];
-
- privblk = mboxblk->priv;
- omap_mbox_unregister();
- iounmap(mbox_base);
- kfree(privblk);
- kfree(mboxblk);
- kfree(list);
-
- return 0;
+ struct omap2_mbox *mbox = platform_get_drvdata(pdev);
+ return mbox_adapter_remove(&mbox->adapter);
}
static struct platform_driver omap2_mbox_driver = {
--
1.8.1.5
^ permalink raw reply related
* [RFC 5/6] mailbox: omap1: move to common mbox framework
From: Courtney Cavin @ 2014-02-08 0:50 UTC (permalink / raw)
To: s-anna, rob.herring, rafael.j.wysocki, mark.langsdorf, tony,
omar.ramirez
Cc: gregkh, pawel.moll, mark.rutland, ijc+devicetree, galak, rob,
linux-doc, devicetree, linux-kernel
In-Reply-To: <1391820619-25487-1-git-send-email-courtney.cavin@sonymobile.com>
Signed-off-by: Courtney Cavin <courtney.cavin@sonymobile.com>
---
drivers/mailbox/Kconfig | 1 -
drivers/mailbox/mailbox-omap1.c | 153 +++++++++++++++++++---------------------
2 files changed, 73 insertions(+), 81 deletions(-)
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index 6befc6e..ae6b09b 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -19,7 +19,6 @@ config PL320_MBOX
config OMAP1_MBOX
tristate "OMAP1 Mailbox framework support"
depends on ARCH_OMAP1
- depends on BROKEN
help
Mailbox implementation for OMAP chips with hardware for
interprocessor communication involving DSP in OMAP1. Say Y here
diff --git a/drivers/mailbox/mailbox-omap1.c b/drivers/mailbox/mailbox-omap1.c
index 9001b76..474890d 100644
--- a/drivers/mailbox/mailbox-omap1.c
+++ b/drivers/mailbox/mailbox-omap1.c
@@ -12,10 +12,10 @@
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/mbox.h>
#include <linux/io.h>
-#include "omap-mbox.h"
-
#define MAILBOX_ARM2DSP1 0x00
#define MAILBOX_ARM2DSP1b 0x04
#define MAILBOX_DSP2ARM1 0x08
@@ -26,8 +26,6 @@
#define MAILBOX_DSP2ARM1_Flag 0x1c
#define MAILBOX_DSP2ARM2_Flag 0x20
-static void __iomem *mbox_base;
-
struct omap_mbox1_fifo {
unsigned long cmd;
unsigned long data;
@@ -39,90 +37,70 @@ struct omap_mbox1_priv {
struct omap_mbox1_fifo rx_fifo;
};
-static inline int mbox_read_reg(size_t ofs)
+struct omap1_mbox {
+ struct mbox_adapter adapter;
+ struct omap_mbox1_priv priv;
+ void __iomem *base;
+ int irq;
+};
+
+static inline int mbox_read_reg(void __iomem *base, size_t ofs)
{
- return __raw_readw(mbox_base + ofs);
+ return __raw_readw(base + ofs);
}
-static inline void mbox_write_reg(u32 val, size_t ofs)
+static inline void mbox_write_reg(void __iomem *base, u32 val, size_t ofs)
{
- __raw_writew(val, mbox_base + ofs);
+ __raw_writew(val, base + ofs);
}
-/* msg */
-static mbox_msg_t omap1_mbox_fifo_read(struct omap_mbox *mbox)
+static int omap1_mbox_put_message(struct mbox_adapter *adap,
+ struct mbox_channel *chan, const void *data, unsigned int len)
+
{
- struct omap_mbox1_fifo *fifo =
- &((struct omap_mbox1_priv *)mbox->priv)->rx_fifo;
- mbox_msg_t msg;
+ struct omap_mbox1_fifo *fifo;
+ struct omap1_mbox *mbox;
+ u32 msg;
+ int i;
- msg = mbox_read_reg(fifo->data);
- msg |= ((mbox_msg_t) mbox_read_reg(fifo->cmd)) << 16;
+ if (len != sizeof(msg))
+ return -EINVAL;
- return msg;
-}
+ msg = ((u32 *)data)[0];
+ mbox = container_of(adap, struct omap1_mbox, adapter);
+ fifo = &mbox->priv.tx_fifo;
-static void
-omap1_mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg)
-{
- struct omap_mbox1_fifo *fifo =
- &((struct omap_mbox1_priv *)mbox->priv)->tx_fifo;
+ /* wait for available space */
+ for (i = 0; i < 100 && mbox_read_reg(mbox->base, fifo->flag); ++i)
+ usleep_range(10, 20);
+ if (i == 100)
+ return -ETIMEDOUT;
- mbox_write_reg(msg & 0xffff, fifo->data);
- mbox_write_reg(msg >> 16, fifo->cmd);
-}
+ mbox_write_reg(mbox->base, msg & 0xffff, fifo->data);
+ mbox_write_reg(mbox->base, msg >> 16, fifo->cmd);
-static int omap1_mbox_fifo_empty(struct omap_mbox *mbox)
-{
return 0;
}
-static int omap1_mbox_fifo_full(struct omap_mbox *mbox)
+static irqreturn_t omap1_mbox_irq(int irq, void *dev)
{
- struct omap_mbox1_fifo *fifo =
- &((struct omap_mbox1_priv *)mbox->priv)->rx_fifo;
+ struct omap1_mbox *mbox = dev;
+ struct omap_mbox1_fifo *fifo;
+ u32 msg;
- return mbox_read_reg(fifo->flag);
-}
+ fifo = &mbox->priv.rx_fifo;
+ msg = mbox_read_reg(mbox->base, fifo->data);
+ msg |= ((u32)mbox_read_reg(mbox->base, fifo->cmd)) << 16;
-/* irq */
-static void
-omap1_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
-{
- if (irq == IRQ_RX)
- enable_irq(mbox->irq);
-}
+ mbox_channel_notify(&mbox->adapter.channels[0], &msg, sizeof(msg));
-static void
-omap1_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
-{
- if (irq == IRQ_RX)
- disable_irq(mbox->irq);
+ return IRQ_HANDLED;
}
-static int
-omap1_mbox_is_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
-{
- if (irq == IRQ_TX)
- return 0;
- return 1;
-}
-
-static struct omap_mbox_ops omap1_mbox_ops = {
- .type = OMAP_MBOX_TYPE1,
- .fifo_read = omap1_mbox_fifo_read,
- .fifo_write = omap1_mbox_fifo_write,
- .fifo_empty = omap1_mbox_fifo_empty,
- .fifo_full = omap1_mbox_fifo_full,
- .enable_irq = omap1_mbox_enable_irq,
- .disable_irq = omap1_mbox_disable_irq,
- .is_irq = omap1_mbox_is_irq,
-};
-
/* FIXME: the following struct should be created automatically by the user id */
/* DSP */
-static struct omap_mbox1_priv omap1_mbox_dsp_priv = {
+static const struct omap_mbox1_priv omap1_mbox_dsp_priv = {
.tx_fifo = {
.cmd = MAILBOX_ARM2DSP1b,
.data = MAILBOX_ARM2DSP1,
@@ -135,44 +113,59 @@ static struct omap_mbox1_priv omap1_mbox_dsp_priv = {
},
};
-static struct omap_mbox mbox_dsp_info = {
- .name = "dsp",
- .ops = &omap1_mbox_ops,
- .priv = &omap1_mbox_dsp_priv,
+static const struct mbox_adapter_ops omap1_mbox_ops = {
+ .owner = THIS_MODULE,
+ .put_message = omap1_mbox_put_message,
};
-static struct omap_mbox *omap1_mboxes[] = { &mbox_dsp_info, NULL };
-
static int omap1_mbox_probe(struct platform_device *pdev)
{
struct resource *mem;
+ struct omap1_mbox *mbox;
int ret;
- struct omap_mbox **list;
- list = omap1_mboxes;
- list[0]->irq = platform_get_irq_byname(pdev, "dsp");
+ mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox), GFP_KERNEL);
+ if (mbox == NULL)
+ return -ENOMEM;
+
+ mbox->irq = platform_get_irq_byname(pdev, "dsp");
+ if (mbox->irq < 0)
+ return -EINVAL;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem)
return -ENOENT;
- mbox_base = ioremap(mem->start, resource_size(mem));
- if (!mbox_base)
+ mbox->base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+ if (!mbox->base)
return -ENOMEM;
- ret = omap_mbox_register(&pdev->dev, list);
- if (ret) {
- iounmap(mbox_base);
+ mbox->priv = omap1_mbox_dsp_priv;
+ mbox->adapter.dev = &pdev->dev;
+ mbox->adapter.ops = &omap1_mbox_ops;
+ mbox->adapter.nchannels = 1;
+
+ ret = mbox_adapter_add(&mbox->adapter);
+ if (ret)
+ return ret;
+
+ ret = devm_request_irq(&pdev->dev, mbox->irq, omap1_mbox_irq, 0,
+ dev_name(&pdev->dev), mbox);
+ if (ret < 0) {
+ mbox_adapter_remove(&mbox->adapter);
return ret;
}
+ platform_set_drvdata(pdev, mbox);
return 0;
}
static int omap1_mbox_remove(struct platform_device *pdev)
{
- omap_mbox_unregister();
- iounmap(mbox_base);
+ struct omap1_mbox *mbox;
+
+ mbox = platform_get_drvdata(pdev);
+ mbox_adapter_remove(&mbox->adapter);
return 0;
}
--
1.8.1.5
^ permalink raw reply related
* [RFC 4/6] mailbox: omap: remove omap-specific framework
From: Courtney Cavin @ 2014-02-08 0:50 UTC (permalink / raw)
To: s-anna, rob.herring, rafael.j.wysocki, mark.langsdorf, tony,
omar.ramirez
Cc: gregkh, pawel.moll, mark.rutland, ijc+devicetree, galak, rob,
linux-doc, devicetree, linux-kernel
In-Reply-To: <1391820619-25487-1-git-send-email-courtney.cavin@sonymobile.com>
This framework is no longer needed, and the users should move over to
the new common framework. Mark the existing implementations as broken,
and deprecate the api, as a stop-gap.
Signed-off-by: Courtney Cavin <courtney.cavin@sonymobile.com>
---
drivers/mailbox/Kconfig | 19 +-
drivers/mailbox/Makefile | 1 -
drivers/mailbox/omap-mailbox.c | 469 -----------------------------------------
drivers/mailbox/omap-mbox.h | 67 ------
include/linux/omap-mailbox.h | 45 +++-
5 files changed, 40 insertions(+), 561 deletions(-)
delete mode 100644 drivers/mailbox/omap-mailbox.c
delete mode 100644 drivers/mailbox/omap-mbox.h
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index c8b5c13..6befc6e 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -16,17 +16,10 @@ config PL320_MBOX
Management Engine, primarily for cpufreq. Say Y here if you want
to use the PL320 IPCM support.
-config OMAP_MBOX
- tristate
- help
- This option is selected by any OMAP architecture specific mailbox
- driver such as CONFIG_OMAP1_MBOX or CONFIG_OMAP2PLUS_MBOX. This
- enables the common OMAP mailbox framework code.
-
config OMAP1_MBOX
tristate "OMAP1 Mailbox framework support"
depends on ARCH_OMAP1
- select OMAP_MBOX
+ depends on BROKEN
help
Mailbox implementation for OMAP chips with hardware for
interprocessor communication involving DSP in OMAP1. Say Y here
@@ -35,19 +28,11 @@ config OMAP1_MBOX
config OMAP2PLUS_MBOX
tristate "OMAP2+ Mailbox framework support"
depends on ARCH_OMAP2PLUS
- select OMAP_MBOX
+ depends on BROKEN
help
Mailbox implementation for OMAP family chips with hardware for
interprocessor communication involving DSP, IVA1.0 and IVA2 in
OMAP2/3; or IPU, IVA HD and DSP in OMAP4/5. Say Y here if you
want to use OMAP2+ Mailbox framework support.
-config OMAP_MBOX_KFIFO_SIZE
- int "Mailbox kfifo default buffer size (bytes)"
- depends on OMAP2PLUS_MBOX || OMAP1_MBOX
- default 256
- help
- Specify the default size of mailbox's kfifo buffers (bytes).
- This can also be changed at runtime (via the mbox_kfifo_size
- module parameter).
endif
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 53712ed..c8e51a0 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -1,7 +1,6 @@
obj-$(CONFIG_MAILBOX) += core.o
obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o
-obj-$(CONFIG_OMAP_MBOX) += omap-mailbox.o
obj-$(CONFIG_OMAP1_MBOX) += mailbox_omap1.o
mailbox_omap1-objs := mailbox-omap1.o
obj-$(CONFIG_OMAP2PLUS_MBOX) += mailbox_omap2.o
diff --git a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c
deleted file mode 100644
index d79a646..0000000
--- a/drivers/mailbox/omap-mailbox.c
+++ /dev/null
@@ -1,469 +0,0 @@
-/*
- * OMAP mailbox driver
- *
- * Copyright (C) 2006-2009 Nokia Corporation. All rights reserved.
- *
- * Contact: Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-#include <linux/mutex.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/kfifo.h>
-#include <linux/err.h>
-#include <linux/notifier.h>
-#include <linux/module.h>
-
-#include "omap-mbox.h"
-
-static struct omap_mbox **mboxes;
-
-static int mbox_configured;
-static DEFINE_MUTEX(mbox_configured_lock);
-
-static unsigned int mbox_kfifo_size = CONFIG_OMAP_MBOX_KFIFO_SIZE;
-module_param(mbox_kfifo_size, uint, S_IRUGO);
-MODULE_PARM_DESC(mbox_kfifo_size, "Size of omap's mailbox kfifo (bytes)");
-
-/* Mailbox FIFO handle functions */
-static inline mbox_msg_t mbox_fifo_read(struct omap_mbox *mbox)
-{
- return mbox->ops->fifo_read(mbox);
-}
-static inline void mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg)
-{
- mbox->ops->fifo_write(mbox, msg);
-}
-static inline int mbox_fifo_empty(struct omap_mbox *mbox)
-{
- return mbox->ops->fifo_empty(mbox);
-}
-static inline int mbox_fifo_full(struct omap_mbox *mbox)
-{
- return mbox->ops->fifo_full(mbox);
-}
-
-/* Mailbox IRQ handle functions */
-static inline void ack_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
-{
- if (mbox->ops->ack_irq)
- mbox->ops->ack_irq(mbox, irq);
-}
-static inline int is_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
-{
- return mbox->ops->is_irq(mbox, irq);
-}
-
-/*
- * message sender
- */
-static int __mbox_poll_for_space(struct omap_mbox *mbox)
-{
- int ret = 0, i = 1000;
-
- while (mbox_fifo_full(mbox)) {
- if (mbox->ops->type == OMAP_MBOX_TYPE2)
- return -1;
- if (--i == 0)
- return -1;
- udelay(1);
- }
- return ret;
-}
-
-int omap_mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg)
-{
- struct omap_mbox_queue *mq = mbox->txq;
- int ret = 0, len;
-
- spin_lock_bh(&mq->lock);
-
- if (kfifo_avail(&mq->fifo) < sizeof(msg)) {
- ret = -ENOMEM;
- goto out;
- }
-
- if (kfifo_is_empty(&mq->fifo) && !__mbox_poll_for_space(mbox)) {
- mbox_fifo_write(mbox, msg);
- goto out;
- }
-
- len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
- WARN_ON(len != sizeof(msg));
-
- tasklet_schedule(&mbox->txq->tasklet);
-
-out:
- spin_unlock_bh(&mq->lock);
- return ret;
-}
-EXPORT_SYMBOL(omap_mbox_msg_send);
-
-void omap_mbox_save_ctx(struct omap_mbox *mbox)
-{
- if (!mbox->ops->save_ctx) {
- dev_err(mbox->dev, "%s:\tno save\n", __func__);
- return;
- }
-
- mbox->ops->save_ctx(mbox);
-}
-EXPORT_SYMBOL(omap_mbox_save_ctx);
-
-void omap_mbox_restore_ctx(struct omap_mbox *mbox)
-{
- if (!mbox->ops->restore_ctx) {
- dev_err(mbox->dev, "%s:\tno restore\n", __func__);
- return;
- }
-
- mbox->ops->restore_ctx(mbox);
-}
-EXPORT_SYMBOL(omap_mbox_restore_ctx);
-
-void omap_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
-{
- mbox->ops->enable_irq(mbox, irq);
-}
-EXPORT_SYMBOL(omap_mbox_enable_irq);
-
-void omap_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
-{
- mbox->ops->disable_irq(mbox, irq);
-}
-EXPORT_SYMBOL(omap_mbox_disable_irq);
-
-static void mbox_tx_tasklet(unsigned long tx_data)
-{
- struct omap_mbox *mbox = (struct omap_mbox *)tx_data;
- struct omap_mbox_queue *mq = mbox->txq;
- mbox_msg_t msg;
- int ret;
-
- while (kfifo_len(&mq->fifo)) {
- if (__mbox_poll_for_space(mbox)) {
- omap_mbox_enable_irq(mbox, IRQ_TX);
- break;
- }
-
- ret = kfifo_out(&mq->fifo, (unsigned char *)&msg,
- sizeof(msg));
- WARN_ON(ret != sizeof(msg));
-
- mbox_fifo_write(mbox, msg);
- }
-}
-
-/*
- * Message receiver(workqueue)
- */
-static void mbox_rx_work(struct work_struct *work)
-{
- struct omap_mbox_queue *mq =
- container_of(work, struct omap_mbox_queue, work);
- mbox_msg_t msg;
- int len;
-
- while (kfifo_len(&mq->fifo) >= sizeof(msg)) {
- len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
- WARN_ON(len != sizeof(msg));
-
- blocking_notifier_call_chain(&mq->mbox->notifier, len,
- (void *)msg);
- spin_lock_irq(&mq->lock);
- if (mq->full) {
- mq->full = false;
- omap_mbox_enable_irq(mq->mbox, IRQ_RX);
- }
- spin_unlock_irq(&mq->lock);
- }
-}
-
-/*
- * Mailbox interrupt handler
- */
-static void __mbox_tx_interrupt(struct omap_mbox *mbox)
-{
- omap_mbox_disable_irq(mbox, IRQ_TX);
- ack_mbox_irq(mbox, IRQ_TX);
- tasklet_schedule(&mbox->txq->tasklet);
-}
-
-static void __mbox_rx_interrupt(struct omap_mbox *mbox)
-{
- struct omap_mbox_queue *mq = mbox->rxq;
- mbox_msg_t msg;
- int len;
-
- while (!mbox_fifo_empty(mbox)) {
- if (unlikely(kfifo_avail(&mq->fifo) < sizeof(msg))) {
- omap_mbox_disable_irq(mbox, IRQ_RX);
- mq->full = true;
- goto nomem;
- }
-
- msg = mbox_fifo_read(mbox);
-
- len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
- WARN_ON(len != sizeof(msg));
-
- if (mbox->ops->type == OMAP_MBOX_TYPE1)
- break;
- }
-
- /* no more messages in the fifo. clear IRQ source. */
- ack_mbox_irq(mbox, IRQ_RX);
-nomem:
- schedule_work(&mbox->rxq->work);
-}
-
-static irqreturn_t mbox_interrupt(int irq, void *p)
-{
- struct omap_mbox *mbox = p;
-
- if (is_mbox_irq(mbox, IRQ_TX))
- __mbox_tx_interrupt(mbox);
-
- if (is_mbox_irq(mbox, IRQ_RX))
- __mbox_rx_interrupt(mbox);
-
- return IRQ_HANDLED;
-}
-
-static struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox,
- void (*work) (struct work_struct *),
- void (*tasklet)(unsigned long))
-{
- struct omap_mbox_queue *mq;
-
- mq = kzalloc(sizeof(struct omap_mbox_queue), GFP_KERNEL);
- if (!mq)
- return NULL;
-
- spin_lock_init(&mq->lock);
-
- if (kfifo_alloc(&mq->fifo, mbox_kfifo_size, GFP_KERNEL))
- goto error;
-
- if (work)
- INIT_WORK(&mq->work, work);
-
- if (tasklet)
- tasklet_init(&mq->tasklet, tasklet, (unsigned long)mbox);
- return mq;
-error:
- kfree(mq);
- return NULL;
-}
-
-static void mbox_queue_free(struct omap_mbox_queue *q)
-{
- kfifo_free(&q->fifo);
- kfree(q);
-}
-
-static int omap_mbox_startup(struct omap_mbox *mbox)
-{
- int ret = 0;
- struct omap_mbox_queue *mq;
-
- mutex_lock(&mbox_configured_lock);
- if (!mbox_configured++) {
- if (likely(mbox->ops->startup)) {
- ret = mbox->ops->startup(mbox);
- if (unlikely(ret))
- goto fail_startup;
- } else
- goto fail_startup;
- }
-
- if (!mbox->use_count++) {
- mq = mbox_queue_alloc(mbox, NULL, mbox_tx_tasklet);
- if (!mq) {
- ret = -ENOMEM;
- goto fail_alloc_txq;
- }
- mbox->txq = mq;
-
- mq = mbox_queue_alloc(mbox, mbox_rx_work, NULL);
- if (!mq) {
- ret = -ENOMEM;
- goto fail_alloc_rxq;
- }
- mbox->rxq = mq;
- mq->mbox = mbox;
- ret = request_irq(mbox->irq, mbox_interrupt, IRQF_SHARED,
- mbox->name, mbox);
- if (unlikely(ret)) {
- pr_err("failed to register mailbox interrupt:%d\n",
- ret);
- goto fail_request_irq;
- }
-
- omap_mbox_enable_irq(mbox, IRQ_RX);
- }
- mutex_unlock(&mbox_configured_lock);
- return 0;
-
-fail_request_irq:
- mbox_queue_free(mbox->rxq);
-fail_alloc_rxq:
- mbox_queue_free(mbox->txq);
-fail_alloc_txq:
- if (mbox->ops->shutdown)
- mbox->ops->shutdown(mbox);
- mbox->use_count--;
-fail_startup:
- mbox_configured--;
- mutex_unlock(&mbox_configured_lock);
- return ret;
-}
-
-static void omap_mbox_fini(struct omap_mbox *mbox)
-{
- mutex_lock(&mbox_configured_lock);
-
- if (!--mbox->use_count) {
- omap_mbox_disable_irq(mbox, IRQ_RX);
- free_irq(mbox->irq, mbox);
- tasklet_kill(&mbox->txq->tasklet);
- flush_work(&mbox->rxq->work);
- mbox_queue_free(mbox->txq);
- mbox_queue_free(mbox->rxq);
- }
-
- if (likely(mbox->ops->shutdown)) {
- if (!--mbox_configured)
- mbox->ops->shutdown(mbox);
- }
-
- mutex_unlock(&mbox_configured_lock);
-}
-
-struct omap_mbox *omap_mbox_get(const char *name, struct notifier_block *nb)
-{
- struct omap_mbox *_mbox, *mbox = NULL;
- int i, ret;
-
- if (!mboxes)
- return ERR_PTR(-EINVAL);
-
- for (i = 0; (_mbox = mboxes[i]); i++) {
- if (!strcmp(_mbox->name, name)) {
- mbox = _mbox;
- break;
- }
- }
-
- if (!mbox)
- return ERR_PTR(-ENOENT);
-
- if (nb)
- blocking_notifier_chain_register(&mbox->notifier, nb);
-
- ret = omap_mbox_startup(mbox);
- if (ret) {
- blocking_notifier_chain_unregister(&mbox->notifier, nb);
- return ERR_PTR(-ENODEV);
- }
-
- return mbox;
-}
-EXPORT_SYMBOL(omap_mbox_get);
-
-void omap_mbox_put(struct omap_mbox *mbox, struct notifier_block *nb)
-{
- blocking_notifier_chain_unregister(&mbox->notifier, nb);
- omap_mbox_fini(mbox);
-}
-EXPORT_SYMBOL(omap_mbox_put);
-
-static struct class omap_mbox_class = { .name = "mbox", };
-
-int omap_mbox_register(struct device *parent, struct omap_mbox **list)
-{
- int ret;
- int i;
-
- mboxes = list;
- if (!mboxes)
- return -EINVAL;
-
- for (i = 0; mboxes[i]; i++) {
- struct omap_mbox *mbox = mboxes[i];
- mbox->dev = device_create(&omap_mbox_class,
- parent, 0, mbox, "%s", mbox->name);
- if (IS_ERR(mbox->dev)) {
- ret = PTR_ERR(mbox->dev);
- goto err_out;
- }
-
- BLOCKING_INIT_NOTIFIER_HEAD(&mbox->notifier);
- }
- return 0;
-
-err_out:
- while (i--)
- device_unregister(mboxes[i]->dev);
- return ret;
-}
-EXPORT_SYMBOL(omap_mbox_register);
-
-int omap_mbox_unregister(void)
-{
- int i;
-
- if (!mboxes)
- return -EINVAL;
-
- for (i = 0; mboxes[i]; i++)
- device_unregister(mboxes[i]->dev);
- mboxes = NULL;
- return 0;
-}
-EXPORT_SYMBOL(omap_mbox_unregister);
-
-static int __init omap_mbox_init(void)
-{
- int err;
-
- err = class_register(&omap_mbox_class);
- if (err)
- return err;
-
- /* kfifo size sanity check: alignment and minimal size */
- mbox_kfifo_size = ALIGN(mbox_kfifo_size, sizeof(mbox_msg_t));
- mbox_kfifo_size = max_t(unsigned int, mbox_kfifo_size,
- sizeof(mbox_msg_t));
-
- return 0;
-}
-subsys_initcall(omap_mbox_init);
-
-static void __exit omap_mbox_exit(void)
-{
- class_unregister(&omap_mbox_class);
-}
-module_exit(omap_mbox_exit);
-
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("omap mailbox: interrupt driven messaging");
-MODULE_AUTHOR("Toshihiro Kobayashi");
-MODULE_AUTHOR("Hiroshi DOYU");
diff --git a/drivers/mailbox/omap-mbox.h b/drivers/mailbox/omap-mbox.h
deleted file mode 100644
index 6cd38fc..0000000
--- a/drivers/mailbox/omap-mbox.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * omap-mbox.h: OMAP mailbox internal definitions
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef OMAP_MBOX_H
-#define OMAP_MBOX_H
-
-#include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/kfifo.h>
-#include <linux/spinlock.h>
-#include <linux/workqueue.h>
-#include <linux/omap-mailbox.h>
-
-typedef int __bitwise omap_mbox_type_t;
-#define OMAP_MBOX_TYPE1 ((__force omap_mbox_type_t) 1)
-#define OMAP_MBOX_TYPE2 ((__force omap_mbox_type_t) 2)
-
-struct omap_mbox_ops {
- omap_mbox_type_t type;
- int (*startup)(struct omap_mbox *mbox);
- void (*shutdown)(struct omap_mbox *mbox);
- /* fifo */
- mbox_msg_t (*fifo_read)(struct omap_mbox *mbox);
- void (*fifo_write)(struct omap_mbox *mbox, mbox_msg_t msg);
- int (*fifo_empty)(struct omap_mbox *mbox);
- int (*fifo_full)(struct omap_mbox *mbox);
- /* irq */
- void (*enable_irq)(struct omap_mbox *mbox,
- omap_mbox_irq_t irq);
- void (*disable_irq)(struct omap_mbox *mbox,
- omap_mbox_irq_t irq);
- void (*ack_irq)(struct omap_mbox *mbox, omap_mbox_irq_t irq);
- int (*is_irq)(struct omap_mbox *mbox, omap_mbox_irq_t irq);
- /* ctx */
- void (*save_ctx)(struct omap_mbox *mbox);
- void (*restore_ctx)(struct omap_mbox *mbox);
-};
-
-struct omap_mbox_queue {
- spinlock_t lock;
- struct kfifo fifo;
- struct work_struct work;
- struct tasklet_struct tasklet;
- struct omap_mbox *mbox;
- bool full;
-};
-
-struct omap_mbox {
- const char *name;
- unsigned int irq;
- struct omap_mbox_queue *txq, *rxq;
- struct omap_mbox_ops *ops;
- struct device *dev;
- void *priv;
- int use_count;
- struct blocking_notifier_head notifier;
-};
-
-int omap_mbox_register(struct device *parent, struct omap_mbox **);
-int omap_mbox_unregister(void);
-
-#endif /* OMAP_MBOX_H */
diff --git a/include/linux/omap-mailbox.h b/include/linux/omap-mailbox.h
index f8322d9..89fb606 100644
--- a/include/linux/omap-mailbox.h
+++ b/include/linux/omap-mailbox.h
@@ -9,6 +9,9 @@
#ifndef OMAP_MAILBOX_H
#define OMAP_MAILBOX_H
+#include <linux/compiler.h>
+#include <linux/mbox.h>
+
typedef u32 mbox_msg_t;
struct omap_mbox;
@@ -16,14 +19,42 @@ typedef int __bitwise omap_mbox_irq_t;
#define IRQ_TX ((__force omap_mbox_irq_t) 1)
#define IRQ_RX ((__force omap_mbox_irq_t) 2)
-int omap_mbox_msg_send(struct omap_mbox *, mbox_msg_t msg);
+static inline int __deprecated omap_mbox_msg_send(struct omap_mbox *mbox,
+ mbox_msg_t msg)
+{
+ struct mbox *m = (struct mbox *)mbox;
+ return mbox_put_message(m, &msg, sizeof(msg));
+}
+
+static inline struct omap_mbox *__deprecated omap_mbox_get(const char *name,
+ struct notifier_block *nb);
+{
+ return (struct omap_mbox *)mbox_request(NULL, name, nb);
+}
+
+static inline void __deprecated omap_mbox_put(struct omap_mbox *mbox,
+ struct notifier_block *nb)
+{
+ struct mbox *m = (struct mbox *)mbox;
+ mbox_release(m, nb);
+}
+
+static inline void __deprecated omap_mbox_save_ctx(struct omap_mbox *mbox)
+{
+}
+
+static inline void __deprecated omap_mbox_restore_ctx(struct omap_mbox *mbox)
+{
+}
-struct omap_mbox *omap_mbox_get(const char *, struct notifier_block *nb);
-void omap_mbox_put(struct omap_mbox *mbox, struct notifier_block *nb);
+static inline void __deprecated omap_mbox_enable_irq(struct omap_mbox *mbox,
+ omap_mbox_irq_t irq)
+{
+}
-void omap_mbox_save_ctx(struct omap_mbox *mbox);
-void omap_mbox_restore_ctx(struct omap_mbox *mbox);
-void omap_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq);
-void omap_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq);
+static inline void __deprecated omap_mbox_disable_irq(struct omap_mbox *mbox,
+ omap_mbox_irq_t irq)
+{
+}
#endif /* OMAP_MAILBOX_H */
--
1.8.1.5
^ permalink raw reply related
* [RFC 3/6] mailbox: pl320: migrate to mbox framework
From: Courtney Cavin @ 2014-02-08 0:50 UTC (permalink / raw)
To: s-anna, rob.herring, rafael.j.wysocki, mark.langsdorf, tony,
omar.ramirez
Cc: gregkh, pawel.moll, mark.rutland, ijc+devicetree, galak, rob,
linux-doc, devicetree, linux-kernel
In-Reply-To: <1391820619-25487-1-git-send-email-courtney.cavin@sonymobile.com>
We don't remove the legacy methods here, but we mark them as deprecated
in the hopes that people with the ability to properly test modifications
can adapt its users.
Signed-off-by: Courtney Cavin <courtney.cavin@sonymobile.com>
---
drivers/mailbox/pl320-ipc.c | 258 ++++++++++++++++++++++++++++++++++----------
include/linux/mailbox.h | 29 ++++-
2 files changed, 225 insertions(+), 62 deletions(-)
diff --git a/drivers/mailbox/pl320-ipc.c b/drivers/mailbox/pl320-ipc.c
index d873cba..b8da247 100644
--- a/drivers/mailbox/pl320-ipc.c
+++ b/drivers/mailbox/pl320-ipc.c
@@ -15,7 +15,6 @@
*/
#include <linux/types.h>
#include <linux/err.h>
-#include <linux/delay.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/interrupt.h>
@@ -27,6 +26,7 @@
#include <linux/amba/bus.h>
#include <linux/mailbox.h>
+#include <linux/mbox.h>
#define IPCMxSOURCE(m) ((m) * 0x40)
#define IPCMxDSET(m) (((m) * 0x40) + 0x004)
@@ -50,131 +50,162 @@
#define A9_SOURCE 1
#define M3_SOURCE 0
-static void __iomem *ipc_base;
-static int ipc_irq;
-static DEFINE_MUTEX(ipc_m1_lock);
-static DECLARE_COMPLETION(ipc_completion);
-static ATOMIC_NOTIFIER_HEAD(ipc_notifier);
+struct pl320 {
+ struct mbox_adapter adapter;
+ void __iomem *base;
+ int irq;
+ struct completion completion;
+};
-static inline void set_destination(int source, int mbox)
+static inline void set_destination(struct pl320 *pl, int source, int mbox)
{
- __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDSET(mbox));
- __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMSET(mbox));
+ __raw_writel(CHAN_MASK(source), pl->base + IPCMxDSET(mbox));
+ __raw_writel(CHAN_MASK(source), pl->base + IPCMxMSET(mbox));
}
-static inline void clear_destination(int source, int mbox)
+static inline void clear_destination(struct pl320 *pl, int source, int mbox)
{
- __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDCLEAR(mbox));
- __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMCLEAR(mbox));
+ __raw_writel(CHAN_MASK(source), pl->base + IPCMxDCLEAR(mbox));
+ __raw_writel(CHAN_MASK(source), pl->base + IPCMxMCLEAR(mbox));
}
-static void __ipc_send(int mbox, u32 *data)
+static void __ipc_send(struct pl320 *pl, int mbox, const u32 *data)
{
int i;
for (i = 0; i < 7; i++)
- __raw_writel(data[i], ipc_base + IPCMxDR(mbox, i));
- __raw_writel(0x1, ipc_base + IPCMxSEND(mbox));
+ __raw_writel(data[i], pl->base + IPCMxDR(mbox, i));
+ __raw_writel(0x1, pl->base + IPCMxSEND(mbox));
}
-static u32 __ipc_rcv(int mbox, u32 *data)
+static u32 __ipc_rcv(struct pl320 *pl, int mbox, u32 *data)
{
int i;
for (i = 0; i < 7; i++)
- data[i] = __raw_readl(ipc_base + IPCMxDR(mbox, i));
+ data[i] = __raw_readl(pl->base + IPCMxDR(mbox, i));
return data[1];
}
/* blocking implmentation from the A9 side, not usuable in interrupts! */
-int pl320_ipc_transmit(u32 *data)
+static int pl320_ipc_put_message(struct mbox_adapter *adap,
+ struct mbox_channel *chan, const void *data, unsigned int len)
{
+ struct pl320 *pl;
+ u32 repl[7];
int ret;
- mutex_lock(&ipc_m1_lock);
+ if (len != 28 || chan->id != 0)
+ return -EINVAL;
- init_completion(&ipc_completion);
- __ipc_send(IPC_TX_MBOX, data);
- ret = wait_for_completion_timeout(&ipc_completion,
+ pl = container_of(adap, struct pl320, adapter);
+ reinit_completion(&pl->completion);
+ __ipc_send(pl, IPC_TX_MBOX, data);
+ ret = wait_for_completion_timeout(&pl->completion,
msecs_to_jiffies(1000));
- if (ret == 0) {
- ret = -ETIMEDOUT;
- goto out;
- }
+ if (ret == 0)
+ return -ETIMEDOUT;
+
+ ret = __ipc_rcv(pl, IPC_TX_MBOX, repl);
- ret = __ipc_rcv(IPC_TX_MBOX, data);
-out:
- mutex_unlock(&ipc_m1_lock);
return ret;
}
-EXPORT_SYMBOL_GPL(pl320_ipc_transmit);
static irqreturn_t ipc_handler(int irq, void *dev)
{
+ struct pl320 *pl = dev;
u32 irq_stat;
u32 data[7];
- irq_stat = __raw_readl(ipc_base + IPCMMIS(1));
+ irq_stat = __raw_readl(pl->base + IPCMMIS(1));
if (irq_stat & MBOX_MASK(IPC_TX_MBOX)) {
- __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX));
- complete(&ipc_completion);
+ __raw_writel(0, pl->base + IPCMxSEND(IPC_TX_MBOX));
+ complete(&pl->completion);
}
if (irq_stat & MBOX_MASK(IPC_RX_MBOX)) {
- __ipc_rcv(IPC_RX_MBOX, data);
- atomic_notifier_call_chain(&ipc_notifier, data[0], data + 1);
- __raw_writel(2, ipc_base + IPCMxSEND(IPC_RX_MBOX));
+ __ipc_rcv(pl, IPC_RX_MBOX, data);
+ mbox_channel_notify(&pl->adapter.channels[0], data, 28);
+ __raw_writel(2, pl->base + IPCMxSEND(IPC_RX_MBOX));
}
return IRQ_HANDLED;
}
-int pl320_ipc_register_notifier(struct notifier_block *nb)
-{
- return atomic_notifier_chain_register(&ipc_notifier, nb);
-}
-EXPORT_SYMBOL_GPL(pl320_ipc_register_notifier);
-
-int pl320_ipc_unregister_notifier(struct notifier_block *nb)
-{
- return atomic_notifier_chain_unregister(&ipc_notifier, nb);
-}
-EXPORT_SYMBOL_GPL(pl320_ipc_unregister_notifier);
+static const struct mbox_adapter_ops pl320_mbox_ops = {
+ .owner = THIS_MODULE,
+ .put_message = pl320_ipc_put_message,
+};
static int pl320_probe(struct amba_device *adev, const struct amba_id *id)
{
+ struct pl320 *pl;
int ret;
- ipc_base = ioremap(adev->res.start, resource_size(&adev->res));
- if (ipc_base == NULL)
+ pl = devm_kzalloc(&adev->dev, sizeof(*pl), GFP_KERNEL);
+ if (pl == NULL)
+ return -ENOMEM;
+ pl->base = ioremap(adev->res.start, resource_size(&adev->res));
+ if (pl->base == NULL)
return -ENOMEM;
- __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX));
+ init_completion(&pl->completion);
- ipc_irq = adev->irq[0];
- ret = request_irq(ipc_irq, ipc_handler, 0, dev_name(&adev->dev), NULL);
+ pl->adapter.dev = &adev->dev;
+ pl->adapter.ops = &pl320_mbox_ops;
+ pl->adapter.nchannels = 1;
+
+ ret = mbox_adapter_add(&pl->adapter);
+ if (ret)
+ goto err;
+
+ __raw_writel(0, pl->base + IPCMxSEND(IPC_TX_MBOX));
+
+ pl->irq = adev->irq[0];
+ ret = request_irq(pl->irq, ipc_handler, 0, dev_name(&adev->dev), pl);
if (ret < 0)
goto err;
/* Init slow mailbox */
__raw_writel(CHAN_MASK(A9_SOURCE),
- ipc_base + IPCMxSOURCE(IPC_TX_MBOX));
+ pl->base + IPCMxSOURCE(IPC_TX_MBOX));
__raw_writel(CHAN_MASK(M3_SOURCE),
- ipc_base + IPCMxDSET(IPC_TX_MBOX));
+ pl->base + IPCMxDSET(IPC_TX_MBOX));
__raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE),
- ipc_base + IPCMxMSET(IPC_TX_MBOX));
+ pl->base + IPCMxMSET(IPC_TX_MBOX));
/* Init receive mailbox */
__raw_writel(CHAN_MASK(M3_SOURCE),
- ipc_base + IPCMxSOURCE(IPC_RX_MBOX));
+ pl->base + IPCMxSOURCE(IPC_RX_MBOX));
__raw_writel(CHAN_MASK(A9_SOURCE),
- ipc_base + IPCMxDSET(IPC_RX_MBOX));
+ pl->base + IPCMxDSET(IPC_RX_MBOX));
__raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE),
- ipc_base + IPCMxMSET(IPC_RX_MBOX));
+ pl->base + IPCMxMSET(IPC_RX_MBOX));
+ amba_set_drvdata(adev, pl);
return 0;
err:
- iounmap(ipc_base);
+ iounmap(pl->base);
return ret;
}
+static int pl320_remove(struct amba_device *adev)
+{
+ struct pl320 *pl;
+ int ret;
+
+ pl = amba_get_drvdata(adev);
+
+ disable_irq(pl->irq);
+
+ ret = mbox_adapter_remove(&pl->adapter);
+ if (ret) {
+ enable_irq(pl->irq);
+ return ret;
+ }
+
+ free_irq(pl->irq, pl);
+ iounmap(pl->base);
+ return 0;
+}
+
static struct amba_id pl320_ids[] = {
{
.id = 0x00041320,
@@ -189,6 +220,7 @@ static struct amba_driver pl320_driver = {
},
.id_table = pl320_ids,
.probe = pl320_probe,
+ .remove = pl320_remove,
};
static int __init ipc_init(void)
@@ -196,3 +228,111 @@ static int __init ipc_init(void)
return amba_driver_register(&pl320_driver);
}
module_init(ipc_init);
+
+/* Legacy API */
+static struct mbox *pl320_mbox;
+static struct notifier_block *pl320_notifier;
+static DEFINE_SPINLOCK(pl320_legacy_lock);
+static DEFINE_MUTEX(pl320_mutex);
+
+static int __pl320_notify(struct notifier_block *nb,
+ unsigned long len, void *data)
+{
+ unsigned long flags;
+ u32 *mdata = data;
+ int rc;
+
+ spin_lock_irqsave(&pl320_legacy_lock, flags);
+ if (!pl320_notifier) {
+ spin_unlock_irqrestore(&pl320_legacy_lock, flags);
+ return NOTIFY_DONE;
+ }
+
+ rc = pl320_notifier->notifier_call(pl320_notifier,
+ mdata[0], mdata + 1);
+ spin_unlock_irqrestore(&pl320_legacy_lock, flags);
+ return rc;
+}
+
+static void __pl320_set_notifier(struct notifier_block *nb)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&pl320_legacy_lock, flags);
+ pl320_notifier = nb;
+ spin_unlock_irqrestore(&pl320_legacy_lock, flags);
+}
+
+static struct notifier_block pl320_nb = {
+ .notifier_call = __pl320_notify,
+};
+
+static int __pl320_legacy_setup(struct notifier_block *nb, bool exist_ok)
+{
+ int rc = 0;
+
+ if (WARN_ON(!exist_ok && pl320_mbox))
+ return -EBUSY;
+
+ if (pl320_mbox)
+ return 0;
+
+ __pl320_set_notifier(nb);
+
+ pl320_mbox = mbox_request(NULL, "pl320", &pl320_nb);
+ if (IS_ERR(pl320_mbox)) {
+ rc = PTR_ERR(pl320_mbox);
+ pl320_mbox = NULL;
+ __pl320_set_notifier(NULL);
+ }
+
+ return rc;
+}
+
+int __pl320_legacy_ipc_transmit(u32 *data)
+{
+ int rc;
+
+ mutex_lock(&pl320_mutex);
+ rc = __pl320_legacy_setup(NULL, true);
+ if (rc)
+ goto out;
+
+ rc = mbox_put_message(pl320_mbox, data, 7 * sizeof(*data));
+out:
+ mutex_unlock(&pl320_mutex);
+
+ return rc;
+}
+EXPORT_SYMBOL(__pl320_legacy_ipc_transmit);
+
+int __pl320_legacy_ipc_register_notifier(struct notifier_block *nb)
+{
+ int rc;
+
+ mutex_lock(&pl320_mutex);
+ rc = __pl320_legacy_setup(nb, false);
+ mutex_unlock(&pl320_mutex);
+
+ return rc;
+}
+EXPORT_SYMBOL(__pl320_legacy_ipc_register_notifier);
+
+int __pl320_legacy_ipc_unregister_notifier(struct notifier_block *nb)
+{
+ mutex_lock(&pl320_mutex);
+
+ if (WARN_ON(!pl320_mbox)) {
+ mutex_unlock(&pl320_mutex);
+ return -EINVAL;
+ }
+
+ mbox_release(pl320_mbox);
+ __pl320_set_notifier(NULL);
+ pl320_mbox = NULL;
+
+ mutex_unlock(&pl320_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(__pl320_legacy_ipc_unregister_notifier);
diff --git a/include/linux/mailbox.h b/include/linux/mailbox.h
index 5161f63..2330954 100644
--- a/include/linux/mailbox.h
+++ b/include/linux/mailbox.h
@@ -12,6 +12,29 @@
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
-int pl320_ipc_transmit(u32 *data);
-int pl320_ipc_register_notifier(struct notifier_block *nb);
-int pl320_ipc_unregister_notifier(struct notifier_block *nb);
+#ifndef PL320_MAILBOX_H
+#define PL320_MAILBOX_H
+
+#include <linux/compiler.h>
+#include <linux/mbox.h>
+
+int __pl320_legacy_ipc_transmit(u32 *data);
+int __pl320_legacy_ipc_register_notifier(struct notifier_block *nb);
+int __pl320_legacy_ipc_unregister_notifier(struct notifier_block *nb);
+
+static inline int __deprecated pl320_ipc_transmit(u32 *data)
+{
+ return __pl320_legacy_ipc_transmit(data);
+}
+static inline int __deprecated
+pl320_ipc_register_notifier(struct notifier_block *nb)
+{
+ return __pl320_legacy_ipc_register_notifier(nb);
+}
+static inline int __deprecated
+pl320_ipc_unregister_notifier(struct notifier_block *nb)
+{
+ return __pl320_legacy_ipc_unregister_notifier(nb);
+}
+
+#endif
--
1.8.1.5
^ permalink raw reply related
* [RFC 2/6] mailbox: document bindings
From: Courtney Cavin @ 2014-02-08 0:50 UTC (permalink / raw)
To: s-anna, rob.herring, rafael.j.wysocki, mark.langsdorf, tony,
omar.ramirez
Cc: gregkh, pawel.moll, mark.rutland, ijc+devicetree, galak, rob,
linux-doc, devicetree, linux-kernel
In-Reply-To: <1391820619-25487-1-git-send-email-courtney.cavin@sonymobile.com>
Signed-off-by: Courtney Cavin <courtney.cavin@sonymobile.com>
---
.../devicetree/bindings/mailbox/mailbox.txt | 44 ++++++++++++++++++++++
1 file changed, 44 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mailbox/mailbox.txt
diff --git a/Documentation/devicetree/bindings/mailbox/mailbox.txt b/Documentation/devicetree/bindings/mailbox/mailbox.txt
new file mode 100644
index 0000000..846eb49
--- /dev/null
+++ b/Documentation/devicetree/bindings/mailbox/mailbox.txt
@@ -0,0 +1,44 @@
+Binding documentation for mailbox providers and consumers
+--
+
+Mailbox providers may be represented by any node in a device tree. These
+nodes are designated as mailbox providers. Consumers can then use a phandle
+to a mailbox provider, along with channel specifier information in order to
+get a mailbox.
+
+MAILBOX PROVIDERS
+
+#mbox-cells:
+ Usage: required
+ Type: u32
+ Desc: Number of cells in a mailbox specifier; Typically 1 for nodes
+ which only need a channel index.
+
+
+Example:
+ mailbox: mailbox {
+ #mbox-cells = <1>;
+ ...
+ };
+
+
+MAILBOX CONSUMERS
+
+mbox:
+ Usage: required
+ Type: < [phandle] [mailbox-specifier] >
+ Desc: List of phandle and mailbox specifier pairs, matching provider's
+ #mbox-cells property
+
+mbox-names:
+ Usage: optional
+ Type: string array
+ Desc: List of mailbox input name strings sorted in the same order as
+ the mbox property. Consumer drivers should use mbox-names
+ to match mailbox input names with mailbox specifiers.
+
+Example:
+ consumer {
+ mbox-names = "comms";
+ mbox = <&mailbox 0>;
+ };
--
1.8.1.5
^ permalink raw reply related
* [RFC 1/6] mailbox: add core framework
From: Courtney Cavin @ 2014-02-08 0:50 UTC (permalink / raw)
To: s-anna-l0cyMroinI0, rob.herring-bsGFqQB8/DxBDgjK7y7TUQ,
rafael.j.wysocki-ral2JQCrhuEAvxtiuMwx3w,
mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ,
tony-4v6yS6AI5VpBDgjK7y7TUQ, omar.ramirez-ew58BrD3rrPQT0dZR+AlfA
Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r, pawel.moll-5wv7dgnIgG8,
mark.rutland-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
galak-sgV2jX0FEOL9JmXXK+q4OQ, rob-VoJi6FS/r0vR7s880joybQ,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1391820619-25487-1-git-send-email-courtney.cavin-/MT0OVThwyLZJqsBc5GL+g@public.gmane.org>
The mailbox drivers are fragmented, and some implement their own core.
Unify the drivers and implement common functionality in a framework.
Signed-off-by: Courtney Cavin <courtney.cavin-/MT0OVThwyLZJqsBc5GL+g@public.gmane.org>
---
drivers/mailbox/Makefile | 1 +
drivers/mailbox/core.c | 573 +++++++++++++++++++++++++++++++++++++++++++++++
include/linux/mbox.h | 175 +++++++++++++++
3 files changed, 749 insertions(+)
create mode 100644 drivers/mailbox/core.c
create mode 100644 include/linux/mbox.h
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index e0facb3..53712ed 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -1,3 +1,4 @@
+obj-$(CONFIG_MAILBOX) += core.o
obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o
obj-$(CONFIG_OMAP_MBOX) += omap-mailbox.o
diff --git a/drivers/mailbox/core.c b/drivers/mailbox/core.c
new file mode 100644
index 0000000..0dc865e
--- /dev/null
+++ b/drivers/mailbox/core.c
@@ -0,0 +1,573 @@
+/*
+ * Generic mailbox implementation
+ *
+ * Copyright (C) 2014 Sony Mobile Communications, AB.
+ *
+ * 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 <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/mbox.h>
+
+static DEFINE_MUTEX(mbox_lock);
+static LIST_HEAD(mbox_adapters);
+
+static DEFINE_MUTEX(mbox_lookup_lock);
+static LIST_HEAD(mbox_lookup_list);
+
+static int __mbox_adapter_request(struct mbox_adapter *adap,
+ struct mbox_channel *chan)
+{
+ int rc;
+
+ if (chan->users > 0) {
+ chan->users++;
+ return 0;
+ }
+
+ if (!try_module_get(adap->ops->owner))
+ return -ENODEV;
+
+ if (adap->ops->request) {
+ rc = adap->ops->request(adap, chan);
+ if (rc) {
+ module_put(adap->ops->owner);
+ return rc;
+ }
+ }
+
+ chan->users++;
+
+ return 0;
+}
+
+static void __mbox_adapter_release(struct mbox_adapter *adap,
+ struct mbox_channel *chan)
+{
+ if (!adap || !chan)
+ return;
+
+ if (chan->users == 0) {
+ dev_err(adap->dev, "device already released\n");
+ return;
+ }
+
+ chan->users--;
+ if (chan->users > 0)
+ return;
+
+ if (adap->ops->release)
+ adap->ops->release(adap, chan);
+ module_put(adap->ops->owner);
+}
+
+static struct mbox_channel *
+mbox_adapter_request_channel(struct mbox_adapter *adap, unsigned int index)
+{
+ struct mbox_channel *chan;
+ int rc;
+
+ if (!adap || index >= adap->nchannels)
+ return ERR_PTR(-EINVAL);
+
+ mutex_lock(&adap->lock);
+ chan = &adap->channels[index];
+
+ rc = __mbox_adapter_request(adap, chan);
+ if (rc)
+ chan = ERR_PTR(rc);
+ mutex_unlock(&adap->lock);
+
+ return chan;
+}
+
+static void mbox_adapter_release_channel(struct mbox_adapter *adap,
+ struct mbox_channel *chan)
+{
+ if (!adap || !chan)
+ return;
+
+ mutex_lock(&adap->lock);
+ __mbox_adapter_release(adap, chan);
+ mutex_unlock(&adap->lock);
+}
+
+static int of_mbox_simple_xlate(struct mbox_adapter *adap,
+ const struct of_phandle_args *args)
+{
+ if (adap->of_n_cells < 1)
+ return -EINVAL;
+ if (args->args[0] >= adap->nchannels)
+ return -EINVAL;
+
+ return args->args[0];
+}
+
+static struct mbox_adapter *of_node_to_mbox_adapter(struct device_node *np)
+{
+ struct mbox_adapter *adap;
+
+ mutex_lock(&mbox_lock);
+ list_for_each_entry(adap, &mbox_adapters, list) {
+ if (adap->dev && adap->dev->of_node == np) {
+ mutex_unlock(&mbox_lock);
+ return adap;
+ }
+ }
+ mutex_unlock(&mbox_lock);
+
+ return ERR_PTR(-EPROBE_DEFER);
+}
+
+static void of_mbox_adapter_add(struct mbox_adapter *adap)
+{
+ if (!adap->dev)
+ return;
+
+ if (!adap->of_xlate) {
+ adap->of_xlate = of_mbox_simple_xlate;
+ adap->of_n_cells = 1;
+ }
+
+ of_node_get(adap->dev->of_node);
+}
+
+static void of_mbox_adapter_remove(struct mbox_adapter *adap)
+{
+ if (!adap->dev)
+ return;
+ of_node_put(adap->dev->of_node);
+}
+
+static struct mbox_channel *
+of_mbox_adapter_request_channel(struct device_node *np, const char *con_id)
+{
+ struct of_phandle_args args;
+ struct mbox_adapter *adap;
+ struct mbox_channel *chan;
+ int index = 0;
+ int rc;
+
+ if (con_id) {
+ index = of_property_match_string(np, "mbox-names", con_id);
+ if (index < 0)
+ return ERR_PTR(index);
+ }
+
+ rc = of_parse_phandle_with_args(np, "mbox", "#mbox-cells",
+ index, &args);
+ if (rc)
+ return ERR_PTR(rc);
+
+ adap = of_node_to_mbox_adapter(args.np);
+ if (IS_ERR(adap)) {
+ chan = ERR_CAST(adap);
+ goto out;
+ }
+
+ if (args.args_count != adap->of_n_cells) {
+ chan = ERR_PTR(-EINVAL);
+ goto out;
+ }
+
+ index = adap->of_xlate(adap, &args);
+ if (index < 0) {
+ chan = ERR_PTR(index);
+ goto out;
+ }
+
+ chan = mbox_adapter_request_channel(adap, index);
+
+out:
+ of_node_put(args.np);
+ return chan;
+}
+
+/**
+ * mbox_adapter_add() - register a new MBOX adapter
+ * @adap: the adapter to add
+ */
+int mbox_adapter_add(struct mbox_adapter *adap)
+{
+ struct mbox_channel *chan;
+ unsigned int i;
+
+ if (!adap || !adap->dev || !adap->ops || !adap->ops->put_message)
+ return -EINVAL;
+ if (adap->nchannels == 0)
+ return -EINVAL;
+
+ adap->channels = kzalloc(adap->nchannels * sizeof(*chan), GFP_KERNEL);
+ if (!adap->channels)
+ return -ENOMEM;
+
+ for (i = 0; i < adap->nchannels; ++i) {
+ chan = &adap->channels[i];
+ ATOMIC_INIT_NOTIFIER_HEAD(&chan->notifier);
+ chan->adapter = adap;
+ chan->id = i;
+ }
+ mutex_init(&adap->lock);
+
+ mutex_lock(&mbox_lock);
+ list_add(&adap->list, &mbox_adapters);
+
+ of_mbox_adapter_add(adap);
+ mutex_unlock(&mbox_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(mbox_adapter_add);
+
+/**
+ * mbox_adapter_remove() - unregisters a MBOX adapter
+ * @adap: the adapter to remove
+ *
+ * This function may return -EBUSY if the adapter provides a channel which
+ * is still requested.
+ */
+int mbox_adapter_remove(struct mbox_adapter *adap)
+{
+ unsigned int i;
+
+ mutex_lock(&mbox_lock);
+
+ for (i = 0; i < adap->nchannels; ++i) {
+ struct mbox_channel *chan = &adap->channels[i];
+ if (chan->users) {
+ mutex_unlock(&mbox_lock);
+ return -EBUSY;
+ }
+ }
+ list_del_init(&adap->list);
+
+ of_mbox_adapter_remove(adap);
+
+ mutex_unlock(&mbox_lock);
+
+ kfree(adap->channels);
+
+ return 0;
+}
+EXPORT_SYMBOL(mbox_adapter_remove);
+
+static int mbox_channel_put_message(struct mbox_channel *chan,
+ const void *data, unsigned int len)
+{
+ int rc;
+
+ mutex_lock(&chan->adapter->lock);
+ rc = chan->adapter->ops->put_message(chan->adapter, chan, data, len);
+ mutex_unlock(&chan->adapter->lock);
+
+ return rc;
+}
+
+/**
+ * mbox_channel_notify() - notify the core that a channel has a message
+ * @chan: the channel which has data
+ * @data: the location of said data
+ * @len: the length of specified data
+ *
+ * This function may be called from interrupt/no-sleep context.
+ */
+int mbox_channel_notify(struct mbox_channel *chan,
+ const void *data, unsigned int len)
+{
+ return atomic_notifier_call_chain(&chan->notifier, len, (void *)data);
+}
+EXPORT_SYMBOL(mbox_channel_notify);
+
+/**
+ * mbox_add_table() - add a lookup table for adapter consumers
+ * @table: array of consumers to register
+ * @num: number of consumers in array
+ */
+void __init mbox_add_table(struct mbox_lookup *table, unsigned int num)
+{
+ mutex_lock(&mbox_lookup_lock);
+ while (num--) {
+ if (table->provider && (table->dev_id || table->con_id))
+ list_add_tail(&table->list, &mbox_lookup_list);
+ table++;
+ }
+ mutex_unlock(&mbox_lookup_lock);
+}
+EXPORT_SYMBOL(mbox_add_table);
+
+static struct mbox_adapter *mbox_adapter_lookup(const char *name)
+{
+ struct mbox_adapter *adap;
+
+ if (!name)
+ return ERR_PTR(-EINVAL);
+
+ mutex_lock(&mbox_lock);
+ list_for_each_entry(adap, &mbox_adapters, list) {
+ const char *aname = dev_name(adap->dev);
+ if (aname && !strcmp(aname, name)) {
+ mutex_unlock(&mbox_lock);
+ return adap;
+ }
+ }
+ mutex_unlock(&mbox_lock);
+
+ return ERR_PTR(-ENODEV);
+}
+
+static struct mbox_adapter *
+mbox_channel_lookup(struct device *dev, const char *con_id, int *index)
+{
+ const char *dev_id = dev ? dev_name(dev) : NULL;
+ struct mbox_adapter *adap = ERR_PTR(-ENODEV);
+ struct mbox_lookup *lp;
+ int match, best;
+
+ best = 0;
+ mutex_lock(&mbox_lookup_lock);
+ list_for_each_entry(lp, &mbox_lookup_list, list) {
+ match = 0;
+ if (lp->dev_id) {
+ if (!dev_id || strcmp(lp->dev_id, dev_id))
+ continue;
+ match += 2;
+ }
+ if (lp->con_id) {
+ if (!con_id || strcmp(lp->con_id, con_id))
+ continue;
+ match += 1;
+ }
+ if (match <= best)
+ continue;
+ adap = mbox_adapter_lookup(lp->provider);
+ if (IS_ERR(adap))
+ continue;
+ if (index)
+ *index = lp->index;
+ if (match == 3)
+ break;
+ best = match;
+ }
+ mutex_unlock(&mbox_lookup_lock);
+
+ return adap;
+}
+
+/**
+ * struct mbox - MBOX channel consumer
+ * @chan: internal MBOX channel
+ * @nb: notifier to call on message available
+ */
+struct mbox {
+ struct mbox_channel *chan;
+ struct notifier_block *nb;
+};
+
+static struct mbox *
+__mbox_alloc(struct mbox_channel *chan, struct notifier_block *nb)
+{
+ struct mbox *mbox;
+
+ mbox = kzalloc(sizeof(*mbox), GFP_KERNEL);
+ if (mbox == NULL)
+ return ERR_PTR(-ENOMEM);
+ mbox->chan = chan;
+ mbox->nb = nb;
+
+ if (mbox->nb)
+ atomic_notifier_chain_register(&chan->notifier, mbox->nb);
+
+ return mbox;
+}
+
+/**
+ * mbox_request() - lookup and request a MBOX channel
+ * @dev: device for channel consumer
+ * @con_id: consumer name
+ * @nb: notifier block used for receiving messages
+ *
+ * The notifier is called as atomic on new messages, so you may not sleep
+ * in the notifier callback function.
+ */
+struct mbox *mbox_request(struct device *dev, const char *con_id,
+ struct notifier_block *nb)
+{
+ struct mbox_adapter *adap;
+ struct mbox_channel *chan;
+ struct mbox *mbox;
+ int index = 0;
+
+ if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node)
+ return of_mbox_request(dev->of_node, con_id, nb);
+
+ adap = mbox_channel_lookup(dev, con_id, &index);
+ if (IS_ERR(adap))
+ return ERR_CAST(adap);
+
+ chan = mbox_adapter_request_channel(adap, index);
+ if (IS_ERR(chan))
+ return ERR_CAST(chan);
+
+ mbox = __mbox_alloc(chan, nb);
+ if (IS_ERR(mbox))
+ mbox_adapter_release_channel(adap, chan);
+ return mbox;
+}
+EXPORT_SYMBOL(mbox_request);
+
+/**
+ * mbox_release() - release a MBOX channel
+ * @mbox: the channel to release
+ *
+ * This releases a channel previously acquired by mbox_request(). Do not call
+ * this function on devm-allocated MBOX channels.
+ */
+void mbox_release(struct mbox *mbox)
+{
+ struct mbox_channel *chan = mbox->chan;
+ if (mbox->nb)
+ atomic_notifier_chain_unregister(&chan->notifier, mbox->nb);
+ mbox_adapter_release_channel(chan->adapter, chan);
+ kfree(mbox);
+}
+EXPORT_SYMBOL(mbox_release);
+
+/**
+ * mbox_put_message() - post a message to the MBOX channel
+ * @mbox: the channel to post to
+ * @data: location of the message
+ * @len: length of the message
+ *
+ * This function might sleep, and may not be called from interrupt context.
+ */
+int mbox_put_message(struct mbox *mbox, const void *data, unsigned int len)
+{
+ might_sleep();
+ return mbox_channel_put_message(mbox->chan, data, len);
+}
+EXPORT_SYMBOL(mbox_put_message);
+
+/**
+ * of_mbox_request() - lookup in DT and request a MBOX channel
+ * @np: device node for lookup
+ * @con_id: consumer id
+ * @nb: notifier block used for receiving messages
+ *
+ * The notifier is called as atomic on new messages, so you may not sleep
+ * in the notifier callback function.
+ */
+struct mbox *of_mbox_request(struct device_node *np, const char *con_id,
+ struct notifier_block *nb)
+{
+ struct mbox_channel *chan;
+ struct mbox *mbox;
+
+ chan = of_mbox_adapter_request_channel(np, con_id);
+ if (IS_ERR(chan))
+ return ERR_CAST(chan);
+
+ mbox = __mbox_alloc(chan, nb);
+ if (IS_ERR(mbox))
+ mbox_adapter_release_channel(chan->adapter, chan);
+ return mbox;
+}
+EXPORT_SYMBOL(of_mbox_request);
+
+static int devm_mbox_match(struct device *dev, void *res, void *data)
+{
+ struct mbox **p = res;
+
+ if (WARN_ON(!p || !*p))
+ return 0;
+
+ return *p == data;
+}
+
+static void __devm_mbox_release(struct device *dev, void *res)
+{
+ mbox_release(*(struct mbox **)res);
+}
+
+/**
+ * devm_mbox_release() - resource managed mbox_release()
+ * @dev: device for channel consumer
+ * @mbox: MBOX channel
+ *
+ * Release a MBOX channel previously allocated using devm_mbox_request() or
+ * devm_of_mbox_request(). Calling this function is usually not necessary,
+ * as devm-allocated resources are automatically released on driver detach.
+ */
+void devm_mbox_release(struct device *dev, struct mbox *mbox)
+{
+ WARN_ON(devres_release(dev, __devm_mbox_release,
+ devm_mbox_match, mbox));
+}
+EXPORT_SYMBOL(devm_mbox_release);
+
+/**
+ * devm_mbox_request() - resource managed mbox_request()
+ * @dev: device for channel consumer
+ * @con_id: consumer id
+ * @nb: notifier block used for receiving messages
+ *
+ * This function behaves like mbox_request() but the acquired channel
+ * will be automatically released on driver detach.
+ */
+struct mbox *devm_mbox_request(struct device *dev, const char *con_id,
+ struct notifier_block *nb)
+{
+ struct mbox **ptr;
+ struct mbox *mbox;
+
+ ptr = devres_alloc(__devm_mbox_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+ mbox = mbox_request(dev, con_id, nb);
+ if (!IS_ERR(mbox)) {
+ *ptr = mbox;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+ return mbox;
+}
+EXPORT_SYMBOL(devm_mbox_request);
+
+/**
+ * devm_mbox_request() - resource managed of_mbox_request()
+ * @dev: device for channel consumer
+ * @np: device node for lookup
+ * @con_id: consumer id
+ * @nb: notifier block used for receiving messages
+ *
+ * This function behaves like of_mbox_request() but the acquired channel
+ * will be automatically released on driver detach.
+ */
+struct mbox *devm_of_mbox_request(struct device *dev, struct device_node *np,
+ const char *con_id, struct notifier_block *nb)
+{
+ struct mbox **ptr;
+ struct mbox *mbox;
+
+ ptr = devres_alloc(__devm_mbox_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+ mbox = of_mbox_request(np, con_id, nb);
+ if (!IS_ERR(mbox)) {
+ *ptr = mbox;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+ return mbox;
+}
+EXPORT_SYMBOL(devm_of_mbox_request);
diff --git a/include/linux/mbox.h b/include/linux/mbox.h
new file mode 100644
index 0000000..d577b24
--- /dev/null
+++ b/include/linux/mbox.h
@@ -0,0 +1,175 @@
+#ifndef __LINUX_MBOX_H
+#define __LINUX_MBOX_H
+
+#include <linux/notifier.h>
+#include <linux/err.h>
+#include <linux/of.h>
+
+struct mbox_adapter;
+struct mbox_channel;
+
+/**
+ * struct mbox_adapter_ops - MBOX adapter operations
+ * @put_message: hook for putting messages in the channels MBOX
+ * @request: optional hook for requesting an MBOX channel
+ * @release: optional hook for releasing an MBOX channel
+ * @owner: helps prevent removal of modules exporting active MBOX channels
+ */
+struct mbox_adapter_ops {
+ int (*put_message)(struct mbox_adapter *, struct mbox_channel *,
+ const void *, unsigned int);
+ int (*request)(struct mbox_adapter *, struct mbox_channel *);
+ int (*release)(struct mbox_adapter *, struct mbox_channel *);
+ struct module *owner;
+};
+
+struct mbox_channel {
+ unsigned int id;
+ unsigned int users;
+ struct mbox_adapter *adapter;
+ struct atomic_notifier_head notifier;
+};
+
+/**
+ * struct mbox_adapter - MBOX adapter abstraction
+ * @dev: device providing MBOX channels
+ * @ops: callback hooks for this adapter
+ * @nchannels: number of MBOX channels controlled by this adapter
+ * @channels: array of MBOX channels managed internally
+ * @of_xlate: OF translation hook for DT lookups
+ * @of_n_cells: number of cells for DT lookups
+ * @list: list node for internal use
+ * @lock: mutex for internal use
+ */
+struct mbox_adapter {
+ struct device *dev;
+ const struct mbox_adapter_ops *ops;
+ unsigned int nchannels;
+ struct mbox_channel *channels;
+ int (*of_xlate)(struct mbox_adapter *,
+ const struct of_phandle_args *args);
+ unsigned int of_n_cells;
+ struct list_head list;
+ struct mutex lock;
+};
+
+#if IS_ENABLED(CONFIG_MAILBOX)
+
+int mbox_adapter_add(struct mbox_adapter *adap);
+int mbox_adapter_remove(struct mbox_adapter *adap);
+
+int mbox_channel_notify(struct mbox_channel *chan,
+ const void *data, unsigned int len);
+
+#else
+
+static inline int mbox_adapter_add(struct mbox_adapter *adap)
+{
+ return -EINVAL;
+}
+static inline int mbox_adapter_remove(struct mbox_adapter *adap)
+{
+ return -EINVAL;
+}
+
+static inline int mbox_channel_notify(struct mbox_channel *chan,
+ const void *data, unsigned int len)
+{
+ return -EINVAL;
+}
+
+#endif
+
+struct mbox;
+
+#if IS_ENABLED(CONFIG_MAILBOX)
+
+struct mbox *mbox_request(struct device *dev, const char *con_id,
+ struct notifier_block *nb);
+void mbox_release(struct mbox *mbox);
+
+int mbox_put_message(struct mbox *mbox, const void *data, unsigned int len);
+
+struct mbox *of_mbox_request(struct device_node *np, const char *con_id,
+ struct notifier_block *nb);
+
+struct mbox *devm_mbox_request(struct device *dev, const char *con_id,
+ struct notifier_block *nb);
+
+struct mbox *devm_of_mbox_request(struct device *dev, struct device_node *np,
+ const char *con_id, struct notifier_block *nb);
+
+void devm_mbox_release(struct device *dev, struct mbox *mbox);
+
+#else
+
+static inline struct mbox *mbox_request(struct device *dev, const char *con_id,
+ struct notifier_block *nb)
+{
+ return ERR_PTR(-ENODEV);
+}
+
+void mbox_release(struct mbox *mbox)
+{
+}
+
+static inline int mbox_put_message(struct mbox *mbox,
+ const void *data, unsigned int len)
+{
+ return -EINVAL;
+}
+
+static inline struct mbox *of_mbox_request(struct device_node *np,
+ const char *con_id, struct notifier_block *nb)
+{
+ return ERR_PTR(-ENODEV);
+}
+
+static inline struct mbox *devm_mbox_request(struct device *dev,
+ const char *con_id, struct notifier_block *nb)
+{
+ return ERR_PTR(-ENODEV);
+}
+
+static inline struct mbox *devm_of_mbox_request(struct device *dev,
+ struct device_node *np, const char *con_id,
+ struct notifier_block *nb)
+{
+ return ERR_PTR(-ENODEV);
+}
+
+static inline void devm_mbox_release(struct device *dev, struct mbox *mbox)
+{
+}
+
+#endif
+
+struct mbox_lookup {
+ struct list_head list;
+ const char *provider;
+ unsigned int index;
+ const char *dev_id;
+ const char *con_id;
+};
+
+#define MBOX_LOOKUP(_provider, _index, _dev_id, _con_id) \
+ { \
+ .provider = _provider, \
+ .index = _index, \
+ .dev_id = _dev_id, \
+ .con_id = _con_id, \
+ }
+
+#if IS_ENABLED(CONFIG_MAILBOX)
+
+void mbox_add_table(struct mbox_lookup *table, unsigned int num);
+
+#else
+
+static inline void mbox_add_table(struct mbox_lookup *table, unsigned int num)
+{
+}
+
+#endif
+
+#endif /* __LINUX_MBOX_H */
--
1.8.1.5
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [RFC 0/6] mailbox: add common framework and port drivers
From: Courtney Cavin @ 2014-02-08 0:50 UTC (permalink / raw)
To: s-anna, rob.herring, rafael.j.wysocki, mark.langsdorf, tony,
omar.ramirez
Cc: gregkh, pawel.moll, mark.rutland, ijc+devicetree, galak, rob,
linux-doc, devicetree, linux-kernel
There is currently no common framework for mailbox drivers, so this is my
attempt to come up with something suitable. There seems to be a need for
making this generic, so I have attempted to do just that. Most of this is
modeled pretty strongly after the pwm core, with some influences from the clock
core.
Looking at the existing use-cases, and some new ones, it would appear that the
requirements here are rather simple. We need essentially two things for
consumers:
- put_message
- callback for receiving messages
The code currently uses atomic notifiers for callbacks. The common omap core
deals with fifos and work-queues in order to escape atomic contexts, but from
what I can see, this is unneeded. I am also of the opinion that the contexts
can be much better managed in the drivers which are working with these
contexts, rather than generically.
Hopefully this will be suitable for the plethora of other drivers around the
kernel which implement mailboxes, as well. In any case, I'm rather interested
to see what the rest of the world thinks.
Keep in mind that while the pl320 & omap code should compile, I don't currently
have a platform on which I can perform proper testing. I also removed the
context save/restore code from omap2 mailbox support, because I think it should
be able to be done via driver suspend/resume, but haven't done a full
investigation just yet.
I'm also aware that breaking omap, just to fix it again probably isn't the best
course of action, and I'm open to suggestions.
-Courtney
Courtney Cavin (6):
mailbox: add core framework
mailbox: document bindings
mailbox: pl320: migrate to mbox framework
mailbox: omap: remove omap-specific framework
mailbox: omap1: move to common mbox framework
mailbox: omap2+: move to common mbox framework
.../devicetree/bindings/mailbox/mailbox.txt | 44 ++
drivers/mailbox/Kconfig | 17 -
drivers/mailbox/Makefile | 2 +-
drivers/mailbox/core.c | 573 +++++++++++++++++++++
drivers/mailbox/mailbox-omap1.c | 153 +++---
drivers/mailbox/mailbox-omap2.c | 315 +++++------
drivers/mailbox/omap-mailbox.c | 469 -----------------
drivers/mailbox/omap-mbox.h | 67 ---
drivers/mailbox/pl320-ipc.c | 258 +++++++---
include/linux/mailbox.h | 29 +-
include/linux/mbox.h | 175 +++++++
include/linux/omap-mailbox.h | 45 +-
12 files changed, 1261 insertions(+), 886 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mailbox/mailbox.txt
create mode 100644 drivers/mailbox/core.c
delete mode 100644 drivers/mailbox/omap-mailbox.c
delete mode 100644 drivers/mailbox/omap-mbox.h
create mode 100644 include/linux/mbox.h
--
1.8.1.5
^ permalink raw reply
* Re: [PATCH] pci: Add support for creating a generic host_bridge from device tree
From: Tanmay Inamdar @ 2014-02-08 0:21 UTC (permalink / raw)
To: Tanmay Inamdar, Arnd Bergmann, devicetree@vger.kernel.org,
linaro-kernel, linux-pci, Will Deacon, LKML, Catalin Marinas,
Bjorn Helgaas, LAKML
In-Reply-To: <20140206101814.GA4993@e106497-lin.cambridge.arm.com>
On Thu, Feb 6, 2014 at 2:18 AM, Liviu Dudau <Liviu.Dudau@arm.com> wrote:
> On Wed, Feb 05, 2014 at 10:26:27PM +0000, Tanmay Inamdar wrote:
>> Hello Liviu,
>>
>> I did not get the first email of this particular patch on any of
>> subscribed mailing lists (don't know why), hence replying here.
>
> Strange, it shows in the MARC and GMANE archive for linux-pci, probably
> a hickup on your receiving side?
>
>>
>> +struct pci_host_bridge *
>> +pci_host_bridge_of_init(struct device *parent, int busno, struct pci_ops *ops,
>> + void *host_data, struct list_head *resources)
>> +{
>> + struct pci_bus *root_bus;
>> + struct pci_host_bridge *bridge;
>> +
>> + /* first parse the host bridge bus ranges */
>> + if (pci_host_bridge_of_get_ranges(parent->of_node, resources))
>> + return NULL;
>> +
>> + /* then create the root bus */
>> + root_bus = pci_create_root_bus(parent, busno, ops, host_data, resources);
>> + if (!root_bus)
>> + return NULL;
>> +
>> + bridge = to_pci_host_bridge(root_bus->bridge);
>> +
>> + return bridge;
>> +}
>>
>> You are keeping the domain_nr inside pci_host_bridge structure. In
>> above API, domain_nr is required in 'pci_find_bus' function called
>> from 'pci_create_root_bus'. Since the bridge is allocated after
>> creating root bus, 'pci_find_bus' always gets domain_nr as 0. This
>> will cause problem for scanning multiple domains.
>
> Good catch. I was switching between creating a pci_controller in arch/arm64 and
> adding the needed bits in pci_host_bridge. After internal review I've decided to
> add the domain_nr to pci_host_bridge, but forgot to update the code everywhere.
>
> Thanks for reviewing this, will fix in v2.
>
> Do you find porting to the new API straight forward?
It is quite straight forward for MEM regions but for IO regions it is
not. You always assume IO resource starting at 0x0. IMO, this will
cause problem for systems with multiple ports / IO windows. You can
take a look at 'drivers/pci/host/pcie-designware.c'.
Also the manipulations of addresses for IO_RESOURCES can be dangerous.
It can make some value negative. For example if my PCI IO range starts
in 32 bit address range say 0x8000_0000 with CPU address
0xb0_8000_0000, window->offset after manipulation will become
negative.
Personally I would like to do get all the PCI and CPU addresses from
my device tree as is and do the 'pci_add_resource_offset'. This will
help me setup my regions correctly without any further arithmetic.
Please let me know what you think.
>
> Best regards,
> Liviu
>
>>
>>
>> On Mon, Feb 3, 2014 at 10:46 AM, Arnd Bergmann <arnd@arndb.de> wrote:
>> > On Monday 03 February 2014 18:33:48 Liviu Dudau wrote:
>> >> +/**
>> >> + * pci_host_bridge_of_get_ranges - Parse PCI host bridge resources from DT
>> >> + * @dev: device node of the host bridge having the range property
>> >> + * @resources: list where the range of resources will be added after DT parsing
>> >> + *
>> >> + * This function will parse the "ranges" property of a PCI host bridge device
>> >> + * node and setup the resource mapping based on its content. It is expected
>> >> + * that the property conforms with the Power ePAPR document.
>> >> + *
>> >> + * Each architecture will then apply their filtering based on the limitations
>> >> + * of each platform. One general restriction seems to be the number of IO space
>> >> + * ranges, the PCI framework makes intensive use of struct resource management,
>> >> + * and for IORESOURCE_IO types they can only be requested if they are contained
>> >> + * within the global ioport_resource, so that should be limited to one IO space
>> >> + * range.
>> >
>> > Actually we have quite a different set of restrictions around I/O space on ARM32
>> > at the moment: Each host bridge can have its own 64KB range in an arbitrary
>> > location on MMIO space, and the total must not exceed 2MB of I/O space.
>> >
>> >> + */
>> >> +static int pci_host_bridge_of_get_ranges(struct device_node *dev,
>> >> + struct list_head *resources)
>> >> +{
>> >> + struct resource *res;
>> >> + struct of_pci_range range;
>> >> + struct of_pci_range_parser parser;
>> >> + int err;
>> >> +
>> >> + pr_info("PCI host bridge %s ranges:\n", dev->full_name);
>> >> +
>> >> + /* Check for ranges property */
>> >> + err = of_pci_range_parser_init(&parser, dev);
>> >> + if (err)
>> >> + return err;
>> >> +
>> >> + pr_debug("Parsing ranges property...\n");
>> >> + for_each_of_pci_range(&parser, &range) {
>> >> + /* Read next ranges element */
>> >> + pr_debug("pci_space: 0x%08x pci_addr:0x%016llx ",
>> >> + range.pci_space, range.pci_addr);
>> >> + pr_debug("cpu_addr:0x%016llx size:0x%016llx\n",
>> >> + range.cpu_addr, range.size);
>> >> +
>> >> + /* If we failed translation or got a zero-sized region
>> >> + * (some FW try to feed us with non sensical zero sized regions
>> >> + * such as power3 which look like some kind of attempt
>> >> + * at exposing the VGA memory hole) then skip this range
>> >> + */
>> >> + if (range.cpu_addr == OF_BAD_ADDR || range.size == 0)
>> >> + continue;
>> >> +
>> >> + res = kzalloc(sizeof(struct resource), GFP_KERNEL);
>> >> + if (!res) {
>> >> + err = -ENOMEM;
>> >> + goto bridge_ranges_nomem;
>> >> + }
>> >> +
>> >> + of_pci_range_to_resource(&range, dev, res);
>> >> +
>> >> + pci_add_resource_offset(resources, res,
>> >> + range.cpu_addr - range.pci_addr);
>> >> + }
>> >
>> > I believe of_pci_range_to_resource() will return the MMIO aperture for the
>> > I/O space window here, which is not what you are supposed to pass into
>> > pci_add_resource_offset.
>> >
>> >> +EXPORT_SYMBOL(pci_host_bridge_of_init);
>> >
>> > EXPORT_SYMBOL_GPL
>> >
>> >> diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
>> >> index 6e34498..16febae 100644
>> >> --- a/drivers/pci/probe.c
>> >> +++ b/drivers/pci/probe.c
>> >> @@ -1787,6 +1787,17 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
>> >> list_for_each_entry_safe(window, n, resources, list) {
>> >> list_move_tail(&window->list, &bridge->windows);
>> >> res = window->res;
>> >> + /*
>> >> + * IO resources are stored in the kernel with a CPU start
>> >> + * address of zero. Adjust the data accordingly and remember
>> >> + * the offset
>> >> + */
>> >> + if (resource_type(res) == IORESOURCE_IO) {
>> >> + bridge->io_offset = res->start;
>> >> + res->end -= res->start;
>> >> + window->offset -= res->start;
>> >> + res->start = 0;
>> >> + }
>> >> offset = window->offset;
>> >> if (res->flags & IORESOURCE_BUS)
>> >
>> > Won't this break all existing host bridges?
>> >
>> > Arnd
>> >
>> > _______________________________________________
>> > linux-arm-kernel mailing list
>> > linux-arm-kernel@lists.infradead.org
>> > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>
>
> --
> ====================
> | I would like to |
> | fix the world, |
> | but they're not |
> | giving me the |
> \ source code! /
> ---------------
> ¯\_(ツ)_/¯
>
> -- IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.
>
> ARM Limited, Registered office 110 Fulbourn Road, Cambridge CB1 9NJ, Registered in England & Wales, Company No: 2557590
> ARM Holdings plc, Registered office 110 Fulbourn Road, Cambridge CB1 9NJ, Registered in England & Wales, Company No: 2548782
>
^ permalink raw reply
* Re: [PATCH v5 00/14] Add support for MSM's mmio clock/reset controller
From: Frank Rowand @ 2014-02-07 23:36 UTC (permalink / raw)
To: Joe Perches
Cc: Stephen Boyd, Mike Turquette, linux-kernel, linux-arm-msm,
linux-arm-kernel, devicetree, Saravana Kannan
In-Reply-To: <1391806318.28199.6.camel@joe-AO722>
On 2/7/2014 12:51 PM, Joe Perches wrote:
> On Fri, 2014-02-07 at 11:38 -0800, Frank Rowand wrote:
>> On 2/6/2014 9:11 PM, Joe Perches wrote:
>>> For patch 1, what checkpatch bug might that be?
> []
>> Sorry, it is patch 2, not patch 1 ("[PATCH v5 02/14] clk: Add set_rate_and_parent() op"):
>>
>> WARNING: Multiple spaces after return type
>> #188: FILE: include/linux/clk-provider.h:154:
>> + int (*set_rate_and_parent)(struct clk_hw *hw,
>>
>> total: 0 errors, 1 warnings, 152 lines checked
>
> Yup, that one might be a bit aggressive.
>
> It's a complaint about function pointer declaration style.
>
> from checkpatch:
> ------------------------------------------------------
> # unnecessary space "type (*funcptr)(args...)"
> elsif ($declare =~ /\s{2,}$/) {
> WARN("SPACING",
> "Multiple spaces after return type\n" . $herecurr);
> }
> ------------------------------------------------------
>
> This is warning about style equivalent to declarations like:
>
> int foo(int bar);
>
> checkpatch doesn't warn about declarations of that style,
> so likely checkpatch shouldn't warn about multiple spaces
> after a function pointer return type either.
>
> I don't have a strong opinion one way or another about it.
>
> If you think it should be silenced, it could be either
> downgraded to a CHK or removed altogether.
OK, now the warning makes sense. I was reading "spaces" to mean
the space character instead of white space.
I don't have a strong opinion either, but downgrading to a CHK
would be nice.
It would be less confusing to me (though awkward sounding) to
change the message to "Multiple whitespaces after return type".
-Frank
^ permalink raw reply
* Re: [PATCH v2] ARM: sunxi: Add driver for sunxi usb phy
From: Hans de Goede @ 2014-02-07 23:27 UTC (permalink / raw)
To: Maxime Ripard
Cc: Kishon Vijay Abraham I,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
In-Reply-To: <20140207223646.GU3192@lukather>
Hi,
On 02/07/2014 11:36 PM, Maxime Ripard wrote:
> Hi Hans,
>
> It looks very nice, I just have a few comments below though.
>
> On Fri, Feb 07, 2014 at 05:33:21PM +0100, Hans de Goede wrote:
>> The Allwinner A1x / A2x SoCs have 2 or 3 usb phys which are all accessed
>> through a single set of registers. Besides this there are also some other
>> phy related bits which need poking, which are per phy, but shared between the
>> ohci and ehci controllers, so these are also controlled from this new phy
>> driver.
>>
>> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>> ---
>> .../devicetree/bindings/phy/sun4i-usb-phy.txt | 28 ++
>> drivers/phy/Kconfig | 11 +
>> drivers/phy/Makefile | 1 +
>> drivers/phy/phy-sun4i-usb.c | 326 +++++++++++++++++++++
>> 4 files changed, 366 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> create mode 100644 drivers/phy/phy-sun4i-usb.c
>>
>> diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> new file mode 100644
>> index 0000000..f7eccb2
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> @@ -0,0 +1,28 @@
>> +Allwinner sun4i USB PHY
>> +-----------------------
>> +
>> +Required properties:
>> +- compatible : should be one of "allwinner,sun4i-a10-usb-phy",
>> + "allwinner,sun5i-a13-usb-phy" or "allwinner,sun7i-a20-usb-phy"
>> +- reg : a list of offset + length pairs, the 1st list entry should point to
>> + the phy base regs, the 2nd entry to the pmu reg for phy1, and the 3th
>> + entry to the pmu reg of phy2 (for devices which have a phy2).
>
> I'm concerned about devices that would only have a phy2 for some
> reason.
phy1 and phy2 are identical, so in that case we would just call the phy
phy1 I guess, and specify its register where ever it lives and be done with
it.
> Using reg-names would be much more robust, and is quite painless to
> use. Just use platform_get_resource_by_name instead of
> platform_get_resource, and that's pretty much it.
The above argument does not really help to convince me to use
register-names, I don't really see them as useful / necessary,
adding support for them will just grow the driver-code, as well
as the devicetree bindings docs, as well as the dts files.
But if you really really want me to use register-names, just say so
and I'll modify the patch.
>
>> +- #phy-cells : from the generic phy bindings, must be 1
>> +
>> +Optional properties:
>> +- clocks : phandle + clock specifier for the phy clock
>> +- clock-names : "usb_phy"
>> +- resets : a list of phandle + reset specifier pairs
>> +- reset-names : "usb0_reset", "usb1_reset", and / or "usb2_reset"
>> +
>> +Example:
>> + usbphy: phy@0x01c13400 {
>> + #phy-cells = <1>;
>> + compatible = "allwinner,sun4i-a10-usb-phy";
>> + /* phy base regs, phy1 pmu reg, phy2 pmu reg */
>> + reg = <0x01c13400 0x10 0x01c14800 0x4 0x01c1c800 0x4>;
>> + clocks = <&usb_clk 8>;
>> + clock-names = "usb_phy";
>> + resets = <&usb_clk 1>, <&usb_clk 2>;
>> + reset-names = "usb1_reset", "usb2_reset";
>> + };
>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>> index afa2354..6070c99 100644
>> --- a/drivers/phy/Kconfig
>> +++ b/drivers/phy/Kconfig
>> @@ -64,4 +64,15 @@ config BCM_KONA_USB2_PHY
>> help
>> Enable this to support the Broadcom Kona USB 2.0 PHY.
>>
>> +config PHY_SUN4I_USB
>> + tristate "Allwinner sunxi SoC USB PHY driver"
>> + depends on ARCH_SUNXI
>> + select GENERIC_PHY
>> + help
>> + Enable this to support the transceiver that is part of Allwinner
>> + sunxi SoCs.
>> +
>> + This driver controls the entire USB PHY block, both the USB OTG
>> + parts, as well as the 2 regular USB 2 host PHYs.
>> +
>> endmenu
>> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
>> index b57c253..9d4f8bb 100644
>> --- a/drivers/phy/Makefile
>> +++ b/drivers/phy/Makefile
>> @@ -9,3 +9,4 @@ obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
>> obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
>> obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
>> obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
>> +obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o
>> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
>> new file mode 100644
>> index 0000000..bd9cb7fa
>> --- /dev/null
>> +++ b/drivers/phy/phy-sun4i-usb.c
>> @@ -0,0 +1,326 @@
>> +/*
>> + * Allwinner sun4i USB phy driver
>> + *
>> + * Copyright (C) 2014 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>> + *
>> + * Based on code from
>> + * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
>> + *
>> + * Modelled after: Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver
>> + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
>> + * Author: Sylwester Nawrocki <s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/io.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/phy/phy.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/reset.h>
>> +
>> +#define REG_ISCR 0x00
>> +#define REG_PHYCTL 0x04
>> +#define REG_PHYBIST 0x08
>> +#define REG_PHYTUNE 0x0c
>> +
>> +#define SUNXI_AHB_ICHR8_EN BIT(10)
>> +#define SUNXI_AHB_INCR4_BURST_EN BIT(9)
>> +#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8)
>> +#define SUNXI_ULPI_BYPASS_EN BIT(0)
>> +
>> +/* Common Control Bits for Both PHYs */
>> +#define PHY_PLL_BW 0x03
>> +#define PHY_RES45_CAL_EN 0x0c
>> +
>> +/* Private Control Bits for Each PHY */
>> +#define PHY_TX_AMPLITUDE_TUNE 0x20
>> +#define PHY_TX_SLEWRATE_TUNE 0x22
>> +#define PHY_VBUSVALID_TH_SEL 0x25
>> +#define PHY_PULLUP_RES_SEL 0x27
>> +#define PHY_OTG_FUNC_EN 0x28
>> +#define PHY_VBUS_DET_EN 0x29
>> +#define PHY_DISCON_TH_SEL 0x2a
>> +
>> +#define MAX_PHYS 3
>> +
>> +struct sun4i_usb_phy_data {
>> + struct clk *clk;
>> + void __iomem *base;
>> + struct mutex mutex;
>> + int num_phys;
>> + u32 disc_thresh;
>> + struct sun4i_usb_phy {
>> + struct phy *phy;
>> + void __iomem *pmu;
>> + struct regulator *vbus;
>> + struct reset_control *reset;
>> + int index;
>> + } phys[MAX_PHYS];
>> +};
>> +
>> +#define to_sun4i_usb_phy_data(phy) \
>> + container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
>> +
>> +static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
>> + int len)
>> +{
>> + struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy);
>> + u32 temp, usbc_bit = BIT(phy->index * 2);
>> + int i;
>> +
>> + mutex_lock(&phy_data->mutex);
>> +
>> + for (i = 0; i < len; i++) {
>> + temp = readl(phy_data->base + REG_PHYCTL);
>> +
>> + /* clear the address portion */
>> + temp &= ~(0xff << 8);
>> +
>> + /* set the address */
>> + temp |= ((addr + i) << 8);
>> + writel(temp, phy_data->base + REG_PHYCTL);
>> +
>> + /* set the data bit and clear usbc bit*/
>> + temp = readb(phy_data->base + REG_PHYCTL);
>> + if (data & 0x1)
>> + temp |= BIT(7);
>> + else
>> + temp &= ~BIT(7);
>> + temp &= ~usbc_bit;
>> + writeb(temp, phy_data->base + REG_PHYCTL);
>> +
>> + /* pulse usbc_bit */
>> + temp = readb(phy_data->base + REG_PHYCTL);
>> + temp |= usbc_bit;
>> + writeb(temp, phy_data->base + REG_PHYCTL);
>> +
>> + temp = readb(phy_data->base + REG_PHYCTL);
>> + temp &= ~usbc_bit;
>> + writeb(temp, phy_data->base + REG_PHYCTL);
>> +
>> + data >>= 1;
>> + }
>> + mutex_unlock(&phy_data->mutex);
>> +}
>> +
>> +static void sun4i_usb_phy_passby(struct sun4i_usb_phy *phy, int enable)
>> +{
>> + u32 bits, reg_value;
>> +
>> + if (!phy->pmu)
>> + return;
>> +
>> + bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN |
>> + SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN;
>> +
>> + reg_value = readl(phy->pmu);
>> +
>> + if (enable)
>> + reg_value |= bits;
>> + else
>> + reg_value &= ~bits;
>> +
>> + writel(reg_value, phy->pmu);
>> +}
>> +
>> +static int sun4i_usb_phy_init(struct phy *_phy)
>> +{
>> + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> + struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> + int ret;
>> +
>> + ret = clk_prepare_enable(data->clk);
>> + if (ret)
>> + return ret;
>> +
>> + ret = reset_control_deassert(phy->reset);
>> + if (ret) {
>> + clk_disable_unprepare(data->clk);
>> + return ret;
>> + }
>> +
>> + /* Adjust PHY's magnitude and rate */
>> + sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
>> +
>> + /* Disconnect threshold adjustment */
>> + sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, data->disc_thresh, 2);
>> +
>> + sun4i_usb_phy_passby(phy, 1);
>> +
>> + return 0;
>> +}
>> +
>> +static int sun4i_usb_phy_exit(struct phy *_phy)
>> +{
>> + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> + struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +
>> + sun4i_usb_phy_passby(phy, 0);
>> + reset_control_assert(phy->reset);
>> + clk_disable_unprepare(data->clk);
>> +
>> + return 0;
>> +}
>> +
>> +static int sun4i_usb_phy_power_on(struct phy *_phy)
>> +{
>> + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> + int ret = 0;
>> +
>> + if (phy->vbus)
>> + ret = regulator_enable(phy->vbus);
>> +
>> + return ret;
>> +}
>> +
>> +static int sun4i_usb_phy_power_off(struct phy *_phy)
>> +{
>> + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +
>> + if (phy->vbus)
>> + regulator_disable(phy->vbus);
>> +
>> + return 0;
>> +}
>> +
>> +static struct phy_ops sun4i_usb_phy_ops = {
>> + .init = sun4i_usb_phy_init,
>> + .exit = sun4i_usb_phy_exit,
>> + .power_on = sun4i_usb_phy_power_on,
>> + .power_off = sun4i_usb_phy_power_off,
>> + .owner = THIS_MODULE,
>> +};
>> +
>> +static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>> + struct of_phandle_args *args)
>> +{
>> + struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
>> +
>> + if (WARN_ON(args->args[0] == 0 || args->args[0] >= data->num_phys))
>> + return ERR_PTR(-ENODEV);
>> +
>> + return data->phys[args->args[0]].phy;
>> +}
>> +
>> +static int sun4i_usb_phy_probe(struct platform_device *pdev)
>> +{
>> + struct sun4i_usb_phy_data *data;
>> + struct device *dev = &pdev->dev;
>> + struct device_node *np = dev->of_node;
>> + void __iomem *pmu = NULL;
>> + struct phy_provider *phy_provider;
>> + struct reset_control *reset;
>> + struct regulator *vbus;
>> + struct phy *phy;
>> + char name[16];
>> + int i;
>> +
>> + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>> + if (!data)
>> + return -ENOMEM;
>> +
>> + mutex_init(&data->mutex);
>> +
>> + if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>> + data->num_phys = 2;
>> + else
>> + data->num_phys = 3;
>> +
>> + if (of_device_is_compatible(np, "allwinner,sun4i-a10-usb-phy"))
>> + data->disc_thresh = 3;
>> + else
>> + data->disc_thresh = 2;
>
> I'd still prefer to pass this through the .data field of of_device_id,
> but it looks much cleaner already :)
The problem with using the .data field is that I can only store a single
integer there. To store 2 I need to: define a struct, create an array
of these structs with initialization. Create an enum for indexing the
array which must be kept in sync with the initializers manually, store
either the index, or a direct pointer to the correct array entry into
the .data field, add code to get the of_device_id from the compatible
string, and then finally extract the settings from the struct again.
See IE how this is done in drivers/ata/ahci_platform.c, I've tried
to come up with a simpler way and failed, for ahci_platform.c the
struct with per compatible-string data is quite big so it makes some
sense to use this construction. Here however not so much, this adds a
whole lot of unnecessary extra code + indirection. I esp. object against
the indirection as that unnecessarily makes it harder to follow whats
going on.
>
>> + data->clk = devm_clk_get(dev, "usb_phy");
>> + if (IS_ERR(data->clk)) {
>> + dev_err(dev, "could not get usb_phy clock\n");
>> + return PTR_ERR(data->clk);
>> + }
>> +
>> + /* Skip 0, 0 is the phy for otg which is not yet supported. */
>> + for (i = 1; i < data->num_phys; i++) {
>> + snprintf(name, sizeof(name), "usb%d_vbus", i);
>> + vbus = devm_regulator_get_optional(dev, name);
>> + if (IS_ERR(vbus)) {
>> + if (PTR_ERR(vbus) == -EPROBE_DEFER)
>> + return -EPROBE_DEFER;
>> + vbus = NULL;
>> + }
>> +
>> + snprintf(name, sizeof(name), "usb%d_reset", i);
>> + reset = devm_reset_control_get(dev, name);
>> + if (IS_ERR(phy)) {
>> + dev_err(dev, "failed to get reset %s\n", name);
>> + return PTR_ERR(phy);
>> + }
>> +
>> + if (i) { /* No pmu for usbc0 */
>> + pmu = devm_ioremap_resource(dev,
>> + platform_get_resource(pdev, IORESOURCE_MEM, i));
>> + if (IS_ERR(pmu))
>> + return PTR_ERR(pmu);
>> + }
>> +
>> + phy = devm_phy_create(dev, &sun4i_usb_phy_ops, NULL);
>> + if (IS_ERR(phy)) {
>> + dev_err(dev, "failed to create PHY %d\n", i);
>> + return PTR_ERR(phy);
>> + }
>> +
>> + data->phys[i].phy = phy;
>> + data->phys[i].pmu = pmu;
>> + data->phys[i].vbus = vbus;
>> + data->phys[i].reset = reset;
>> + data->phys[i].index = i;
>> + phy_set_drvdata(phy, &data->phys[i]);
>> + }
>> +
>> + data->base = devm_ioremap_resource(dev,
>> + platform_get_resource(pdev, IORESOURCE_MEM, 0));
>> + if (IS_ERR(data->base))
>> + return PTR_ERR(data->base);
>> +
>> + dev_set_drvdata(dev, data);
>> + phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>> + if (IS_ERR(phy_provider))
>> + return PTR_ERR(phy_provider);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct of_device_id sun4i_usb_phy_of_match[] = {
>> + { .compatible = "allwinner,sun4i-a10-usb-phy" },
>> + { .compatible = "allwinner,sun5i-a13-usb-phy" },
>> + { .compatible = "allwinner,sun7i-a20-usb-phy" },
>> + { },
>> +};
>> +MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
>> +
>> +static struct platform_driver sun4i_usb_phy_driver = {
>> + .probe = sun4i_usb_phy_probe,
>> + .driver = {
>> + .of_match_table = sun4i_usb_phy_of_match,
>> + .name = "sun4i-usb-phy",
>> + .owner = THIS_MODULE,
>> + }
>> +};
>> +module_platform_driver(sun4i_usb_phy_driver);
>> +
>> +MODULE_DESCRIPTION("Allwinner sun4i USB phy driver");
>> +MODULE_AUTHOR("Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>");
>> +MODULE_LICENSE("GPL v2");
>> --
>> 1.8.4.2
>>
>
Regards,
Hans
^ permalink raw reply
* Re: [PATCH v9 0/4] ohci-platform and ehci-plaform patches rebased on 3.14-rc1
From: Hans de Goede @ 2014-02-07 23:05 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Alan Stern, Tony Prisk, linux-usb, Maxime Ripard,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree
In-Reply-To: <20140207223101.GA21062-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>
Hi,
On 02/07/2014 11:31 PM, Greg Kroah-Hartman wrote:
> On Fri, Feb 07, 2014 at 04:36:39PM +0100, Hans de Goede wrote:
>> Hi Greg,
>>
>> Here is v9 of my ohci-platform and ehci-platform patchset, It is just a
>> rebase (with some manual conflict resolution), to make it easier for you
>> to throw this into usb-next, there are no other changes.
>
> I've applied these, thanks. I saw a number of older series and
> revisions, and I assumed this was the latest, right?
Yes this was the latest.
Regards,
Hans
^ permalink raw reply
* Re: [PATCHv4 4/7] hwspinlock/core: add common OF helpers
From: Bjorn Andersson @ 2014-02-07 22:49 UTC (permalink / raw)
To: Suman Anna
Cc: Ohad Ben-Cohen, Mark Rutland, Tony Lindgren, Kumar Gala,
linux-kernel@vger.kernel.org, linux-omap,
devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org
In-Reply-To: <1389658764-39199-5-git-send-email-s-anna@ti.com>
On Mon, Jan 13, 2014 at 4:19 PM, Suman Anna <s-anna@ti.com> wrote:
> This patch adds three new OF helper functions to use/request
> locks from a hwspinlock device instantiated through a
> device-tree blob.
Nice, I ran in to the problem of needing a probe deferral on a
hwspinlock earlier this week so I implemented this yesterday...then I
got a pointer to your series.
[snip]
> /**
> + * of_hwspin_lock_request_specific() - request a OF phandle-based specific lock
> + * @np: device node from which to request the specific hwlock
> + * @propname: property name containing hwlock specifier(s)
> + * @index: index of the hwlock
> + *
> + * This function is the OF equivalent of hwspin_lock_request_specific(). This
> + * function provides a means for users of the hwspinlock module to request a
> + * specific hwspinlock using the phandle of the hwspinlock device. The requested
> + * lock number is indexed relative to the hwspinlock device, unlike the
> + * hwspin_lock_request_specific() which is an absolute lock number.
> + *
> + * Returns the address of the assigned hwspinlock, or NULL on error
> + */
> +struct hwspinlock *of_hwspin_lock_request_specific(struct device_node *np,
> + const char *propname, int index)
> +{
> + struct hwspinlock_device *bank;
> + struct of_phandle_args args;
> + int id;
> + int ret;
> +
> + ret = of_parse_phandle_with_args(np, propname, "#hwlock-cells", index,
> + &args);
> + if (ret) {
> + pr_warn("%s: can't parse hwlocks property of node '%s[%d]' ret = %d\n",
> + __func__, np->full_name, index, ret);
> + return NULL;
> + }
of_parse_phandle_with_args() already does pr_err if it can't find the
phandle and on some of the issues related to arguments. So please
remove this pr_warn().
It seems to be standard practice to pass the error value back to the
consumer, so you should
return ERR_PTR(ret); here instead of the NULL...
> +
> + mutex_lock(&hwspinlock_tree_lock);
> + list_for_each_entry(bank, &hwspinlock_devices, list)
> + if (bank->dev->of_node == args.np)
> + break;
> + mutex_unlock(&hwspinlock_tree_lock);
> + if (&bank->list == &hwspinlock_devices) {
> + pr_warn("%s: requested hwspinlock device %s is not registered\n",
> + __func__, args.np->full_name);
> + return NULL;
...especially since you want the consumer to have the ability to
identify this error. Here you should
return ERR_PTR(-EPROBE_DEFER); so that the consumer knows that this
lock is not _yet_ registered, but will be in the future.
You should remove this pr_warn as well. The standard use of this
function would be in a probe() and just returning this error value
from that probe will give you a line in the log indicating that this
was in fact the issue.
> + }
> +
> + id = bank->ops->of_xlate(bank, &args);
> + if (id < 0 || id >= bank->num_locks) {
> + pr_warn("%s: requested lock %d is either out of range [0, %d] or failed translation\n",
> + __func__, id, bank->num_locks - 1);
> + return NULL;
Please return ERR_PTR(-EINVAL); here.
Looking forward to your next spin, as I will actually use this interface :)
Regards,
Bjorn
^ permalink raw reply
* Re: [PATCH V3] net/dt: Add support for overriding phy configuration from device tree
From: Florian Fainelli @ 2014-02-07 22:43 UTC (permalink / raw)
To: David Laight
Cc: Matthew Garrett, netdev,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Kishon Vijay Abraham I
In-Reply-To: <063D6719AE5E284EB5DD2968C1650D6D0F6B8BCA-VkEWCZq2GCInGFn1LkZF6NBPR1lH4CV8@public.gmane.org>
2014-02-05 David Laight <David.Laight-JxhZ9S5GRejQT0dZR+AlfA@public.gmane.org>:
> From: Florian Fainelli
>> It would be good to explain exactly how your hardware is broken
>> exactly. I really do not think that such a fine-grained setting where
>> you could disable, e.g: 100BaseT_Full, but allow 100BaseT_Half to
>> remain usable makes that much sense. In general, Gigabit might be
>> badly broken, but 100 and 10Mbits/sec should work fine. How about the
>> MASTER-SLAVE bit, is overriding it really required?
>
> There are plenty of systems out there where you'd want to disable
> either HDX or FDX modes.
> The MAC unit has to know whether the PHY is in HDX or FDX in order
> to work properly. Many do not need to know the speed - since the
> PHY is responsible for the tx/rx fifo clock.
> Getting the negotiated speed out of the PHY can be difficult, while
> the ANAR can easily be set.
> Unfortunately it is usually impossible to disable the 'fall-back'
> 10M HDX.
The problem that I have with that approach in general is that:
- it bloats the code for a set of properties that are going to be used
by hopefully a few percentage of the actual Device Trees out there
- it puts no limits on what is acceptable/best-practice to be put in
terms of configuration in the Device Tree, how about the 16x16 other
register values out there which are standardized?
- a PHY fixup should be registered based on the top-level compatible
property for a given board where the specific PHY on a specific board
is known to be broken
- make things incredibly harder to debug than they are today
I do acknowledge the need to have a solution to these problems, but
this seems to duplicate existing mechanisms available (e.g: PHY
fixups) without leveraging information that should be properly flagged
in the Device Tree (board model, root-node compatible string etc...)
to allow software to take corrective measures.
--
Florian
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v2] ARM: sunxi: Add driver for sunxi usb phy
From: Maxime Ripard @ 2014-02-07 22:36 UTC (permalink / raw)
To: Hans de Goede
Cc: Kishon Vijay Abraham I,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
In-Reply-To: <1391790801-27864-1-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 13764 bytes --]
Hi Hans,
It looks very nice, I just have a few comments below though.
On Fri, Feb 07, 2014 at 05:33:21PM +0100, Hans de Goede wrote:
> The Allwinner A1x / A2x SoCs have 2 or 3 usb phys which are all accessed
> through a single set of registers. Besides this there are also some other
> phy related bits which need poking, which are per phy, but shared between the
> ohci and ehci controllers, so these are also controlled from this new phy
> driver.
>
> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> ---
> .../devicetree/bindings/phy/sun4i-usb-phy.txt | 28 ++
> drivers/phy/Kconfig | 11 +
> drivers/phy/Makefile | 1 +
> drivers/phy/phy-sun4i-usb.c | 326 +++++++++++++++++++++
> 4 files changed, 366 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
> create mode 100644 drivers/phy/phy-sun4i-usb.c
>
> diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
> new file mode 100644
> index 0000000..f7eccb2
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
> @@ -0,0 +1,28 @@
> +Allwinner sun4i USB PHY
> +-----------------------
> +
> +Required properties:
> +- compatible : should be one of "allwinner,sun4i-a10-usb-phy",
> + "allwinner,sun5i-a13-usb-phy" or "allwinner,sun7i-a20-usb-phy"
> +- reg : a list of offset + length pairs, the 1st list entry should point to
> + the phy base regs, the 2nd entry to the pmu reg for phy1, and the 3th
> + entry to the pmu reg of phy2 (for devices which have a phy2).
I'm concerned about devices that would only have a phy2 for some
reason.
Using reg-names would be much more robust, and is quite painless to
use. Just use platform_get_resource_by_name instead of
platform_get_resource, and that's pretty much it.
> +- #phy-cells : from the generic phy bindings, must be 1
> +
> +Optional properties:
> +- clocks : phandle + clock specifier for the phy clock
> +- clock-names : "usb_phy"
> +- resets : a list of phandle + reset specifier pairs
> +- reset-names : "usb0_reset", "usb1_reset", and / or "usb2_reset"
> +
> +Example:
> + usbphy: phy@0x01c13400 {
> + #phy-cells = <1>;
> + compatible = "allwinner,sun4i-a10-usb-phy";
> + /* phy base regs, phy1 pmu reg, phy2 pmu reg */
> + reg = <0x01c13400 0x10 0x01c14800 0x4 0x01c1c800 0x4>;
> + clocks = <&usb_clk 8>;
> + clock-names = "usb_phy";
> + resets = <&usb_clk 1>, <&usb_clk 2>;
> + reset-names = "usb1_reset", "usb2_reset";
> + };
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index afa2354..6070c99 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -64,4 +64,15 @@ config BCM_KONA_USB2_PHY
> help
> Enable this to support the Broadcom Kona USB 2.0 PHY.
>
> +config PHY_SUN4I_USB
> + tristate "Allwinner sunxi SoC USB PHY driver"
> + depends on ARCH_SUNXI
> + select GENERIC_PHY
> + help
> + Enable this to support the transceiver that is part of Allwinner
> + sunxi SoCs.
> +
> + This driver controls the entire USB PHY block, both the USB OTG
> + parts, as well as the 2 regular USB 2 host PHYs.
> +
> endmenu
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index b57c253..9d4f8bb 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -9,3 +9,4 @@ obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
> obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
> obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
> obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
> +obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o
> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
> new file mode 100644
> index 0000000..bd9cb7fa
> --- /dev/null
> +++ b/drivers/phy/phy-sun4i-usb.c
> @@ -0,0 +1,326 @@
> +/*
> + * Allwinner sun4i USB phy driver
> + *
> + * Copyright (C) 2014 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> + *
> + * Based on code from
> + * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
> + *
> + * Modelled after: Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver
> + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
> + * Author: Sylwester Nawrocki <s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/reset.h>
> +
> +#define REG_ISCR 0x00
> +#define REG_PHYCTL 0x04
> +#define REG_PHYBIST 0x08
> +#define REG_PHYTUNE 0x0c
> +
> +#define SUNXI_AHB_ICHR8_EN BIT(10)
> +#define SUNXI_AHB_INCR4_BURST_EN BIT(9)
> +#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8)
> +#define SUNXI_ULPI_BYPASS_EN BIT(0)
> +
> +/* Common Control Bits for Both PHYs */
> +#define PHY_PLL_BW 0x03
> +#define PHY_RES45_CAL_EN 0x0c
> +
> +/* Private Control Bits for Each PHY */
> +#define PHY_TX_AMPLITUDE_TUNE 0x20
> +#define PHY_TX_SLEWRATE_TUNE 0x22
> +#define PHY_VBUSVALID_TH_SEL 0x25
> +#define PHY_PULLUP_RES_SEL 0x27
> +#define PHY_OTG_FUNC_EN 0x28
> +#define PHY_VBUS_DET_EN 0x29
> +#define PHY_DISCON_TH_SEL 0x2a
> +
> +#define MAX_PHYS 3
> +
> +struct sun4i_usb_phy_data {
> + struct clk *clk;
> + void __iomem *base;
> + struct mutex mutex;
> + int num_phys;
> + u32 disc_thresh;
> + struct sun4i_usb_phy {
> + struct phy *phy;
> + void __iomem *pmu;
> + struct regulator *vbus;
> + struct reset_control *reset;
> + int index;
> + } phys[MAX_PHYS];
> +};
> +
> +#define to_sun4i_usb_phy_data(phy) \
> + container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
> +
> +static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
> + int len)
> +{
> + struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy);
> + u32 temp, usbc_bit = BIT(phy->index * 2);
> + int i;
> +
> + mutex_lock(&phy_data->mutex);
> +
> + for (i = 0; i < len; i++) {
> + temp = readl(phy_data->base + REG_PHYCTL);
> +
> + /* clear the address portion */
> + temp &= ~(0xff << 8);
> +
> + /* set the address */
> + temp |= ((addr + i) << 8);
> + writel(temp, phy_data->base + REG_PHYCTL);
> +
> + /* set the data bit and clear usbc bit*/
> + temp = readb(phy_data->base + REG_PHYCTL);
> + if (data & 0x1)
> + temp |= BIT(7);
> + else
> + temp &= ~BIT(7);
> + temp &= ~usbc_bit;
> + writeb(temp, phy_data->base + REG_PHYCTL);
> +
> + /* pulse usbc_bit */
> + temp = readb(phy_data->base + REG_PHYCTL);
> + temp |= usbc_bit;
> + writeb(temp, phy_data->base + REG_PHYCTL);
> +
> + temp = readb(phy_data->base + REG_PHYCTL);
> + temp &= ~usbc_bit;
> + writeb(temp, phy_data->base + REG_PHYCTL);
> +
> + data >>= 1;
> + }
> + mutex_unlock(&phy_data->mutex);
> +}
> +
> +static void sun4i_usb_phy_passby(struct sun4i_usb_phy *phy, int enable)
> +{
> + u32 bits, reg_value;
> +
> + if (!phy->pmu)
> + return;
> +
> + bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN |
> + SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN;
> +
> + reg_value = readl(phy->pmu);
> +
> + if (enable)
> + reg_value |= bits;
> + else
> + reg_value &= ~bits;
> +
> + writel(reg_value, phy->pmu);
> +}
> +
> +static int sun4i_usb_phy_init(struct phy *_phy)
> +{
> + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
> + struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
> + int ret;
> +
> + ret = clk_prepare_enable(data->clk);
> + if (ret)
> + return ret;
> +
> + ret = reset_control_deassert(phy->reset);
> + if (ret) {
> + clk_disable_unprepare(data->clk);
> + return ret;
> + }
> +
> + /* Adjust PHY's magnitude and rate */
> + sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
> +
> + /* Disconnect threshold adjustment */
> + sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, data->disc_thresh, 2);
> +
> + sun4i_usb_phy_passby(phy, 1);
> +
> + return 0;
> +}
> +
> +static int sun4i_usb_phy_exit(struct phy *_phy)
> +{
> + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
> + struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
> +
> + sun4i_usb_phy_passby(phy, 0);
> + reset_control_assert(phy->reset);
> + clk_disable_unprepare(data->clk);
> +
> + return 0;
> +}
> +
> +static int sun4i_usb_phy_power_on(struct phy *_phy)
> +{
> + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
> + int ret = 0;
> +
> + if (phy->vbus)
> + ret = regulator_enable(phy->vbus);
> +
> + return ret;
> +}
> +
> +static int sun4i_usb_phy_power_off(struct phy *_phy)
> +{
> + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
> +
> + if (phy->vbus)
> + regulator_disable(phy->vbus);
> +
> + return 0;
> +}
> +
> +static struct phy_ops sun4i_usb_phy_ops = {
> + .init = sun4i_usb_phy_init,
> + .exit = sun4i_usb_phy_exit,
> + .power_on = sun4i_usb_phy_power_on,
> + .power_off = sun4i_usb_phy_power_off,
> + .owner = THIS_MODULE,
> +};
> +
> +static struct phy *sun4i_usb_phy_xlate(struct device *dev,
> + struct of_phandle_args *args)
> +{
> + struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
> +
> + if (WARN_ON(args->args[0] == 0 || args->args[0] >= data->num_phys))
> + return ERR_PTR(-ENODEV);
> +
> + return data->phys[args->args[0]].phy;
> +}
> +
> +static int sun4i_usb_phy_probe(struct platform_device *pdev)
> +{
> + struct sun4i_usb_phy_data *data;
> + struct device *dev = &pdev->dev;
> + struct device_node *np = dev->of_node;
> + void __iomem *pmu = NULL;
> + struct phy_provider *phy_provider;
> + struct reset_control *reset;
> + struct regulator *vbus;
> + struct phy *phy;
> + char name[16];
> + int i;
> +
> + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + mutex_init(&data->mutex);
> +
> + if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
> + data->num_phys = 2;
> + else
> + data->num_phys = 3;
> +
> + if (of_device_is_compatible(np, "allwinner,sun4i-a10-usb-phy"))
> + data->disc_thresh = 3;
> + else
> + data->disc_thresh = 2;
I'd still prefer to pass this through the .data field of of_device_id,
but it looks much cleaner already :)
> + data->clk = devm_clk_get(dev, "usb_phy");
> + if (IS_ERR(data->clk)) {
> + dev_err(dev, "could not get usb_phy clock\n");
> + return PTR_ERR(data->clk);
> + }
> +
> + /* Skip 0, 0 is the phy for otg which is not yet supported. */
> + for (i = 1; i < data->num_phys; i++) {
> + snprintf(name, sizeof(name), "usb%d_vbus", i);
> + vbus = devm_regulator_get_optional(dev, name);
> + if (IS_ERR(vbus)) {
> + if (PTR_ERR(vbus) == -EPROBE_DEFER)
> + return -EPROBE_DEFER;
> + vbus = NULL;
> + }
> +
> + snprintf(name, sizeof(name), "usb%d_reset", i);
> + reset = devm_reset_control_get(dev, name);
> + if (IS_ERR(phy)) {
> + dev_err(dev, "failed to get reset %s\n", name);
> + return PTR_ERR(phy);
> + }
> +
> + if (i) { /* No pmu for usbc0 */
> + pmu = devm_ioremap_resource(dev,
> + platform_get_resource(pdev, IORESOURCE_MEM, i));
> + if (IS_ERR(pmu))
> + return PTR_ERR(pmu);
> + }
> +
> + phy = devm_phy_create(dev, &sun4i_usb_phy_ops, NULL);
> + if (IS_ERR(phy)) {
> + dev_err(dev, "failed to create PHY %d\n", i);
> + return PTR_ERR(phy);
> + }
> +
> + data->phys[i].phy = phy;
> + data->phys[i].pmu = pmu;
> + data->phys[i].vbus = vbus;
> + data->phys[i].reset = reset;
> + data->phys[i].index = i;
> + phy_set_drvdata(phy, &data->phys[i]);
> + }
> +
> + data->base = devm_ioremap_resource(dev,
> + platform_get_resource(pdev, IORESOURCE_MEM, 0));
> + if (IS_ERR(data->base))
> + return PTR_ERR(data->base);
> +
> + dev_set_drvdata(dev, data);
> + phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
> + if (IS_ERR(phy_provider))
> + return PTR_ERR(phy_provider);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id sun4i_usb_phy_of_match[] = {
> + { .compatible = "allwinner,sun4i-a10-usb-phy" },
> + { .compatible = "allwinner,sun5i-a13-usb-phy" },
> + { .compatible = "allwinner,sun7i-a20-usb-phy" },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
> +
> +static struct platform_driver sun4i_usb_phy_driver = {
> + .probe = sun4i_usb_phy_probe,
> + .driver = {
> + .of_match_table = sun4i_usb_phy_of_match,
> + .name = "sun4i-usb-phy",
> + .owner = THIS_MODULE,
> + }
> +};
> +module_platform_driver(sun4i_usb_phy_driver);
> +
> +MODULE_DESCRIPTION("Allwinner sun4i USB phy driver");
> +MODULE_AUTHOR("Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>");
> +MODULE_LICENSE("GPL v2");
> --
> 1.8.4.2
>
--
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: [PATCH v9 0/4] ohci-platform and ehci-plaform patches rebased on 3.14-rc1
From: Greg Kroah-Hartman @ 2014-02-07 22:31 UTC (permalink / raw)
To: Hans de Goede
Cc: Alan Stern, Tony Prisk, linux-usb, Maxime Ripard,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree
In-Reply-To: <1391787403-20961-1-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
On Fri, Feb 07, 2014 at 04:36:39PM +0100, Hans de Goede wrote:
> Hi Greg,
>
> Here is v9 of my ohci-platform and ehci-platform patchset, It is just a
> rebase (with some manual conflict resolution), to make it easier for you
> to throw this into usb-next, there are no other changes.
I've applied these, thanks. I saw a number of older series and
revisions, and I assumed this was the latest, right? If not, can you
resend anything that I might have missed?
thanks,
greg k-h
^ permalink raw reply
* Re: [PATCH v2 06/15] dt: binding: add binding for ImgTec IR block
From: Rob Herring @ 2014-02-07 22:29 UTC (permalink / raw)
To: Mark Rutland
Cc: James Hogan, Mauro Carvalho Chehab,
linux-media-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Rob Herring,
Pawel Moll, Ian Campbell, Kumar Gala,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Rob Landley,
linux-doc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Tomasz Figa
In-Reply-To: <20140207145053.GF25314-NuALmloUBlrZROr8t4l/smS4ubULX0JqMm0uRHvK7Nw@public.gmane.org>
On Fri, Feb 7, 2014 at 8:50 AM, Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org> wrote:
> Hi Rob,
>
> On Fri, Feb 07, 2014 at 02:33:27PM +0000, Rob Herring wrote:
>> On Thu, Feb 6, 2014 at 8:41 AM, James Hogan <james.hogan-1AXoQHu6uovQT0dZR+AlfA@public.gmane.org> wrote:
>> > Hi Rob,
>> >
>> > On 06/02/14 14:33, Rob Herring wrote:
>> >> On Fri, Jan 17, 2014 at 7:58 AM, James Hogan <james.hogan-1AXoQHu6uovQT0dZR+AlfA@public.gmane.org> wrote:
>> >>> +Required properties:
>> >>> +- compatible: Should be "img,ir1"
>> >>
>> >> Kind of short for a name. I don't have anything much better, but how
>> >> about img,ir-rev1.
>> >
>> > Okay, that sounds reasonable.
>> >
>> >>> +Optional properties:
>> >>> +- clocks: List of clock specifiers as described in standard
>> >>> + clock bindings.
>> >>> +- clock-names: List of clock names corresponding to the clocks
>> >>> + specified in the clocks property.
>> >>> + Accepted clock names are:
>> >>> + "core": Core clock (defaults to 32.768KHz if omitted).
>> >>> + "sys": System side (fast) clock.
>> >>> + "mod": Power modulation clock.
>> >>
>> >> You need to define the order of clocks including how they are
>> >> interpreted with different number of clocks (not relying on the name).
>> >
>> > Would it be sufficient to specify that "clock-names" is required if
>> > "clocks" is provided (i.e. unnamed clocks aren't used), or is there some
>> > other reason that clock-names shouldn't be relied upon?
>>
>> irq-names, reg-names, clock-names, etc. are considered optional to
>> their associated property and the order is supposed to be defined.
>> clock-names is a bit different in that clk_get needs a name, so it
>> effectively is required by Linux when there is more than 1 clock.
>> Really, we should fix Linux.
>
> If they're optional then you can't handle optional entries (i.e. when
> nothing's wired to an input), and this is counter to the style I've been
> recommending to people (defining clocks in terms of clock-names).
>
> I really don't see the point in any *-names property if they don't
> define the list and allow for optional / reordered lists. Why does the
> order have to be fixed rather than using the -names properties? It's
> already a de-facto standard.
Maybe for clocks, but I don't think we should treat clocks differently
from other properties. We've already got enough variation in binding
styles, I'd like to be consistent across interrupts, reg, clocks, etc.
>> Regardless, my other point is still valid. A given h/w block has a
>> fixed number of clocks. You may have them all connected to the same
>> source in some cases, but that does not change the number of inputs.
>> Defining what are the valid combinations needs to be done. Seems like
>> this could be:
>>
>> <none> - default to 32KHz
>> <core> - only a "baud" clock
>> <core>, <sys>, <mod> - all clocks
>
> For more complex IP blocks you might have more inputs than you actually
> have clocks wired to.
>
> How do you handle an unwired input in the middle of the list, or a new
> revision of the IP block that got rid of the first clock input from the
> list but is otherwise compatible?
fixed-clock with freq of 0 for unwired (really wired to gnd) inputs?
With a new compatible string if it is a new block.
Rob
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH v4 7/7] ARM: dts: sun5i: Add support for mmc
From: David Lanzendörfer @ 2014-02-07 21:33 UTC (permalink / raw)
To: devicetree-u79uwXL29TY76Z2rM5mHXA, Ulf Hansson, Laurent Pinchart,
Mike Turquette, Simon Baatz, Hans de Goede, Emilio López,
linux-mmc-u79uwXL29TY76Z2rM5mHXA, Chris Ball,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, H Hartley Sweeten,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Tejun Heo, Maxime Ripard,
Guennadi Liakhovetski,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20140207212709.24624.58619.stgit-GPtPHOohwllnsqa/0SyWJQ@public.gmane.org>
Signed-off-by: David Lanzendörfer <david.lanzendoerfer-Z7Kmv9EsliU@public.gmane.org>
Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts | 30 +++++++++++++++
arch/arm/boot/dts/sun5i-a10s.dtsi | 44 ++++++++++++++++++++++
arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts | 15 ++++++++
arch/arm/boot/dts/sun5i-a13-olinuxino.dts | 15 ++++++++
arch/arm/boot/dts/sun5i-a13.dtsi | 37 +++++++++++++++++++
5 files changed, 141 insertions(+)
diff --git a/arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts b/arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts
index 3c9f8b3..7189adf55 100644
--- a/arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts
+++ b/arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts
@@ -34,7 +34,37 @@
};
};
+ mmc0: mmc@01c0f000 {
+ pinctrl-names = "default", "default";
+ pinctrl-0 = <&mmc0_pins_a>;
+ pinctrl-1 = <&mmc0_cd_pin_olinuxino_micro>;
+ cd-gpios = <&pio 6 1 0>; /* PG1 */
+ status = "okay";
+ };
+
+ mmc1: mmc@01c10000 {
+ pinctrl-names = "default", "default";
+ pinctrl-0 = <&mmc1_pins_a>;
+ pinctrl-1 = <&mmc1_cd_pin_olinuxino_micro>;
+ cd-gpios = <&pio 6 13 0>; /* PG13 */
+ status = "okay";
+ };
+
pinctrl@01c20800 {
+ mmc0_cd_pin_olinuxino_micro: mmc0_cd_pin@0 {
+ allwinner,pins = "PG1";
+ allwinner,function = "gpio_in";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ mmc1_cd_pin_olinuxino_micro: mmc1_cd_pin@0 {
+ allwinner,pins = "PG13";
+ allwinner,function = "gpio_in";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
led_pins_olinuxino: led_pins@0 {
allwinner,pins = "PE3";
allwinner,function = "gpio_out";
diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi
index ea16054..abff7f8 100644
--- a/arch/arm/boot/dts/sun5i-a10s.dtsi
+++ b/arch/arm/boot/dts/sun5i-a10s.dtsi
@@ -293,6 +293,36 @@
#size-cells = <0>;
};
+ mmc0: mmc@01c0f000 {
+ compatible = "allwinner,sun5i-a13-mmc";
+ reg = <0x01c0f000 0x1000>;
+ clocks = <&ahb_gates 8>, <&mmc0_clk>;
+ clock-names = "ahb", "mod";
+ interrupts = <32>;
+ bus-width = <4>;
+ status = "disabled";
+ };
+
+ mmc1: mmc@01c10000 {
+ compatible = "allwinner,sun5i-a13-mmc";
+ reg = <0x01c10000 0x1000>;
+ clocks = <&ahb_gates 9>, <&mmc1_clk>;
+ clock-names = "ahb", "mod";
+ interrupts = <33>;
+ bus-width = <4>;
+ status = "disabled";
+ };
+
+ mmc2: mmc@01c11000 {
+ compatible = "allwinner,sun5i-a13-mmc";
+ reg = <0x01c11000 0x1000>;
+ clocks = <&ahb_gates 10>, <&mmc2_clk>;
+ clock-names = "ahb", "mod";
+ interrupts = <34>;
+ bus-width = <4>;
+ status = "disabled";
+ };
+
intc: interrupt-controller@01c20400 {
compatible = "allwinner,sun4i-ic";
reg = <0x01c20400 0x400>;
@@ -363,6 +393,20 @@
allwinner,drive = <0>;
allwinner,pull = <0>;
};
+
+ mmc0_pins_a: mmc0@0 {
+ allwinner,pins = "PF0","PF1","PF2","PF3","PF4","PF5";
+ allwinner,function = "mmc0";
+ allwinner,drive = <3>;
+ allwinner,pull = <0>;
+ };
+
+ mmc1_pins_a: mmc1@0 {
+ allwinner,pins = "PG3","PG4","PG5","PG6","PG7","PG8";
+ allwinner,function = "mmc1";
+ allwinner,drive = <3>;
+ allwinner,pull = <0>;
+ };
};
timer@01c20c00 {
diff --git a/arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts b/arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts
index fe2ce0a..6ae5867 100644
--- a/arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts
+++ b/arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts
@@ -20,7 +20,22 @@
compatible = "olimex,a13-olinuxino-micro", "allwinner,sun5i-a13";
soc@01c00000 {
+ mmc0: mmc@01c0f000 {
+ pinctrl-names = "default", "default";
+ pinctrl-0 = <&mmc0_pins_a>;
+ pinctrl-1 = <&mmc0_cd_pin_olinuxinom>;
+ cd-gpios = <&pio 6 0 0>; /* PG0 */
+ status = "okay";
+ };
+
pinctrl@01c20800 {
+ mmc0_cd_pin_olinuxinom: mmc0_cd_pin@0 {
+ allwinner,pins = "PG0";
+ allwinner,function = "gpio_in";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
led_pins_olinuxinom: led_pins@0 {
allwinner,pins = "PG9";
allwinner,function = "gpio_out";
diff --git a/arch/arm/boot/dts/sun5i-a13-olinuxino.dts b/arch/arm/boot/dts/sun5i-a13-olinuxino.dts
index a4ba5ff..b23237b 100644
--- a/arch/arm/boot/dts/sun5i-a13-olinuxino.dts
+++ b/arch/arm/boot/dts/sun5i-a13-olinuxino.dts
@@ -19,7 +19,22 @@
compatible = "olimex,a13-olinuxino", "allwinner,sun5i-a13";
soc@01c00000 {
+ mmc0: mmc@01c0f000 {
+ pinctrl-names = "default", "default";
+ pinctrl-0 = <&mmc0_pins_a>;
+ pinctrl-1 = <&mmc0_cd_pin_olinuxino>;
+ cd-gpios = <&pio 6 0 0>; /* PG0 */
+ status = "okay";
+ };
+
pinctrl@01c20800 {
+ mmc0_cd_pin_olinuxino: mmc0_cd_pin@0 {
+ allwinner,pins = "PG0";
+ allwinner,function = "gpio_in";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
led_pins_olinuxino: led_pins@0 {
allwinner,pins = "PG9";
allwinner,function = "gpio_out";
diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi
index 320335a..5d71c82 100644
--- a/arch/arm/boot/dts/sun5i-a13.dtsi
+++ b/arch/arm/boot/dts/sun5i-a13.dtsi
@@ -274,6 +274,36 @@
#size-cells = <1>;
ranges;
+ mmc0: mmc@01c0f000 {
+ compatible = "allwinner,sun5i-a13-mmc";
+ reg = <0x01c0f000 0x1000>;
+ clocks = <&ahb_gates 8>, <&mmc0_clk>;
+ clock-names = "ahb", "mod";
+ interrupts = <32>;
+ bus-width = <4>;
+ status = "disabled";
+ };
+
+ mmc1: mmc@01c10000 {
+ compatible = "allwinner,sun5i-a13-mmc";
+ reg = <0x01c10000 0x1000>;
+ clocks = <&ahb_gates 9>, <&mmc1_clk>;
+ clock-names = "ahb", "mod";
+ interrupts = <33>;
+ bus-width = <4>;
+ status = "disabled";
+ };
+
+ mmc2: mmc@01c11000 {
+ compatible = "allwinner,sun5i-a13-mmc";
+ reg = <0x01c11000 0x1000>;
+ clocks = <&ahb_gates 10>, <&mmc2_clk>;
+ clock-names = "ahb", "mod";
+ interrupts = <34>;
+ bus-width = <4>;
+ status = "disabled";
+ };
+
intc: interrupt-controller@01c20400 {
compatible = "allwinner,sun4i-ic";
reg = <0x01c20400 0x400>;
@@ -326,6 +356,13 @@
allwinner,drive = <0>;
allwinner,pull = <0>;
};
+
+ mmc0_pins_a: mmc0@0 {
+ allwinner,pins = "PF0","PF1","PF2","PF3","PF4","PF5";
+ allwinner,function = "mmc0";
+ allwinner,drive = <3>;
+ allwinner,pull = <0>;
+ };
};
timer@01c20c00 {
--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/groups/opt_out.
^ permalink raw reply related
* [PATCH v4 6/7] ARM: dts: sun4i: Add support for mmc
From: David Lanzendörfer @ 2014-02-07 21:33 UTC (permalink / raw)
To: devicetree-u79uwXL29TY76Z2rM5mHXA, Ulf Hansson, Laurent Pinchart,
Mike Turquette, Simon Baatz, Hans de Goede, Emilio López,
linux-mmc-u79uwXL29TY76Z2rM5mHXA, Chris Ball,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, H Hartley Sweeten,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Tejun Heo, Maxime Ripard,
Guennadi Liakhovetski,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20140207212709.24624.58619.stgit-GPtPHOohwllnsqa/0SyWJQ@public.gmane.org>
Signed-off-by: David Lanzendörfer <david.lanzendoerfer-Z7Kmv9EsliU@public.gmane.org>
Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
arch/arm/boot/dts/sun4i-a10-a1000.dts | 8 ++++
arch/arm/boot/dts/sun4i-a10-cubieboard.dts | 8 ++++
arch/arm/boot/dts/sun4i-a10.dtsi | 54 ++++++++++++++++++++++++++++
3 files changed, 70 insertions(+)
diff --git a/arch/arm/boot/dts/sun4i-a10-a1000.dts b/arch/arm/boot/dts/sun4i-a10-a1000.dts
index d4b081d..a879ef3 100644
--- a/arch/arm/boot/dts/sun4i-a10-a1000.dts
+++ b/arch/arm/boot/dts/sun4i-a10-a1000.dts
@@ -35,6 +35,14 @@
};
};
+ mmc0: mmc@01c0f000 {
+ pinctrl-names = "default", "default";
+ pinctrl-0 = <&mmc0_pins_a>;
+ pinctrl-1 = <&mmc0_cd_pin_reference_design>;
+ cd-gpios = <&pio 7 1 0>; /* PH1 */
+ status = "okay";
+ };
+
pinctrl@01c20800 {
emac_power_pin_a1000: emac_power_pin@0 {
allwinner,pins = "PH15";
diff --git a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts
index b139ee6..20b976a 100644
--- a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts
+++ b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts
@@ -33,6 +33,14 @@
};
};
+ mmc0: mmc@01c0f000 {
+ pinctrl-names = "default", "default";
+ pinctrl-0 = <&mmc0_pins_a>;
+ pinctrl-1 = <&mmc0_cd_pin_reference_design>;
+ cd-gpios = <&pio 7 1 0>; /* PH1 */
+ status = "okay";
+ };
+
pinctrl@01c20800 {
led_pins_cubieboard: led_pins@0 {
allwinner,pins = "PH20", "PH21";
diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi
index 040bb0e..c941ca3 100644
--- a/arch/arm/boot/dts/sun4i-a10.dtsi
+++ b/arch/arm/boot/dts/sun4i-a10.dtsi
@@ -330,6 +330,46 @@
#size-cells = <0>;
};
+ mmc0: mmc@01c0f000 {
+ compatible = "allwinner,sun5i-a13-mmc";
+ reg = <0x01c0f000 0x1000>;
+ clocks = <&ahb_gates 8>, <&mmc0_clk>;
+ clock-names = "ahb", "mod";
+ interrupts = <32>;
+ bus-width = <4>;
+ status = "disabled";
+ };
+
+ mmc1: mmc@01c10000 {
+ compatible = "allwinner,sun5i-a13-mmc";
+ reg = <0x01c10000 0x1000>;
+ clocks = <&ahb_gates 9>, <&mmc1_clk>;
+ clock-names = "ahb", "mod";
+ interrupts = <33>;
+ bus-width = <4>;
+ status = "disabled";
+ };
+
+ mmc2: mmc@01c11000 {
+ compatible = "allwinner,sun5i-a13-mmc";
+ reg = <0x01c11000 0x1000>;
+ clocks = <&ahb_gates 10>, <&mmc2_clk>;
+ clock-names = "ahb", "mod";
+ interrupts = <34>;
+ bus-width = <4>;
+ status = "disabled";
+ };
+
+ mmc3: mmc@01c12000 {
+ compatible = "allwinner,sun5i-a13-mmc";
+ reg = <0x01c12000 0x1000>;
+ clocks = <&ahb_gates 11>, <&mmc3_clk>;
+ clock-names = "ahb", "mod";
+ interrupts = <35>;
+ bus-width = <4>;
+ status = "disabled";
+ };
+
intc: interrupt-controller@01c20400 {
compatible = "allwinner,sun4i-ic";
reg = <0x01c20400 0x400>;
@@ -400,6 +440,20 @@
allwinner,drive = <0>;
allwinner,pull = <0>;
};
+
+ mmc0_pins_a: mmc0@0 {
+ allwinner,pins = "PF0","PF1","PF2","PF3","PF4","PF5";
+ allwinner,function = "mmc0";
+ allwinner,drive = <3>;
+ allwinner,pull = <0>;
+ };
+
+ mmc0_cd_pin_reference_design: mmc0_cd_pin@0 {
+ allwinner,pins = "PH1";
+ allwinner,function = "gpio_in";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
};
timer@01c20c00 {
--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/groups/opt_out.
^ permalink raw reply related
* [PATCH v4 5/7] ARM: dts: sun7i: Add support for mmc
From: David Lanzendörfer @ 2014-02-07 21:33 UTC (permalink / raw)
To: devicetree-u79uwXL29TY76Z2rM5mHXA, Ulf Hansson, Laurent Pinchart,
Mike Turquette, Simon Baatz, Hans de Goede, Emilio López,
linux-mmc-u79uwXL29TY76Z2rM5mHXA, Chris Ball,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, H Hartley Sweeten,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Tejun Heo, Maxime Ripard,
Guennadi Liakhovetski,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20140207212709.24624.58619.stgit-GPtPHOohwllnsqa/0SyWJQ@public.gmane.org>
Signed-off-by: David Lanzendörfer <david.lanzendoerfer-Z7Kmv9EsliU@public.gmane.org>
Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
arch/arm/boot/dts/sun7i-a20-cubieboard2.dts | 8 +++
arch/arm/boot/dts/sun7i-a20-cubietruck.dts | 8 +++
arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts | 23 +++++++++
arch/arm/boot/dts/sun7i-a20.dtsi | 61 +++++++++++++++++++++++
4 files changed, 100 insertions(+)
diff --git a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
index 5c51cb8..ae800b6 100644
--- a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
+++ b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
@@ -34,6 +34,14 @@
};
};
+ mmc0: mmc@01c0f000 {
+ pinctrl-names = "default", "default";
+ pinctrl-0 = <&mmc0_pins_a>;
+ pinctrl-1 = <&mmc0_cd_pin_reference_design>;
+ cd-gpios = <&pio 7 1 0>; /* PH1 */
+ status = "okay";
+ };
+
pinctrl@01c20800 {
led_pins_cubieboard2: led_pins@0 {
allwinner,pins = "PH20", "PH21";
diff --git a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
index f9dcb61..370cef84 100644
--- a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
+++ b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
@@ -19,6 +19,14 @@
compatible = "cubietech,cubietruck", "allwinner,sun7i-a20";
soc@01c00000 {
+ mmc0: mmc@01c0f000 {
+ pinctrl-names = "default", "default";
+ pinctrl-0 = <&mmc0_pins_a>;
+ pinctrl-1 = <&mmc0_cd_pin_reference_design>;
+ cd-gpios = <&pio 7 1 0>; /* PH1 */
+ status = "okay";
+ };
+
pinctrl@01c20800 {
led_pins_cubietruck: led_pins@0 {
allwinner,pins = "PH7", "PH11", "PH20", "PH21";
diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts
index ead3013..46dbe5b 100644
--- a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts
+++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts
@@ -34,7 +34,30 @@
};
};
+ mmc0: mmc@01c0f000 {
+ pinctrl-names = "default", "default";
+ pinctrl-0 = <&mmc0_pins_a>;
+ pinctrl-1 = <&mmc0_cd_pin_reference_design>;
+ cd-gpios = <&pio 7 1 0>; /* PH1 */
+ status = "okay";
+ };
+
+ mmc3: mmc@01c12000 {
+ pinctrl-names = "default", "default";
+ pinctrl-0 = <&mmc3_pins_a>;
+ pinctrl-1 = <&mmc3_cd_pin_olinuxinom>;
+ cd-gpios = <&pio 7 11 0>; /* PH11 */
+ status = "okay";
+ };
+
pinctrl@01c20800 {
+ mmc3_cd_pin_olinuxinom: mmc3_cd_pin@0 {
+ allwinner,pins = "PH11";
+ allwinner,function = "gpio_in";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
led_pins_olinuxino: led_pins@0 {
allwinner,pins = "PH2";
allwinner,function = "gpio_out";
diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index 119f066..4cd6210 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -355,6 +355,46 @@
#size-cells = <0>;
};
+ mmc0: mmc@01c0f000 {
+ compatible = "allwinner,sun5i-a13-mmc";
+ reg = <0x01c0f000 0x1000>;
+ clocks = <&ahb_gates 8>, <&mmc0_clk>;
+ clock-names = "ahb", "mod";
+ interrupts = <0 32 4>;
+ bus-width = <4>;
+ status = "disabled";
+ };
+
+ mmc1: mmc@01c10000 {
+ compatible = "allwinner,sun5i-a13-mmc";
+ reg = <0x01c10000 0x1000>;
+ clocks = <&ahb_gates 9>, <&mmc1_clk>;
+ clock-names = "ahb", "mod";
+ interrupts = <0 33 4>;
+ bus-width = <4>;
+ status = "disabled";
+ };
+
+ mmc2: mmc@01c11000 {
+ compatible = "allwinner,sun5i-a13-mmc";
+ reg = <0x01c11000 0x1000>;
+ clocks = <&ahb_gates 10>, <&mmc2_clk>;
+ clock-names = "ahb", "mod";
+ interrupts = <0 34 4>;
+ bus-width = <4>;
+ status = "disabled";
+ };
+
+ mmc3: mmc@01c12000 {
+ compatible = "allwinner,sun5i-a13-mmc";
+ reg = <0x01c12000 0x1000>;
+ clocks = <&ahb_gates 11>, <&mmc3_clk>;
+ clock-names = "ahb", "mod";
+ interrupts = <0 35 4>;
+ bus-width = <4>;
+ status = "disabled";
+ };
+
pio: pinctrl@01c20800 {
compatible = "allwinner,sun7i-a20-pinctrl";
reg = <0x01c20800 0x400>;
@@ -432,6 +472,27 @@
allwinner,drive = <0>;
allwinner,pull = <0>;
};
+
+ mmc0_pins_a: mmc0@0 {
+ allwinner,pins = "PF0","PF1","PF2","PF3","PF4","PF5";
+ allwinner,function = "mmc0";
+ allwinner,drive = <3>;
+ allwinner,pull = <0>;
+ };
+
+ mmc0_cd_pin_reference_design: mmc0_cd_pin@0 {
+ allwinner,pins = "PH1";
+ allwinner,function = "gpio_in";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ mmc3_pins_a: mmc3@0 {
+ allwinner,pins = "PI4","PI5","PI6","PI7","PI8","PI9";
+ allwinner,function = "mmc3";
+ allwinner,drive = <3>;
+ allwinner,pull = <0>;
+ };
};
timer@01c20c00 {
--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/groups/opt_out.
^ permalink raw reply related
* [PATCH v4 4/7] ARM: sunxi: Add driver for SD/MMC hosts found on Allwinner sunxi SoCs
From: David Lanzendörfer @ 2014-02-07 21:33 UTC (permalink / raw)
To: devicetree-u79uwXL29TY76Z2rM5mHXA, Ulf Hansson, Laurent Pinchart,
Mike Turquette, Simon Baatz, Hans de Goede, Emilio López,
linux-mmc-u79uwXL29TY76Z2rM5mHXA, Chris Ball,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, H Hartley Sweeten,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Tejun Heo, Maxime Ripard,
Guennadi Liakhovetski,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20140207212709.24624.58619.stgit-GPtPHOohwllnsqa/0SyWJQ@public.gmane.org>
This is based on the driver Allwinner ships in their Android kernel sources.
Initial porting to upstream kernels done by David Lanzendörfer, additional
fixes and cleanups by Hans de Goede.
It uses dma in bus-master mode using a built-in designware idmac controller,
which is identical to the one found in the mmc-dw hosts.
The rest of the host is not identical to mmc-dw.
Signed-off-by: David Lanzendörfer <david.lanzendoerfer-Z7Kmv9EsliU@public.gmane.org>
Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
drivers/mmc/host/Kconfig | 7
drivers/mmc/host/Makefile | 2
drivers/mmc/host/sunxi-mci.c | 871 ++++++++++++++++++++++++++++++++++++++++++
drivers/mmc/host/sunxi-mci.h | 239 ++++++++++++
4 files changed, 1119 insertions(+)
create mode 100644 drivers/mmc/host/sunxi-mci.c
create mode 100644 drivers/mmc/host/sunxi-mci.h
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 1384f67..7caf266 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -689,3 +689,10 @@ config MMC_REALTEK_PCI
help
Say Y here to include driver code to support SD/MMC card interface
of Realtek PCI-E card reader
+
+config MMC_SUNXI
+ tristate "Allwinner sunxi SD/MMC Host Controller support"
+ depends on ARCH_SUNXI
+ help
+ This selects support for the SD/MMC Host Controller on
+ Allwinner sunxi SoCs.
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 3483b6b..6c9cbd3 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -54,6 +54,8 @@ obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o
obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o
+obj-$(CONFIG_MMC_SUNXI) += sunxi-mci.o
+
obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o
obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o
obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o
diff --git a/drivers/mmc/host/sunxi-mci.c b/drivers/mmc/host/sunxi-mci.c
new file mode 100644
index 0000000..8b47c99
--- /dev/null
+++ b/drivers/mmc/host/sunxi-mci.c
@@ -0,0 +1,871 @@
+/*
+ * Driver for sunxi SD/MMC host controllers
+ * (C) Copyright 2014-2015 Reuuimlla Technology Co., Ltd.
+ * (C) Copyright 2014-2015 Aaron Maoye <leafy.myeh-jFKXxz0WcGyYHARAtoI1EgC/G2K4zDHf@public.gmane.org>
+ * (C) Copyright 2014-2015 O2S GmbH <www.o2s.ch>
+ * (C) Copyright 2014-2015 David Lanzendörfer <david.lanzendoerfer@o2s.ch>
+ * (C) Copyright 2014-2015 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+#include <linux/clk.h>
+#include <linux/clk-private.h>
+#include <linux/clk/sunxi.h>
+
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/scatterlist.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/of_address.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/core.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/slot-gpio.h>
+
+#include "sunxi-mci.h"
+
+static int sunxi_mmc_init_host(struct mmc_host *mmc)
+{
+ u32 rval;
+ struct sunxi_mmc_host *smc_host = mmc_priv(mmc);
+ int ret;
+
+ ret = clk_prepare_enable(smc_host->clk_ahb);
+ if (ret) {
+ dev_err(mmc_dev(smc_host->mmc), "AHB clk err %d\n", ret);
+ return ret;
+ }
+ ret = clk_prepare_enable(smc_host->clk_mod);
+ if (ret) {
+ dev_err(mmc_dev(smc_host->mmc), "MOD clk err %d\n", ret);
+ clk_disable_unprepare(smc_host->clk_ahb);
+ return ret;
+ }
+
+ /* reset controller */
+ rval = mci_readl(smc_host, REG_GCTRL) | SDXC_HWReset;
+ mci_writel(smc_host, REG_GCTRL, rval);
+
+ mci_writel(smc_host, REG_FTRGL, 0x20070008);
+ mci_writel(smc_host, REG_TMOUT, 0xffffffff);
+ mci_writel(smc_host, REG_IMASK, smc_host->sdio_imask);
+ mci_writel(smc_host, REG_RINTR, 0xffffffff);
+ mci_writel(smc_host, REG_DBGC, 0xdeb);
+ mci_writel(smc_host, REG_FUNS, 0xceaa0000);
+ mci_writel(smc_host, REG_DLBA, smc_host->sg_dma);
+ rval = mci_readl(smc_host, REG_GCTRL)|SDXC_INTEnb;
+ rval &= ~SDXC_AccessDoneDirect;
+ mci_writel(smc_host, REG_GCTRL, rval);
+
+ return 0;
+}
+
+static void sunxi_mmc_exit_host(struct sunxi_mmc_host *smc_host)
+{
+ mci_writel(smc_host, REG_GCTRL, SDXC_HWReset);
+ clk_disable_unprepare(smc_host->clk_ahb);
+ clk_disable_unprepare(smc_host->clk_mod);
+}
+
+/* /\* UHS-I Operation Modes */
+/* * DS 25MHz 12.5MB/s 3.3V */
+/* * HS 50MHz 25MB/s 3.3V */
+/* * SDR12 25MHz 12.5MB/s 1.8V */
+/* * SDR25 50MHz 25MB/s 1.8V */
+/* * SDR50 100MHz 50MB/s 1.8V */
+/* * SDR104 208MHz 104MB/s 1.8V */
+/* * DDR50 50MHz 50MB/s 1.8V */
+/* * MMC Operation Modes */
+/* * DS 26MHz 26MB/s 3/1.8/1.2V */
+/* * HS 52MHz 52MB/s 3/1.8/1.2V */
+/* * HSDDR 52MHz 104MB/s 3/1.8/1.2V */
+/* * HS200 200MHz 200MB/s 1.8/1.2V */
+/* * */
+/* * Spec. Timing */
+/* * SD3.0 */
+/* * Fcclk Tcclk Fsclk Tsclk Tis Tih odly RTis RTih */
+/* * 400K 2.5us 24M 41ns 5ns 5ns 1 2209ns 41ns */
+/* * 25M 40ns 600M 1.67ns 5ns 5ns 3 14.99ns 5.01ns */
+/* * 50M 20ns 600M 1.67ns 6ns 2ns 3 14.99ns 5.01ns */
+/* * 50MDDR 20ns 600M 1.67ns 6ns 0.8ns 2 6.67ns 3.33ns */
+/* * 104M 9.6ns 600M 1.67ns 3ns 0.8ns 1 7.93ns 1.67ns */
+/* * 208M 4.8ns 600M 1.67ns 1.4ns 0.8ns 1 3.33ns 1.67ns */
+
+/* * 25M 40ns 300M 3.33ns 5ns 5ns 2 13.34ns 6.66ns */
+/* * 50M 20ns 300M 3.33ns 6ns 2ns 2 13.34ns 6.66ns */
+/* * 50MDDR 20ns 300M 3.33ns 6ns 0.8ns 1 6.67ns 3.33ns */
+/* * 104M 9.6ns 300M 3.33ns 3ns 0.8ns 0 7.93ns 1.67ns */
+/* * 208M 4.8ns 300M 3.33ns 1.4ns 0.8ns 0 3.13ns 1.67ns */
+
+/* * eMMC4.5 */
+/* * 400K 2.5us 24M 41ns 3ns 3ns 1 2209ns 41ns */
+/* * 25M 40ns 600M 1.67ns 3ns 3ns 3 14.99ns 5.01ns */
+/* * 50M 20ns 600M 1.67ns 3ns 3ns 3 14.99ns 5.01ns */
+/* * 50MDDR 20ns 600M 1.67ns 2.5ns 2.5ns 2 6.67ns 3.33ns */
+/* * 200M 5ns 600M 1.67ns 1.4ns 0.8ns 1 3.33ns 1.67ns */
+/* *\/ */
+
+static void sunxi_mmc_init_idma_des(struct sunxi_mmc_host *host,
+ struct mmc_data *data)
+{
+ struct sunxi_idma_des *pdes = (struct sunxi_idma_des *)host->sg_cpu;
+ struct sunxi_idma_des *pdes_pa = (struct sunxi_idma_des *)host->sg_dma;
+ int i, max_len = (1 << host->idma_des_size_bits);
+
+ for (i = 0; i < data->sg_len; i++) {
+ pdes[i].config = SDXC_IDMAC_DES0_CH | SDXC_IDMAC_DES0_OWN |
+ SDXC_IDMAC_DES0_DIC;
+
+ if (data->sg[i].length == max_len)
+ pdes[i].buf_size = 0; /* 0 == max_len */
+ else
+ pdes[i].buf_size = data->sg[i].length;
+
+ pdes[i].buf_addr_ptr1 = sg_dma_address(&data->sg[i]);
+ pdes[i].buf_addr_ptr2 = (u32)&pdes_pa[i + 1];
+ }
+
+ pdes[0].config |= SDXC_IDMAC_DES0_FD;
+ pdes[i - 1].config = SDXC_IDMAC_DES0_OWN | SDXC_IDMAC_DES0_LD;
+
+ wmb(); /* Ensure idma_des hit main mem before we start the idmac */
+}
+
+static enum dma_data_direction sunxi_mmc_get_dma_dir(struct mmc_data *data)
+{
+ if (data->flags & MMC_DATA_WRITE)
+ return DMA_TO_DEVICE;
+ else
+ return DMA_FROM_DEVICE;
+}
+
+static int sunxi_mmc_prepare_dma(struct sunxi_mmc_host *smc_host,
+ struct mmc_data *data)
+{
+ u32 dma_len;
+ u32 i;
+ u32 temp;
+ struct scatterlist *sg;
+
+ dma_len = dma_map_sg(mmc_dev(smc_host->mmc), data->sg, data->sg_len,
+ sunxi_mmc_get_dma_dir(data));
+ if (dma_len == 0) {
+ dev_err(mmc_dev(smc_host->mmc), "dma_map_sg failed\n");
+ return -ENOMEM;
+ }
+
+ for_each_sg(data->sg, sg, data->sg_len, i) {
+ if (sg->offset & 3 || sg->length & 3) {
+ dev_err(mmc_dev(smc_host->mmc),
+ "unaligned scatterlist: os %x length %d\n",
+ sg->offset, sg->length);
+ return -EINVAL;
+ }
+ }
+
+ sunxi_mmc_init_idma_des(smc_host, data);
+
+ temp = mci_readl(smc_host, REG_GCTRL);
+ temp |= SDXC_DMAEnb;
+ mci_writel(smc_host, REG_GCTRL, temp);
+ temp |= SDXC_DMAReset;
+ mci_writel(smc_host, REG_GCTRL, temp);
+ mci_writel(smc_host, REG_DMAC, SDXC_IDMACSoftRST);
+
+ if (!(data->flags & MMC_DATA_WRITE))
+ mci_writel(smc_host, REG_IDIE, SDXC_IDMACReceiveInt);
+
+ mci_writel(smc_host, REG_DMAC, SDXC_IDMACFixBurst | SDXC_IDMACIDMAOn);
+
+ return 0;
+}
+
+static void sunxi_mmc_send_manual_stop(struct sunxi_mmc_host *host,
+ struct mmc_request *req)
+{
+ u32 cmd_val = SDXC_Start | SDXC_RspExp | SDXC_StopAbortCMD
+ | SDXC_CheckRspCRC | MMC_STOP_TRANSMISSION;
+ u32 ri = 0;
+ unsigned long expire = jiffies + msecs_to_jiffies(1000);
+
+ mci_writel(host, REG_CARG, 0);
+ mci_writel(host, REG_CMDR, cmd_val);
+
+ do {
+ ri = mci_readl(host, REG_RINTR);
+ } while (!(ri & (SDXC_CmdDone | SDXC_IntErrBit)) &&
+ time_before(jiffies, expire));
+
+ if (ri & SDXC_IntErrBit) {
+ dev_err(mmc_dev(host->mmc), "send stop command failed\n");
+ if (req->stop)
+ req->stop->resp[0] = -ETIMEDOUT;
+ } else {
+ if (req->stop)
+ req->stop->resp[0] = mci_readl(host, REG_RESP0);
+ }
+
+ mci_writel(host, REG_RINTR, 0xffff);
+}
+
+static void sunxi_mmc_dump_errinfo(struct sunxi_mmc_host *smc_host)
+{
+ struct mmc_command *cmd = smc_host->mrq->cmd;
+ struct mmc_data *data = smc_host->mrq->data;
+
+ /* For some cmds timeout is normal with sd/mmc cards */
+ if ((smc_host->int_sum & SDXC_IntErrBit) == SDXC_RespTimeout &&
+ (cmd->opcode == 5 || cmd->opcode == 52))
+ return;
+
+ dev_err(mmc_dev(smc_host->mmc),
+ "smc %d err, cmd %d,%s%s%s%s%s%s%s%s%s%s !!\n",
+ smc_host->mmc->index, cmd->opcode,
+ data ? (data->flags & MMC_DATA_WRITE ? " WR" : " RD") : "",
+ smc_host->int_sum & SDXC_RespErr ? " RE" : "",
+ smc_host->int_sum & SDXC_RespCRCErr ? " RCE" : "",
+ smc_host->int_sum & SDXC_DataCRCErr ? " DCE" : "",
+ smc_host->int_sum & SDXC_RespTimeout ? " RTO" : "",
+ smc_host->int_sum & SDXC_DataTimeout ? " DTO" : "",
+ smc_host->int_sum & SDXC_FIFORunErr ? " FE" : "",
+ smc_host->int_sum & SDXC_HardWLocked ? " HL" : "",
+ smc_host->int_sum & SDXC_StartBitErr ? " SBE" : "",
+ smc_host->int_sum & SDXC_EndBitErr ? " EBE" : ""
+ );
+}
+
+static void sunxi_mmc_finalize_request(struct sunxi_mmc_host *host)
+{
+ struct mmc_request *mrq;
+ unsigned long iflags;
+
+ spin_lock_irqsave(&host->lock, iflags);
+
+ mrq = host->mrq;
+ if (!mrq) {
+ spin_unlock_irqrestore(&host->lock, iflags);
+ dev_err(mmc_dev(host->mmc), "no request to finalize\n");
+ return;
+ }
+
+ if (host->int_sum & SDXC_IntErrBit) {
+ sunxi_mmc_dump_errinfo(host);
+ mrq->cmd->error = -ETIMEDOUT;
+ if (mrq->data)
+ mrq->data->error = -ETIMEDOUT;
+ if (mrq->stop)
+ mrq->stop->error = -ETIMEDOUT;
+ } else {
+ if (mrq->cmd->flags & MMC_RSP_136) {
+ mrq->cmd->resp[0] = mci_readl(host, REG_RESP3);
+ mrq->cmd->resp[1] = mci_readl(host, REG_RESP2);
+ mrq->cmd->resp[2] = mci_readl(host, REG_RESP1);
+ mrq->cmd->resp[3] = mci_readl(host, REG_RESP0);
+ } else {
+ mrq->cmd->resp[0] = mci_readl(host, REG_RESP0);
+ }
+ if (mrq->data)
+ mrq->data->bytes_xfered =
+ mrq->data->blocks * mrq->data->blksz;
+ }
+
+ if (mrq->data) {
+ struct mmc_data *data = mrq->data;
+ u32 temp;
+
+ mci_writel(host, REG_IDST, 0x337);
+ mci_writel(host, REG_DMAC, 0);
+ temp = mci_readl(host, REG_GCTRL);
+ mci_writel(host, REG_GCTRL, temp|SDXC_DMAReset);
+ temp &= ~SDXC_DMAEnb;
+ mci_writel(host, REG_GCTRL, temp);
+ temp |= SDXC_FIFOReset;
+ mci_writel(host, REG_GCTRL, temp);
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ sunxi_mmc_get_dma_dir(data));
+ }
+
+ mci_writel(host, REG_RINTR, 0xffff);
+
+ dev_dbg(mmc_dev(host->mmc), "req done, resp %08x %08x %08x %08x\n",
+ mrq->cmd->resp[0], mrq->cmd->resp[1],
+ mrq->cmd->resp[2], mrq->cmd->resp[3]);
+
+ host->mrq = NULL;
+ host->int_sum = 0;
+ host->wait_dma = 0;
+
+ spin_unlock_irqrestore(&host->lock, iflags);
+
+ if (mrq->data && mrq->data->error) {
+ dev_err(mmc_dev(host->mmc),
+ "data error, sending stop command\n");
+ sunxi_mmc_send_manual_stop(host, mrq);
+ }
+
+ mmc_request_done(host->mmc, mrq);
+}
+
+static irqreturn_t sunxi_mmc_irq(int irq, void *dev_id)
+{
+ struct sunxi_mmc_host *host = dev_id;
+ u32 finalize = 0;
+ u32 sdio_int = 0;
+ u32 msk_int;
+ u32 idma_int;
+
+ spin_lock(&host->lock);
+
+ idma_int = mci_readl(host, REG_IDST);
+ msk_int = mci_readl(host, REG_MISTA);
+
+ dev_dbg(mmc_dev(host->mmc), "irq: rq %p mi %08x idi %08x\n",
+ host->mrq, msk_int, idma_int);
+
+ if (host->mrq) {
+ if (idma_int & SDXC_IDMACReceiveInt)
+ host->wait_dma = 0;
+
+ host->int_sum |= msk_int;
+
+ /* Wait for CmdDone on RespTimeout before finishing the req */
+ if ((host->int_sum & SDXC_RespTimeout) &&
+ !(host->int_sum & SDXC_CmdDone))
+ mci_writel(host, REG_IMASK,
+ host->sdio_imask | SDXC_CmdDone);
+ else if (host->int_sum & SDXC_IntErrBit)
+ finalize = 1; /* Don't wait for dma on error */
+ else if (host->int_sum & SDXC_IntDoneBit && !host->wait_dma)
+ finalize = 1; /* Done */
+
+ if (finalize) {
+ mci_writel(host, REG_IMASK, host->sdio_imask);
+ mci_writel(host, REG_IDIE, 0);
+ }
+ }
+
+ if (msk_int & SDXC_SDIOInt)
+ sdio_int = 1;
+
+ mci_writel(host, REG_RINTR, msk_int);
+ mci_writel(host, REG_IDST, idma_int);
+
+ spin_unlock(&host->lock);
+
+ if (finalize)
+ tasklet_schedule(&host->tasklet);
+
+ if (sdio_int)
+ mmc_signal_sdio_irq(host->mmc);
+
+ return IRQ_HANDLED;
+}
+
+static void sunxi_mmc_tasklet(unsigned long data)
+{
+ struct sunxi_mmc_host *smc_host = (struct sunxi_mmc_host *) data;
+ sunxi_mmc_finalize_request(smc_host);
+}
+
+static void sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en)
+{
+ unsigned long expire = jiffies + msecs_to_jiffies(2000);
+ u32 rval;
+
+ rval = mci_readl(host, REG_CLKCR);
+ rval &= ~(SDXC_CardClkOn | SDXC_LowPowerOn);
+
+ if (oclk_en)
+ rval |= SDXC_CardClkOn;
+
+ if (!host->io_flag)
+ rval |= SDXC_LowPowerOn;
+
+ mci_writel(host, REG_CLKCR, rval);
+
+ rval = SDXC_Start | SDXC_UPCLKOnly | SDXC_WaitPreOver;
+ if (host->voltage_switching)
+ rval |= SDXC_VolSwitch;
+ mci_writel(host, REG_CMDR, rval);
+
+ do {
+ rval = mci_readl(host, REG_CMDR);
+ } while (time_before(jiffies, expire) && (rval & SDXC_Start));
+
+ if (rval & SDXC_Start) {
+ dev_err(mmc_dev(host->mmc), "fatal err update clk timeout\n");
+ host->ferror = 1;
+ }
+}
+
+static void sunxi_mmc_set_clk_dly(struct sunxi_mmc_host *smc_host,
+ u32 oclk_dly, u32 sclk_dly)
+{
+ unsigned long iflags;
+ struct clk_hw *hw = __clk_get_hw(smc_host->clk_mod);
+
+ spin_lock_irqsave(&smc_host->lock, iflags);
+ clk_sunxi_mmc_phase_control(hw, sclk_dly, oclk_dly);
+ spin_unlock_irqrestore(&smc_host->lock, iflags);
+}
+
+struct sunxi_mmc_clk_dly mmc_clk_dly[MMC_CLK_MOD_NUM] = {
+ { MMC_CLK_400K, 0, 7 },
+ { MMC_CLK_25M, 0, 5 },
+ { MMC_CLK_50M, 3, 5 },
+ { MMC_CLK_50MDDR, 2, 4 },
+ { MMC_CLK_50MDDR_8BIT, 2, 4 },
+ { MMC_CLK_100M, 1, 4 },
+ { MMC_CLK_200M, 1, 4 },
+};
+
+static void sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *smc_host,
+ unsigned int rate)
+{
+ u32 newrate;
+ u32 src_clk;
+ u32 oclk_dly;
+ u32 sclk_dly;
+ u32 temp;
+ struct sunxi_mmc_clk_dly *dly = NULL;
+
+ newrate = clk_round_rate(smc_host->clk_mod, rate);
+ if (smc_host->clk_mod_rate == newrate) {
+ dev_dbg(mmc_dev(smc_host->mmc), "clk already %d, rounded %d\n",
+ rate, newrate);
+ return;
+ }
+
+ dev_dbg(mmc_dev(smc_host->mmc), "setting clk to %d, rounded %d\n",
+ rate, newrate);
+
+ /* setting clock rate */
+ clk_disable(smc_host->clk_mod);
+ clk_set_rate(smc_host->clk_mod, newrate);
+ clk_enable(smc_host->clk_mod);
+ smc_host->clk_mod_rate = newrate = clk_get_rate(smc_host->clk_mod);
+ dev_dbg(mmc_dev(smc_host->mmc), "clk is now %d\n", newrate);
+
+ sunxi_mmc_oclk_onoff(smc_host, 0);
+ /* clear internal divider */
+ temp = mci_readl(smc_host, REG_CLKCR);
+ temp &= ~0xff;
+ mci_writel(smc_host, REG_CLKCR, temp);
+
+ /* determine delays */
+ if (rate <= 400000) {
+ dly = &mmc_clk_dly[MMC_CLK_400K];
+ } else if (rate <= 25000000) {
+ dly = &mmc_clk_dly[MMC_CLK_25M];
+ } else if (rate <= 50000000) {
+ if (smc_host->ddr) {
+ if (smc_host->bus_width == 8)
+ dly = &mmc_clk_dly[MMC_CLK_50MDDR_8BIT];
+ else
+ dly = &mmc_clk_dly[MMC_CLK_50MDDR];
+ } else {
+ dly = &mmc_clk_dly[MMC_CLK_50M];
+ }
+ } else if (rate <= 104000000) {
+ dly = &mmc_clk_dly[MMC_CLK_100M];
+ } else if (rate <= 208000000) {
+ dly = &mmc_clk_dly[MMC_CLK_200M];
+ } else {
+ dly = &mmc_clk_dly[MMC_CLK_50M];
+ }
+
+ oclk_dly = dly->oclk_dly;
+ sclk_dly = dly->sclk_dly;
+
+ src_clk = clk_get_rate(clk_get_parent(smc_host->clk_mod));
+
+ if (src_clk >= 300000000 && src_clk <= 400000000) {
+ if (oclk_dly)
+ oclk_dly--;
+ if (sclk_dly)
+ sclk_dly--;
+ }
+
+ sunxi_mmc_set_clk_dly(smc_host, oclk_dly, sclk_dly);
+ sunxi_mmc_oclk_onoff(smc_host, 1);
+
+ /* oclk_onoff sets various irq status bits, clear these */
+ mci_writel(smc_host, REG_RINTR,
+ mci_readl(smc_host, REG_RINTR) & ~SDXC_SDIOInt);
+}
+
+static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct sunxi_mmc_host *host = mmc_priv(mmc);
+ u32 temp;
+ s32 err;
+
+ /* Set the power state */
+ switch (ios->power_mode) {
+ case MMC_POWER_ON:
+ break;
+
+ case MMC_POWER_UP:
+ if (!IS_ERR(host->vmmc)) {
+ mmc_regulator_set_ocr(host->mmc, host->vmmc, ios->vdd);
+ udelay(200);
+ }
+
+ err = sunxi_mmc_init_host(mmc);
+ if (err) {
+ host->ferror = 1;
+ return;
+ }
+ enable_irq(host->irq);
+
+ dev_dbg(mmc_dev(host->mmc), "power on!\n");
+ host->ferror = 0;
+ break;
+
+ case MMC_POWER_OFF:
+ dev_dbg(mmc_dev(host->mmc), "power off!\n");
+ disable_irq(host->irq);
+ sunxi_mmc_exit_host(host);
+ if (!IS_ERR(host->vmmc))
+ mmc_regulator_set_ocr(host->mmc, host->vmmc, 0);
+ host->ferror = 0;
+ break;
+ }
+
+ /* set bus width */
+ switch (ios->bus_width) {
+ case MMC_BUS_WIDTH_1:
+ mci_writel(host, REG_WIDTH, SDXC_WIDTH1);
+ host->bus_width = 1;
+ break;
+ case MMC_BUS_WIDTH_4:
+ mci_writel(host, REG_WIDTH, SDXC_WIDTH4);
+ host->bus_width = 4;
+ break;
+ case MMC_BUS_WIDTH_8:
+ mci_writel(host, REG_WIDTH, SDXC_WIDTH8);
+ host->bus_width = 8;
+ break;
+ }
+
+ /* set ddr mode */
+ temp = mci_readl(host, REG_GCTRL);
+ if (ios->timing == MMC_TIMING_UHS_DDR50) {
+ temp |= SDXC_DDR_MODE;
+ host->ddr = 1;
+ } else {
+ temp &= ~SDXC_DDR_MODE;
+ host->ddr = 0;
+ }
+ mci_writel(host, REG_GCTRL, temp);
+
+ /* set up clock */
+ if (ios->clock && ios->power_mode) {
+ dev_dbg(mmc_dev(host->mmc), "ios->clock: %d\n", ios->clock);
+ sunxi_mmc_clk_set_rate(host, ios->clock);
+ usleep_range(50000, 55000);
+ }
+}
+
+static void sunxi_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+ struct sunxi_mmc_host *smc_host = mmc_priv(mmc);
+ unsigned long flags;
+ u32 imask;
+
+ spin_lock_irqsave(&smc_host->lock, flags);
+ imask = mci_readl(smc_host, REG_IMASK);
+ if (enable) {
+ smc_host->sdio_imask = SDXC_SDIOInt;
+ imask |= SDXC_SDIOInt;
+ } else {
+ smc_host->sdio_imask = 0;
+ imask &= ~SDXC_SDIOInt;
+ }
+ mci_writel(smc_host, REG_IMASK, imask);
+ spin_unlock_irqrestore(&smc_host->lock, flags);
+}
+
+static void sunxi_mmc_hw_reset(struct mmc_host *mmc)
+{
+ struct sunxi_mmc_host *smc_host = mmc_priv(mmc);
+ mci_writel(smc_host, REG_HWRST, 0);
+ udelay(10);
+ mci_writel(smc_host, REG_HWRST, 1);
+ udelay(300);
+}
+
+static void sunxi_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct sunxi_mmc_host *host = mmc_priv(mmc);
+ struct mmc_command *cmd = mrq->cmd;
+ struct mmc_data *data = mrq->data;
+ unsigned long iflags;
+ u32 imask = SDXC_IntErrBit;
+ u32 cmd_val = SDXC_Start | (cmd->opcode & 0x3f);
+ u32 byte_cnt = 0;
+ int ret;
+
+ if (!mmc_gpio_get_cd(mmc) || host->ferror) {
+ dev_dbg(mmc_dev(host->mmc), "no medium present\n");
+ mrq->cmd->error = -ENOMEDIUM;
+ mmc_request_done(mmc, mrq);
+ return;
+ }
+
+ if (data) {
+ byte_cnt = data->blksz * data->blocks;
+ mci_writel(host, REG_BLKSZ, data->blksz);
+ mci_writel(host, REG_BCNTR, byte_cnt);
+ ret = sunxi_mmc_prepare_dma(host, data);
+ if (ret < 0) {
+ dev_err(mmc_dev(host->mmc), "prepare DMA failed\n");
+ cmd->error = ret;
+ cmd->data->error = ret;
+ mmc_request_done(host->mmc, mrq);
+ return;
+ }
+ }
+
+ if (cmd->opcode == MMC_GO_IDLE_STATE) {
+ cmd_val |= SDXC_SendInitSeq;
+ imask |= SDXC_CmdDone;
+ }
+
+ if (cmd->opcode == SD_SWITCH_VOLTAGE) {
+ cmd_val |= SDXC_VolSwitch;
+ imask |= SDXC_VolChgDone;
+ host->voltage_switching = 1;
+ sunxi_mmc_oclk_onoff(host, 1);
+ }
+
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ cmd_val |= SDXC_RspExp;
+ if (cmd->flags & MMC_RSP_136)
+ cmd_val |= SDXC_LongRsp;
+ if (cmd->flags & MMC_RSP_CRC)
+ cmd_val |= SDXC_CheckRspCRC;
+
+ if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC) {
+ cmd_val |= SDXC_DataExp | SDXC_WaitPreOver;
+ if (cmd->data->flags & MMC_DATA_STREAM) {
+ imask |= SDXC_AutoCMDDone;
+ cmd_val |= SDXC_Seqmod | SDXC_SendAutoStop;
+ }
+ if (cmd->data->stop) {
+ imask |= SDXC_AutoCMDDone;
+ cmd_val |= SDXC_SendAutoStop;
+ } else
+ imask |= SDXC_DataOver;
+
+ if (cmd->data->flags & MMC_DATA_WRITE)
+ cmd_val |= SDXC_Write;
+ else
+ host->wait_dma = 1;
+ } else
+ imask |= SDXC_CmdDone;
+ } else
+ imask |= SDXC_CmdDone;
+
+ dev_dbg(mmc_dev(host->mmc), "cmd %d(%08x) arg %x ie 0x%08x len %d\n",
+ cmd_val & 0x3f, cmd_val, cmd->arg, imask,
+ mrq->data ? mrq->data->blksz * mrq->data->blocks : 0);
+
+ spin_lock_irqsave(&host->lock, iflags);
+ host->mrq = mrq;
+ mci_writel(host, REG_IMASK, host->sdio_imask | imask);
+ spin_unlock_irqrestore(&host->lock, iflags);
+
+ mci_writel(host, REG_CARG, cmd->arg);
+ mci_writel(host, REG_CMDR, cmd_val);
+}
+
+static const struct of_device_id sunxi_mmc_of_match[] = {
+ { .compatible = "allwinner,sun4i-a10-mmc", },
+ { .compatible = "allwinner,sun5i-a13-mmc", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match);
+
+static struct mmc_host_ops sunxi_mmc_ops = {
+ .request = sunxi_mmc_request,
+ .set_ios = sunxi_mmc_set_ios,
+ .get_ro = mmc_gpio_get_ro,
+ .get_cd = mmc_gpio_get_cd,
+ .enable_sdio_irq = sunxi_mmc_enable_sdio_irq,
+ .hw_reset = sunxi_mmc_hw_reset,
+};
+
+static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host,
+ struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ int ret;
+
+ if (of_device_is_compatible(np, "allwinner,sun4i-a10-mmc"))
+ host->idma_des_size_bits = 13;
+ else
+ host->idma_des_size_bits = 16;
+
+ host->vmmc = devm_regulator_get_optional(&pdev->dev, "vmmc");
+ if (IS_ERR(host->vmmc) && PTR_ERR(host->vmmc) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ host->reg_base = devm_ioremap_resource(&pdev->dev,
+ platform_get_resource(pdev, IORESOURCE_MEM, 0));
+ if (IS_ERR(host->reg_base))
+ return PTR_ERR(host->reg_base);
+
+ host->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
+ if (IS_ERR(host->clk_ahb)) {
+ dev_err(&pdev->dev, "Could not get ahb clock\n");
+ return PTR_ERR(host->clk_ahb);
+ }
+
+ host->clk_mod = devm_clk_get(&pdev->dev, "mod");
+ if (IS_ERR(host->clk_mod)) {
+ dev_err(&pdev->dev, "Could not get mod clock\n");
+ return PTR_ERR(host->clk_mod);
+ }
+
+ /* Make sure the controller is in a sane state before enabling irqs */
+ ret = sunxi_mmc_init_host(host->mmc);
+ if (ret)
+ return ret;
+
+ host->irq = platform_get_irq(pdev, 0);
+ ret = devm_request_irq(&pdev->dev, host->irq, sunxi_mmc_irq, 0,
+ "sunxi-mci", host);
+ if (ret == 0)
+ disable_irq(host->irq);
+
+ /* And put it back in reset */
+ sunxi_mmc_exit_host(host);
+
+ return ret;
+}
+
+static int sunxi_mmc_probe(struct platform_device *pdev)
+{
+ struct sunxi_mmc_host *host;
+ struct mmc_host *mmc;
+ int ret;
+
+ mmc = mmc_alloc_host(sizeof(struct sunxi_mmc_host), &pdev->dev);
+ if (!mmc) {
+ dev_err(&pdev->dev, "mmc alloc host failed\n");
+ return -ENOMEM;
+ }
+
+ ret = mmc_of_parse(mmc);
+ if (ret)
+ goto error_free_host;
+
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+ spin_lock_init(&host->lock);
+ tasklet_init(&host->tasklet, sunxi_mmc_tasklet, (unsigned long)host);
+
+ ret = sunxi_mmc_resource_request(host, pdev);
+ if (ret)
+ goto error_free_host;
+
+ host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE,
+ &host->sg_dma, GFP_KERNEL);
+ if (!host->sg_cpu) {
+ dev_err(&pdev->dev, "Failed to allocate DMA descriptor mem\n");
+ ret = -ENOMEM;
+ goto error_free_host;
+ }
+
+ mmc->ops = &sunxi_mmc_ops;
+ mmc->max_blk_count = 8192;
+ mmc->max_blk_size = 4096;
+ mmc->max_segs = PAGE_SIZE / sizeof(struct sunxi_idma_des);
+ mmc->max_seg_size = (1 << host->idma_des_size_bits);
+ mmc->max_req_size = mmc->max_seg_size * mmc->max_segs;
+ /* 400kHz ~ 50MHz */
+ mmc->f_min = 400000;
+ mmc->f_max = 50000000;
+ /* available voltages */
+ if (!IS_ERR(host->vmmc))
+ mmc->ocr_avail = mmc_regulator_get_ocrmask(host->vmmc);
+ else
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+ mmc->caps = MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
+ MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 |
+ MMC_CAP_UHS_DDR50 | MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL |
+ MMC_CAP_DRIVER_TYPE_A;
+
+ if (host->bus_width == 4)
+ mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+ mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP;
+
+ ret = mmc_add_host(mmc);
+
+ if (ret)
+ goto error_free_dma;
+
+ dev_info(&pdev->dev, "base:0x%p irq:%u\n", host->reg_base, host->irq);
+ platform_set_drvdata(pdev, mmc);
+ return 0;
+
+error_free_dma:
+ dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+error_free_host:
+ mmc_free_host(mmc);
+ return ret;
+}
+
+static int sunxi_mmc_remove(struct platform_device *pdev)
+{
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct sunxi_mmc_host *host = mmc_priv(mmc);
+
+ mmc_remove_host(mmc);
+ sunxi_mmc_exit_host(host);
+ tasklet_disable(&host->tasklet);
+ dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+ mmc_free_host(mmc);
+
+ return 0;
+}
+
+static struct platform_driver sunxi_mmc_driver = {
+ .driver = {
+ .name = "sunxi-mci",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(sunxi_mmc_of_match),
+ },
+ .probe = sunxi_mmc_probe,
+ .remove = sunxi_mmc_remove,
+};
+module_platform_driver(sunxi_mmc_driver);
+
+MODULE_DESCRIPTION("Allwinner's SD/MMC Card Controller Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("David Lanzendörfer <david.lanzendoerfer-Z7Kmv9EsliU@public.gmane.org>");
+MODULE_ALIAS("platform:sunxi-mmc");
diff --git a/drivers/mmc/host/sunxi-mci.h b/drivers/mmc/host/sunxi-mci.h
new file mode 100644
index 0000000..4f3c2d9
--- /dev/null
+++ b/drivers/mmc/host/sunxi-mci.h
@@ -0,0 +1,239 @@
+/*
+ * Driver for sunxi SD/MMC host controllers
+ * (C) Copyright 2014-2015 Reuuimlla Technology Co., Ltd.
+ * (C) Copyright 2014-2015 Aaron Maoye <leafy.myeh-jFKXxz0WcGyYHARAtoI1EgC/G2K4zDHf@public.gmane.org>
+ * (C) Copyright 2014-2015 O2S GmbH <www.o2s.ch>
+ * (C) Copyright 2014-2015 David Lanzendörfer <david.lanzendoerfer@o2s.ch>
+ * (C) Copyright 2014-2015 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#ifndef __SUNXI_MCI_H__
+#define __SUNXI_MCI_H__
+
+/* register offset define */
+#define SDXC_REG_GCTRL (0x00) /* SMC Global Control Register */
+#define SDXC_REG_CLKCR (0x04) /* SMC Clock Control Register */
+#define SDXC_REG_TMOUT (0x08) /* SMC Time Out Register */
+#define SDXC_REG_WIDTH (0x0C) /* SMC Bus Width Register */
+#define SDXC_REG_BLKSZ (0x10) /* SMC Block Size Register */
+#define SDXC_REG_BCNTR (0x14) /* SMC Byte Count Register */
+#define SDXC_REG_CMDR (0x18) /* SMC Command Register */
+#define SDXC_REG_CARG (0x1C) /* SMC Argument Register */
+#define SDXC_REG_RESP0 (0x20) /* SMC Response Register 0 */
+#define SDXC_REG_RESP1 (0x24) /* SMC Response Register 1 */
+#define SDXC_REG_RESP2 (0x28) /* SMC Response Register 2 */
+#define SDXC_REG_RESP3 (0x2C) /* SMC Response Register 3 */
+#define SDXC_REG_IMASK (0x30) /* SMC Interrupt Mask Register */
+#define SDXC_REG_MISTA (0x34) /* SMC Masked Interrupt Status Register */
+#define SDXC_REG_RINTR (0x38) /* SMC Raw Interrupt Status Register */
+#define SDXC_REG_STAS (0x3C) /* SMC Status Register */
+#define SDXC_REG_FTRGL (0x40) /* SMC FIFO Threshold Watermark Registe */
+#define SDXC_REG_FUNS (0x44) /* SMC Function Select Register */
+#define SDXC_REG_CBCR (0x48) /* SMC CIU Byte Count Register */
+#define SDXC_REG_BBCR (0x4C) /* SMC BIU Byte Count Register */
+#define SDXC_REG_DBGC (0x50) /* SMC Debug Enable Register */
+#define SDXC_REG_HWRST (0x78) /* SMC Card Hardware Reset for Register */
+#define SDXC_REG_DMAC (0x80) /* SMC IDMAC Control Register */
+#define SDXC_REG_DLBA (0x84) /* SMC IDMAC Descriptor List Base Addre */
+#define SDXC_REG_IDST (0x88) /* SMC IDMAC Status Register */
+#define SDXC_REG_IDIE (0x8C) /* SMC IDMAC Interrupt Enable Register */
+#define SDXC_REG_CHDA (0x90)
+#define SDXC_REG_CBDA (0x94)
+
+#define mci_readl(host, reg) \
+ readl((host)->reg_base + SDXC_##reg)
+#define mci_writel(host, reg, value) \
+ writel((value), (host)->reg_base + SDXC_##reg)
+
+/* global control register bits */
+#define SDXC_SoftReset BIT(0)
+#define SDXC_FIFOReset BIT(1)
+#define SDXC_DMAReset BIT(2)
+#define SDXC_HWReset (SDXC_SoftReset|SDXC_FIFOReset|SDXC_DMAReset)
+#define SDXC_INTEnb BIT(4)
+#define SDXC_DMAEnb BIT(5)
+#define SDXC_DebounceEnb BIT(8)
+#define SDXC_PosedgeLatchData BIT(9)
+#define SDXC_DDR_MODE BIT(10)
+#define SDXC_MemAccessDone BIT(29)
+#define SDXC_AccessDoneDirect BIT(30)
+#define SDXC_ACCESS_BY_AHB BIT(31)
+#define SDXC_ACCESS_BY_DMA (0U << 31)
+/* clock control bits */
+#define SDXC_CardClkOn BIT(16)
+#define SDXC_LowPowerOn BIT(17)
+/* bus width */
+#define SDXC_WIDTH1 (0)
+#define SDXC_WIDTH4 (1)
+#define SDXC_WIDTH8 (2)
+/* smc command bits */
+#define SDXC_RspExp BIT(6)
+#define SDXC_LongRsp BIT(7)
+#define SDXC_CheckRspCRC BIT(8)
+#define SDXC_DataExp BIT(9)
+#define SDXC_Write BIT(10)
+#define SDXC_Seqmod BIT(11)
+#define SDXC_SendAutoStop BIT(12)
+#define SDXC_WaitPreOver BIT(13)
+#define SDXC_StopAbortCMD BIT(14)
+#define SDXC_SendInitSeq BIT(15)
+#define SDXC_UPCLKOnly BIT(21)
+#define SDXC_RdCEATADev BIT(22)
+#define SDXC_CCSExp BIT(23)
+#define SDXC_EnbBoot BIT(24)
+#define SDXC_AltBootOpt BIT(25)
+#define SDXC_BootACKExp BIT(26)
+#define SDXC_BootAbort BIT(27)
+#define SDXC_VolSwitch BIT(28)
+#define SDXC_UseHoldReg BIT(29)
+#define SDXC_Start BIT(31)
+/* interrupt bits */
+#define SDXC_RespErr BIT(1)
+#define SDXC_CmdDone BIT(2)
+#define SDXC_DataOver BIT(3)
+#define SDXC_TxDataReq BIT(4)
+#define SDXC_RxDataReq BIT(5)
+#define SDXC_RespCRCErr BIT(6)
+#define SDXC_DataCRCErr BIT(7)
+#define SDXC_RespTimeout BIT(8)
+#define SDXC_DataTimeout BIT(9)
+#define SDXC_VolChgDone BIT(10)
+#define SDXC_FIFORunErr BIT(11)
+#define SDXC_HardWLocked BIT(12)
+#define SDXC_StartBitErr BIT(13)
+#define SDXC_AutoCMDDone BIT(14)
+#define SDXC_EndBitErr BIT(15)
+#define SDXC_SDIOInt BIT(16)
+#define SDXC_CardInsert BIT(30)
+#define SDXC_CardRemove BIT(31)
+#define SDXC_IntErrBit (SDXC_RespErr | SDXC_RespCRCErr | \
+ SDXC_DataCRCErr | SDXC_RespTimeout | \
+ SDXC_DataTimeout | SDXC_FIFORunErr | \
+ SDXC_HardWLocked | SDXC_StartBitErr | \
+ SDXC_EndBitErr) /* 0xbbc2 */
+#define SDXC_IntDoneBit (SDXC_AutoCMDDone | SDXC_DataOver | \
+ SDXC_CmdDone | SDXC_VolChgDone)
+/* status */
+#define SDXC_RXWLFlag BIT(0)
+#define SDXC_TXWLFlag BIT(1)
+#define SDXC_FIFOEmpty BIT(2)
+#define SDXC_FIFOFull BIT(3)
+#define SDXC_CardPresent BIT(8)
+#define SDXC_CardDataBusy BIT(9)
+#define SDXC_DataFSMBusy BIT(10)
+#define SDXC_DMAReq BIT(31)
+#define SDXC_FIFO_SIZE (16)
+/* Function select */
+#define SDXC_CEATAOn (0xceaaU << 16)
+#define SDXC_SendIrqRsp BIT(0)
+#define SDXC_SDIORdWait BIT(1)
+#define SDXC_AbtRdData BIT(2)
+#define SDXC_SendCCSD BIT(8)
+#define SDXC_SendAutoStopCCSD BIT(9)
+#define SDXC_CEATADevIntEnb BIT(10)
+/* IDMA controller bus mod bit field */
+#define SDXC_IDMACSoftRST BIT(0)
+#define SDXC_IDMACFixBurst BIT(1)
+#define SDXC_IDMACIDMAOn BIT(7)
+#define SDXC_IDMACRefetchDES BIT(31)
+/* IDMA status bit field */
+#define SDXC_IDMACTransmitInt BIT(0)
+#define SDXC_IDMACReceiveInt BIT(1)
+#define SDXC_IDMACFatalBusErr BIT(2)
+#define SDXC_IDMACDesInvalid BIT(4)
+#define SDXC_IDMACCardErrSum BIT(5)
+#define SDXC_IDMACNormalIntSum BIT(8)
+#define SDXC_IDMACAbnormalIntSum BIT(9)
+#define SDXC_IDMACHostAbtInTx BIT(10)
+#define SDXC_IDMACHostAbtInRx BIT(10)
+#define SDXC_IDMACIdle (0U << 13)
+#define SDXC_IDMACSuspend (1U << 13)
+#define SDXC_IDMACDESCRd (2U << 13)
+#define SDXC_IDMACDESCCheck (3U << 13)
+#define SDXC_IDMACRdReqWait (4U << 13)
+#define SDXC_IDMACWrReqWait (5U << 13)
+#define SDXC_IDMACRd (6U << 13)
+#define SDXC_IDMACWr (7U << 13)
+#define SDXC_IDMACDESCClose (8U << 13)
+
+struct sunxi_idma_des {
+ u32 config;
+#define SDXC_IDMAC_DES0_DIC BIT(1) /* disable interrupt on completion */
+#define SDXC_IDMAC_DES0_LD BIT(2) /* last descriptor */
+#define SDXC_IDMAC_DES0_FD BIT(3) /* first descriptor */
+#define SDXC_IDMAC_DES0_CH BIT(4) /* chain mode */
+#define SDXC_IDMAC_DES0_ER BIT(5) /* end of ring */
+#define SDXC_IDMAC_DES0_CES BIT(30) /* card error summary */
+#define SDXC_IDMAC_DES0_OWN BIT(31) /* 1-idma owns it, 0-host owns it */
+
+ /*
+ * If the idma-des-size-bits of property is ie 13, bufsize bits are:
+ * Bits 0-12: buf1 size
+ * Bits 13-25: buf2 size
+ * Bits 26-31: not used
+ * Since we only ever set buf1 size, we can simply store it directly.
+ */
+ u32 buf_size;
+ u32 buf_addr_ptr1;
+ u32 buf_addr_ptr2;
+};
+
+struct sunxi_mmc_host {
+ struct mmc_host *mmc;
+ struct regulator *vmmc;
+
+ /* IO mapping base */
+ void __iomem *reg_base;
+
+ spinlock_t lock;
+ struct tasklet_struct tasklet;
+
+ /* clock management */
+ struct clk *clk_ahb;
+ struct clk *clk_mod;
+
+ /* ios information */
+ u32 clk_mod_rate;
+ u32 bus_width;
+ u32 idma_des_size_bits;
+ u32 ddr;
+ u32 voltage_switching;
+
+ /* irq */
+ int irq;
+ u32 int_sum;
+ u32 sdio_imask;
+
+ /* flags */
+ u32 power_on:1;
+ u32 io_flag:1;
+ u32 wait_dma:1;
+
+ dma_addr_t sg_dma;
+ void *sg_cpu;
+
+ struct mmc_request *mrq;
+ u32 ferror;
+};
+
+#define MMC_CLK_400K 0
+#define MMC_CLK_25M 1
+#define MMC_CLK_50M 2
+#define MMC_CLK_50MDDR 3
+#define MMC_CLK_50MDDR_8BIT 4
+#define MMC_CLK_100M 5
+#define MMC_CLK_200M 6
+#define MMC_CLK_MOD_NUM 7
+
+struct sunxi_mmc_clk_dly {
+ u32 mode;
+ u32 oclk_dly;
+ u32 sclk_dly;
+};
+
+#endif
--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/groups/opt_out.
^ permalink raw reply related
* [PATCH v4 3/7] ARM: sunxi: clk: export clk_sunxi_mmc_phase_control
From: David Lanzendörfer @ 2014-02-07 21:33 UTC (permalink / raw)
To: devicetree-u79uwXL29TY76Z2rM5mHXA, Ulf Hansson, Laurent Pinchart,
Mike Turquette, Simon Baatz, Hans de Goede, Emilio López,
linux-mmc-u79uwXL29TY76Z2rM5mHXA, Chris Ball,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, H Hartley Sweeten,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Tejun Heo, Maxime Ripard,
Guennadi Liakhovetski,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20140207212709.24624.58619.stgit-GPtPHOohwllnsqa/0SyWJQ@public.gmane.org>
From: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
include/linux/clk/sunxi.h | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
create mode 100644 include/linux/clk/sunxi.h
diff --git a/include/linux/clk/sunxi.h b/include/linux/clk/sunxi.h
new file mode 100644
index 0000000..1ef5c89
--- /dev/null
+++ b/include/linux/clk/sunxi.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2013 - Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_CLK_SUNXI_H_
+#define __LINUX_CLK_SUNXI_H_
+
+#include <linux/clk.h>
+
+void clk_sunxi_mmc_phase_control(struct clk_hw *hw, u8 sample, u8 output);
+
+#endif
^ permalink raw reply related
* [PATCH v4 2/7] clk: sunxi: Implement MMC phase control
From: David Lanzendörfer @ 2014-02-07 21:32 UTC (permalink / raw)
To: devicetree-u79uwXL29TY76Z2rM5mHXA, Ulf Hansson, Laurent Pinchart,
Mike Turquette, Simon Baatz, Hans de Goede, Emilio López,
linux-mmc-u79uwXL29TY76Z2rM5mHXA, Chris Ball,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, H Hartley Sweeten,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Tejun Heo, Maxime Ripard,
Guennadi Liakhovetski,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20140207212709.24624.58619.stgit-GPtPHOohwllnsqa/0SyWJQ@public.gmane.org>
From: Emilio López <emilio-0Z03zUJReD5OxF6Tv1QG9Q@public.gmane.org>
Signed-off-by: Emilio López <emilio-0Z03zUJReD5OxF6Tv1QG9Q@public.gmane.org>
---
drivers/clk/sunxi/clk-sunxi.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c
index abb6c5a..33b9977 100644
--- a/drivers/clk/sunxi/clk-sunxi.c
+++ b/drivers/clk/sunxi/clk-sunxi.c
@@ -377,6 +377,41 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate,
/**
+ * clk_sunxi_mmc_phase_control() - configures MMC clock phase control
+ */
+
+void clk_sunxi_mmc_phase_control(struct clk_hw *hw, u8 sample, u8 output)
+{
+ #define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw)
+ #define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw)
+
+ struct clk_composite *composite = to_clk_composite(hw);
+ struct clk_hw *rate_hw = composite->rate_hw;
+ struct clk_factors *factors = to_clk_factors(rate_hw);
+ unsigned long flags = 0;
+ u32 reg;
+
+ if (factors->lock)
+ spin_lock_irqsave(factors->lock, flags);
+
+ reg = readl(factors->reg);
+
+ /* set sample clock phase control */
+ reg &= ~(0x7 << 20);
+ reg |= ((sample & 0x7) << 20);
+
+ /* set output clock phase control */
+ reg &= ~(0x7 << 8);
+ reg |= ((output & 0x7) << 8);
+
+ writel(reg, factors->reg);
+
+ if (factors->lock)
+ spin_unlock_irqrestore(factors->lock, flags);
+}
+
+
+/**
* sunxi_factors_clk_setup() - Setup function for factor clocks
*/
--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/groups/opt_out.
^ permalink raw reply related
* [PATCH v4 1/7] clk: sunxi: factors: automatic reparenting support
From: David Lanzendörfer @ 2014-02-07 21:32 UTC (permalink / raw)
To: devicetree-u79uwXL29TY76Z2rM5mHXA, Ulf Hansson, Laurent Pinchart,
Mike Turquette, Simon Baatz, Hans de Goede, Emilio López,
linux-mmc-u79uwXL29TY76Z2rM5mHXA, Chris Ball,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, H Hartley Sweeten,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Tejun Heo, Maxime Ripard,
Guennadi Liakhovetski,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20140207212709.24624.58619.stgit-GPtPHOohwllnsqa/0SyWJQ@public.gmane.org>
From: Emilio López <emilio-0Z03zUJReD5OxF6Tv1QG9Q@public.gmane.org>
This commit implements .determine_rate, so that our factor clocks can be
reparented when needed.
Signed-off-by: Emilio López <emilio-0Z03zUJReD5OxF6Tv1QG9Q@public.gmane.org>
---
drivers/clk/sunxi/clk-factors.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c
index 9e23264..3806d97 100644
--- a/drivers/clk/sunxi/clk-factors.c
+++ b/drivers/clk/sunxi/clk-factors.c
@@ -77,6 +77,41 @@ static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate,
return rate;
}
+static long clk_factors_determine_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *best_parent_rate,
+ struct clk **best_parent_p)
+{
+ struct clk *clk = hw->clk, *parent, *best_parent = NULL;
+ int i, num_parents;
+ unsigned long parent_rate, best = 0, child_rate, best_child_rate = 0;
+
+ /* find the parent that can help provide the fastest rate <= rate */
+ num_parents = __clk_get_num_parents(clk);
+ for (i = 0; i < num_parents; i++) {
+ parent = clk_get_parent_by_index(clk, i);
+ if (!parent)
+ continue;
+ if (__clk_get_flags(clk) & CLK_SET_RATE_PARENT)
+ parent_rate = __clk_round_rate(parent, rate);
+ else
+ parent_rate = __clk_get_rate(parent);
+
+ child_rate = clk_factors_round_rate(hw, rate, &parent_rate);
+
+ if (child_rate <= rate && child_rate > best_child_rate) {
+ best_parent = parent;
+ best = parent_rate;
+ best_child_rate = child_rate;
+ }
+ }
+
+ if (best_parent)
+ *best_parent_p = best_parent;
+ *best_parent_rate = best;
+
+ return best_child_rate;
+}
+
static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
@@ -113,6 +148,7 @@ static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
}
const struct clk_ops clk_factors_ops = {
+ .determine_rate = clk_factors_determine_rate,
.recalc_rate = clk_factors_recalc_rate,
.round_rate = clk_factors_round_rate,
.set_rate = clk_factors_set_rate,
--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/groups/opt_out.
^ permalink raw reply related
* [PATCH v4 0/7] ARM: sunxi: Add driver for SD/MMC hosts found on allwinner sunxi SOCs
From: David Lanzendörfer @ 2014-02-07 21:32 UTC (permalink / raw)
To: devicetree-u79uwXL29TY76Z2rM5mHXA, Ulf Hansson, Laurent Pinchart,
Mike Turquette, Simon Baatz, Hans de Goede, Emilio López,
linux-mmc-u79uwXL29TY76Z2rM5mHXA, Chris Ball,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, H Hartley Sweeten,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Tejun Heo, Maxime Ripard,
Guennadi Liakhovetski,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Hello
The following patchset adds support for the SD/MMC host found in the Allwinner SoCs.
It contains all the necessary modifications for clock environment and also the device
tree script modification which add it to all the boards using it.
The clock environment function needed for phase offset configuration has
been proposed and implemented by Emilio.
A lot of work and cleanup has been done by Hans de Goede. Special thanks to him!
This patchset is the 4th attempt to send this driver upstream.
Changes since v1:
-Using mmc_of_parse instead of diy dt parsing
-Adding nodes for all mmc controller to the dtsi files,
including sofar unused controllers
-Using generic GPIO slot library for WP/CD
-Adding additional MMC device nodes into DTSI files
Changes since v2:
-Add missing Signed-off-by tags
-Stop using __raw_readl / __raw_writel so that barriers are properly used
-Adding missing new lines
-Adding missing patch for automatic reparenting of clocks
Changes since v3:
-Move clk_enable / disable into host_init / exit (Hans)
-Fix hang on boot caused by irq storm (Hans)
regards
David
---
David Lanzendörfer (4):
ARM: sunxi: Add driver for SD/MMC hosts found on Allwinner sunxi SoCs
ARM: dts: sun7i: Add support for mmc
ARM: dts: sun4i: Add support for mmc
ARM: dts: sun5i: Add support for mmc
Emilio López (2):
clk: sunxi: factors: automatic reparenting support
clk: sunxi: Implement MMC phase control
Hans de Goede (1):
ARM: sunxi: clk: export clk_sunxi_mmc_phase_control
arch/arm/boot/dts/sun4i-a10-a1000.dts | 8
arch/arm/boot/dts/sun4i-a10-cubieboard.dts | 8
arch/arm/boot/dts/sun4i-a10.dtsi | 54 +
arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts | 30 +
arch/arm/boot/dts/sun5i-a10s.dtsi | 44 +
arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts | 15
arch/arm/boot/dts/sun5i-a13-olinuxino.dts | 15
arch/arm/boot/dts/sun5i-a13.dtsi | 37 +
arch/arm/boot/dts/sun7i-a20-cubieboard2.dts | 8
arch/arm/boot/dts/sun7i-a20-cubietruck.dts | 8
arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts | 23 +
arch/arm/boot/dts/sun7i-a20.dtsi | 61 ++
drivers/clk/sunxi/clk-factors.c | 36 +
drivers/clk/sunxi/clk-sunxi.c | 35 +
drivers/mmc/host/Kconfig | 7
drivers/mmc/host/Makefile | 2
drivers/mmc/host/sunxi-mci.c | 871 ++++++++++++++++++++++
drivers/mmc/host/sunxi-mci.h | 239 ++++++
include/linux/clk/sunxi.h | 22 +
19 files changed, 1523 insertions(+)
create mode 100644 drivers/mmc/host/sunxi-mci.c
create mode 100644 drivers/mmc/host/sunxi-mci.h
create mode 100644 include/linux/clk/sunxi.h
--
Signature
--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/groups/opt_out.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox