* [PATCH v3 2/2] ARM: mxs: add dma device
From: Shawn Guo @ 2011-02-23 3:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110222183036.GB22310@pengutronix.de>
Hi Uwe,
On Tue, Feb 22, 2011 at 07:30:36PM +0100, Uwe Kleine-K?nig wrote:
> Hi Shawn,
>
> On Tue, Feb 22, 2011 at 08:27:34PM +0800, Shawn Guo wrote:
> > > > diff --git a/arch/arm/mach-mxs/devices/platform-dma.c b/arch/arm/mach-mxs/devices/platform-dma.c
> > > > new file mode 100644
> > > > index 0000000..ee3220e
> > > > --- /dev/null
> > > > +++ b/arch/arm/mach-mxs/devices/platform-dma.c
> > > I'd prefer to have that called platform-mxs-dma.c to match the driver
> > > name. (At least that's what you use for the data structs.)
> > >
> > If you put this comment a little bit earlier on platform-auart.c which
> > is the first example that saves the "mxs" from driver name, I would
> > have platform-mxs-dma.c and platform-mxs-mmc.c from the start.
> I already noticed I missed auart and planned to follow up with a patch.
> Sorry if this resulted in an extra iteration for you.
>
Can we keep it as it is for now, and get your patch fix this
"mxs global" thing?
Sascha plans to rename fb.h to mxsfb.h, and I'm not sure if you will
do the same to rename dma.h and mmc.h under mach-mxs/include/mach to
align everything with driver name. Just a reminder, this will
require changes in dma and mmc drivers accordingly, also will bring
plat-mxc a mxs-dma.h later support gpmi with mxs dma.
--
Regards,
Shawn
^ permalink raw reply
* Problem with "ARM: tegra: Move tegra_common_init to tegra_init_early"
From: Stephen Warren @ 2011-02-23 3:12 UTC (permalink / raw)
To: linux-arm-kernel
The following commit makes the Tegra APB DMA engine fail to initialize correctly:
commit 0cf6230af909a86f81907455eca2a5c9b8f68fe6
ARM: tegra: Move tegra_common_init to tegra_init_early
Move tegra_common_init to tegra_init_early, and set it
as the init_early entry in the machine struct.
Initializes the clocks earlier so that timers can enable
their clocks.
Also reorders the members in the Harmony and Trimslice
boards' machine structs to match the order they are
called in.
The reason is that tegra_init_early_ calls tegra_dma_init which calls
request_threaded_irq, which fails since the IRQ hasn't yet been marked valid;
that only happens in tegra_init_irq, which gets called after tegra_init_early.
This used to work OK, since tegra_init_early was tegra_common_init, which got
called after tegra_init_irq, basically from the beginning of tegra_harmony_init.
I tried moving the call to tegra_dma_init back to each of the very end of
tegra_init_irq and the very beginning of tegra_harmony_init. Both of these
resulted in a kernel that wouldn't boot.
Simply removing the call to tegra_dma_init completely does allow the kernel
to boot, but obviously, DMA isn't available.
I'm a little stumped why moving tegra_dma_init causes a boot failure. Does
anyone have any helpful ideas, or any alternative ways to fix the underlying
problem?
Thanks.
--
nvpublic
^ permalink raw reply
* [PATCH] msm: add single-wire serial bus interface (SSBI) driver
From: Nicolas Pitre @ 2011-02-23 3:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <8ya8vx7jvqh.fsf@huya.qualcomm.com>
On Tue, 22 Feb 2011, David Brown wrote:
> On Tue, Feb 22 2011, Nicolas Pitre wrote:
>
> > And if someone else comes up with a SSBI interface, then it will be much
> > easier to notice the already existing driver if it is in the driver
> > directory rather than somewhere else in some unrelated (from that
> > person's pov) obscure directory.
>
> Well, I'm fairly sure that nobody would be making an SSBI interface, but
> point taken.
It's not necessarily the SSBI interface per se that is interesting to
other people, but rather the fact that the code driving it might be
relying upon generic kernel infrastructure, such as driver or bus
registration, interrupt requests, resource allocation, etc.
> So what kinds of things constitute drivers versus arch-specific code?
> Currently, iommu drivers seem to be sprinkled throughout arch.
Yes, and so are clock source and clock event "drivers". Some things
fall into a gray area and this is not always clear what the proper
location is for them. But as a rule of thumb you should follow what
most other people did, and if there is no example to follow then just go
with drivers/msm/ by default which should be a pretty safe bet.
Nicolas
^ permalink raw reply
* [PATCH 2/2] ARM i.MX23/28: Add framebuffer device support
From: Shawn Guo @ 2011-02-23 3:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110221141842.GP24426@pengutronix.de>
Hi Sascha,
On Mon, Feb 21, 2011 at 03:18:42PM +0100, Sascha Hauer wrote:
> On Fri, Feb 18, 2011 at 05:36:23PM +0800, Shawn Guo wrote:
> > On Fri, Feb 18, 2011 at 10:26:50AM +0100, Sascha Hauer wrote:
> > > On Fri, Feb 18, 2011 at 05:17:18PM +0800, Shawn Guo wrote:
> > > > > > I understand that "mxsfb" was picked up for the device name to
> > > > > > reflect the driver name. But since this is the device under mach- mxs
> > > > > > folder, can we simply call it "fb" just like the way you name fb.h?
> > > > > >
> > > > > > In this way, we have all mxs platform device naming schema aligned,
> > > > > > auart, duart, dma, fb, fec, flexcan, mmc ...
> > > > >
> > > > > I see it the other way round. mach/fb.h is too generic, I would prefer
> > > > > mach/mxsfb.h to be able to add support for a different framebuffer
> > > > > later. So I better change fb.h to mxsfb.h.
> > > > >
> > > > I'm fine with either way, but just wondering what kind of fb later
> > > > it is and its naming, relatively to mxsfs.
> > >
> > > IPU...?
> > >
> > AFAICT, this is something that never going to happen.
>
> Just the first thing that came to my mind. It could also be the lcd
> controller known from i.MX1/2
>
What about only having one pair fb.h/platform-fb.c to accommodate all
possible framebuffer driver stuff there with appropriate name-space
added for definitions inside the files? Less files, and some common
definitions and functions can be consolidated inside the file.
--
Regards,
Shawn
^ permalink raw reply
* [RFC] ARM: Cortex-A9: Enable dynamic clock gating
From: Todd Poynor @ 2011-02-23 3:55 UTC (permalink / raw)
To: linux-arm-kernel
Enable dynamic high level clock gating for Cortex-A9 CPUs, as
described in 2.3.3 "Dynamic high level clock gating" of the
Cortex-A9 TRM. This may cut the clock of the integer core,
system control block, and Data Engine in certain conditions.
Add ARM errata 720791 to avoid corrupting the Jazelle
instruction stream on earlier Cortex-A9 revisions.
Signed-off-by: Todd Poynor <toddpoynor@google.com>
---
Can anyone advise whether this feature should be selectively
enabled (or otherwise modified) due to secured register access,
introduced latencies observed, etc.?
This has been tested on a few Tegra 2 boards without problems
observed thus far, and some preliminary testing indicates it
may result in fairly significant power savings. Any additional
testing greatly appreciated.
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 166efa2..632ec97 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1202,6 +1202,16 @@ config ARM_ERRATA_753970
This has the same effect as the cache sync operation: store buffer
drain and waiting for all buffers empty.
+config ARM_ERRATA_720791
+ bool "ARM errata: Dynamic high-level clock gating corrupts the Jazelle instruction stream"
+ depends on CPU_V7
+ help
+ This option enables the workaround for the 720791 Cortex-A9
+ (r1p0..r1p2) erratum. The Jazelle instruction stream may be
+ corrupted when dynamic high-level clock gating is enabled.
+ This workaround disables gating the Core clock when the Instruction
+ side is waiting for a Page Table Walk answer or linefill completion.
+
endmenu
source "arch/arm/common/Kconfig"
diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S
index 8e33562..aa1733a 100644
--- a/arch/arm/mm/proc-v7.S
+++ b/arch/arm/mm/proc-v7.S
@@ -241,6 +241,16 @@ __v7_setup:
2: ldr r10, =0x00000c09 @ Cortex-A9 primary part number
teq r0, r10
bne 3f
+ cmp r6, #0x10 @ power ctrl reg added r1p0
+ mrcge p15, 0, r10, c15, c0, 0 @ read power control register
+ orrge r10, r10, #1 @ enable dynamic clock gating
+ mcrge p15, 0, r10, c15, c0, 0 @ write power control register
+#ifdef CONFIG_ARM_ERRATA_720791
+ teq r5, #0x00100000 @ only present in r1p*
+ mrceq p15, 0, r10, c15, c0, 2 @ read "chicken power ctrl" reg
+ orreq r10, r10, #0x30 @ disable core clk gate on
+ mcreq p15, 0, r10, c15, c0, 2 @ instr-side waits
+#endif
#ifdef CONFIG_ARM_ERRATA_742230
cmp r6, #0x22 @ only present up to r2p2
mrcle p15, 0, r10, c15, c0, 1 @ read diagnostic register
--
1.7.3.1
^ permalink raw reply related
* [PATCH v3] dmaengine: mxs-dma: add dma support for i.MX23/28
From: Koul, Vinod @ 2011-02-23 4:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298283699-14635-1-git-send-email-shawn.guo@freescale.com>
On Mon, 2011-02-21 at 15:51 +0530, Shawn Guo wrote:
> This patch adds dma support for Freescale MXS-based SoC i.MX23/28,
> including apbh-dma and apbx-dma.
>
> * apbh-dma and apbx-dma are supported in the driver as two mxs-dma
> instances.
>
> * apbh-dma is different between mx23 and mx28, hardware version
> register is used to differentiate.
>
> * mxs-dma supports pio function besides data transfer. The driver
> uses dma_data_direction DMA_NONE to identify the pio mode, and
> steals sgl and sg_len to get pio words and numbers from clients.
>
> * mxs dmaengine has some very specific features, like sense function
> and the special NAND support (nand_lock, nand_wait4ready). These
> are too specific to implemented in generic dmaengine driver.
>
> * The driver refers to imx-sdma and only a single descriptor is
> statically assigned to each channel.
>
> Signed-off-by: Shawn Guo <shawn.guo@freescale.com>
> ---
> arch/arm/mach-mxs/include/mach/dma.h | 26 ++
> drivers/dma/Kconfig | 8 +
> drivers/dma/Makefile | 1 +
> drivers/dma/mxs-dma.c | 719 ++++++++++++++++++++++++++++++++++
> 4 files changed, 754 insertions(+), 0 deletions(-)
> create mode 100644 arch/arm/mach-mxs/include/mach/dma.h
> create mode 100644 drivers/dma/mxs-dma.c
[snip]
> +#define MXS_DMA_APBH 0
> +#define MXS_DMA_APBX 1
> +#define dma_is_apbh() (mxs_dma->dev_id == MXS_DMA_APBH)
> +
> +#define APBH_VERSION_LATEST 3
> +#define apbh_is_old() (mxs_dma->version < APBH_VERSION_LATEST)
> +
> +#define HW_APBHX_CTRL0 0x000
> +#define BM_APBH_CTRL0_APB_BURST8_EN (1 << 29)
> +#define BM_APBH_CTRL0_APB_BURST_EN (1 << 28)
> +#define BP_APBH_CTRL0_CLKGATE_CHANNEL (8)
> +#define BP_APBH_CTRL0_RESET_CHANNEL (16)
can you please use consistent spacing here...
> +#define HW_APBHX_CTRL1 0x010
> +#define HW_APBHX_CTRL2 0x020
> +#define HW_APBHX_CHANNEL_CTRL 0x030
> +#define BP_APBHX_CHANNEL_CTRL_RESET_CHANNEL (16)
> +#define HW_APBH_VERSION (cpu_is_mx23() ? 0x3f0 : 0x800)
> +#define HW_APBX_VERSION 0x800
> +#define BP_APBHX_VERSION_MAJOR (24)
same here, with alignment as well please
> +#define HW_APBHX_CHn_NXTCMDAR(n) \
> + (((dma_is_apbh() && apbh_is_old()) ? 0x050 : 0x110) + (n) * 0x70)
> +#define HW_APBHX_CHn_SEMA(n) \
> + (((dma_is_apbh() && apbh_is_old()) ? 0x080 : 0x140) + (n) * 0x70)
> +
> +/*
> + * ccw bits definitions
> + *
> + * COMMAND: 0..1 (2)
> + * CHAIN: 2 (1)
> + * IRQ: 3 (1)
> + * NAND_LOCK: 4 (1) - not implemented
> + * NAND_WAIT4READY: 5 (1) - not implemented
> + * DEC_SEM: 6 (1)
> + * WAIT4END: 7 (1)
> + * HALT_ON_TERMINATE: 8 (1)
> + * TERMINATE_FLUSH: 9 (1)
> + * RESERVED: 10..11 (2)
> + * PIO_NUM: 12..15 (4)
> + */
> +#define BP_CCW_COMMAND 0
> +#define BM_CCW_COMMAND (3 << 0)
> +#define CCW_CHAIN (1 << 2)
> +#define CCW_IRQ (1 << 3)
> +#define CCW_DEC_SEM (1 << 6)
> +#define CCW_WAIT4END (1 << 7)
> +#define CCW_HALT_ON_TERM (1 << 8)
> +#define CCW_TERM_FLUSH (1 << 9)
> +#define BP_CCW_PIO_NUM 12
> +#define BM_CCW_PIO_NUM (0xf << 12)
> +
> +#define BF_CCW(value, field) (((value) << BP_CCW_##field) & BM_CCW_##field)
> +
> +#define MXS_DMA_CMD_NO_XFER 0
> +#define MXS_DMA_CMD_WRITE 1
> +#define MXS_DMA_CMD_READ 2
> +#define MXS_DMA_CMD_DMA_SENSE 3 /* not implemented */
> +
> +struct mxs_dma_ccw {
> + u32 next;
> + u16 bits;
> + u16 xfer_bytes;
> +#define MAX_XFER_BYTES 0xff00
> + u32 bufaddr;
> +#define MXS_PIO_WORDS 16
> + u32 pio_words[MXS_PIO_WORDS];
> +};
> +
> +#define NUM_CCW (int)(PAGE_SIZE / sizeof(struct mxs_dma_ccw))
> +
> +struct mxs_dma_chan {
> + struct mxs_dma_engine *mxs_dma;
> + struct dma_chan chan;
> + struct dma_async_tx_descriptor desc;
> + struct tasklet_struct tasklet;
> + int chan_irq;
> + struct mxs_dma_ccw *ccw;
> + dma_addr_t ccw_phys;
> + dma_cookie_t last_completed;
> + enum dma_status status;
> + unsigned int flags;
> +#define MXS_DMA_SG_LOOP (1 << 0)
> +};
> +
> +struct mxs_dma_engine {
> + int dev_id;
> + unsigned int version;
> + void __iomem *base;
> + struct clk *clk;
> + struct dma_device dma_device;
> + struct device_dma_parameters dma_parms;
> +#define MXS_DMA_CHANNELS 16
> + struct mxs_dma_chan mxs_chans[MXS_DMA_CHANNELS];
> +};
> +
> +static void mxs_dma_reset_chan(struct mxs_dma_chan *mxs_chan)
> +{
> + struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
> + int chan_id = mxs_chan->chan.chan_id;
> +
> + if (dma_is_apbh() && apbh_is_old())
> + writel(1 << (chan_id + BP_APBH_CTRL0_RESET_CHANNEL),
> + mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR);
> + else
> + writel(1 << (chan_id + BP_APBHX_CHANNEL_CTRL_RESET_CHANNEL),
> + mxs_dma->base + HW_APBHX_CHANNEL_CTRL + MXS_SET_ADDR);
> +}
> +
> +static void mxs_dma_enable_chan(struct mxs_dma_chan *mxs_chan)
> +{
> + struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
> + int chan_id = mxs_chan->chan.chan_id;
> +
> + /* set cmd_addr up */
> + writel(mxs_chan->ccw_phys,
> + mxs_dma->base + HW_APBHX_CHn_NXTCMDAR(chan_id));
> +
> + /* enable apbh channel clock */
> + if (dma_is_apbh()) {
> + if (apbh_is_old())
> + writel(1 << (chan_id + BP_APBH_CTRL0_CLKGATE_CHANNEL),
> + mxs_dma->base + HW_APBHX_CTRL0 + MXS_CLR_ADDR);
> + else
> + writel(1 << chan_id,
> + mxs_dma->base + HW_APBHX_CTRL0 + MXS_CLR_ADDR);
> + }
> +
> + /* write 1 to SEMA to kick off the channel */
> + writel(1, mxs_dma->base + HW_APBHX_CHn_SEMA(chan_id));
> +}
> +
> +static void mxs_dma_disable_chan(struct mxs_dma_chan *mxs_chan)
> +{
> + struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
> + int chan_id = mxs_chan->chan.chan_id;
> +
> + /* disable apbh channel clock */
> + if (dma_is_apbh()) {
> + if (apbh_is_old())
> + writel(1 << (chan_id + BP_APBH_CTRL0_CLKGATE_CHANNEL),
> + mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR);
> + else
> + writel(1 << chan_id,
> + mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR);
> + }
> +
> + mxs_chan->status = DMA_SUCCESS;
> +}
> +
> +static void mxs_dma_pause_chan(struct mxs_dma_chan *mxs_chan)
> +{
> + struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
> + int chan_id = mxs_chan->chan.chan_id;
> +
> + /* freeze the channel */
> + if (dma_is_apbh() && apbh_is_old())
> + writel(1 << chan_id,
> + mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR);
> + else
> + writel(1 << chan_id,
> + mxs_dma->base + HW_APBHX_CHANNEL_CTRL + MXS_SET_ADDR);
> +
> + mxs_chan->status = DMA_PAUSED;
> +}
> +
> +static void mxs_dma_resume_chan(struct mxs_dma_chan *mxs_chan)
> +{
> + struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
> + int chan_id = mxs_chan->chan.chan_id;
> +
> + /* unfreeze the channel */
> + if (dma_is_apbh() && apbh_is_old())
> + writel(1 << chan_id,
> + mxs_dma->base + HW_APBHX_CTRL0 + MXS_CLR_ADDR);
> + else
> + writel(1 << chan_id,
> + mxs_dma->base + HW_APBHX_CHANNEL_CTRL + MXS_CLR_ADDR);
> +
> + mxs_chan->status = DMA_IN_PROGRESS;
> +}
> +
> +static dma_cookie_t mxs_dma_assign_cookie(struct mxs_dma_chan *mxs_chan)
> +{
> + dma_cookie_t cookie = mxs_chan->chan.cookie;
> +
> + if (++cookie < 0)
> + cookie = 1;
> +
> + mxs_chan->chan.cookie = cookie;
> + mxs_chan->desc.cookie = cookie;
> +
> + return cookie;
> +}
> +
> +static struct mxs_dma_chan *to_mxs_dma_chan(struct dma_chan *chan)
> +{
> + return container_of(chan, struct mxs_dma_chan, chan);
> +}
> +
> +static dma_cookie_t mxs_dma_tx_submit(struct dma_async_tx_descriptor *tx)
> +{
> + struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(tx->chan);
> +
> + mxs_dma_enable_chan(mxs_chan);
> +
> + return mxs_dma_assign_cookie(mxs_chan);
> +}
> +
> +static void mxs_dma_tasklet(unsigned long data)
> +{
> + struct mxs_dma_chan *mxs_chan = (struct mxs_dma_chan *) data;
> +
> + if (mxs_chan->desc.callback)
> + mxs_chan->desc.callback(mxs_chan->desc.callback_param);
> +}
any reason why you are using tasklet? Since these kind of DMA operations
need very fast and efficient interrupt handling, I feel the threaded irq
are the best approach?
> +
> +static irqreturn_t mxs_dma_int_handler(int irq, void *dev_id)
> +{
> + struct mxs_dma_engine *mxs_dma = dev_id;
> + u32 stat1, stat2;
> +
> + /* completion status */
> + stat1 = readl(mxs_dma->base + HW_APBHX_CTRL1);
> + stat1 &= 0xffff;
Magic number?
> + writel(stat1, mxs_dma->base + HW_APBHX_CTRL1 + MXS_CLR_ADDR);
> +
> + /* error status */
> + stat2 = readl(mxs_dma->base + HW_APBHX_CTRL2);
> + writel(stat2, mxs_dma->base + HW_APBHX_CTRL2 + MXS_CLR_ADDR);
> +
> + /*
> + * When both completion and error of termination bits set at the
> + * same time, we do not take it as an error. IOW, it only becomes
> + * an error we need to handler here in case of ether it's an bus
> + * error or a termination error with no completion.
> + */
> + stat2 = ((stat2 >> 16) & stat2) | /* bus error */
> + (~(stat2 >> 16) & stat2 & ~stat1); /* termination with no completion */
> +
> + /* combine error and completion status for checking */
> + stat1 = (stat2 << 16) | stat1;
would it be possible for you to avoid these magic number, will help you
when you add more controllers...
> + while (stat1) {
> + int channel = fls(stat1) - 1;
> + struct mxs_dma_chan *mxs_chan =
> + &mxs_dma->mxs_chans[channel % 16];
> +
> + if (channel >= 16) {
> + dev_dbg(mxs_dma->dma_device.dev, "%s: error in channel %d\n",
> + __func__, channel - 16);
> + mxs_chan->status = DMA_ERROR;
> + mxs_dma_reset_chan(mxs_chan);
> + } else {
> + if (mxs_chan->flags & MXS_DMA_SG_LOOP)
> + mxs_chan->status = DMA_IN_PROGRESS;
> + else
> + mxs_chan->status = DMA_SUCCESS;
> + }
> +
> + stat1 &= ~(1 << channel);
> +
> + if (mxs_chan->status == DMA_SUCCESS)
> + mxs_chan->last_completed = mxs_chan->desc.cookie;
> +
> + /* schedule tasklet on this channel */
> + tasklet_schedule(&mxs_chan->tasklet);
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int mxs_dma_alloc_chan_resources(struct dma_chan *chan)
> +{
> + struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
> + struct mxs_dma_data *data = chan->private;
> + struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
> + int ret;
> +
> + if (!data)
> + return -EINVAL;
> +
> + mxs_chan->chan_irq = data->chan_irq;
> +
> + mxs_chan->ccw = dma_alloc_coherent(mxs_dma->dma_device.dev, PAGE_SIZE,
> + &mxs_chan->ccw_phys, GFP_KERNEL);
> + if (!mxs_chan->ccw) {
> + ret = -ENOMEM;
> + goto err_alloc;
> + }
> +
> + memset(mxs_chan->ccw, 0, PAGE_SIZE);
> +
> + ret = request_irq(mxs_chan->chan_irq, mxs_dma_int_handler,
> + 0, "mxs-dma", mxs_dma);
> + if (ret)
> + goto err_irq;
> +
> + ret = clk_enable(mxs_dma->clk);
> + if (ret)
> + goto err_clk;
> +
> + mxs_dma_reset_chan(mxs_chan);
> +
> + dma_async_tx_descriptor_init(&mxs_chan->desc, chan);
> + mxs_chan->desc.tx_submit = mxs_dma_tx_submit;
> +
> + /* the descriptor is ready */
> + async_tx_ack(&mxs_chan->desc);
> +
> + return 0;
> +
> +err_clk:
> + free_irq(mxs_chan->chan_irq, mxs_dma);
> +err_irq:
> + dma_free_coherent(mxs_dma->dma_device.dev, PAGE_SIZE,
> + mxs_chan->ccw, mxs_chan->ccw_phys);
> +err_alloc:
> + return ret;
> +}
> +
> +static void mxs_dma_free_chan_resources(struct dma_chan *chan)
> +{
> + struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
> + struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
> +
> + mxs_dma_disable_chan(mxs_chan);
> +
> + free_irq(mxs_chan->chan_irq, mxs_dma);
> +
> + dma_free_coherent(mxs_dma->dma_device.dev, PAGE_SIZE,
> + mxs_chan->ccw, mxs_chan->ccw_phys);
> +
> + clk_disable(mxs_dma->clk);
> +}
> +
> +static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg(
> + struct dma_chan *chan, struct scatterlist *sgl,
> + unsigned int sg_len, enum dma_data_direction direction,
> + unsigned long append)
> +{
> + struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
> + struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
> + struct mxs_dma_ccw *ccw;
> + struct scatterlist *sg;
> + int i, j;
> + u32 *pio;
> + static int idx = 0;
> +
> + if (mxs_chan->status == DMA_IN_PROGRESS && !append)
> + return NULL;
shouldn't you be servicing this request. The whole idea for having
multiple descriptors is that client can have multiple requests and
submits when the situation warrants so. This doesn't mean submission of
transaction, which should be done using submit()
> +
> + if (sg_len + (append ? idx : 0) > NUM_CCW) {
> + dev_err(mxs_dma->dma_device.dev,
> + "maximum number of sg exceeded: %d > %d\n",
> + sg_len, NUM_CCW);
> + goto err_out;
> + }
> +
> + mxs_chan->status = DMA_IN_PROGRESS;
> + mxs_chan->flags = 0;
> +
> + /*
> + * If the sg is prepared with append flag set, the sg
> + * will be appended to the last prepared sg.
> + */
> + if (append) {
> + BUG_ON(idx < 1);
> + ccw = &mxs_chan->ccw[idx - 1];
> + ccw->next = mxs_chan->ccw_phys + sizeof(*ccw) * idx;
> + ccw->bits |= CCW_CHAIN;
> + ccw->bits &= ~CCW_IRQ;
> + ccw->bits &= ~CCW_DEC_SEM;
> + ccw->bits &= ~CCW_WAIT4END;
> + } else {
> + idx = 0;
> + }
> +
> + if (direction == DMA_NONE) {
> + ccw = &mxs_chan->ccw[idx++];
> + pio = (u32 *) sgl;
> +
> + for (j = 0; j < sg_len;)
> + ccw->pio_words[j++] = *pio++;
> +
> + ccw->bits = 0;
> + ccw->bits |= CCW_IRQ;
> + ccw->bits |= CCW_DEC_SEM;
> + ccw->bits |= CCW_WAIT4END;
> + ccw->bits |= CCW_HALT_ON_TERM;
> + ccw->bits |= CCW_TERM_FLUSH;
> + ccw->bits |= BF_CCW(sg_len, PIO_NUM);
> + ccw->bits |= BF_CCW(MXS_DMA_CMD_NO_XFER, COMMAND);
> + } else {
> + for_each_sg(sgl, sg, sg_len, i) {
> + if (sg->length > MAX_XFER_BYTES) {
> + dev_err(mxs_dma->dma_device.dev, "maximum bytes for sg entry exceeded: %d > %d\n",
> + sg->length, MAX_XFER_BYTES);
> + goto err_out;
> + }
> +
> + ccw = &mxs_chan->ccw[idx++];
> +
> + ccw->next = mxs_chan->ccw_phys + sizeof(*ccw) * idx;
> + ccw->bufaddr = sg->dma_address;
> + ccw->xfer_bytes = sg->length;
> +
> + ccw->bits = 0;
> + ccw->bits |= CCW_CHAIN;
> + ccw->bits |= CCW_HALT_ON_TERM;
> + ccw->bits |= CCW_TERM_FLUSH;
> + ccw->bits |= BF_CCW(direction == DMA_FROM_DEVICE ?
> + MXS_DMA_CMD_WRITE : MXS_DMA_CMD_READ,
> + COMMAND);
> +
> + if (i + 1 == sg_len) {
> + ccw->bits &= ~CCW_CHAIN;
> + ccw->bits |= CCW_IRQ;
> + ccw->bits |= CCW_DEC_SEM;
> + ccw->bits |= CCW_WAIT4END;
> + }
> + }
> + }
> +
> + return &mxs_chan->desc;
> +
> +err_out:
> + mxs_chan->status = DMA_ERROR;
> + return NULL;
> +}
> +
> +static struct dma_async_tx_descriptor *mxs_dma_prep_dma_cyclic(
> + struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len,
> + size_t period_len, enum dma_data_direction direction)
> +{
> + struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
> + struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
> + int num_periods = buf_len / period_len;
> + int i = 0, buf = 0;
> +
> + if (mxs_chan->status == DMA_IN_PROGRESS)
> + return NULL;
> +
> + mxs_chan->status = DMA_IN_PROGRESS;
> + mxs_chan->flags |= MXS_DMA_SG_LOOP;
> +
> + if (num_periods > NUM_CCW) {
> + dev_err(mxs_dma->dma_device.dev,
> + "maximum number of sg exceeded: %d > %d\n",
> + num_periods, NUM_CCW);
> + goto err_out;
> + }
> +
> + if (period_len > MAX_XFER_BYTES) {
> + dev_err(mxs_dma->dma_device.dev,
> + "maximum period size exceeded: %d > %d\n",
> + period_len, MAX_XFER_BYTES);
> + goto err_out;
> + }
> +
> + while (buf < buf_len) {
> + struct mxs_dma_ccw *ccw = &mxs_chan->ccw[i];
> +
> + if (i + 1 == num_periods)
> + ccw->next = mxs_chan->ccw_phys;
> + else
> + ccw->next = mxs_chan->ccw_phys + sizeof(*ccw) * (i + 1);
> +
> + ccw->bufaddr = dma_addr;
> + ccw->xfer_bytes = period_len;
> +
> + ccw->bits = 0;
> + ccw->bits |= CCW_CHAIN;
> + ccw->bits |= CCW_IRQ;
> + ccw->bits |= CCW_HALT_ON_TERM;
> + ccw->bits |= CCW_TERM_FLUSH;
> + ccw->bits |= BF_CCW(direction == DMA_FROM_DEVICE ?
> + MXS_DMA_CMD_WRITE : MXS_DMA_CMD_READ, COMMAND);
> +
> + dma_addr += period_len;
> + buf += period_len;
> +
> + i++;
> + }
> +
> + return &mxs_chan->desc;
> +
> +err_out:
> + mxs_chan->status = DMA_ERROR;
> + return NULL;
> +}
> +
> +static int mxs_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
> + unsigned long arg)
> +{
> + struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
> + int ret = 0;
> +
> + switch (cmd) {
> + case DMA_TERMINATE_ALL:
> + mxs_dma_disable_chan(mxs_chan);
> + break;
> + case DMA_PAUSE:
> + mxs_dma_pause_chan(mxs_chan);
> + break;
> + case DMA_RESUME:
> + mxs_dma_resume_chan(mxs_chan);
> + break;
> + default:
> + ret = -ENOSYS;
> + }
> +
> + return ret;
> +}
> +
> +static enum dma_status mxs_dma_tx_status(struct dma_chan *chan,
> + dma_cookie_t cookie, struct dma_tx_state *txstate)
> +{
> + struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
> + dma_cookie_t last_used;
> +
> + last_used = chan->cookie;
> + dma_set_tx_state(txstate, mxs_chan->last_completed, last_used, 0);
> +
> + return mxs_chan->status;
> +}
> +
> +static void mxs_dma_issue_pending(struct dma_chan *chan)
> +{
> + /*
> + * Nothing to do. We only have a single descriptor.
> + */
Hmmm, going back to earlier comment and thinking on this again, any
reason why you choose to do it this way. Wont it be nicer to have
multiple descriptors?
> +}
> +
> +static int __init mxs_dma_init(struct mxs_dma_engine *mxs_dma)
> +{
> + int ret;
> +
> + ret = clk_enable(mxs_dma->clk);
> + if (ret)
> + goto err_out;
> +
> + ret = mxs_reset_block(mxs_dma->base);
> + if (ret)
> + goto err_out;
> +
> + /* only major version matters */
> + mxs_dma->version = readl(mxs_dma->base +
> + ((mxs_dma->dev_id == MXS_DMA_APBX) ?
> + HW_APBX_VERSION : HW_APBH_VERSION)) >>
> + BP_APBHX_VERSION_MAJOR;
> +
> + /* enable apbh burst */
> + if (dma_is_apbh()) {
> + writel(BM_APBH_CTRL0_APB_BURST_EN,
> + mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR);
> + writel(BM_APBH_CTRL0_APB_BURST8_EN,
> + mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR);
> + }
> +
> + /* enable irq for all the channels */
> + writel(0xffff << 16, mxs_dma->base + HW_APBHX_CTRL1 + MXS_SET_ADDR);
> +
> + clk_disable(mxs_dma->clk);
> +
> + return 0;
> +
> +err_out:
> + return ret;
> +}
> +
> +static int __init mxs_dma_probe(struct platform_device *pdev)
> +{
> + const struct platform_device_id *id_entry =
> + platform_get_device_id(pdev);
> + struct mxs_dma_engine *mxs_dma;
> + struct resource *iores;
> + int ret, i;
> +
> + mxs_dma = kzalloc(sizeof(*mxs_dma), GFP_KERNEL);
> + if (!mxs_dma)
> + return -ENOMEM;
> +
> + mxs_dma->dev_id = id_entry->driver_data;
> +
> + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +
> + if (!request_mem_region(iores->start, resource_size(iores),
> + pdev->name)) {
> + ret = -EBUSY;
> + goto err_request_region;
> + }
> +
> + mxs_dma->base = ioremap(iores->start, resource_size(iores));
> + if (!mxs_dma->base) {
> + ret = -ENOMEM;
> + goto err_ioremap;
> + }
> +
> + mxs_dma->clk = clk_get(&pdev->dev, NULL);
> + if (IS_ERR(mxs_dma->clk)) {
> + ret = PTR_ERR(mxs_dma->clk);
> + goto err_clk;
> + }
> +
> + dma_cap_set(DMA_SLAVE, mxs_dma->dma_device.cap_mask);
> + dma_cap_set(DMA_CYCLIC, mxs_dma->dma_device.cap_mask);
> +
> + INIT_LIST_HEAD(&mxs_dma->dma_device.channels);
> +
> + /* Initialize channel parameters */
> + for (i = 0; i < MXS_DMA_CHANNELS; i++) {
> + struct mxs_dma_chan *mxs_chan = &mxs_dma->mxs_chans[i];
> +
> + mxs_chan->mxs_dma = mxs_dma;
> + mxs_chan->chan.device = &mxs_dma->dma_device;
> +
> + tasklet_init(&mxs_chan->tasklet, mxs_dma_tasklet,
> + (unsigned long) mxs_chan);
> +
> +
> + /* Add the channel to mxs_chan list */
> + list_add_tail(&mxs_chan->chan.device_node, &mxs_dma->dma_device.channels);
> + }
> +
> + ret = mxs_dma_init(mxs_dma);
> + if (ret)
> + goto err_init;
> +
> + mxs_dma->dma_device.dev = &pdev->dev;
> +
> + /* mxs_dma gets 65535 bytes maximum sg size */
> + mxs_dma->dma_device.dev->dma_parms = &mxs_dma->dma_parms;
> + dma_set_max_seg_size(mxs_dma->dma_device.dev, MAX_XFER_BYTES);
> +
> + mxs_dma->dma_device.device_alloc_chan_resources = mxs_dma_alloc_chan_resources;
> + mxs_dma->dma_device.device_free_chan_resources = mxs_dma_free_chan_resources;
> + mxs_dma->dma_device.device_tx_status = mxs_dma_tx_status;
> + mxs_dma->dma_device.device_prep_slave_sg = mxs_dma_prep_slave_sg;
> + mxs_dma->dma_device.device_prep_dma_cyclic = mxs_dma_prep_dma_cyclic;
> + mxs_dma->dma_device.device_control = mxs_dma_control;
> + mxs_dma->dma_device.device_issue_pending = mxs_dma_issue_pending;
> +
> + ret = dma_async_device_register(&mxs_dma->dma_device);
> + if (ret) {
> + dev_err(mxs_dma->dma_device.dev, "unable to register\n");
> + goto err_init;
> + }
> +
> + dev_info(mxs_dma->dma_device.dev, "initialized\n");
> +
> + return 0;
> +
> +err_init:
> + clk_put(mxs_dma->clk);
> +err_clk:
> + iounmap(mxs_dma->base);
> +err_ioremap:
> + release_mem_region(iores->start, resource_size(iores));
> +err_request_region:
> + kfree(mxs_dma);
> + return ret;
> +}
> +
> +static struct platform_device_id mxs_dma_type[] = {
> + {
> + .name = "mxs-dma-apbh",
> + .driver_data = MXS_DMA_APBH,
> + }, {
> + .name = "mxs-dma-apbx",
> + .driver_data = MXS_DMA_APBX,
> + }
> +};
> +
> +static struct platform_driver mxs_dma_driver = {
> + .driver = {
> + .name = "mxs-dma",
> + },
> + .id_table = mxs_dma_type,
> +};
> +
> +static int __init mxs_dma_module_init(void)
> +{
> + return platform_driver_probe(&mxs_dma_driver, mxs_dma_probe);
> +}
> +subsys_initcall(mxs_dma_module_init);
No exit??
--
~Vinod
Intel Corp.
^ permalink raw reply
* [PATCH] wip: convert imx27 to common struct clk
From: Saravana Kannan @ 2011-02-23 4:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298417599-23522-1-git-send-email-u.kleine-koenig@pengutronix.de>
On 02/22/2011 03:33 PM, Uwe Kleine-K?nig wrote:
> Signed-off-by: Uwe Kleine-K?nig<u.kleine-koenig@pengutronix.de>
> ---
> Hello,
>
> on top of .38-rc6 + making the clk_{un,}prepare stubs static inline,
> this patch makes use of Jeremy's common struct clk (v13) on a i.MX27 based
> machine.
>
> This is compile tested using mx21_defconfig and runtime tested using
> mx27_defconfig.
>
> I had to degrade one WARN_ON to WARN_ON_ONCE in drivers/clk/clk.c to
> actually make it work. Otherwise console output results in a warning
> that results in console output ...
You won't be able to do mainline the WARN_ON_ONCE because that will only
warn for the first clock that violates whatever condition it's warning
about.
Your probably need to fix your serial driver. What serial driver are you
using?
-Saravana
^ permalink raw reply
* Problem with "ARM: tegra: Move tegra_common_init to tegra_init_early"
From: Olof Johansson @ 2011-02-23 4:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <74CDBE0F657A3D45AFBB94109FB122FF03112A5C22@HQMAIL01.nvidia.com>
Grmbl. I missed the:
[ 0.000000] Failed to register IRQ 136 for DMA 0
When I booted the series on harmony and seaboard last night, since the
board came up to prompt and seemed otherwise healthy. Sorry about
that.
-Olof
On Tue, Feb 22, 2011 at 7:12 PM, Stephen Warren <swarren@nvidia.com> wrote:
> The following commit makes the Tegra APB DMA engine fail to initialize correctly:
>
> commit 0cf6230af909a86f81907455eca2a5c9b8f68fe6
>
> ? ?ARM: tegra: Move tegra_common_init to tegra_init_early
>
> ? ?Move tegra_common_init to tegra_init_early, and set it
> ? ?as the init_early entry in the machine struct.
> ? ?Initializes the clocks earlier so that timers can enable
> ? ?their clocks.
>
> ? ?Also reorders the members in the Harmony and Trimslice
> ? ?boards' machine structs to match the order they are
> ? ?called in.
>
> The reason is that tegra_init_early_ calls tegra_dma_init which calls
> request_threaded_irq, which fails since the IRQ hasn't yet been marked valid;
> that only happens in tegra_init_irq, which gets called after tegra_init_early.
>
> This used to work OK, since tegra_init_early was tegra_common_init, which got
> called after tegra_init_irq, basically from the beginning of tegra_harmony_init.
>
> I tried moving the call to tegra_dma_init back to each of the very end of
> tegra_init_irq and the very beginning of tegra_harmony_init. Both of these
> resulted in a kernel that wouldn't boot.
>
> Simply removing the call to tegra_dma_init completely does allow the kernel
> to boot, but obviously, DMA isn't available.
>
> I'm a little stumped why moving tegra_dma_init causes a boot failure. Does
> anyone have any helpful ideas, or any alternative ways to fix the underlying
> problem?
>
> Thanks.
>
> --
> nvpublic
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at ?http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply
* [PATCH v4 7/7] dt: add documentation of ARM dt boot interface
From: Grant Likely @ 2011-02-23 4:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <alpine.LFD.2.00.1102222149370.22028@xanadu.home>
On Tue, Feb 22, 2011 at 7:55 PM, Nicolas Pitre <nicolas.pitre@linaro.org> wrote:
> On Tue, 22 Feb 2011, Grant Likely wrote:
>
>> +The boot loader must load a device tree image (dtb) into system ram
>> +at a 64bit aligned address and initialize it with the boot data.
> [...]
>> + ? ? ? ? ? ? ? ?r2 : physical pointer to the device-tree block
>> + ? ? ? ? ? ? ? ?(defined in chapter II) in RAM. ?Device tree can be located
>> + ? ? ? ? ? ? ? ?anywhere in system RAM, but it should be aligned on a 32 bit
>> + ? ? ? ? ? ? ? ?boundary.
>
> So... which one is true?
Oops, I thought I fixed that. It should be 64 bit boundary (actually,
32 bit is probably just fine, but there are some header fields which
are u64 physical address values so it probably makes sense to keep it
aligned for 64 bits).
g.
>
>
> Nicolas
>
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* [PATCH v2 13/13] tty: pruss SUART driver
From: Subhasish Ghosh @ 2011-02-23 5:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110222143704.GA978@suse.de>
I could not follow the recommendations clearly.
This is just to clarify.
Currently, I have the following files for the suart implementation:
drivers/tty/serial/da8xx_pruss/pruss_suart_api.h
drivers/tty/serial/da8xx_pruss/pruss_suart_err.h
drivers/tty/serial/da8xx_pruss/pruss_suart_regs.h
drivers/tty/serial/da8xx_pruss/pruss_suart_board.h
drivers/tty/serial/da8xx_pruss/pruss_suart_mcasp.h
drivers/tty/serial/da8xx_pruss/pruss_suart_utils.h
drivers/tty/serial/da8xx_pruss/pruss_suart_api.c
drivers/tty/serial/da8xx_pruss/pruss_suart.c
drivers/tty/serial/da8xx_pruss/pruss_suart_utils.c
Of these, I will be removing pruss_suart_err.h as part of the Linux error
code cleanup.
But, I need to keep at least pruss_suart_board.h as a separate file, as
this defines
configurations which will be often modified by users, I don't want to mix it
with other files.
Should I combine rest of the headers into a single file ? and keep the other
three .c files under "drivers/tty/serial/"
and remove the da8xx_pruss directory altogether.
--------------------------------------------------
From: "Greg KH" <gregkh@suse.de>
Sent: Tuesday, February 22, 2011 8:07 PM
To: "Subhasish Ghosh" <subhasish@mistralsolutions.com>
Cc: "Arnd Bergmann" <arnd@arndb.de>; <linux-arm-kernel@lists.infradead.org>;
"Thomas Gleixner" <tglx@linutronix.de>; "Alan Cox"
<alan@lxorguk.ukuu.org.uk>; <sachi@mistralsolutions.com>;
<davinci-linux-open-source@linux.davincidsp.com>; <nsekhar@ti.com>; "open
list" <linux-kernel@vger.kernel.org>; <m-watkins@ti.com>
Subject: Re: [PATCH v2 13/13] tty: pruss SUART driver
> On Tue, Feb 22, 2011 at 02:12:32PM +0530, Subhasish Ghosh wrote:
>> Hello,
>>
>> I had kept separate files to affirm the modularity and ease of
>> portability of the system.
>>
>> There are three different interfaces,
>> 1. The Linux driver interface
>> 2. The PRU control interface
>> 3. The McASP serializer interface.
>>
>> To maintain modularity, I had classified the files respectively as :
>> 1. pruss_suart.c
>> 2. pruss_suart_api.c
>> 3. pruss_suart_utils.c
>>
>> This is not a single device which can be expressed as a single file,
>> but functionally different devices logically cascaded together to
>> work in unison.
>>
>> We use the PRU for packet processing, but the actual data is
>> transmitted/received through the
>> McASP, which we use as a serializer.
>>
>> I feel to combine these disparate functionalities into a single file
>> will not
>>
>> 1. Help better understanding the device. I mean, why should a TTY
>> UART driver be aware of the McASP or the PRU.
>> 2. In case of a bug in the API layer or McASP, the driver need not
>> be touched, thus improve maintainability.
>> 3. If we need to port it to another Linux version, just editing the
>> driver file should suffice, this will reduce bugs while porting.
>
> If your code is in the kernel tree, you do not need to ever port it to a
> new version, as it will happen automatically as new kernels are
> released, so this really isn't anything to worry about.
>
>> To me, combining all of these into a single file only creates a
>> mess. This is the reason I had separated them into different files!!
>> I don't understand why should it be better to have all of these into
>> a single file.
>
> As Alan stated, just use 3 files in the directory with the other
> drivers, you don't need a subdir for something small like this.
>
> thanks,
>
> greg k-h
^ permalink raw reply
* [PATCH v3] dmaengine: mxs-dma: add dma support for i.MX23/28
From: Koul, Vinod @ 2011-02-23 5:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110223055709.GC20346@S2100-06.ap.freescale.net>
On Wed, 2011-02-23 at 11:27 +0530, Shawn Guo wrote:
> Hi Vinod,
>
> Thanks for the review.
>
> On Wed, Feb 23, 2011 at 09:31:41AM +0530, Koul, Vinod wrote:
> > On Mon, 2011-02-21 at 15:51 +0530, Shawn Guo wrote:
> > > This patch adds dma support for Freescale MXS-based SoC i.MX23/28,
> > > including apbh-dma and apbx-dma.
> > >
> > > * apbh-dma and apbx-dma are supported in the driver as two mxs-dma
> > > instances.
> > >
> > > * apbh-dma is different between mx23 and mx28, hardware version
> > > register is used to differentiate.
> > >
> > > * mxs-dma supports pio function besides data transfer. The driver
> > > uses dma_data_direction DMA_NONE to identify the pio mode, and
> > > steals sgl and sg_len to get pio words and numbers from clients.
> > >
> > > * mxs dmaengine has some very specific features, like sense function
> > > and the special NAND support (nand_lock, nand_wait4ready). These
> > > are too specific to implemented in generic dmaengine driver.
> > >
> > > * The driver refers to imx-sdma and only a single descriptor is
> > > statically assigned to each channel.
> > >
> > > Signed-off-by: Shawn Guo <shawn.guo@freescale.com>
> > > ---
> > > arch/arm/mach-mxs/include/mach/dma.h | 26 ++
> > > drivers/dma/Kconfig | 8 +
> > > drivers/dma/Makefile | 1 +
> > > drivers/dma/mxs-dma.c | 719 ++++++++++++++++++++++++++++++++++
> > > 4 files changed, 754 insertions(+), 0 deletions(-)
> > > create mode 100644 arch/arm/mach-mxs/include/mach/dma.h
> > > create mode 100644 drivers/dma/mxs-dma.c
> > [snip]
> >
> > > +#define MXS_DMA_APBH 0
> > > +#define MXS_DMA_APBX 1
> > > +#define dma_is_apbh() (mxs_dma->dev_id == MXS_DMA_APBH)
> > > +
> > > +#define APBH_VERSION_LATEST 3
> > > +#define apbh_is_old() (mxs_dma->version < APBH_VERSION_LATEST)
> > > +
> > > +#define HW_APBHX_CTRL0 0x000
> > > +#define BM_APBH_CTRL0_APB_BURST8_EN (1 << 29)
> > > +#define BM_APBH_CTRL0_APB_BURST_EN (1 << 28)
> > > +#define BP_APBH_CTRL0_CLKGATE_CHANNEL (8)
> > > +#define BP_APBH_CTRL0_RESET_CHANNEL (16)
> > can you please use consistent spacing here...
> >
> I thought it's a little bit easier to read these BM/BP defined
> against the register HW_......
>
> > > +#define HW_APBHX_CTRL1 0x010
> > > +#define HW_APBHX_CTRL2 0x020
> > > +#define HW_APBHX_CHANNEL_CTRL 0x030
> > > +#define BP_APBHX_CHANNEL_CTRL_RESET_CHANNEL (16)
> > > +#define HW_APBH_VERSION (cpu_is_mx23() ? 0x3f0 : 0x800)
> > > +#define HW_APBX_VERSION 0x800
> > > +#define BP_APBHX_VERSION_MAJOR (24)
> > same here, with alignment as well please
>
> ditto
Typically you can split the sections for HW_xxx and BP_xx with a space
line. This is more of a cosmetic change, I am not going to hold the
patch for this.
> > > +static void mxs_dma_tasklet(unsigned long data)
> > > +{
> > > + struct mxs_dma_chan *mxs_chan = (struct mxs_dma_chan *) data;
> > > +
> > > + if (mxs_chan->desc.callback)
> > > + mxs_chan->desc.callback(mxs_chan->desc.callback_param);
> > > +}
> > any reason why you are using tasklet? Since these kind of DMA operations
> > need very fast and efficient interrupt handling, I feel the threaded irq
> > are the best approach?
>
> There was some discussion on this.
>
> http://lists.infradead.org/pipermail/linux-arm-kernel/2011-February/041251.html
Well in that case you are calling the callback from irq handler which is
not so good. The tasklet is other extreme (slow). Given that this is
also intended for audio I am not sure if calling period elapsed from
tasklet callback will not cause any audio over/underruns, last I tried
it used to break quite often in stressed situations.
A threaded irq gives you a kernel thread for processing and calling the
callback but doesnt block your irq handler.
>
> > > +
> > > +static irqreturn_t mxs_dma_int_handler(int irq, void *dev_id)
> > > +{
> > > + struct mxs_dma_engine *mxs_dma = dev_id;
> > > + u32 stat1, stat2;
> > > +
> > > + /* completion status */
> > > + stat1 = readl(mxs_dma->base + HW_APBHX_CTRL1);
> > > + stat1 &= 0xffff;
> > Magic number?
> >
> Define macro instead?
Sound reasonable
>
> > > + writel(stat1, mxs_dma->base + HW_APBHX_CTRL1 + MXS_CLR_ADDR);
> > > +
> > > + /* error status */
> > > + stat2 = readl(mxs_dma->base + HW_APBHX_CTRL2);
> > > + writel(stat2, mxs_dma->base + HW_APBHX_CTRL2 + MXS_CLR_ADDR);
> > > +
> > > + /*
> > > + * When both completion and error of termination bits set at the
> > > + * same time, we do not take it as an error. IOW, it only becomes
> > > + * an error we need to handler here in case of ether it's an bus
> > > + * error or a termination error with no completion.
> > > + */
> > > + stat2 = ((stat2 >> 16) & stat2) | /* bus error */
> > > + (~(stat2 >> 16) & stat2 & ~stat1); /* termination with no completion */
> > > +
> > > + /* combine error and completion status for checking */
> > > + stat1 = (stat2 << 16) | stat1;
> > would it be possible for you to avoid these magic number, will help you
> > when you add more controllers...
> >
> I really do not see the possibility to add more controllers into this
> driver, but I can define macro instead if you are uncomfortable with
> them.
Thats today, how about 2-3 years down the line when you have similar
controllers for future rev of this. Writing new driver for next gen in
not a good idea
--
~Vinod
^ permalink raw reply
* [PATCH v3] dmaengine: mxs-dma: add dma support for i.MX23/28
From: Shawn Guo @ 2011-02-23 5:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298433701.23371.236.camel@vkoul-udesk3>
Hi Vinod,
Thanks for the review.
On Wed, Feb 23, 2011 at 09:31:41AM +0530, Koul, Vinod wrote:
> On Mon, 2011-02-21 at 15:51 +0530, Shawn Guo wrote:
> > This patch adds dma support for Freescale MXS-based SoC i.MX23/28,
> > including apbh-dma and apbx-dma.
> >
> > * apbh-dma and apbx-dma are supported in the driver as two mxs-dma
> > instances.
> >
> > * apbh-dma is different between mx23 and mx28, hardware version
> > register is used to differentiate.
> >
> > * mxs-dma supports pio function besides data transfer. The driver
> > uses dma_data_direction DMA_NONE to identify the pio mode, and
> > steals sgl and sg_len to get pio words and numbers from clients.
> >
> > * mxs dmaengine has some very specific features, like sense function
> > and the special NAND support (nand_lock, nand_wait4ready). These
> > are too specific to implemented in generic dmaengine driver.
> >
> > * The driver refers to imx-sdma and only a single descriptor is
> > statically assigned to each channel.
> >
> > Signed-off-by: Shawn Guo <shawn.guo@freescale.com>
> > ---
> > arch/arm/mach-mxs/include/mach/dma.h | 26 ++
> > drivers/dma/Kconfig | 8 +
> > drivers/dma/Makefile | 1 +
> > drivers/dma/mxs-dma.c | 719 ++++++++++++++++++++++++++++++++++
> > 4 files changed, 754 insertions(+), 0 deletions(-)
> > create mode 100644 arch/arm/mach-mxs/include/mach/dma.h
> > create mode 100644 drivers/dma/mxs-dma.c
> [snip]
>
> > +#define MXS_DMA_APBH 0
> > +#define MXS_DMA_APBX 1
> > +#define dma_is_apbh() (mxs_dma->dev_id == MXS_DMA_APBH)
> > +
> > +#define APBH_VERSION_LATEST 3
> > +#define apbh_is_old() (mxs_dma->version < APBH_VERSION_LATEST)
> > +
> > +#define HW_APBHX_CTRL0 0x000
> > +#define BM_APBH_CTRL0_APB_BURST8_EN (1 << 29)
> > +#define BM_APBH_CTRL0_APB_BURST_EN (1 << 28)
> > +#define BP_APBH_CTRL0_CLKGATE_CHANNEL (8)
> > +#define BP_APBH_CTRL0_RESET_CHANNEL (16)
> can you please use consistent spacing here...
>
I thought it's a little bit easier to read these BM/BP defined
against the register HW_......
> > +#define HW_APBHX_CTRL1 0x010
> > +#define HW_APBHX_CTRL2 0x020
> > +#define HW_APBHX_CHANNEL_CTRL 0x030
> > +#define BP_APBHX_CHANNEL_CTRL_RESET_CHANNEL (16)
> > +#define HW_APBH_VERSION (cpu_is_mx23() ? 0x3f0 : 0x800)
> > +#define HW_APBX_VERSION 0x800
> > +#define BP_APBHX_VERSION_MAJOR (24)
> same here, with alignment as well please
ditto
> > +#define HW_APBHX_CHn_NXTCMDAR(n) \
> > + (((dma_is_apbh() && apbh_is_old()) ? 0x050 : 0x110) + (n) * 0x70)
> > +#define HW_APBHX_CHn_SEMA(n) \
> > + (((dma_is_apbh() && apbh_is_old()) ? 0x080 : 0x140) + (n) * 0x70)
> > +
> > +/*
> > + * ccw bits definitions
> > + *
> > + * COMMAND: 0..1 (2)
> > + * CHAIN: 2 (1)
> > + * IRQ: 3 (1)
> > + * NAND_LOCK: 4 (1) - not implemented
> > + * NAND_WAIT4READY: 5 (1) - not implemented
> > + * DEC_SEM: 6 (1)
> > + * WAIT4END: 7 (1)
> > + * HALT_ON_TERMINATE: 8 (1)
> > + * TERMINATE_FLUSH: 9 (1)
> > + * RESERVED: 10..11 (2)
> > + * PIO_NUM: 12..15 (4)
> > + */
> > +#define BP_CCW_COMMAND 0
> > +#define BM_CCW_COMMAND (3 << 0)
> > +#define CCW_CHAIN (1 << 2)
> > +#define CCW_IRQ (1 << 3)
> > +#define CCW_DEC_SEM (1 << 6)
> > +#define CCW_WAIT4END (1 << 7)
> > +#define CCW_HALT_ON_TERM (1 << 8)
> > +#define CCW_TERM_FLUSH (1 << 9)
> > +#define BP_CCW_PIO_NUM 12
> > +#define BM_CCW_PIO_NUM (0xf << 12)
> > +
> > +#define BF_CCW(value, field) (((value) << BP_CCW_##field) & BM_CCW_##field)
> > +
> > +#define MXS_DMA_CMD_NO_XFER 0
> > +#define MXS_DMA_CMD_WRITE 1
> > +#define MXS_DMA_CMD_READ 2
> > +#define MXS_DMA_CMD_DMA_SENSE 3 /* not implemented */
> > +
> > +struct mxs_dma_ccw {
> > + u32 next;
> > + u16 bits;
> > + u16 xfer_bytes;
> > +#define MAX_XFER_BYTES 0xff00
> > + u32 bufaddr;
> > +#define MXS_PIO_WORDS 16
> > + u32 pio_words[MXS_PIO_WORDS];
> > +};
> > +
> > +#define NUM_CCW (int)(PAGE_SIZE / sizeof(struct mxs_dma_ccw))
> > +
> > +struct mxs_dma_chan {
> > + struct mxs_dma_engine *mxs_dma;
> > + struct dma_chan chan;
> > + struct dma_async_tx_descriptor desc;
> > + struct tasklet_struct tasklet;
> > + int chan_irq;
> > + struct mxs_dma_ccw *ccw;
> > + dma_addr_t ccw_phys;
> > + dma_cookie_t last_completed;
> > + enum dma_status status;
> > + unsigned int flags;
> > +#define MXS_DMA_SG_LOOP (1 << 0)
> > +};
> > +
> > +struct mxs_dma_engine {
> > + int dev_id;
> > + unsigned int version;
> > + void __iomem *base;
> > + struct clk *clk;
> > + struct dma_device dma_device;
> > + struct device_dma_parameters dma_parms;
> > +#define MXS_DMA_CHANNELS 16
> > + struct mxs_dma_chan mxs_chans[MXS_DMA_CHANNELS];
> > +};
> > +
> > +static void mxs_dma_reset_chan(struct mxs_dma_chan *mxs_chan)
> > +{
> > + struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
> > + int chan_id = mxs_chan->chan.chan_id;
> > +
> > + if (dma_is_apbh() && apbh_is_old())
> > + writel(1 << (chan_id + BP_APBH_CTRL0_RESET_CHANNEL),
> > + mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR);
> > + else
> > + writel(1 << (chan_id + BP_APBHX_CHANNEL_CTRL_RESET_CHANNEL),
> > + mxs_dma->base + HW_APBHX_CHANNEL_CTRL + MXS_SET_ADDR);
> > +}
> > +
> > +static void mxs_dma_enable_chan(struct mxs_dma_chan *mxs_chan)
> > +{
> > + struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
> > + int chan_id = mxs_chan->chan.chan_id;
> > +
> > + /* set cmd_addr up */
> > + writel(mxs_chan->ccw_phys,
> > + mxs_dma->base + HW_APBHX_CHn_NXTCMDAR(chan_id));
> > +
> > + /* enable apbh channel clock */
> > + if (dma_is_apbh()) {
> > + if (apbh_is_old())
> > + writel(1 << (chan_id + BP_APBH_CTRL0_CLKGATE_CHANNEL),
> > + mxs_dma->base + HW_APBHX_CTRL0 + MXS_CLR_ADDR);
> > + else
> > + writel(1 << chan_id,
> > + mxs_dma->base + HW_APBHX_CTRL0 + MXS_CLR_ADDR);
> > + }
> > +
> > + /* write 1 to SEMA to kick off the channel */
> > + writel(1, mxs_dma->base + HW_APBHX_CHn_SEMA(chan_id));
> > +}
> > +
> > +static void mxs_dma_disable_chan(struct mxs_dma_chan *mxs_chan)
> > +{
> > + struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
> > + int chan_id = mxs_chan->chan.chan_id;
> > +
> > + /* disable apbh channel clock */
> > + if (dma_is_apbh()) {
> > + if (apbh_is_old())
> > + writel(1 << (chan_id + BP_APBH_CTRL0_CLKGATE_CHANNEL),
> > + mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR);
> > + else
> > + writel(1 << chan_id,
> > + mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR);
> > + }
> > +
> > + mxs_chan->status = DMA_SUCCESS;
> > +}
> > +
> > +static void mxs_dma_pause_chan(struct mxs_dma_chan *mxs_chan)
> > +{
> > + struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
> > + int chan_id = mxs_chan->chan.chan_id;
> > +
> > + /* freeze the channel */
> > + if (dma_is_apbh() && apbh_is_old())
> > + writel(1 << chan_id,
> > + mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR);
> > + else
> > + writel(1 << chan_id,
> > + mxs_dma->base + HW_APBHX_CHANNEL_CTRL + MXS_SET_ADDR);
> > +
> > + mxs_chan->status = DMA_PAUSED;
> > +}
> > +
> > +static void mxs_dma_resume_chan(struct mxs_dma_chan *mxs_chan)
> > +{
> > + struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
> > + int chan_id = mxs_chan->chan.chan_id;
> > +
> > + /* unfreeze the channel */
> > + if (dma_is_apbh() && apbh_is_old())
> > + writel(1 << chan_id,
> > + mxs_dma->base + HW_APBHX_CTRL0 + MXS_CLR_ADDR);
> > + else
> > + writel(1 << chan_id,
> > + mxs_dma->base + HW_APBHX_CHANNEL_CTRL + MXS_CLR_ADDR);
> > +
> > + mxs_chan->status = DMA_IN_PROGRESS;
> > +}
> > +
> > +static dma_cookie_t mxs_dma_assign_cookie(struct mxs_dma_chan *mxs_chan)
> > +{
> > + dma_cookie_t cookie = mxs_chan->chan.cookie;
> > +
> > + if (++cookie < 0)
> > + cookie = 1;
> > +
> > + mxs_chan->chan.cookie = cookie;
> > + mxs_chan->desc.cookie = cookie;
> > +
> > + return cookie;
> > +}
> > +
> > +static struct mxs_dma_chan *to_mxs_dma_chan(struct dma_chan *chan)
> > +{
> > + return container_of(chan, struct mxs_dma_chan, chan);
> > +}
> > +
> > +static dma_cookie_t mxs_dma_tx_submit(struct dma_async_tx_descriptor *tx)
> > +{
> > + struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(tx->chan);
> > +
> > + mxs_dma_enable_chan(mxs_chan);
> > +
> > + return mxs_dma_assign_cookie(mxs_chan);
> > +}
> > +
> > +static void mxs_dma_tasklet(unsigned long data)
> > +{
> > + struct mxs_dma_chan *mxs_chan = (struct mxs_dma_chan *) data;
> > +
> > + if (mxs_chan->desc.callback)
> > + mxs_chan->desc.callback(mxs_chan->desc.callback_param);
> > +}
> any reason why you are using tasklet? Since these kind of DMA operations
> need very fast and efficient interrupt handling, I feel the threaded irq
> are the best approach?
There was some discussion on this.
http://lists.infradead.org/pipermail/linux-arm-kernel/2011-February/041251.html
> > +
> > +static irqreturn_t mxs_dma_int_handler(int irq, void *dev_id)
> > +{
> > + struct mxs_dma_engine *mxs_dma = dev_id;
> > + u32 stat1, stat2;
> > +
> > + /* completion status */
> > + stat1 = readl(mxs_dma->base + HW_APBHX_CTRL1);
> > + stat1 &= 0xffff;
> Magic number?
>
Define macro instead?
> > + writel(stat1, mxs_dma->base + HW_APBHX_CTRL1 + MXS_CLR_ADDR);
> > +
> > + /* error status */
> > + stat2 = readl(mxs_dma->base + HW_APBHX_CTRL2);
> > + writel(stat2, mxs_dma->base + HW_APBHX_CTRL2 + MXS_CLR_ADDR);
> > +
> > + /*
> > + * When both completion and error of termination bits set at the
> > + * same time, we do not take it as an error. IOW, it only becomes
> > + * an error we need to handler here in case of ether it's an bus
> > + * error or a termination error with no completion.
> > + */
> > + stat2 = ((stat2 >> 16) & stat2) | /* bus error */
> > + (~(stat2 >> 16) & stat2 & ~stat1); /* termination with no completion */
> > +
> > + /* combine error and completion status for checking */
> > + stat1 = (stat2 << 16) | stat1;
> would it be possible for you to avoid these magic number, will help you
> when you add more controllers...
>
I really do not see the possibility to add more controllers into this
driver, but I can define macro instead if you are uncomfortable with
them.
> > + while (stat1) {
> > + int channel = fls(stat1) - 1;
> > + struct mxs_dma_chan *mxs_chan =
> > + &mxs_dma->mxs_chans[channel % 16];
> > +
> > + if (channel >= 16) {
> > + dev_dbg(mxs_dma->dma_device.dev, "%s: error in channel %d\n",
> > + __func__, channel - 16);
> > + mxs_chan->status = DMA_ERROR;
> > + mxs_dma_reset_chan(mxs_chan);
> > + } else {
> > + if (mxs_chan->flags & MXS_DMA_SG_LOOP)
> > + mxs_chan->status = DMA_IN_PROGRESS;
> > + else
> > + mxs_chan->status = DMA_SUCCESS;
> > + }
> > +
> > + stat1 &= ~(1 << channel);
> > +
> > + if (mxs_chan->status == DMA_SUCCESS)
> > + mxs_chan->last_completed = mxs_chan->desc.cookie;
> > +
> > + /* schedule tasklet on this channel */
> > + tasklet_schedule(&mxs_chan->tasklet);
> > + }
> > +
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static int mxs_dma_alloc_chan_resources(struct dma_chan *chan)
> > +{
> > + struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
> > + struct mxs_dma_data *data = chan->private;
> > + struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
> > + int ret;
> > +
> > + if (!data)
> > + return -EINVAL;
> > +
> > + mxs_chan->chan_irq = data->chan_irq;
> > +
> > + mxs_chan->ccw = dma_alloc_coherent(mxs_dma->dma_device.dev, PAGE_SIZE,
> > + &mxs_chan->ccw_phys, GFP_KERNEL);
> > + if (!mxs_chan->ccw) {
> > + ret = -ENOMEM;
> > + goto err_alloc;
> > + }
> > +
> > + memset(mxs_chan->ccw, 0, PAGE_SIZE);
> > +
> > + ret = request_irq(mxs_chan->chan_irq, mxs_dma_int_handler,
> > + 0, "mxs-dma", mxs_dma);
> > + if (ret)
> > + goto err_irq;
> > +
> > + ret = clk_enable(mxs_dma->clk);
> > + if (ret)
> > + goto err_clk;
> > +
> > + mxs_dma_reset_chan(mxs_chan);
> > +
> > + dma_async_tx_descriptor_init(&mxs_chan->desc, chan);
> > + mxs_chan->desc.tx_submit = mxs_dma_tx_submit;
> > +
> > + /* the descriptor is ready */
> > + async_tx_ack(&mxs_chan->desc);
> > +
> > + return 0;
> > +
> > +err_clk:
> > + free_irq(mxs_chan->chan_irq, mxs_dma);
> > +err_irq:
> > + dma_free_coherent(mxs_dma->dma_device.dev, PAGE_SIZE,
> > + mxs_chan->ccw, mxs_chan->ccw_phys);
> > +err_alloc:
> > + return ret;
> > +}
> > +
> > +static void mxs_dma_free_chan_resources(struct dma_chan *chan)
> > +{
> > + struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
> > + struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
> > +
> > + mxs_dma_disable_chan(mxs_chan);
> > +
> > + free_irq(mxs_chan->chan_irq, mxs_dma);
> > +
> > + dma_free_coherent(mxs_dma->dma_device.dev, PAGE_SIZE,
> > + mxs_chan->ccw, mxs_chan->ccw_phys);
> > +
> > + clk_disable(mxs_dma->clk);
> > +}
> > +
> > +static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg(
> > + struct dma_chan *chan, struct scatterlist *sgl,
> > + unsigned int sg_len, enum dma_data_direction direction,
> > + unsigned long append)
> > +{
> > + struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
> > + struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
> > + struct mxs_dma_ccw *ccw;
> > + struct scatterlist *sg;
> > + int i, j;
> > + u32 *pio;
> > + static int idx = 0;
> > +
> > + if (mxs_chan->status == DMA_IN_PROGRESS && !append)
> > + return NULL;
> shouldn't you be servicing this request. The whole idea for having
> multiple descriptors is that client can have multiple requests and
> submits when the situation warrants so. This doesn't mean submission of
> transaction, which should be done using submit()
It does not support multiple descriptors.
> > +
> > + if (sg_len + (append ? idx : 0) > NUM_CCW) {
> > + dev_err(mxs_dma->dma_device.dev,
> > + "maximum number of sg exceeded: %d > %d\n",
> > + sg_len, NUM_CCW);
> > + goto err_out;
> > + }
> > +
> > + mxs_chan->status = DMA_IN_PROGRESS;
> > + mxs_chan->flags = 0;
> > +
> > + /*
> > + * If the sg is prepared with append flag set, the sg
> > + * will be appended to the last prepared sg.
> > + */
> > + if (append) {
> > + BUG_ON(idx < 1);
> > + ccw = &mxs_chan->ccw[idx - 1];
> > + ccw->next = mxs_chan->ccw_phys + sizeof(*ccw) * idx;
> > + ccw->bits |= CCW_CHAIN;
> > + ccw->bits &= ~CCW_IRQ;
> > + ccw->bits &= ~CCW_DEC_SEM;
> > + ccw->bits &= ~CCW_WAIT4END;
> > + } else {
> > + idx = 0;
> > + }
> > +
> > + if (direction == DMA_NONE) {
> > + ccw = &mxs_chan->ccw[idx++];
> > + pio = (u32 *) sgl;
> > +
> > + for (j = 0; j < sg_len;)
> > + ccw->pio_words[j++] = *pio++;
> > +
> > + ccw->bits = 0;
> > + ccw->bits |= CCW_IRQ;
> > + ccw->bits |= CCW_DEC_SEM;
> > + ccw->bits |= CCW_WAIT4END;
> > + ccw->bits |= CCW_HALT_ON_TERM;
> > + ccw->bits |= CCW_TERM_FLUSH;
> > + ccw->bits |= BF_CCW(sg_len, PIO_NUM);
> > + ccw->bits |= BF_CCW(MXS_DMA_CMD_NO_XFER, COMMAND);
> > + } else {
> > + for_each_sg(sgl, sg, sg_len, i) {
> > + if (sg->length > MAX_XFER_BYTES) {
> > + dev_err(mxs_dma->dma_device.dev, "maximum bytes for sg entry exceeded: %d > %d\n",
> > + sg->length, MAX_XFER_BYTES);
> > + goto err_out;
> > + }
> > +
> > + ccw = &mxs_chan->ccw[idx++];
> > +
> > + ccw->next = mxs_chan->ccw_phys + sizeof(*ccw) * idx;
> > + ccw->bufaddr = sg->dma_address;
> > + ccw->xfer_bytes = sg->length;
> > +
> > + ccw->bits = 0;
> > + ccw->bits |= CCW_CHAIN;
> > + ccw->bits |= CCW_HALT_ON_TERM;
> > + ccw->bits |= CCW_TERM_FLUSH;
> > + ccw->bits |= BF_CCW(direction == DMA_FROM_DEVICE ?
> > + MXS_DMA_CMD_WRITE : MXS_DMA_CMD_READ,
> > + COMMAND);
> > +
> > + if (i + 1 == sg_len) {
> > + ccw->bits &= ~CCW_CHAIN;
> > + ccw->bits |= CCW_IRQ;
> > + ccw->bits |= CCW_DEC_SEM;
> > + ccw->bits |= CCW_WAIT4END;
> > + }
> > + }
> > + }
> > +
> > + return &mxs_chan->desc;
> > +
> > +err_out:
> > + mxs_chan->status = DMA_ERROR;
> > + return NULL;
> > +}
> > +
> > +static struct dma_async_tx_descriptor *mxs_dma_prep_dma_cyclic(
> > + struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len,
> > + size_t period_len, enum dma_data_direction direction)
> > +{
> > + struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
> > + struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
> > + int num_periods = buf_len / period_len;
> > + int i = 0, buf = 0;
> > +
> > + if (mxs_chan->status == DMA_IN_PROGRESS)
> > + return NULL;
> > +
> > + mxs_chan->status = DMA_IN_PROGRESS;
> > + mxs_chan->flags |= MXS_DMA_SG_LOOP;
> > +
> > + if (num_periods > NUM_CCW) {
> > + dev_err(mxs_dma->dma_device.dev,
> > + "maximum number of sg exceeded: %d > %d\n",
> > + num_periods, NUM_CCW);
> > + goto err_out;
> > + }
> > +
> > + if (period_len > MAX_XFER_BYTES) {
> > + dev_err(mxs_dma->dma_device.dev,
> > + "maximum period size exceeded: %d > %d\n",
> > + period_len, MAX_XFER_BYTES);
> > + goto err_out;
> > + }
> > +
> > + while (buf < buf_len) {
> > + struct mxs_dma_ccw *ccw = &mxs_chan->ccw[i];
> > +
> > + if (i + 1 == num_periods)
> > + ccw->next = mxs_chan->ccw_phys;
> > + else
> > + ccw->next = mxs_chan->ccw_phys + sizeof(*ccw) * (i + 1);
> > +
> > + ccw->bufaddr = dma_addr;
> > + ccw->xfer_bytes = period_len;
> > +
> > + ccw->bits = 0;
> > + ccw->bits |= CCW_CHAIN;
> > + ccw->bits |= CCW_IRQ;
> > + ccw->bits |= CCW_HALT_ON_TERM;
> > + ccw->bits |= CCW_TERM_FLUSH;
> > + ccw->bits |= BF_CCW(direction == DMA_FROM_DEVICE ?
> > + MXS_DMA_CMD_WRITE : MXS_DMA_CMD_READ, COMMAND);
> > +
> > + dma_addr += period_len;
> > + buf += period_len;
> > +
> > + i++;
> > + }
> > +
> > + return &mxs_chan->desc;
> > +
> > +err_out:
> > + mxs_chan->status = DMA_ERROR;
> > + return NULL;
> > +}
> > +
> > +static int mxs_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
> > + unsigned long arg)
> > +{
> > + struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
> > + int ret = 0;
> > +
> > + switch (cmd) {
> > + case DMA_TERMINATE_ALL:
> > + mxs_dma_disable_chan(mxs_chan);
> > + break;
> > + case DMA_PAUSE:
> > + mxs_dma_pause_chan(mxs_chan);
> > + break;
> > + case DMA_RESUME:
> > + mxs_dma_resume_chan(mxs_chan);
> > + break;
> > + default:
> > + ret = -ENOSYS;
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +static enum dma_status mxs_dma_tx_status(struct dma_chan *chan,
> > + dma_cookie_t cookie, struct dma_tx_state *txstate)
> > +{
> > + struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
> > + dma_cookie_t last_used;
> > +
> > + last_used = chan->cookie;
> > + dma_set_tx_state(txstate, mxs_chan->last_completed, last_used, 0);
> > +
> > + return mxs_chan->status;
> > +}
> > +
> > +static void mxs_dma_issue_pending(struct dma_chan *chan)
> > +{
> > + /*
> > + * Nothing to do. We only have a single descriptor.
> > + */
> Hmmm, going back to earlier comment and thinking on this again, any
> reason why you choose to do it this way. Wont it be nicer to have
> multiple descriptors?
None of the mxs dma client drivers necessarily needs multiple
descriptors support, so I chose to implement the driver in a
simplified way with the reference to imx-sdma driver.
> > +}
> > +
> > +static int __init mxs_dma_init(struct mxs_dma_engine *mxs_dma)
> > +{
> > + int ret;
> > +
> > + ret = clk_enable(mxs_dma->clk);
> > + if (ret)
> > + goto err_out;
> > +
> > + ret = mxs_reset_block(mxs_dma->base);
> > + if (ret)
> > + goto err_out;
> > +
> > + /* only major version matters */
> > + mxs_dma->version = readl(mxs_dma->base +
> > + ((mxs_dma->dev_id == MXS_DMA_APBX) ?
> > + HW_APBX_VERSION : HW_APBH_VERSION)) >>
> > + BP_APBHX_VERSION_MAJOR;
> > +
> > + /* enable apbh burst */
> > + if (dma_is_apbh()) {
> > + writel(BM_APBH_CTRL0_APB_BURST_EN,
> > + mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR);
> > + writel(BM_APBH_CTRL0_APB_BURST8_EN,
> > + mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR);
> > + }
> > +
> > + /* enable irq for all the channels */
> > + writel(0xffff << 16, mxs_dma->base + HW_APBHX_CTRL1 + MXS_SET_ADDR);
> > +
> > + clk_disable(mxs_dma->clk);
> > +
> > + return 0;
> > +
> > +err_out:
> > + return ret;
> > +}
> > +
> > +static int __init mxs_dma_probe(struct platform_device *pdev)
> > +{
> > + const struct platform_device_id *id_entry =
> > + platform_get_device_id(pdev);
> > + struct mxs_dma_engine *mxs_dma;
> > + struct resource *iores;
> > + int ret, i;
> > +
> > + mxs_dma = kzalloc(sizeof(*mxs_dma), GFP_KERNEL);
> > + if (!mxs_dma)
> > + return -ENOMEM;
> > +
> > + mxs_dma->dev_id = id_entry->driver_data;
> > +
> > + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +
> > + if (!request_mem_region(iores->start, resource_size(iores),
> > + pdev->name)) {
> > + ret = -EBUSY;
> > + goto err_request_region;
> > + }
> > +
> > + mxs_dma->base = ioremap(iores->start, resource_size(iores));
> > + if (!mxs_dma->base) {
> > + ret = -ENOMEM;
> > + goto err_ioremap;
> > + }
> > +
> > + mxs_dma->clk = clk_get(&pdev->dev, NULL);
> > + if (IS_ERR(mxs_dma->clk)) {
> > + ret = PTR_ERR(mxs_dma->clk);
> > + goto err_clk;
> > + }
> > +
> > + dma_cap_set(DMA_SLAVE, mxs_dma->dma_device.cap_mask);
> > + dma_cap_set(DMA_CYCLIC, mxs_dma->dma_device.cap_mask);
> > +
> > + INIT_LIST_HEAD(&mxs_dma->dma_device.channels);
> > +
> > + /* Initialize channel parameters */
> > + for (i = 0; i < MXS_DMA_CHANNELS; i++) {
> > + struct mxs_dma_chan *mxs_chan = &mxs_dma->mxs_chans[i];
> > +
> > + mxs_chan->mxs_dma = mxs_dma;
> > + mxs_chan->chan.device = &mxs_dma->dma_device;
> > +
> > + tasklet_init(&mxs_chan->tasklet, mxs_dma_tasklet,
> > + (unsigned long) mxs_chan);
> > +
> > +
> > + /* Add the channel to mxs_chan list */
> > + list_add_tail(&mxs_chan->chan.device_node, &mxs_dma->dma_device.channels);
> > + }
> > +
> > + ret = mxs_dma_init(mxs_dma);
> > + if (ret)
> > + goto err_init;
> > +
> > + mxs_dma->dma_device.dev = &pdev->dev;
> > +
> > + /* mxs_dma gets 65535 bytes maximum sg size */
> > + mxs_dma->dma_device.dev->dma_parms = &mxs_dma->dma_parms;
> > + dma_set_max_seg_size(mxs_dma->dma_device.dev, MAX_XFER_BYTES);
> > +
> > + mxs_dma->dma_device.device_alloc_chan_resources = mxs_dma_alloc_chan_resources;
> > + mxs_dma->dma_device.device_free_chan_resources = mxs_dma_free_chan_resources;
> > + mxs_dma->dma_device.device_tx_status = mxs_dma_tx_status;
> > + mxs_dma->dma_device.device_prep_slave_sg = mxs_dma_prep_slave_sg;
> > + mxs_dma->dma_device.device_prep_dma_cyclic = mxs_dma_prep_dma_cyclic;
> > + mxs_dma->dma_device.device_control = mxs_dma_control;
> > + mxs_dma->dma_device.device_issue_pending = mxs_dma_issue_pending;
> > +
> > + ret = dma_async_device_register(&mxs_dma->dma_device);
> > + if (ret) {
> > + dev_err(mxs_dma->dma_device.dev, "unable to register\n");
> > + goto err_init;
> > + }
> > +
> > + dev_info(mxs_dma->dma_device.dev, "initialized\n");
> > +
> > + return 0;
> > +
> > +err_init:
> > + clk_put(mxs_dma->clk);
> > +err_clk:
> > + iounmap(mxs_dma->base);
> > +err_ioremap:
> > + release_mem_region(iores->start, resource_size(iores));
> > +err_request_region:
> > + kfree(mxs_dma);
> > + return ret;
> > +}
> > +
> > +static struct platform_device_id mxs_dma_type[] = {
> > + {
> > + .name = "mxs-dma-apbh",
> > + .driver_data = MXS_DMA_APBH,
> > + }, {
> > + .name = "mxs-dma-apbx",
> > + .driver_data = MXS_DMA_APBX,
> > + }
> > +};
> > +
> > +static struct platform_driver mxs_dma_driver = {
> > + .driver = {
> > + .name = "mxs-dma",
> > + },
> > + .id_table = mxs_dma_type,
> > +};
> > +
> > +static int __init mxs_dma_module_init(void)
> > +{
> > + return platform_driver_probe(&mxs_dma_driver, mxs_dma_probe);
> > +}
> > +subsys_initcall(mxs_dma_module_init);
> No exit??
>
No exit. It's pretty common for dma driver, as it's a fundamental
block and should be built-in other than module.
--
Regards,
Shawn
^ permalink raw reply
* [PATCH 1/5] ARM: S5PV310: Add missing GPYx banks.
From: Kukjin Kim @ 2011-02-23 6:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297927527-1338-2-git-send-email-m.szyprowski@samsung.com>
Marek Szyrowski wrote:
>
> This patch adds missing GPYx gpio banks on Samsung S5PC210 platform.
>
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
> arch/arm/mach-s5pv310/gpiolib.c | 49
> +++++++++++++++++++++++++++++
> arch/arm/mach-s5pv310/include/mach/gpio.h | 23 +++++++++++++-
> 2 files changed, 71 insertions(+), 1 deletions(-)
>
> diff --git a/arch/arm/mach-s5pv310/gpiolib.c b/arch/arm/mach-
> s5pv310/gpiolib.c
> index 55217b8..f417ecd 100644
> --- a/arch/arm/mach-s5pv310/gpiolib.c
> +++ b/arch/arm/mach-s5pv310/gpiolib.c
> @@ -199,6 +199,55 @@ static struct s3c_gpio_chip s5pv310_gpio_part2_4bit[]
=
> {
> .label = "GPL2",
> },
> }, {
> + .config = &gpio_cfg_noint,
> + .chip = {
> + .base = S5PV310_GPY0(0),
> + .ngpio = S5PV310_GPIO_Y0_NR,
> + .label = "GPY0",
> + },
> + }, {
> + .config = &gpio_cfg_noint,
> + .chip = {
> + .base = S5PV310_GPY1(0),
> + .ngpio = S5PV310_GPIO_Y1_NR,
> + .label = "GPY1",
> + },
> + }, {
> + .config = &gpio_cfg_noint,
> + .chip = {
> + .base = S5PV310_GPY2(0),
> + .ngpio = S5PV310_GPIO_Y2_NR,
> + .label = "GPY2",
> + },
> + }, {
> + .config = &gpio_cfg_noint,
> + .chip = {
> + .base = S5PV310_GPY3(0),
> + .ngpio = S5PV310_GPIO_Y3_NR,
> + .label = "GPY3",
> + },
> + }, {
> + .config = &gpio_cfg_noint,
> + .chip = {
> + .base = S5PV310_GPY4(0),
> + .ngpio = S5PV310_GPIO_Y4_NR,
> + .label = "GPY4",
> + },
> + }, {
> + .config = &gpio_cfg_noint,
> + .chip = {
> + .base = S5PV310_GPY5(0),
> + .ngpio = S5PV310_GPIO_Y5_NR,
> + .label = "GPY5",
> + },
> + }, {
> + .config = &gpio_cfg_noint,
> + .chip = {
> + .base = S5PV310_GPY6(0),
> + .ngpio = S5PV310_GPIO_Y6_NR,
> + .label = "GPY6",
> + },
> + }, {
> .base = (S5P_VA_GPIO2 + 0xC00),
> .config = &gpio_cfg_noint,
> .irq_base = IRQ_EINT(0),
> diff --git a/arch/arm/mach-s5pv310/include/mach/gpio.h b/arch/arm/mach-
> s5pv310/include/mach/gpio.h
> index 20cb80c..4b44463 100644
> --- a/arch/arm/mach-s5pv310/include/mach/gpio.h
> +++ b/arch/arm/mach-s5pv310/include/mach/gpio.h
> @@ -50,6 +50,13 @@
> #define S5PV310_GPIO_X1_NR (8)
> #define S5PV310_GPIO_X2_NR (8)
> #define S5PV310_GPIO_X3_NR (8)
> +#define S5PV310_GPIO_Y0_NR (6)
> +#define S5PV310_GPIO_Y1_NR (4)
> +#define S5PV310_GPIO_Y2_NR (6)
> +#define S5PV310_GPIO_Y3_NR (8)
> +#define S5PV310_GPIO_Y4_NR (8)
> +#define S5PV310_GPIO_Y5_NR (8)
> +#define S5PV310_GPIO_Y6_NR (8)
> #define S5PV310_GPIO_Z_NR (7)
>
> /* GPIO bank numbers */
> @@ -87,7 +94,14 @@ enum s5p_gpio_number {
> S5PV310_GPIO_X1_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_X0),
> S5PV310_GPIO_X2_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_X1),
> S5PV310_GPIO_X3_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_X2),
> - S5PV310_GPIO_Z_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_X3),
> + S5PV310_GPIO_Y0_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_X3),
> + S5PV310_GPIO_Y1_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_Y0),
> + S5PV310_GPIO_Y2_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_Y1),
> + S5PV310_GPIO_Y3_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_Y2),
> + S5PV310_GPIO_Y4_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_Y3),
> + S5PV310_GPIO_Y5_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_Y4),
> + S5PV310_GPIO_Y6_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_Y5),
> + S5PV310_GPIO_Z_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_Y6),
> };
>
> /* S5PV310 GPIO number definitions */
> @@ -120,6 +134,13 @@ enum s5p_gpio_number {
> #define S5PV310_GPX1(_nr) (S5PV310_GPIO_X1_START + (_nr))
> #define S5PV310_GPX2(_nr) (S5PV310_GPIO_X2_START + (_nr))
> #define S5PV310_GPX3(_nr) (S5PV310_GPIO_X3_START + (_nr))
> +#define S5PV310_GPY0(_nr) (S5PV310_GPIO_Y0_START + (_nr))
> +#define S5PV310_GPY1(_nr) (S5PV310_GPIO_Y1_START + (_nr))
> +#define S5PV310_GPY2(_nr) (S5PV310_GPIO_Y2_START + (_nr))
> +#define S5PV310_GPY3(_nr) (S5PV310_GPIO_Y3_START + (_nr))
> +#define S5PV310_GPY4(_nr) (S5PV310_GPIO_Y4_START + (_nr))
> +#define S5PV310_GPY5(_nr) (S5PV310_GPIO_Y5_START + (_nr))
> +#define S5PV310_GPY6(_nr) (S5PV310_GPIO_Y6_START + (_nr))
> #define S5PV310_GPZ(_nr) (S5PV310_GPIO_Z_START + (_nr))
>
> /* the end of the S5PV310 specific gpios */
> --
Ok...but as you know, need to re-work based on latest.
Could you re-submit this based on my for-next which includes Exynos4?
Thanks.
Best regards,
Kgene.
--
Kukjin Kim <kgene.kim@samsung.com>, Senior Engineer,
SW Solution Development Team, Samsung Electronics Co., Ltd.
^ permalink raw reply
* [PATCH 4/5] ARM: s5pv310: update IRQ combiner to use EOI in parent chip
From: Kyungmin Park @ 2011-02-23 6:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298302096-21275-5-git-send-email-will.deacon@arm.com>
Tested-by: Kyungmin Park <kyungmin.park@samsung.com>
It's boot and working at mainline.
It's just for record.
Current mainline codes doesn't have full features of s5pv310 (aka
s5pc210 or exynos4210).
So I back-ported the same codes to 2.6.36. it's booted and working at
boot time, but some times later it's hang.
no serial. I think as irq cores at arm changed, it requires the more
patched than just this patch.
Thank you,
Kyungmin Park
On Tue, Feb 22, 2011 at 12:28 AM, Will Deacon <will.deacon@arm.com> wrote:
> The IRQ combiner code invokes the ->irq_{un}mask routines of the parent
> chip.
>
> This patch updates the cascaded handler to use EOI now that the GIC has
> moved to using the fasteoi flow model.
>
> Cc: Kyungmin Park <kyungmin.park@samsung.com>
> Signed-off-by: Will Deacon <will.deacon@arm.com>
> ---
> ?arch/arm/mach-s5pv310/irq-combiner.c | ? ?7 ++-----
> ?1 files changed, 2 insertions(+), 5 deletions(-)
>
> diff --git a/arch/arm/mach-s5pv310/irq-combiner.c b/arch/arm/mach-s5pv310/irq-combiner.c
> index 1ea4a9e..24d5604 100644
> --- a/arch/arm/mach-s5pv310/irq-combiner.c
> +++ b/arch/arm/mach-s5pv310/irq-combiner.c
> @@ -59,9 +59,6 @@ static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
> ? ? ? ?unsigned int cascade_irq, combiner_irq;
> ? ? ? ?unsigned long status;
>
> - ? ? ? /* primary controller ack'ing */
> - ? ? ? chip->irq_ack(&desc->irq_data);
> -
> ? ? ? ?spin_lock(&irq_controller_lock);
> ? ? ? ?status = __raw_readl(chip_data->base + COMBINER_INT_STATUS);
> ? ? ? ?spin_unlock(&irq_controller_lock);
> @@ -79,8 +76,8 @@ static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
> ? ? ? ? ? ? ? ?generic_handle_irq(cascade_irq);
>
> ?out:
> - ? ? ? /* primary controller unmasking */
> - ? ? ? chip->irq_unmask(&desc->irq_data);
> + ? ? ? /* primary controller EOI */
> + ? ? ? chip->irq_eoi(&desc->irq_data);
> ?}
>
> ?static struct irq_chip combiner_chip = {
> --
> 1.7.0.4
>
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
^ permalink raw reply
* [PATCH] ARM: EXYNOS4: Fix wrong constants in the hotplug assembly code.
From: Kukjin Kim @ 2011-02-23 7:03 UTC (permalink / raw)
To: linux-arm-kernel
This patch fixes wrong constants in the hotplug assembly code for
Exynos4 such as Russell's changing in vexpress hotplug and fixes
hard-coded control register constatns also.
Reported-by: Changhwan Youn <chaos.youn@samsung.com>
Cc: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
---
arch/arm/mach-exynos4/hotplug.c | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/arm/mach-exynos4/hotplug.c b/arch/arm/mach-exynos4/hotplug.c
index 4c42f9c..2b5909e 100644
--- a/arch/arm/mach-exynos4/hotplug.c
+++ b/arch/arm/mach-exynos4/hotplug.c
@@ -30,13 +30,13 @@ static inline void cpu_enter_lowpower(void)
* Turn off coherency
*/
" mrc p15, 0, %0, c1, c0, 1\n"
- " bic %0, %0, #0x20\n"
+ " bic %0, %0, %3\n"
" mcr p15, 0, %0, c1, c0, 1\n"
" mrc p15, 0, %0, c1, c0, 0\n"
" bic %0, %0, %2\n"
" mcr p15, 0, %0, c1, c0, 0\n"
: "=&r" (v)
- : "r" (0), "Ir" (CR_C)
+ : "r" (0), "Ir" (CR_C), "Ir" (0x40)
: "cc");
}
@@ -49,10 +49,10 @@ static inline void cpu_leave_lowpower(void)
" orr %0, %0, %1\n"
" mcr p15, 0, %0, c1, c0, 0\n"
" mrc p15, 0, %0, c1, c0, 1\n"
- " orr %0, %0, #0x20\n"
+ " orr %0, %0, %2\n"
" mcr p15, 0, %0, c1, c0, 1\n"
: "=&r" (v)
- : "Ir" (CR_C)
+ : "Ir" (CR_C), "Ir" (0x40)
: "cc");
}
--
1.7.1
^ permalink raw reply related
* [PATCH 0/8] OMAP2+: hwmod/clockevent: allow late-init of individual hwmods
From: Paul Walmsley @ 2011-02-23 7:11 UTC (permalink / raw)
To: linux-arm-kernel
Hello,
This series adds the ability to late-initialize individual
hwmods. The goal here is for clockevent (and eventually
clocksource) hwmods to be late-initialized individually, and
right before they are needed, in the timer init code. Then
omap_hwmod_late_init(), which late-inits the rest of the hwmods,
is intended to run as an initcall -- much later in the boot
process.
This series includes the OMAP2/3 hwmod data for the GPTIMERs that
Tarun posted earlier. This data is necessary for this new code
to avoid warnings during boot.
Boot-tested on N800, OMAP34xx Beagleboard and OMAP4430ES2 Panda.
Applies on Tony's 04aa67dec63b61c1a8b9b6d001262250f1a92130
("Merge branch 'for-tony' of git://gitorious.org/usb/usb into omap-for-linus")
- Paul
---
hwmod_clockevent_2.6.39
text data bss dec hex filename
5774609 497512 5596888 11869009 b51b51 vmlinux.omap2plus_defconfig.orig
5778597 504584 5596856 11880037 b54665 vmlinux.omap2plus_defconfig
Paul Walmsley (5):
OMAP2+: hwmod: find MPU initiator hwmod during in _register()
OMAP2+: hwmod: allow multiple calls to omap_hwmod_init()
OMAP2+: hwmod: ignore attempts to re-late-init a hwmod
OMAP2+: hwmod: add ability to late-init individual hwmods
OMAP2+: clockevent: late-init GPTIMER clockevent hwmod right before timer init
Thara Gopinath (3):
OMAP2420: hwmod data: add dmtimer
OMAP2430: hwmod data: add dmtimer
OMAP3: hwmod data: add dmtimer
arch/arm/mach-omap2/omap_hwmod.c | 124 +++--
arch/arm/mach-omap2/omap_hwmod_2420_data.c | 634 +++++++++++++++++++++++++
arch/arm/mach-omap2/omap_hwmod_2430_data.c | 633 +++++++++++++++++++++++++
arch/arm/mach-omap2/omap_hwmod_3xxx_data.c | 649 ++++++++++++++++++++++++++
arch/arm/mach-omap2/timer-gp.c | 8
arch/arm/plat-omap/include/plat/dmtimer.h | 11
arch/arm/plat-omap/include/plat/omap_hwmod.h | 3
7 files changed, 2024 insertions(+), 38 deletions(-)
^ permalink raw reply
* [PATCH 1/8] OMAP2420: hwmod data: add dmtimer
From: Paul Walmsley @ 2011-02-23 7:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110223070455.5874.51326.stgit@twilight.localdomain>
From: Thara Gopinath <thara@ti.com>
Add dmtimer data.
Signed-off-by: Thara Gopinath <thara@ti.com>
Signed-off-by: Tarun Kanti DebBarma <tarun.kanti@ti.com>
Acked-by: Benoit Cousson <b-cousson@ti.com>
---
arch/arm/mach-omap2/omap_hwmod_2420_data.c | 634 ++++++++++++++++++++++++++++
arch/arm/plat-omap/include/plat/dmtimer.h | 11
2 files changed, 645 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-omap2/omap_hwmod_2420_data.c b/arch/arm/mach-omap2/omap_hwmod_2420_data.c
index 7fffd34..837c9df 100644
--- a/arch/arm/mach-omap2/omap_hwmod_2420_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_2420_data.c
@@ -19,6 +19,7 @@
#include <plat/i2c.h>
#include <plat/gpio.h>
#include <plat/mcspi.h>
+#include <plat/dmtimer.h>
#include "omap_hwmod_common_data.h"
@@ -318,6 +319,625 @@ static struct omap_hwmod omap2420_iva_hwmod = {
.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2420)
};
+/* Timer Common */
+static struct omap_hwmod_class_sysconfig omap2420_timer_sysc = {
+ .rev_offs = 0x0000,
+ .sysc_offs = 0x0010,
+ .syss_offs = 0x0014,
+ .sysc_flags = (SYSC_HAS_SIDLEMODE | SYSC_HAS_CLOCKACTIVITY |
+ SYSC_HAS_ENAWAKEUP | SYSC_HAS_SOFTRESET |
+ SYSC_HAS_AUTOIDLE),
+ .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
+ .sysc_fields = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap2420_timer_hwmod_class = {
+ .name = "timer",
+ .sysc = &omap2420_timer_sysc,
+ .rev = OMAP_TIMER_IP_VERSION_1,
+};
+
+/* timer1 */
+static struct omap_hwmod omap2420_timer1_hwmod;
+static struct omap_hwmod_irq_info omap2420_timer1_mpu_irqs[] = {
+ { .irq = 37, },
+};
+
+static struct omap_hwmod_addr_space omap2420_timer1_addrs[] = {
+ {
+ .pa_start = 0x48028000,
+ .pa_end = 0x48028000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_wkup -> timer1 */
+static struct omap_hwmod_ocp_if omap2420_l4_wkup__timer1 = {
+ .master = &omap2420_l4_wkup_hwmod,
+ .slave = &omap2420_timer1_hwmod,
+ .clk = "gpt1_ick",
+ .addr = omap2420_timer1_addrs,
+ .addr_cnt = ARRAY_SIZE(omap2420_timer1_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer1 slave port */
+static struct omap_hwmod_ocp_if *omap2420_timer1_slaves[] = {
+ &omap2420_l4_wkup__timer1,
+};
+
+/* timer1 hwmod */
+static struct omap_hwmod omap2420_timer1_hwmod = {
+ .name = "timer1",
+ .mpu_irqs = omap2420_timer1_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap2420_timer1_mpu_irqs),
+ .main_clk = "gpt1_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP24XX_EN_GPT1_SHIFT,
+ .module_offs = WKUP_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP24XX_ST_GPT1_SHIFT,
+ },
+ },
+ .slaves = omap2420_timer1_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap2420_timer1_slaves),
+ .class = &omap2420_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2420)
+};
+
+/* timer2 */
+static struct omap_hwmod omap2420_timer2_hwmod;
+static struct omap_hwmod_irq_info omap2420_timer2_mpu_irqs[] = {
+ { .irq = 38, },
+};
+
+static struct omap_hwmod_addr_space omap2420_timer2_addrs[] = {
+ {
+ .pa_start = 0x4802a000,
+ .pa_end = 0x4802a000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer2 */
+static struct omap_hwmod_ocp_if omap2420_l4_core__timer2 = {
+ .master = &omap2420_l4_core_hwmod,
+ .slave = &omap2420_timer2_hwmod,
+ .clk = "gpt2_ick",
+ .addr = omap2420_timer2_addrs,
+ .addr_cnt = ARRAY_SIZE(omap2420_timer2_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer2 slave port */
+static struct omap_hwmod_ocp_if *omap2420_timer2_slaves[] = {
+ &omap2420_l4_core__timer2,
+};
+
+/* timer2 hwmod */
+static struct omap_hwmod omap2420_timer2_hwmod = {
+ .name = "timer2",
+ .mpu_irqs = omap2420_timer2_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap2420_timer2_mpu_irqs),
+ .main_clk = "gpt2_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP24XX_EN_GPT2_SHIFT,
+ .module_offs = CORE_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP24XX_ST_GPT2_SHIFT,
+ },
+ },
+ .slaves = omap2420_timer2_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap2420_timer2_slaves),
+ .class = &omap2420_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2420)
+};
+
+/* timer3 */
+static struct omap_hwmod omap2420_timer3_hwmod;
+static struct omap_hwmod_irq_info omap2420_timer3_mpu_irqs[] = {
+ { .irq = 39, },
+};
+
+static struct omap_hwmod_addr_space omap2420_timer3_addrs[] = {
+ {
+ .pa_start = 0x48078000,
+ .pa_end = 0x48078000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer3 */
+static struct omap_hwmod_ocp_if omap2420_l4_core__timer3 = {
+ .master = &omap2420_l4_core_hwmod,
+ .slave = &omap2420_timer3_hwmod,
+ .clk = "gpt3_ick",
+ .addr = omap2420_timer3_addrs,
+ .addr_cnt = ARRAY_SIZE(omap2420_timer3_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer3 slave port */
+static struct omap_hwmod_ocp_if *omap2420_timer3_slaves[] = {
+ &omap2420_l4_core__timer3,
+};
+
+/* timer3 hwmod */
+static struct omap_hwmod omap2420_timer3_hwmod = {
+ .name = "timer3",
+ .mpu_irqs = omap2420_timer3_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap2420_timer3_mpu_irqs),
+ .main_clk = "gpt3_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP24XX_EN_GPT3_SHIFT,
+ .module_offs = CORE_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP24XX_ST_GPT3_SHIFT,
+ },
+ },
+ .slaves = omap2420_timer3_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap2420_timer3_slaves),
+ .class = &omap2420_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2420)
+};
+
+/* timer4 */
+static struct omap_hwmod omap2420_timer4_hwmod;
+static struct omap_hwmod_irq_info omap2420_timer4_mpu_irqs[] = {
+ { .irq = 40, },
+};
+
+static struct omap_hwmod_addr_space omap2420_timer4_addrs[] = {
+ {
+ .pa_start = 0x4807a000,
+ .pa_end = 0x4807a000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer4 */
+static struct omap_hwmod_ocp_if omap2420_l4_core__timer4 = {
+ .master = &omap2420_l4_core_hwmod,
+ .slave = &omap2420_timer4_hwmod,
+ .clk = "gpt4_ick",
+ .addr = omap2420_timer4_addrs,
+ .addr_cnt = ARRAY_SIZE(omap2420_timer4_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer4 slave port */
+static struct omap_hwmod_ocp_if *omap2420_timer4_slaves[] = {
+ &omap2420_l4_core__timer4,
+};
+
+/* timer4 hwmod */
+static struct omap_hwmod omap2420_timer4_hwmod = {
+ .name = "timer4",
+ .mpu_irqs = omap2420_timer4_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap2420_timer4_mpu_irqs),
+ .main_clk = "gpt4_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP24XX_EN_GPT4_SHIFT,
+ .module_offs = CORE_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP24XX_ST_GPT4_SHIFT,
+ },
+ },
+ .slaves = omap2420_timer4_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap2420_timer4_slaves),
+ .class = &omap2420_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2420)
+};
+
+/* timer5 */
+static struct omap_hwmod omap2420_timer5_hwmod;
+static struct omap_hwmod_irq_info omap2420_timer5_mpu_irqs[] = {
+ { .irq = 41, },
+};
+
+static struct omap_hwmod_addr_space omap2420_timer5_addrs[] = {
+ {
+ .pa_start = 0x4807c000,
+ .pa_end = 0x4807c000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer5 */
+static struct omap_hwmod_ocp_if omap2420_l4_core__timer5 = {
+ .master = &omap2420_l4_core_hwmod,
+ .slave = &omap2420_timer5_hwmod,
+ .clk = "gpt5_ick",
+ .addr = omap2420_timer5_addrs,
+ .addr_cnt = ARRAY_SIZE(omap2420_timer5_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer5 slave port */
+static struct omap_hwmod_ocp_if *omap2420_timer5_slaves[] = {
+ &omap2420_l4_core__timer5,
+};
+
+/* timer5 hwmod */
+static struct omap_hwmod omap2420_timer5_hwmod = {
+ .name = "timer5",
+ .mpu_irqs = omap2420_timer5_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap2420_timer5_mpu_irqs),
+ .main_clk = "gpt5_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP24XX_EN_GPT5_SHIFT,
+ .module_offs = CORE_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP24XX_ST_GPT5_SHIFT,
+ },
+ },
+ .slaves = omap2420_timer5_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap2420_timer5_slaves),
+ .class = &omap2420_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2420)
+};
+
+
+/* timer6 */
+static struct omap_hwmod omap2420_timer6_hwmod;
+static struct omap_hwmod_irq_info omap2420_timer6_mpu_irqs[] = {
+ { .irq = 42, },
+};
+
+static struct omap_hwmod_addr_space omap2420_timer6_addrs[] = {
+ {
+ .pa_start = 0x4807e000,
+ .pa_end = 0x4807e000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer6 */
+static struct omap_hwmod_ocp_if omap2420_l4_core__timer6 = {
+ .master = &omap2420_l4_core_hwmod,
+ .slave = &omap2420_timer6_hwmod,
+ .clk = "gpt6_ick",
+ .addr = omap2420_timer6_addrs,
+ .addr_cnt = ARRAY_SIZE(omap2420_timer6_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer6 slave port */
+static struct omap_hwmod_ocp_if *omap2420_timer6_slaves[] = {
+ &omap2420_l4_core__timer6,
+};
+
+/* timer6 hwmod */
+static struct omap_hwmod omap2420_timer6_hwmod = {
+ .name = "timer6",
+ .mpu_irqs = omap2420_timer6_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap2420_timer6_mpu_irqs),
+ .main_clk = "gpt6_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP24XX_EN_GPT6_SHIFT,
+ .module_offs = CORE_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP24XX_ST_GPT6_SHIFT,
+ },
+ },
+ .slaves = omap2420_timer6_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap2420_timer6_slaves),
+ .class = &omap2420_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2420)
+};
+
+/* timer7 */
+static struct omap_hwmod omap2420_timer7_hwmod;
+static struct omap_hwmod_irq_info omap2420_timer7_mpu_irqs[] = {
+ { .irq = 43, },
+};
+
+static struct omap_hwmod_addr_space omap2420_timer7_addrs[] = {
+ {
+ .pa_start = 0x48080000,
+ .pa_end = 0x48080000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer7 */
+static struct omap_hwmod_ocp_if omap2420_l4_core__timer7 = {
+ .master = &omap2420_l4_core_hwmod,
+ .slave = &omap2420_timer7_hwmod,
+ .clk = "gpt7_ick",
+ .addr = omap2420_timer7_addrs,
+ .addr_cnt = ARRAY_SIZE(omap2420_timer7_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer7 slave port */
+static struct omap_hwmod_ocp_if *omap2420_timer7_slaves[] = {
+ &omap2420_l4_core__timer7,
+};
+
+/* timer7 hwmod */
+static struct omap_hwmod omap2420_timer7_hwmod = {
+ .name = "timer7",
+ .mpu_irqs = omap2420_timer7_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap2420_timer7_mpu_irqs),
+ .main_clk = "gpt7_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP24XX_EN_GPT7_SHIFT,
+ .module_offs = CORE_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP24XX_ST_GPT7_SHIFT,
+ },
+ },
+ .slaves = omap2420_timer7_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap2420_timer7_slaves),
+ .class = &omap2420_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2420)
+};
+
+/* timer8 */
+static struct omap_hwmod omap2420_timer8_hwmod;
+static struct omap_hwmod_irq_info omap2420_timer8_mpu_irqs[] = {
+ { .irq = 44, },
+};
+
+static struct omap_hwmod_addr_space omap2420_timer8_addrs[] = {
+ {
+ .pa_start = 0x48082000,
+ .pa_end = 0x48082000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer8 */
+static struct omap_hwmod_ocp_if omap2420_l4_core__timer8 = {
+ .master = &omap2420_l4_core_hwmod,
+ .slave = &omap2420_timer8_hwmod,
+ .clk = "gpt8_ick",
+ .addr = omap2420_timer8_addrs,
+ .addr_cnt = ARRAY_SIZE(omap2420_timer8_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer8 slave port */
+static struct omap_hwmod_ocp_if *omap2420_timer8_slaves[] = {
+ &omap2420_l4_core__timer8,
+};
+
+/* timer8 hwmod */
+static struct omap_hwmod omap2420_timer8_hwmod = {
+ .name = "timer8",
+ .mpu_irqs = omap2420_timer8_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap2420_timer8_mpu_irqs),
+ .main_clk = "gpt8_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP24XX_EN_GPT8_SHIFT,
+ .module_offs = CORE_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP24XX_ST_GPT8_SHIFT,
+ },
+ },
+ .slaves = omap2420_timer8_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap2420_timer8_slaves),
+ .class = &omap2420_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2420)
+};
+
+/* timer9 */
+static struct omap_hwmod omap2420_timer9_hwmod;
+static struct omap_hwmod_irq_info omap2420_timer9_mpu_irqs[] = {
+ { .irq = 45, },
+};
+
+static struct omap_hwmod_addr_space omap2420_timer9_addrs[] = {
+ {
+ .pa_start = 0x48084000,
+ .pa_end = 0x48084000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer9 */
+static struct omap_hwmod_ocp_if omap2420_l4_core__timer9 = {
+ .master = &omap2420_l4_core_hwmod,
+ .slave = &omap2420_timer9_hwmod,
+ .clk = "gpt9_ick",
+ .addr = omap2420_timer9_addrs,
+ .addr_cnt = ARRAY_SIZE(omap2420_timer9_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer9 slave port */
+static struct omap_hwmod_ocp_if *omap2420_timer9_slaves[] = {
+ &omap2420_l4_core__timer9,
+};
+
+/* timer9 hwmod */
+static struct omap_hwmod omap2420_timer9_hwmod = {
+ .name = "timer9",
+ .mpu_irqs = omap2420_timer9_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap2420_timer9_mpu_irqs),
+ .main_clk = "gpt9_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP24XX_EN_GPT9_SHIFT,
+ .module_offs = CORE_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP24XX_ST_GPT9_SHIFT,
+ },
+ },
+ .slaves = omap2420_timer9_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap2420_timer9_slaves),
+ .class = &omap2420_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2420)
+};
+
+/* timer10 */
+static struct omap_hwmod omap2420_timer10_hwmod;
+static struct omap_hwmod_irq_info omap2420_timer10_mpu_irqs[] = {
+ { .irq = 46, },
+};
+
+static struct omap_hwmod_addr_space omap2420_timer10_addrs[] = {
+ {
+ .pa_start = 0x48086000,
+ .pa_end = 0x48086000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer10 */
+static struct omap_hwmod_ocp_if omap2420_l4_core__timer10 = {
+ .master = &omap2420_l4_core_hwmod,
+ .slave = &omap2420_timer10_hwmod,
+ .clk = "gpt10_ick",
+ .addr = omap2420_timer10_addrs,
+ .addr_cnt = ARRAY_SIZE(omap2420_timer10_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer10 slave port */
+static struct omap_hwmod_ocp_if *omap2420_timer10_slaves[] = {
+ &omap2420_l4_core__timer10,
+};
+
+/* timer10 hwmod */
+static struct omap_hwmod omap2420_timer10_hwmod = {
+ .name = "timer10",
+ .mpu_irqs = omap2420_timer10_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap2420_timer10_mpu_irqs),
+ .main_clk = "gpt10_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP24XX_EN_GPT10_SHIFT,
+ .module_offs = CORE_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP24XX_ST_GPT10_SHIFT,
+ },
+ },
+ .slaves = omap2420_timer10_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap2420_timer10_slaves),
+ .class = &omap2420_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2420)
+};
+
+/* timer11 */
+static struct omap_hwmod omap2420_timer11_hwmod;
+static struct omap_hwmod_irq_info omap2420_timer11_mpu_irqs[] = {
+ { .irq = 47, },
+};
+
+static struct omap_hwmod_addr_space omap2420_timer11_addrs[] = {
+ {
+ .pa_start = 0x48088000,
+ .pa_end = 0x48088000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer11 */
+static struct omap_hwmod_ocp_if omap2420_l4_core__timer11 = {
+ .master = &omap2420_l4_core_hwmod,
+ .slave = &omap2420_timer11_hwmod,
+ .clk = "gpt11_ick",
+ .addr = omap2420_timer11_addrs,
+ .addr_cnt = ARRAY_SIZE(omap2420_timer11_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer11 slave port */
+static struct omap_hwmod_ocp_if *omap2420_timer11_slaves[] = {
+ &omap2420_l4_core__timer11,
+};
+
+/* timer11 hwmod */
+static struct omap_hwmod omap2420_timer11_hwmod = {
+ .name = "timer11",
+ .mpu_irqs = omap2420_timer11_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap2420_timer11_mpu_irqs),
+ .main_clk = "gpt11_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP24XX_EN_GPT11_SHIFT,
+ .module_offs = CORE_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP24XX_ST_GPT11_SHIFT,
+ },
+ },
+ .slaves = omap2420_timer11_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap2420_timer11_slaves),
+ .class = &omap2420_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2420)
+};
+
+/* timer12 */
+static struct omap_hwmod omap2420_timer12_hwmod;
+static struct omap_hwmod_irq_info omap2420_timer12_mpu_irqs[] = {
+ { .irq = 48, },
+};
+
+static struct omap_hwmod_addr_space omap2420_timer12_addrs[] = {
+ {
+ .pa_start = 0x4808a000,
+ .pa_end = 0x4808a000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer12 */
+static struct omap_hwmod_ocp_if omap2420_l4_core__timer12 = {
+ .master = &omap2420_l4_core_hwmod,
+ .slave = &omap2420_timer12_hwmod,
+ .clk = "gpt12_ick",
+ .addr = omap2420_timer12_addrs,
+ .addr_cnt = ARRAY_SIZE(omap2420_timer12_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer12 slave port */
+static struct omap_hwmod_ocp_if *omap2420_timer12_slaves[] = {
+ &omap2420_l4_core__timer12,
+};
+
+/* timer12 hwmod */
+static struct omap_hwmod omap2420_timer12_hwmod = {
+ .name = "timer12",
+ .mpu_irqs = omap2420_timer12_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap2420_timer12_mpu_irqs),
+ .main_clk = "gpt12_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP24XX_EN_GPT12_SHIFT,
+ .module_offs = CORE_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP24XX_ST_GPT12_SHIFT,
+ },
+ },
+ .slaves = omap2420_timer12_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap2420_timer12_slaves),
+ .class = &omap2420_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2420)
+};
+
/* l4_wkup -> wd_timer2 */
static struct omap_hwmod_addr_space omap2420_wd_timer2_addrs[] = {
{
@@ -1022,6 +1642,20 @@ static __initdata struct omap_hwmod *omap2420_hwmods[] = {
&omap2420_l4_wkup_hwmod,
&omap2420_mpu_hwmod,
&omap2420_iva_hwmod,
+
+ &omap2420_timer1_hwmod,
+ &omap2420_timer2_hwmod,
+ &omap2420_timer3_hwmod,
+ &omap2420_timer4_hwmod,
+ &omap2420_timer5_hwmod,
+ &omap2420_timer6_hwmod,
+ &omap2420_timer7_hwmod,
+ &omap2420_timer8_hwmod,
+ &omap2420_timer9_hwmod,
+ &omap2420_timer10_hwmod,
+ &omap2420_timer11_hwmod,
+ &omap2420_timer12_hwmod,
+
&omap2420_wd_timer2_hwmod,
&omap2420_uart1_hwmod,
&omap2420_uart2_hwmod,
diff --git a/arch/arm/plat-omap/include/plat/dmtimer.h b/arch/arm/plat-omap/include/plat/dmtimer.h
index dfa3aff..d6c70d2 100644
--- a/arch/arm/plat-omap/include/plat/dmtimer.h
+++ b/arch/arm/plat-omap/include/plat/dmtimer.h
@@ -3,6 +3,12 @@
*
* OMAP Dual-Mode Timers
*
+ * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
+ * Tarun Kanti DebBarma <tarun.kanti@ti.com>
+ * Thara Gopinath <thara@ti.com>
+ *
+ * Platform device conversion and hwmod support.
+ *
* Copyright (C) 2005 Nokia Corporation
* Author: Lauri Leukkunen <lauri.leukkunen@nokia.com>
* PWM and clock framwork support by Timo Teras.
@@ -44,6 +50,11 @@
#define OMAP_TIMER_TRIGGER_OVERFLOW 0x01
#define OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE 0x02
+/*
+ * IP revision identifier so that Highlander IP
+ * in OMAP4 can be distinguished.
+ */
+#define OMAP_TIMER_IP_VERSION_1 0x1
struct omap_dm_timer;
extern struct omap_dm_timer *gptimer_wakeup;
extern struct sys_timer omap_timer;
^ permalink raw reply related
* [PATCH 2/8] OMAP2430: hwmod data: add dmtimer
From: Paul Walmsley @ 2011-02-23 7:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110223070455.5874.51326.stgit@twilight.localdomain>
From: Thara Gopinath <thara@ti.com>
Add dmtimer data.
Signed-off-by: Thara Gopinath <thara@ti.com>
Signed-off-by: Tarun Kanti DebBarma <tarun.kanti@ti.com>
Acked-by: Benoit Cousson <b-cousson@ti.com>
---
arch/arm/mach-omap2/omap_hwmod_2430_data.c | 633 ++++++++++++++++++++++++++++
1 files changed, 633 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-omap2/omap_hwmod_2430_data.c b/arch/arm/mach-omap2/omap_hwmod_2430_data.c
index 7ba688a..84474f5 100644
--- a/arch/arm/mach-omap2/omap_hwmod_2430_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_2430_data.c
@@ -19,6 +19,7 @@
#include <plat/i2c.h>
#include <plat/gpio.h>
#include <plat/mcspi.h>
+#include <plat/dmtimer.h>
#include "omap_hwmod_common_data.h"
@@ -375,6 +376,624 @@ static struct omap_hwmod omap2430_iva_hwmod = {
.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2430)
};
+/* Timer Common */
+static struct omap_hwmod_class_sysconfig omap2430_timer_sysc = {
+ .rev_offs = 0x0000,
+ .sysc_offs = 0x0010,
+ .syss_offs = 0x0014,
+ .sysc_flags = (SYSC_HAS_SIDLEMODE | SYSC_HAS_CLOCKACTIVITY |
+ SYSC_HAS_ENAWAKEUP | SYSC_HAS_SOFTRESET |
+ SYSC_HAS_AUTOIDLE),
+ .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
+ .sysc_fields = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap2430_timer_hwmod_class = {
+ .name = "timer",
+ .sysc = &omap2430_timer_sysc,
+ .rev = OMAP_TIMER_IP_VERSION_1,
+};
+
+/* timer1 */
+static struct omap_hwmod omap2430_timer1_hwmod;
+static struct omap_hwmod_irq_info omap2430_timer1_mpu_irqs[] = {
+ { .irq = 37, },
+};
+
+static struct omap_hwmod_addr_space omap2430_timer1_addrs[] = {
+ {
+ .pa_start = 0x49018000,
+ .pa_end = 0x49018000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_wkup -> timer1 */
+static struct omap_hwmod_ocp_if omap2430_l4_wkup__timer1 = {
+ .master = &omap2430_l4_wkup_hwmod,
+ .slave = &omap2430_timer1_hwmod,
+ .clk = "gpt1_ick",
+ .addr = omap2430_timer1_addrs,
+ .addr_cnt = ARRAY_SIZE(omap2430_timer1_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer1 slave port */
+static struct omap_hwmod_ocp_if *omap2430_timer1_slaves[] = {
+ &omap2430_l4_wkup__timer1,
+};
+
+/* timer1 hwmod */
+static struct omap_hwmod omap2430_timer1_hwmod = {
+ .name = "timer1",
+ .mpu_irqs = omap2430_timer1_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap2430_timer1_mpu_irqs),
+ .main_clk = "gpt1_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP24XX_EN_GPT1_SHIFT,
+ .module_offs = WKUP_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP24XX_ST_GPT1_SHIFT,
+ },
+ },
+ .slaves = omap2430_timer1_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap2430_timer1_slaves),
+ .class = &omap2430_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2430)
+};
+
+/* timer2 */
+static struct omap_hwmod omap2430_timer2_hwmod;
+static struct omap_hwmod_irq_info omap2430_timer2_mpu_irqs[] = {
+ { .irq = 38, },
+};
+
+static struct omap_hwmod_addr_space omap2430_timer2_addrs[] = {
+ {
+ .pa_start = 0x4802a000,
+ .pa_end = 0x4802a000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer2 */
+static struct omap_hwmod_ocp_if omap2430_l4_core__timer2 = {
+ .master = &omap2430_l4_core_hwmod,
+ .slave = &omap2430_timer2_hwmod,
+ .clk = "gpt2_ick",
+ .addr = omap2430_timer2_addrs,
+ .addr_cnt = ARRAY_SIZE(omap2430_timer2_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer2 slave port */
+static struct omap_hwmod_ocp_if *omap2430_timer2_slaves[] = {
+ &omap2430_l4_core__timer2,
+};
+
+/* timer2 hwmod */
+static struct omap_hwmod omap2430_timer2_hwmod = {
+ .name = "timer2",
+ .mpu_irqs = omap2430_timer2_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap2430_timer2_mpu_irqs),
+ .main_clk = "gpt2_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP24XX_EN_GPT2_SHIFT,
+ .module_offs = CORE_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP24XX_ST_GPT2_SHIFT,
+ },
+ },
+ .slaves = omap2430_timer2_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap2430_timer2_slaves),
+ .class = &omap2430_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2430)
+};
+
+/* timer3 */
+static struct omap_hwmod omap2430_timer3_hwmod;
+static struct omap_hwmod_irq_info omap2430_timer3_mpu_irqs[] = {
+ { .irq = 39, },
+};
+
+static struct omap_hwmod_addr_space omap2430_timer3_addrs[] = {
+ {
+ .pa_start = 0x48078000,
+ .pa_end = 0x48078000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer3 */
+static struct omap_hwmod_ocp_if omap2430_l4_core__timer3 = {
+ .master = &omap2430_l4_core_hwmod,
+ .slave = &omap2430_timer3_hwmod,
+ .clk = "gpt3_ick",
+ .addr = omap2430_timer3_addrs,
+ .addr_cnt = ARRAY_SIZE(omap2430_timer3_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer3 slave port */
+static struct omap_hwmod_ocp_if *omap2430_timer3_slaves[] = {
+ &omap2430_l4_core__timer3,
+};
+
+/* timer3 hwmod */
+static struct omap_hwmod omap2430_timer3_hwmod = {
+ .name = "timer3",
+ .mpu_irqs = omap2430_timer3_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap2430_timer3_mpu_irqs),
+ .main_clk = "gpt3_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP24XX_EN_GPT3_SHIFT,
+ .module_offs = CORE_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP24XX_ST_GPT3_SHIFT,
+ },
+ },
+ .slaves = omap2430_timer3_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap2430_timer3_slaves),
+ .class = &omap2430_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2430)
+};
+
+/* timer4 */
+static struct omap_hwmod omap2430_timer4_hwmod;
+static struct omap_hwmod_irq_info omap2430_timer4_mpu_irqs[] = {
+ { .irq = 40, },
+};
+
+static struct omap_hwmod_addr_space omap2430_timer4_addrs[] = {
+ {
+ .pa_start = 0x4807a000,
+ .pa_end = 0x4807a000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer4 */
+static struct omap_hwmod_ocp_if omap2430_l4_core__timer4 = {
+ .master = &omap2430_l4_core_hwmod,
+ .slave = &omap2430_timer4_hwmod,
+ .clk = "gpt4_ick",
+ .addr = omap2430_timer4_addrs,
+ .addr_cnt = ARRAY_SIZE(omap2430_timer4_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer4 slave port */
+static struct omap_hwmod_ocp_if *omap2430_timer4_slaves[] = {
+ &omap2430_l4_core__timer4,
+};
+
+/* timer4 hwmod */
+static struct omap_hwmod omap2430_timer4_hwmod = {
+ .name = "timer4",
+ .mpu_irqs = omap2430_timer4_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap2430_timer4_mpu_irqs),
+ .main_clk = "gpt4_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP24XX_EN_GPT4_SHIFT,
+ .module_offs = CORE_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP24XX_ST_GPT4_SHIFT,
+ },
+ },
+ .slaves = omap2430_timer4_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap2430_timer4_slaves),
+ .class = &omap2430_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2430)
+};
+
+/* timer5 */
+static struct omap_hwmod omap2430_timer5_hwmod;
+static struct omap_hwmod_irq_info omap2430_timer5_mpu_irqs[] = {
+ { .irq = 41, },
+};
+
+static struct omap_hwmod_addr_space omap2430_timer5_addrs[] = {
+ {
+ .pa_start = 0x4807c000,
+ .pa_end = 0x4807c000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer5 */
+static struct omap_hwmod_ocp_if omap2430_l4_core__timer5 = {
+ .master = &omap2430_l4_core_hwmod,
+ .slave = &omap2430_timer5_hwmod,
+ .clk = "gpt5_ick",
+ .addr = omap2430_timer5_addrs,
+ .addr_cnt = ARRAY_SIZE(omap2430_timer5_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer5 slave port */
+static struct omap_hwmod_ocp_if *omap2430_timer5_slaves[] = {
+ &omap2430_l4_core__timer5,
+};
+
+/* timer5 hwmod */
+static struct omap_hwmod omap2430_timer5_hwmod = {
+ .name = "timer5",
+ .mpu_irqs = omap2430_timer5_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap2430_timer5_mpu_irqs),
+ .main_clk = "gpt5_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP24XX_EN_GPT5_SHIFT,
+ .module_offs = CORE_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP24XX_ST_GPT5_SHIFT,
+ },
+ },
+ .slaves = omap2430_timer5_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap2430_timer5_slaves),
+ .class = &omap2430_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2430)
+};
+
+/* timer6 */
+static struct omap_hwmod omap2430_timer6_hwmod;
+static struct omap_hwmod_irq_info omap2430_timer6_mpu_irqs[] = {
+ { .irq = 42, },
+};
+
+static struct omap_hwmod_addr_space omap2430_timer6_addrs[] = {
+ {
+ .pa_start = 0x4807e000,
+ .pa_end = 0x4807e000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer6 */
+static struct omap_hwmod_ocp_if omap2430_l4_core__timer6 = {
+ .master = &omap2430_l4_core_hwmod,
+ .slave = &omap2430_timer6_hwmod,
+ .clk = "gpt6_ick",
+ .addr = omap2430_timer6_addrs,
+ .addr_cnt = ARRAY_SIZE(omap2430_timer6_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer6 slave port */
+static struct omap_hwmod_ocp_if *omap2430_timer6_slaves[] = {
+ &omap2430_l4_core__timer6,
+};
+
+/* timer6 hwmod */
+static struct omap_hwmod omap2430_timer6_hwmod = {
+ .name = "timer6",
+ .mpu_irqs = omap2430_timer6_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap2430_timer6_mpu_irqs),
+ .main_clk = "gpt6_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP24XX_EN_GPT6_SHIFT,
+ .module_offs = CORE_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP24XX_ST_GPT6_SHIFT,
+ },
+ },
+ .slaves = omap2430_timer6_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap2430_timer6_slaves),
+ .class = &omap2430_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2430)
+};
+
+/* timer7 */
+static struct omap_hwmod omap2430_timer7_hwmod;
+static struct omap_hwmod_irq_info omap2430_timer7_mpu_irqs[] = {
+ { .irq = 43, },
+};
+
+static struct omap_hwmod_addr_space omap2430_timer7_addrs[] = {
+ {
+ .pa_start = 0x48080000,
+ .pa_end = 0x48080000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer7 */
+static struct omap_hwmod_ocp_if omap2430_l4_core__timer7 = {
+ .master = &omap2430_l4_core_hwmod,
+ .slave = &omap2430_timer7_hwmod,
+ .clk = "gpt7_ick",
+ .addr = omap2430_timer7_addrs,
+ .addr_cnt = ARRAY_SIZE(omap2430_timer7_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer7 slave port */
+static struct omap_hwmod_ocp_if *omap2430_timer7_slaves[] = {
+ &omap2430_l4_core__timer7,
+};
+
+/* timer7 hwmod */
+static struct omap_hwmod omap2430_timer7_hwmod = {
+ .name = "timer7",
+ .mpu_irqs = omap2430_timer7_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap2430_timer7_mpu_irqs),
+ .main_clk = "gpt7_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP24XX_EN_GPT7_SHIFT,
+ .module_offs = CORE_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP24XX_ST_GPT7_SHIFT,
+ },
+ },
+ .slaves = omap2430_timer7_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap2430_timer7_slaves),
+ .class = &omap2430_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2430)
+};
+
+/* timer8 */
+static struct omap_hwmod omap2430_timer8_hwmod;
+static struct omap_hwmod_irq_info omap2430_timer8_mpu_irqs[] = {
+ { .irq = 44, },
+};
+
+static struct omap_hwmod_addr_space omap2430_timer8_addrs[] = {
+ {
+ .pa_start = 0x48082000,
+ .pa_end = 0x48082000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer8 */
+static struct omap_hwmod_ocp_if omap2430_l4_core__timer8 = {
+ .master = &omap2430_l4_core_hwmod,
+ .slave = &omap2430_timer8_hwmod,
+ .clk = "gpt8_ick",
+ .addr = omap2430_timer8_addrs,
+ .addr_cnt = ARRAY_SIZE(omap2430_timer8_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer8 slave port */
+static struct omap_hwmod_ocp_if *omap2430_timer8_slaves[] = {
+ &omap2430_l4_core__timer8,
+};
+
+/* timer8 hwmod */
+static struct omap_hwmod omap2430_timer8_hwmod = {
+ .name = "timer8",
+ .mpu_irqs = omap2430_timer8_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap2430_timer8_mpu_irqs),
+ .main_clk = "gpt8_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP24XX_EN_GPT8_SHIFT,
+ .module_offs = CORE_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP24XX_ST_GPT8_SHIFT,
+ },
+ },
+ .slaves = omap2430_timer8_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap2430_timer8_slaves),
+ .class = &omap2430_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2430)
+};
+
+/* timer9 */
+static struct omap_hwmod omap2430_timer9_hwmod;
+static struct omap_hwmod_irq_info omap2430_timer9_mpu_irqs[] = {
+ { .irq = 45, },
+};
+
+static struct omap_hwmod_addr_space omap2430_timer9_addrs[] = {
+ {
+ .pa_start = 0x48084000,
+ .pa_end = 0x48084000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer9 */
+static struct omap_hwmod_ocp_if omap2430_l4_core__timer9 = {
+ .master = &omap2430_l4_core_hwmod,
+ .slave = &omap2430_timer9_hwmod,
+ .clk = "gpt9_ick",
+ .addr = omap2430_timer9_addrs,
+ .addr_cnt = ARRAY_SIZE(omap2430_timer9_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer9 slave port */
+static struct omap_hwmod_ocp_if *omap2430_timer9_slaves[] = {
+ &omap2430_l4_core__timer9,
+};
+
+/* timer9 hwmod */
+static struct omap_hwmod omap2430_timer9_hwmod = {
+ .name = "timer9",
+ .mpu_irqs = omap2430_timer9_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap2430_timer9_mpu_irqs),
+ .main_clk = "gpt9_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP24XX_EN_GPT9_SHIFT,
+ .module_offs = CORE_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP24XX_ST_GPT9_SHIFT,
+ },
+ },
+ .slaves = omap2430_timer9_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap2430_timer9_slaves),
+ .class = &omap2430_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2430)
+};
+
+/* timer10 */
+static struct omap_hwmod omap2430_timer10_hwmod;
+static struct omap_hwmod_irq_info omap2430_timer10_mpu_irqs[] = {
+ { .irq = 46, },
+};
+
+static struct omap_hwmod_addr_space omap2430_timer10_addrs[] = {
+ {
+ .pa_start = 0x48086000,
+ .pa_end = 0x48086000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer10 */
+static struct omap_hwmod_ocp_if omap2430_l4_core__timer10 = {
+ .master = &omap2430_l4_core_hwmod,
+ .slave = &omap2430_timer10_hwmod,
+ .clk = "gpt10_ick",
+ .addr = omap2430_timer10_addrs,
+ .addr_cnt = ARRAY_SIZE(omap2430_timer10_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer10 slave port */
+static struct omap_hwmod_ocp_if *omap2430_timer10_slaves[] = {
+ &omap2430_l4_core__timer10,
+};
+
+/* timer10 hwmod */
+static struct omap_hwmod omap2430_timer10_hwmod = {
+ .name = "timer10",
+ .mpu_irqs = omap2430_timer10_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap2430_timer10_mpu_irqs),
+ .main_clk = "gpt10_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP24XX_EN_GPT10_SHIFT,
+ .module_offs = CORE_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP24XX_ST_GPT10_SHIFT,
+ },
+ },
+ .slaves = omap2430_timer10_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap2430_timer10_slaves),
+ .class = &omap2430_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2430)
+};
+
+/* timer11 */
+static struct omap_hwmod omap2430_timer11_hwmod;
+static struct omap_hwmod_irq_info omap2430_timer11_mpu_irqs[] = {
+ { .irq = 47, },
+};
+
+static struct omap_hwmod_addr_space omap2430_timer11_addrs[] = {
+ {
+ .pa_start = 0x48088000,
+ .pa_end = 0x48088000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer11 */
+static struct omap_hwmod_ocp_if omap2430_l4_core__timer11 = {
+ .master = &omap2430_l4_core_hwmod,
+ .slave = &omap2430_timer11_hwmod,
+ .clk = "gpt11_ick",
+ .addr = omap2430_timer11_addrs,
+ .addr_cnt = ARRAY_SIZE(omap2430_timer11_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer11 slave port */
+static struct omap_hwmod_ocp_if *omap2430_timer11_slaves[] = {
+ &omap2430_l4_core__timer11,
+};
+
+/* timer11 hwmod */
+static struct omap_hwmod omap2430_timer11_hwmod = {
+ .name = "timer11",
+ .mpu_irqs = omap2430_timer11_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap2430_timer11_mpu_irqs),
+ .main_clk = "gpt11_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP24XX_EN_GPT11_SHIFT,
+ .module_offs = CORE_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP24XX_ST_GPT11_SHIFT,
+ },
+ },
+ .slaves = omap2430_timer11_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap2430_timer11_slaves),
+ .class = &omap2430_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2430)
+};
+
+/* timer12 */
+static struct omap_hwmod omap2430_timer12_hwmod;
+static struct omap_hwmod_irq_info omap2430_timer12_mpu_irqs[] = {
+ { .irq = 48, },
+};
+
+static struct omap_hwmod_addr_space omap2430_timer12_addrs[] = {
+ {
+ .pa_start = 0x4808a000,
+ .pa_end = 0x4808a000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer12 */
+static struct omap_hwmod_ocp_if omap2430_l4_core__timer12 = {
+ .master = &omap2430_l4_core_hwmod,
+ .slave = &omap2430_timer12_hwmod,
+ .clk = "gpt12_ick",
+ .addr = omap2430_timer12_addrs,
+ .addr_cnt = ARRAY_SIZE(omap2430_timer12_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer12 slave port */
+static struct omap_hwmod_ocp_if *omap2430_timer12_slaves[] = {
+ &omap2430_l4_core__timer12,
+};
+
+/* timer12 hwmod */
+static struct omap_hwmod omap2430_timer12_hwmod = {
+ .name = "timer12",
+ .mpu_irqs = omap2430_timer12_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap2430_timer12_mpu_irqs),
+ .main_clk = "gpt12_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP24XX_EN_GPT12_SHIFT,
+ .module_offs = CORE_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP24XX_ST_GPT12_SHIFT,
+ },
+ },
+ .slaves = omap2430_timer12_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap2430_timer12_slaves),
+ .class = &omap2430_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2430)
+};
+
/* l4_wkup -> wd_timer2 */
static struct omap_hwmod_addr_space omap2430_wd_timer2_addrs[] = {
{
@@ -1236,6 +1855,20 @@ static __initdata struct omap_hwmod *omap2430_hwmods[] = {
&omap2430_l4_wkup_hwmod,
&omap2430_mpu_hwmod,
&omap2430_iva_hwmod,
+
+ &omap2430_timer1_hwmod,
+ &omap2430_timer2_hwmod,
+ &omap2430_timer3_hwmod,
+ &omap2430_timer4_hwmod,
+ &omap2430_timer5_hwmod,
+ &omap2430_timer6_hwmod,
+ &omap2430_timer7_hwmod,
+ &omap2430_timer8_hwmod,
+ &omap2430_timer9_hwmod,
+ &omap2430_timer10_hwmod,
+ &omap2430_timer11_hwmod,
+ &omap2430_timer12_hwmod,
+
&omap2430_wd_timer2_hwmod,
&omap2430_uart1_hwmod,
&omap2430_uart2_hwmod,
^ permalink raw reply related
* [PATCH 3/8] OMAP3: hwmod data: add dmtimer
From: Paul Walmsley @ 2011-02-23 7:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110223070455.5874.51326.stgit@twilight.localdomain>
From: Thara Gopinath <thara@ti.com>
Add dmtimer data.
Signed-off-by: Thara Gopinath <thara@ti.com>
Signed-off-by: Tarun Kanti DebBarma <tarun.kanti@ti.com>
Acked-by: Benoit Cousson <b-cousson@ti.com>
---
arch/arm/mach-omap2/omap_hwmod_3xxx_data.c | 649 ++++++++++++++++++++++++++++
1 files changed, 649 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
index 879f55f..17e5852 100644
--- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
@@ -23,6 +23,7 @@
#include <plat/gpio.h>
#include <plat/smartreflex.h>
#include <plat/mcspi.h>
+#include <plat/dmtimer.h>
#include "omap_hwmod_common_data.h"
@@ -495,6 +496,640 @@ static struct omap_hwmod omap3xxx_iva_hwmod = {
.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
};
+/* timer class */
+static struct omap_hwmod_class_sysconfig omap3xxx_timer_1ms_sysc = {
+ .rev_offs = 0x0000,
+ .sysc_offs = 0x0010,
+ .syss_offs = 0x0014,
+ .sysc_flags = (SYSC_HAS_SIDLEMODE | SYSC_HAS_CLOCKACTIVITY |
+ SYSC_HAS_ENAWAKEUP | SYSC_HAS_SOFTRESET |
+ SYSC_HAS_EMUFREE | SYSC_HAS_AUTOIDLE),
+ .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
+ .sysc_fields = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap3xxx_timer_1ms_hwmod_class = {
+ .name = "timer",
+ .sysc = &omap3xxx_timer_1ms_sysc,
+ .rev = OMAP_TIMER_IP_VERSION_1,
+};
+
+static struct omap_hwmod_class_sysconfig omap3xxx_timer_sysc = {
+ .rev_offs = 0x0000,
+ .sysc_offs = 0x0010,
+ .syss_offs = 0x0014,
+ .sysc_flags = (SYSC_HAS_SIDLEMODE | SYSC_HAS_ENAWAKEUP |
+ SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE),
+ .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
+ .sysc_fields = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap3xxx_timer_hwmod_class = {
+ .name = "timer",
+ .sysc = &omap3xxx_timer_sysc,
+ .rev = OMAP_TIMER_IP_VERSION_1,
+};
+
+/* timer1 */
+static struct omap_hwmod omap3xxx_timer1_hwmod;
+static struct omap_hwmod_irq_info omap3xxx_timer1_mpu_irqs[] = {
+ { .irq = 37, },
+};
+
+static struct omap_hwmod_addr_space omap3xxx_timer1_addrs[] = {
+ {
+ .pa_start = 0x48318000,
+ .pa_end = 0x48318000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_wkup -> timer1 */
+static struct omap_hwmod_ocp_if omap3xxx_l4_wkup__timer1 = {
+ .master = &omap3xxx_l4_wkup_hwmod,
+ .slave = &omap3xxx_timer1_hwmod,
+ .clk = "gpt1_ick",
+ .addr = omap3xxx_timer1_addrs,
+ .addr_cnt = ARRAY_SIZE(omap3xxx_timer1_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer1 slave port */
+static struct omap_hwmod_ocp_if *omap3xxx_timer1_slaves[] = {
+ &omap3xxx_l4_wkup__timer1,
+};
+
+/* timer1 hwmod */
+static struct omap_hwmod omap3xxx_timer1_hwmod = {
+ .name = "timer1",
+ .mpu_irqs = omap3xxx_timer1_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap3xxx_timer1_mpu_irqs),
+ .main_clk = "gpt1_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP3430_EN_GPT1_SHIFT,
+ .module_offs = WKUP_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP3430_ST_GPT1_SHIFT,
+ },
+ },
+ .slaves = omap3xxx_timer1_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap3xxx_timer1_slaves),
+ .class = &omap3xxx_timer_1ms_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+};
+
+/* timer2 */
+static struct omap_hwmod omap3xxx_timer2_hwmod;
+static struct omap_hwmod_irq_info omap3xxx_timer2_mpu_irqs[] = {
+ { .irq = 38, },
+};
+
+static struct omap_hwmod_addr_space omap3xxx_timer2_addrs[] = {
+ {
+ .pa_start = 0x49032000,
+ .pa_end = 0x49032000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_per -> timer2 */
+static struct omap_hwmod_ocp_if omap3xxx_l4_per__timer2 = {
+ .master = &omap3xxx_l4_per_hwmod,
+ .slave = &omap3xxx_timer2_hwmod,
+ .clk = "gpt2_ick",
+ .addr = omap3xxx_timer2_addrs,
+ .addr_cnt = ARRAY_SIZE(omap3xxx_timer2_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer2 slave port */
+static struct omap_hwmod_ocp_if *omap3xxx_timer2_slaves[] = {
+ &omap3xxx_l4_per__timer2,
+};
+
+/* timer2 hwmod */
+static struct omap_hwmod omap3xxx_timer2_hwmod = {
+ .name = "timer2",
+ .mpu_irqs = omap3xxx_timer2_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap3xxx_timer2_mpu_irqs),
+ .main_clk = "gpt2_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP3430_EN_GPT2_SHIFT,
+ .module_offs = OMAP3430_PER_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP3430_ST_GPT2_SHIFT,
+ },
+ },
+ .slaves = omap3xxx_timer2_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap3xxx_timer2_slaves),
+ .class = &omap3xxx_timer_1ms_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+};
+
+/* timer3 */
+static struct omap_hwmod omap3xxx_timer3_hwmod;
+static struct omap_hwmod_irq_info omap3xxx_timer3_mpu_irqs[] = {
+ { .irq = 39, },
+};
+
+static struct omap_hwmod_addr_space omap3xxx_timer3_addrs[] = {
+ {
+ .pa_start = 0x49034000,
+ .pa_end = 0x49034000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_per -> timer3 */
+static struct omap_hwmod_ocp_if omap3xxx_l4_per__timer3 = {
+ .master = &omap3xxx_l4_per_hwmod,
+ .slave = &omap3xxx_timer3_hwmod,
+ .clk = "gpt3_ick",
+ .addr = omap3xxx_timer3_addrs,
+ .addr_cnt = ARRAY_SIZE(omap3xxx_timer3_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer3 slave port */
+static struct omap_hwmod_ocp_if *omap3xxx_timer3_slaves[] = {
+ &omap3xxx_l4_per__timer3,
+};
+
+/* timer3 hwmod */
+static struct omap_hwmod omap3xxx_timer3_hwmod = {
+ .name = "timer3",
+ .mpu_irqs = omap3xxx_timer3_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap3xxx_timer3_mpu_irqs),
+ .main_clk = "gpt3_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP3430_EN_GPT3_SHIFT,
+ .module_offs = OMAP3430_PER_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP3430_ST_GPT3_SHIFT,
+ },
+ },
+ .slaves = omap3xxx_timer3_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap3xxx_timer3_slaves),
+ .class = &omap3xxx_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+};
+
+/* timer4 */
+static struct omap_hwmod omap3xxx_timer4_hwmod;
+static struct omap_hwmod_irq_info omap3xxx_timer4_mpu_irqs[] = {
+ { .irq = 40, },
+};
+
+static struct omap_hwmod_addr_space omap3xxx_timer4_addrs[] = {
+ {
+ .pa_start = 0x49036000,
+ .pa_end = 0x49036000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_per -> timer4 */
+static struct omap_hwmod_ocp_if omap3xxx_l4_per__timer4 = {
+ .master = &omap3xxx_l4_per_hwmod,
+ .slave = &omap3xxx_timer4_hwmod,
+ .clk = "gpt4_ick",
+ .addr = omap3xxx_timer4_addrs,
+ .addr_cnt = ARRAY_SIZE(omap3xxx_timer4_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer4 slave port */
+static struct omap_hwmod_ocp_if *omap3xxx_timer4_slaves[] = {
+ &omap3xxx_l4_per__timer4,
+};
+
+/* timer4 hwmod */
+static struct omap_hwmod omap3xxx_timer4_hwmod = {
+ .name = "timer4",
+ .mpu_irqs = omap3xxx_timer4_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap3xxx_timer4_mpu_irqs),
+ .main_clk = "gpt4_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP3430_EN_GPT4_SHIFT,
+ .module_offs = OMAP3430_PER_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP3430_ST_GPT4_SHIFT,
+ },
+ },
+ .slaves = omap3xxx_timer4_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap3xxx_timer4_slaves),
+ .class = &omap3xxx_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+};
+
+/* timer5 */
+static struct omap_hwmod omap3xxx_timer5_hwmod;
+static struct omap_hwmod_irq_info omap3xxx_timer5_mpu_irqs[] = {
+ { .irq = 41, },
+};
+
+static struct omap_hwmod_addr_space omap3xxx_timer5_addrs[] = {
+ {
+ .pa_start = 0x49038000,
+ .pa_end = 0x49038000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_per -> timer5 */
+static struct omap_hwmod_ocp_if omap3xxx_l4_per__timer5 = {
+ .master = &omap3xxx_l4_per_hwmod,
+ .slave = &omap3xxx_timer5_hwmod,
+ .clk = "gpt5_ick",
+ .addr = omap3xxx_timer5_addrs,
+ .addr_cnt = ARRAY_SIZE(omap3xxx_timer5_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer5 slave port */
+static struct omap_hwmod_ocp_if *omap3xxx_timer5_slaves[] = {
+ &omap3xxx_l4_per__timer5,
+};
+
+/* timer5 hwmod */
+static struct omap_hwmod omap3xxx_timer5_hwmod = {
+ .name = "timer5",
+ .mpu_irqs = omap3xxx_timer5_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap3xxx_timer5_mpu_irqs),
+ .main_clk = "gpt5_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP3430_EN_GPT5_SHIFT,
+ .module_offs = OMAP3430_PER_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP3430_ST_GPT5_SHIFT,
+ },
+ },
+ .slaves = omap3xxx_timer5_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap3xxx_timer5_slaves),
+ .class = &omap3xxx_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+};
+
+/* timer6 */
+static struct omap_hwmod omap3xxx_timer6_hwmod;
+static struct omap_hwmod_irq_info omap3xxx_timer6_mpu_irqs[] = {
+ { .irq = 42, },
+};
+
+static struct omap_hwmod_addr_space omap3xxx_timer6_addrs[] = {
+ {
+ .pa_start = 0x4903A000,
+ .pa_end = 0x4903A000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_per -> timer6 */
+static struct omap_hwmod_ocp_if omap3xxx_l4_per__timer6 = {
+ .master = &omap3xxx_l4_per_hwmod,
+ .slave = &omap3xxx_timer6_hwmod,
+ .clk = "gpt6_ick",
+ .addr = omap3xxx_timer6_addrs,
+ .addr_cnt = ARRAY_SIZE(omap3xxx_timer6_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer6 slave port */
+static struct omap_hwmod_ocp_if *omap3xxx_timer6_slaves[] = {
+ &omap3xxx_l4_per__timer6,
+};
+
+/* timer6 hwmod */
+static struct omap_hwmod omap3xxx_timer6_hwmod = {
+ .name = "timer6",
+ .mpu_irqs = omap3xxx_timer6_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap3xxx_timer6_mpu_irqs),
+ .main_clk = "gpt6_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP3430_EN_GPT6_SHIFT,
+ .module_offs = OMAP3430_PER_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP3430_ST_GPT6_SHIFT,
+ },
+ },
+ .slaves = omap3xxx_timer6_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap3xxx_timer6_slaves),
+ .class = &omap3xxx_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+};
+
+/* timer7 */
+static struct omap_hwmod omap3xxx_timer7_hwmod;
+static struct omap_hwmod_irq_info omap3xxx_timer7_mpu_irqs[] = {
+ { .irq = 43, },
+};
+
+static struct omap_hwmod_addr_space omap3xxx_timer7_addrs[] = {
+ {
+ .pa_start = 0x4903C000,
+ .pa_end = 0x4903C000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_per -> timer7 */
+static struct omap_hwmod_ocp_if omap3xxx_l4_per__timer7 = {
+ .master = &omap3xxx_l4_per_hwmod,
+ .slave = &omap3xxx_timer7_hwmod,
+ .clk = "gpt7_ick",
+ .addr = omap3xxx_timer7_addrs,
+ .addr_cnt = ARRAY_SIZE(omap3xxx_timer7_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer7 slave port */
+static struct omap_hwmod_ocp_if *omap3xxx_timer7_slaves[] = {
+ &omap3xxx_l4_per__timer7,
+};
+
+/* timer7 hwmod */
+static struct omap_hwmod omap3xxx_timer7_hwmod = {
+ .name = "timer7",
+ .mpu_irqs = omap3xxx_timer7_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap3xxx_timer7_mpu_irqs),
+ .main_clk = "gpt7_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP3430_EN_GPT7_SHIFT,
+ .module_offs = OMAP3430_PER_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP3430_ST_GPT7_SHIFT,
+ },
+ },
+ .slaves = omap3xxx_timer7_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap3xxx_timer7_slaves),
+ .class = &omap3xxx_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+};
+
+/* timer8 */
+static struct omap_hwmod omap3xxx_timer8_hwmod;
+static struct omap_hwmod_irq_info omap3xxx_timer8_mpu_irqs[] = {
+ { .irq = 44, },
+};
+
+static struct omap_hwmod_addr_space omap3xxx_timer8_addrs[] = {
+ {
+ .pa_start = 0x4903E000,
+ .pa_end = 0x4903E000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_per -> timer8 */
+static struct omap_hwmod_ocp_if omap3xxx_l4_per__timer8 = {
+ .master = &omap3xxx_l4_per_hwmod,
+ .slave = &omap3xxx_timer8_hwmod,
+ .clk = "gpt8_ick",
+ .addr = omap3xxx_timer8_addrs,
+ .addr_cnt = ARRAY_SIZE(omap3xxx_timer8_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer8 slave port */
+static struct omap_hwmod_ocp_if *omap3xxx_timer8_slaves[] = {
+ &omap3xxx_l4_per__timer8,
+};
+
+/* timer8 hwmod */
+static struct omap_hwmod omap3xxx_timer8_hwmod = {
+ .name = "timer8",
+ .mpu_irqs = omap3xxx_timer8_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap3xxx_timer8_mpu_irqs),
+ .main_clk = "gpt8_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP3430_EN_GPT8_SHIFT,
+ .module_offs = OMAP3430_PER_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP3430_ST_GPT8_SHIFT,
+ },
+ },
+ .slaves = omap3xxx_timer8_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap3xxx_timer8_slaves),
+ .class = &omap3xxx_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+};
+
+/* timer9 */
+static struct omap_hwmod omap3xxx_timer9_hwmod;
+static struct omap_hwmod_irq_info omap3xxx_timer9_mpu_irqs[] = {
+ { .irq = 45, },
+};
+
+static struct omap_hwmod_addr_space omap3xxx_timer9_addrs[] = {
+ {
+ .pa_start = 0x49040000,
+ .pa_end = 0x49040000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_per -> timer9 */
+static struct omap_hwmod_ocp_if omap3xxx_l4_per__timer9 = {
+ .master = &omap3xxx_l4_per_hwmod,
+ .slave = &omap3xxx_timer9_hwmod,
+ .clk = "gpt9_ick",
+ .addr = omap3xxx_timer9_addrs,
+ .addr_cnt = ARRAY_SIZE(omap3xxx_timer9_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer9 slave port */
+static struct omap_hwmod_ocp_if *omap3xxx_timer9_slaves[] = {
+ &omap3xxx_l4_per__timer9,
+};
+
+/* timer9 hwmod */
+static struct omap_hwmod omap3xxx_timer9_hwmod = {
+ .name = "timer9",
+ .mpu_irqs = omap3xxx_timer9_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap3xxx_timer9_mpu_irqs),
+ .main_clk = "gpt9_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP3430_EN_GPT9_SHIFT,
+ .module_offs = OMAP3430_PER_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP3430_ST_GPT9_SHIFT,
+ },
+ },
+ .slaves = omap3xxx_timer9_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap3xxx_timer9_slaves),
+ .class = &omap3xxx_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+};
+
+/* timer10 */
+static struct omap_hwmod omap3xxx_timer10_hwmod;
+static struct omap_hwmod_irq_info omap3xxx_timer10_mpu_irqs[] = {
+ { .irq = 46, },
+};
+
+static struct omap_hwmod_addr_space omap3xxx_timer10_addrs[] = {
+ {
+ .pa_start = 0x48086000,
+ .pa_end = 0x48086000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer10 */
+static struct omap_hwmod_ocp_if omap3xxx_l4_core__timer10 = {
+ .master = &omap3xxx_l4_core_hwmod,
+ .slave = &omap3xxx_timer10_hwmod,
+ .clk = "gpt10_ick",
+ .addr = omap3xxx_timer10_addrs,
+ .addr_cnt = ARRAY_SIZE(omap3xxx_timer10_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer10 slave port */
+static struct omap_hwmod_ocp_if *omap3xxx_timer10_slaves[] = {
+ &omap3xxx_l4_core__timer10,
+};
+
+/* timer10 hwmod */
+static struct omap_hwmod omap3xxx_timer10_hwmod = {
+ .name = "timer10",
+ .mpu_irqs = omap3xxx_timer10_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap3xxx_timer10_mpu_irqs),
+ .main_clk = "gpt10_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP3430_EN_GPT10_SHIFT,
+ .module_offs = CORE_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP3430_ST_GPT10_SHIFT,
+ },
+ },
+ .slaves = omap3xxx_timer10_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap3xxx_timer10_slaves),
+ .class = &omap3xxx_timer_1ms_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+};
+
+/* timer11 */
+static struct omap_hwmod omap3xxx_timer11_hwmod;
+static struct omap_hwmod_irq_info omap3xxx_timer11_mpu_irqs[] = {
+ { .irq = 47, },
+};
+
+static struct omap_hwmod_addr_space omap3xxx_timer11_addrs[] = {
+ {
+ .pa_start = 0x48088000,
+ .pa_end = 0x48088000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer11 */
+static struct omap_hwmod_ocp_if omap3xxx_l4_core__timer11 = {
+ .master = &omap3xxx_l4_core_hwmod,
+ .slave = &omap3xxx_timer11_hwmod,
+ .clk = "gpt11_ick",
+ .addr = omap3xxx_timer11_addrs,
+ .addr_cnt = ARRAY_SIZE(omap3xxx_timer11_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer11 slave port */
+static struct omap_hwmod_ocp_if *omap3xxx_timer11_slaves[] = {
+ &omap3xxx_l4_core__timer11,
+};
+
+/* timer11 hwmod */
+static struct omap_hwmod omap3xxx_timer11_hwmod = {
+ .name = "timer11",
+ .mpu_irqs = omap3xxx_timer11_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap3xxx_timer11_mpu_irqs),
+ .main_clk = "gpt11_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP3430_EN_GPT11_SHIFT,
+ .module_offs = CORE_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP3430_ST_GPT11_SHIFT,
+ },
+ },
+ .slaves = omap3xxx_timer11_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap3xxx_timer11_slaves),
+ .class = &omap3xxx_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+};
+
+/* timer12*/
+static struct omap_hwmod omap3xxx_timer12_hwmod;
+static struct omap_hwmod_irq_info omap3xxx_timer12_mpu_irqs[] = {
+ { .irq = 95, },
+};
+
+static struct omap_hwmod_addr_space omap3xxx_timer12_addrs[] = {
+ {
+ .pa_start = 0x48304000,
+ .pa_end = 0x48304000 + SZ_1K - 1,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+/* l4_core -> timer12 */
+static struct omap_hwmod_ocp_if omap3xxx_l4_core__timer12 = {
+ .master = &omap3xxx_l4_core_hwmod,
+ .slave = &omap3xxx_timer12_hwmod,
+ .clk = "gpt12_ick",
+ .addr = omap3xxx_timer12_addrs,
+ .addr_cnt = ARRAY_SIZE(omap3xxx_timer12_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* timer12 slave port */
+static struct omap_hwmod_ocp_if *omap3xxx_timer12_slaves[] = {
+ &omap3xxx_l4_core__timer12,
+};
+
+/* timer12 hwmod */
+static struct omap_hwmod omap3xxx_timer12_hwmod = {
+ .name = "timer12",
+ .mpu_irqs = omap3xxx_timer12_mpu_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap3xxx_timer12_mpu_irqs),
+ .main_clk = "gpt12_fck",
+ .prcm = {
+ .omap2 = {
+ .prcm_reg_id = 1,
+ .module_bit = OMAP3430_EN_GPT12_SHIFT,
+ .module_offs = WKUP_MOD,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP3430_ST_GPT12_SHIFT,
+ },
+ },
+ .slaves = omap3xxx_timer12_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap3xxx_timer12_slaves),
+ .class = &omap3xxx_timer_hwmod_class,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+};
+
/* l4_wkup -> wd_timer2 */
static struct omap_hwmod_addr_space omap3xxx_wd_timer2_addrs[] = {
{
@@ -1795,6 +2430,20 @@ static __initdata struct omap_hwmod *omap3xxx_hwmods[] = {
&omap3xxx_l4_wkup_hwmod,
&omap3xxx_mpu_hwmod,
&omap3xxx_iva_hwmod,
+
+ &omap3xxx_timer1_hwmod,
+ &omap3xxx_timer2_hwmod,
+ &omap3xxx_timer3_hwmod,
+ &omap3xxx_timer4_hwmod,
+ &omap3xxx_timer5_hwmod,
+ &omap3xxx_timer6_hwmod,
+ &omap3xxx_timer7_hwmod,
+ &omap3xxx_timer8_hwmod,
+ &omap3xxx_timer9_hwmod,
+ &omap3xxx_timer10_hwmod,
+ &omap3xxx_timer11_hwmod,
+ &omap3xxx_timer12_hwmod,
+
&omap3xxx_wd_timer2_hwmod,
&omap3xxx_uart1_hwmod,
&omap3xxx_uart2_hwmod,
^ permalink raw reply related
* [PATCH 4/8] OMAP2+: hwmod: find MPU initiator hwmod during in _register()
From: Paul Walmsley @ 2011-02-23 7:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110223070455.5874.51326.stgit@twilight.localdomain>
Move the code that looks for the MPU initiator hwmod to run during
the individual hwmod _register() function. (Previously, it ran after
all hwmods were registered in the omap_hwmod_late_init() function.)
This is done so code can late-initialize a few individual hwmods --
for example, for the system timer -- before the entire set of hwmods is
initialized later in boot via omap_hwmod_late_init().
Signed-off-by: Paul Walmsley <paul@pwsan.com>
Cc: Beno?t Cousson <b-cousson@ti.com>
Cc: Kevin Hilman <khilman@ti.com>
---
arch/arm/mach-omap2/omap_hwmod.c | 23 +++++++++++++++--------
1 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
index 9e89a58..41f548e 100644
--- a/arch/arm/mach-omap2/omap_hwmod.c
+++ b/arch/arm/mach-omap2/omap_hwmod.c
@@ -1455,7 +1455,7 @@ static int _setup(struct omap_hwmod *oh, void *data)
*/
static int __init _register(struct omap_hwmod *oh)
{
- int ret, ms_id;
+ int ms_id;
if (!oh || !oh->name || !oh->class || !oh->class->name ||
(oh->_state != _HWMOD_STATE_UNKNOWN))
@@ -1478,9 +1478,14 @@ static int __init _register(struct omap_hwmod *oh)
oh->_state = _HWMOD_STATE_REGISTERED;
- ret = 0;
+ /*
+ * XXX Rather than doing a strcmp(), this should test a flag
+ * set in the hwmod data, inserted by the autogenerator code.
+ */
+ if (!strcmp(oh->name, MPU_INITIATOR_NAME))
+ mpu_oh = oh;
- return ret;
+ return 0;
}
@@ -1644,22 +1649,24 @@ static int __init _populate_mpu_rt_base(struct omap_hwmod *oh, void *data)
*
* Must be called after omap2_clk_init(). Resolves the struct clk names
* to struct clk pointers for each registered omap_hwmod. Also calls
- * _setup() on each hwmod. Returns 0.
+ * _setup() on each hwmod. Returns 0 upon success or -EINVAL upon error.
*/
static int __init omap_hwmod_late_init(void)
{
int r;
+ if (!mpu_oh) {
+ pr_err("omap_hwmod: %s: MPU initiator hwmod %s not yet registered\n",
+ __func__, MPU_INITIATOR_NAME);
+ return -EINVAL;
+ }
+
r = omap_hwmod_for_each(_populate_mpu_rt_base, NULL);
/* XXX check return value */
r = omap_hwmod_for_each(_init_clocks, NULL);
WARN(r, "omap_hwmod: omap_hwmod_late_init(): _init_clocks failed\n");
- mpu_oh = omap_hwmod_lookup(MPU_INITIATOR_NAME);
- WARN(!mpu_oh, "omap_hwmod: could not find MPU initiator hwmod %s\n",
- MPU_INITIATOR_NAME);
-
omap_hwmod_for_each(_setup, NULL);
return 0;
^ permalink raw reply related
* [PATCH 5/8] OMAP2+: hwmod: allow multiple calls to omap_hwmod_init()
From: Paul Walmsley @ 2011-02-23 7:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110223070455.5874.51326.stgit@twilight.localdomain>
There's no longer any reason why we should prevent multiple
calls to omap_hwmod_init(). It is now simply used to register an
array of hwmods.
This should allow a subset of hwmods (e.g., hwmods
handling the system clocksource and clockevents) to be registered
earlier than the remaining mass of hwmods.
Signed-off-by: Paul Walmsley <paul@pwsan.com>
Cc: Beno?t Cousson <b-cousson@ti.com>
Cc: Kevin Hilman <khilman@ti.com>
---
arch/arm/mach-omap2/omap_hwmod.c | 29 ++++++++++-------------------
1 files changed, 10 insertions(+), 19 deletions(-)
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
index 41f548e..86eacaf 100644
--- a/arch/arm/mach-omap2/omap_hwmod.c
+++ b/arch/arm/mach-omap2/omap_hwmod.c
@@ -162,9 +162,6 @@ static LIST_HEAD(omap_hwmod_list);
/* mpu_oh: used to add/remove MPU initiator from sleepdep list */
static struct omap_hwmod *mpu_oh;
-/* inited: 0 if omap_hwmod_init() has not yet been called; 1 otherwise */
-static u8 inited;
-
/* Private functions */
@@ -1600,26 +1597,20 @@ int omap_hwmod_for_each(int (*fn)(struct omap_hwmod *oh, void *data),
*/
int __init omap_hwmod_init(struct omap_hwmod **ohs)
{
- struct omap_hwmod *oh;
- int r;
-
- if (inited)
- return -EINVAL;
-
- inited = 1;
+ int r, i;
if (!ohs)
return 0;
- oh = *ohs;
- while (oh) {
- if (omap_chip_is(oh->omap_chip)) {
- r = _register(oh);
- WARN(r, "omap_hwmod: %s: _register returned "
- "%d\n", oh->name, r);
- }
- oh = *++ohs;
- }
+ i = 0;
+ do {
+ if (!omap_chip_is(ohs[i]->omap_chip))
+ continue;
+
+ r = _register(ohs[i]);
+ WARN(r, "omap_hwmod: %s: _register returned %d\n", ohs[i]->name,
+ r);
+ } while (ohs[++i]);
return 0;
}
^ permalink raw reply related
* [PATCH 6/8] OMAP2+: hwmod: ignore attempts to re-late-init a hwmod
From: Paul Walmsley @ 2011-02-23 7:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110223070455.5874.51326.stgit@twilight.localdomain>
Previously, if a hwmod had already been late-initialized, and the code
attempted to late-initialize the hwmod again, an error would be
returned. This is not really useful behavior if we wish to allow the
OMAP core code to late-init the hwmods needed for the Linux
clocksources and clockevents _before_ the rest of the hwmods are
late-inited. So, instead of generating errors, just ignore the attempt
to re-late-initialize the hwmod.
Signed-off-by: Paul Walmsley <paul@pwsan.com>
Cc: Beno?t Cousson <b-cousson@ti.com>
Cc: Kevin Hilman <khilman@ti.com>
---
arch/arm/mach-omap2/omap_hwmod.c | 19 +++++++++++--------
1 files changed, 11 insertions(+), 8 deletions(-)
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
index 86eacaf..1b6eb1f 100644
--- a/arch/arm/mach-omap2/omap_hwmod.c
+++ b/arch/arm/mach-omap2/omap_hwmod.c
@@ -902,17 +902,15 @@ static struct omap_hwmod *_lookup(const char *name)
* @data: not used; pass NULL
*
* Called by omap_hwmod_late_init() (after omap2_clk_init()).
- * Resolves all clock names embedded in the hwmod. Returns -EINVAL if
- * the omap_hwmod has not yet been registered or if the clocks have
- * already been initialized, 0 on success, or a non-zero error on
- * failure.
+ * Resolves all clock names embedded in the hwmod. Returns 0 on
+ * success, or a negative error code on failure.
*/
static int _init_clocks(struct omap_hwmod *oh, void *data)
{
int ret = 0;
- if (!oh || (oh->_state != _HWMOD_STATE_REGISTERED))
- return -EINVAL;
+ if (oh->_state != _HWMOD_STATE_REGISTERED)
+ return 0;
pr_debug("omap_hwmod: %s: looking up clocks\n", oh->name);
@@ -1351,14 +1349,16 @@ static int _shutdown(struct omap_hwmod *oh)
* @oh: struct omap_hwmod *
*
* Writes the CLOCKACTIVITY bits @clockact to the hwmod @oh
- * OCP_SYSCONFIG register. Returns -EINVAL if the hwmod is in the
- * wrong state or returns 0.
+ * OCP_SYSCONFIG register. Returns 0.
*/
static int _setup(struct omap_hwmod *oh, void *data)
{
int i, r;
u8 postsetup_state;
+ if (oh->_state != _HWMOD_STATE_CLKS_INITED)
+ return 0;
+
/* Set iclk autoidle mode */
if (oh->slaves_cnt > 0) {
for (i = 0; i < oh->slaves_cnt; i++) {
@@ -1624,6 +1624,9 @@ int __init omap_hwmod_init(struct omap_hwmod **ohs)
*/
static int __init _populate_mpu_rt_base(struct omap_hwmod *oh, void *data)
{
+ if (oh->_state != _HWMOD_STATE_REGISTERED)
+ return 0;
+
if (oh->_int_flags & _HWMOD_NO_MPU_PORT)
return 0;
^ permalink raw reply related
* [PATCH 7/8] OMAP2+: hwmod: add ability to late-init individual hwmods
From: Paul Walmsley @ 2011-02-23 7:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110223070455.5874.51326.stgit@twilight.localdomain>
Add omap_hwmod_late_init_one(), which is intended for use early in
boot to selectively init the hwmods needed for system clocksources
and clockevents, and any other hwmod that is needed in early boot.
omap_hwmod_late_init() can then be called later in the boot process.
The point is to minimize the amount of code that needs to be run early
in the boot process.
Signed-off-by: Paul Walmsley <paul@pwsan.com>
Cc: Beno?t Cousson <b-cousson@ti.com>
Cc: Kevin Hilman <khilman@ti.com>
Cc: Santosh Shilimkar <santosh.shilimkar@ti.com>
Cc: Tony Lindgren <tony@atomide.com>
---
arch/arm/mach-omap2/omap_hwmod.c | 53 +++++++++++++++++++++++++-
arch/arm/plat-omap/include/plat/omap_hwmod.h | 3 +
2 files changed, 54 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
index 1b6eb1f..83ca219 100644
--- a/arch/arm/mach-omap2/omap_hwmod.c
+++ b/arch/arm/mach-omap2/omap_hwmod.c
@@ -1639,6 +1639,55 @@ static int __init _populate_mpu_rt_base(struct omap_hwmod *oh, void *data)
}
/**
+ * omap_hwmod_late_init_one - XXX
+ *
+ * XXX Fix this documentation
+ *
+ * Must be called after omap2_clk_init(). Resolves the struct clk
+ * names to struct clk pointers for each registered omap_hwmod. Also
+ * calls _setup() on each hwmod. Returns -EINVAL upon error or 0 upon
+ * success.
+ */
+int __init omap_hwmod_late_init_one(const char *oh_name)
+{
+ struct omap_hwmod *oh;
+ int r;
+
+ pr_debug("omap_hwmod: %s: %s\n", oh_name, __func__);
+
+ if (!mpu_oh) {
+ pr_err("omap_hwmod: %s: cannot late_init_one: MPU initiator hwmod %s not yet registered\n",
+ oh_name, MPU_INITIATOR_NAME);
+ return -EINVAL;
+ }
+
+ oh = _lookup(oh_name);
+ if (!oh) {
+ WARN(1, "omap_hwmod: %s: hwmod not yet registered\n", oh_name);
+ return -EINVAL;
+ }
+
+ if (mpu_oh->_state == _HWMOD_STATE_REGISTERED && oh != mpu_oh)
+ omap_hwmod_late_init_one(MPU_INITIATOR_NAME);
+
+ r = _populate_mpu_rt_base(oh, NULL);
+ if (IS_ERR_VALUE(r)) {
+ WARN(1, "omap_hwmod: %s: couldn't set mpu_rt_base\n", oh_name);
+ return -EINVAL;
+ }
+
+ r = _init_clocks(oh, NULL);
+ if (IS_ERR_VALUE(r)) {
+ WARN(1, "omap_hwmod: %s: couldn't init clocks\n", oh_name);
+ return -EINVAL;
+ }
+
+ _setup(oh, NULL);
+
+ return 0;
+}
+
+/**
* omap_hwmod_late_init - do some post-clock framework initialization
*
* Must be called after omap2_clk_init(). Resolves the struct clk names
@@ -1657,9 +1706,9 @@ static int __init omap_hwmod_late_init(void)
r = omap_hwmod_for_each(_populate_mpu_rt_base, NULL);
- /* XXX check return value */
r = omap_hwmod_for_each(_init_clocks, NULL);
- WARN(r, "omap_hwmod: omap_hwmod_late_init(): _init_clocks failed\n");
+ WARN(IS_ERR_VALUE(r),
+ "omap_hwmod: %s: _init_clocks failed\n", __func__);
omap_hwmod_for_each(_setup, NULL);
diff --git a/arch/arm/plat-omap/include/plat/omap_hwmod.h b/arch/arm/plat-omap/include/plat/omap_hwmod.h
index fedd829..48b8dd3 100644
--- a/arch/arm/plat-omap/include/plat/omap_hwmod.h
+++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h
@@ -30,6 +30,7 @@
#define __ARCH_ARM_PLAT_OMAP_INCLUDE_MACH_OMAP_HWMOD_H
#include <linux/kernel.h>
+#include <linux/init.h>
#include <linux/list.h>
#include <linux/ioport.h>
#include <linux/spinlock.h>
@@ -540,6 +541,8 @@ struct omap_hwmod *omap_hwmod_lookup(const char *name);
int omap_hwmod_for_each(int (*fn)(struct omap_hwmod *oh, void *data),
void *data);
+int __init omap_hwmod_late_init_one(const char *name);
+
int omap_hwmod_enable(struct omap_hwmod *oh);
int _omap_hwmod_enable(struct omap_hwmod *oh);
int omap_hwmod_idle(struct omap_hwmod *oh);
^ permalink raw reply related
* [PATCH 8/8] OMAP2+: clockevent: late-init GPTIMER clockevent hwmod right before timer init
From: Paul Walmsley @ 2011-02-23 7:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110223070455.5874.51326.stgit@twilight.localdomain>
Late-initialize the GPTIMER hwmod used for the clockevent source immediately
before it is used. This avoids the need to late-initialize all of the hwmods
until the boot process is further along. (In general, we want to defer
as much as possible until late in the boot process.)
Signed-off-by: Paul Walmsley <paul@pwsan.com>
Cc: Beno?t Cousson <b-cousson@ti.com>
Cc: Tony Lindgren <tony@atomide.com>
Cc: Kevin Hilman <khilman@ti.com>
Cc: Santosh Shilimkar <santosh.shilimkar@ti.com>
---
arch/arm/mach-omap2/timer-gp.c | 8 +++++++-
1 files changed, 7 insertions(+), 1 deletions(-)
diff --git a/arch/arm/mach-omap2/timer-gp.c b/arch/arm/mach-omap2/timer-gp.c
index 0fc550e..a4e51a2 100644
--- a/arch/arm/mach-omap2/timer-gp.c
+++ b/arch/arm/mach-omap2/timer-gp.c
@@ -40,10 +40,11 @@
#include <plat/dmtimer.h>
#include <asm/localtimer.h>
#include <asm/sched_clock.h>
+#include <plat/common.h>
#include "timer-gp.h"
+#include <plat/omap_hwmod.h>
-#include <plat/common.h>
/* MAX_GPTIMER_ID: number of GPTIMERs on the chip */
#define MAX_GPTIMER_ID 12
@@ -133,9 +134,13 @@ static void __init omap2_gp_clockevent_init(void)
{
u32 tick_rate;
int src;
+ const char *clockevent_hwmod_name;
inited = 1;
+ clockevent_hwmod_name = (gptimer_id == 12) ? "timer12" : "timer1";
+ omap_hwmod_late_init_one(clockevent_hwmod_name);
+
gptimer = omap_dm_timer_request_specific(gptimer_id);
BUG_ON(gptimer == NULL);
gptimer_wakeup = gptimer;
@@ -250,6 +255,7 @@ static void __init omap2_gp_timer_init(void)
BUG_ON(!twd_base);
}
#endif
+
omap_dm_timer_init();
omap2_gp_clockevent_init();
^ permalink raw reply related
* Problem with "ARM: tegra: Move tegra_common_init to tegra_init_early"
From: Colin Cross @ 2011-02-23 7:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <74CDBE0F657A3D45AFBB94109FB122FF03112A5C22@HQMAIL01.nvidia.com>
On Tue, Feb 22, 2011 at 7:12 PM, Stephen Warren <swarren@nvidia.com> wrote:
> The following commit makes the Tegra APB DMA engine fail to initialize correctly:
>
> commit 0cf6230af909a86f81907455eca2a5c9b8f68fe6
>
> ? ?ARM: tegra: Move tegra_common_init to tegra_init_early
>
> ? ?Move tegra_common_init to tegra_init_early, and set it
> ? ?as the init_early entry in the machine struct.
> ? ?Initializes the clocks earlier so that timers can enable
> ? ?their clocks.
>
> ? ?Also reorders the members in the Harmony and Trimslice
> ? ?boards' machine structs to match the order they are
> ? ?called in.
>
> The reason is that tegra_init_early_ calls tegra_dma_init which calls
> request_threaded_irq, which fails since the IRQ hasn't yet been marked valid;
> that only happens in tegra_init_irq, which gets called after tegra_init_early.
>
> This used to work OK, since tegra_init_early was tegra_common_init, which got
> called after tegra_init_irq, basically from the beginning of tegra_harmony_init.
>
> I tried moving the call to tegra_dma_init back to each of the very end of
> tegra_init_irq and the very beginning of tegra_harmony_init. Both of these
> resulted in a kernel that wouldn't boot.
>
> Simply removing the call to tegra_dma_init completely does allow the kernel
> to boot, but obviously, DMA isn't available.
>
> I'm a little stumped why moving tegra_dma_init causes a boot failure. Does
> anyone have any helpful ideas, or any alternative ways to fix the underlying
> problem?
I don't know why it still fails if you put it back at the beginning of
the machine init, and it boots without errors on my board if I move
tegra_dma_init to a postcore_initcall, but I don't have any drivers on
my board that use DMA.
Can you provide kernel logs?
^ 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