* [PATCH v4 0/5] rtc: omap dt support (for am33xx)
From: Afzal Mohammed @ 2012-10-19 9:59 UTC (permalink / raw)
To: linux-arm-kernel
Hi Andrew,
This series enhances rtc-omap driver so as to be usable on
am33xx SoC by adding DT support (Beagle Bone uses am33xx).
This is a revised version of series that was posted on
27th July 2012 with the subject,
"omap-am33xx rtc dt support".
It seems rtc maintainer in inactive and hence sending it you.
Please help this reach mainline.
Brief:
Enhance rtc-omap driver to make it DT capable. This is
required to support boards that boot only with DT like
Beagle Bone. rtc-omap driver is used in DaVinci platforms
and they are also now going DT way, and these changes make
their DT migration easier. rtc-omap driver is made
intelligent enough to handle kicker mechanism. This also
helps in removing kicker mechanism support done for DaVinci
at platform level.
This series has been made over 3.7-rc1
This has been tested on Beaglebone (am33xx platform) and on
DaVinci da850 evm.
Regards
Afzal
v4:
Resolve build error during module build
Improve DT documentation
v3:
Remove unit address in DT node
Use readb/writeb instead of __raw_* variants
Remove unnecessary parens
v2:
Use device name da830-rtc instead of am1808-rtc, similar change in
compatible for DT
Newly added register name made similar to that existing in the driver
Better commit message description
Modify Kconfig help, resolve checkpatch warning
Afzal Mohammed (4):
rtc: omap: kicker mechanism support
ARM: davinci: remove rtc kicker release
rtc: omap: dt support
rtc: omap: depend on am33xx
Vaibhav Hiremath (1):
rtc: omap: Add runtime pm support
Documentation/devicetree/bindings/rtc/rtc-omap.txt | 17 +++++
arch/arm/mach-davinci/devices-da8xx.c | 13 +---
drivers/rtc/Kconfig | 10 +--
drivers/rtc/rtc-omap.c | 80 +++++++++++++++++++++-
4 files changed, 101 insertions(+), 19 deletions(-)
create mode 100644 Documentation/devicetree/bindings/rtc/rtc-omap.txt
--
1.7.12
^ permalink raw reply
* [PATCH] gpio: clps711x: localise <mach/gpio.h> header
From: Linus Walleij @ 2012-10-19 9:58 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20121016004741.485430a1c243795488fbc69b@mail.ru>
On Mon, Oct 15, 2012 at 10:47 PM, Alexander Shiyan <shc_work@mail.ru> wrote:
>> We don't want to add new files to <mach/*>.
> OK, but since it will be used only in board code, I reintroduce it
> in arch/arm/mach-clps711x/common.h. On my opinion is no reason to
> overload include/linux/*.
Sure. That's even better.
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH v2] ARM: SMP_TWD: make setup()/stop() reentrant
From: Linus Walleij @ 2012-10-19 9:56 UTC (permalink / raw)
To: linux-arm-kernel
This makes the SMP_TWD clock .setup()/.stop() pair reentrant by
not re-fetching the clk and re-registering the clock event every
time .setup() is called. We also make sure to call the
clk_enable()/clk_disable() pair on subsequent calls.
As it has been brought to my knowledge that this pair is going
to be called from atomic contexts for CPU clusters coming and
going, the clk_prepare()/clk_unprepare() calls cannot be called
on subsequent .setup()/.stop() iterations.
The patch assumes that the code will make sure that
twd_set_mode() is called through .set_mode() on the clock
event *after* the .setup() call, so that the timer registers
are fully re-programmed after a new .setup() cycle.
Cc: Shawn Guo <shawn.guo@linaro.org>
Reported-by: Peter Chen <peter.chen@freescale.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
Peter/Shawn: can you please respond with a Tested-by from your
system(s) to indicate if this works as expected?
---
arch/arm/kernel/smp_twd.c | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c
index b92d524..229231a 100644
--- a/arch/arm/kernel/smp_twd.c
+++ b/arch/arm/kernel/smp_twd.c
@@ -31,6 +31,7 @@ static void __iomem *twd_base;
static struct clk *twd_clk;
static unsigned long twd_timer_rate;
+static bool initial_setup_called;
static struct clock_event_device __percpu **twd_evt;
static int twd_ppi;
@@ -93,6 +94,8 @@ static void twd_timer_stop(struct clock_event_device *clk)
{
twd_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
disable_percpu_irq(clk->irq);
+ if (!IS_ERR(twd_clk))
+ clk_disable(twd_clk);
}
#ifdef CONFIG_COMMON_CLK
@@ -265,8 +268,21 @@ static int __cpuinit twd_timer_setup(struct clock_event_device *clk)
{
struct clock_event_device **this_cpu_clk;
- if (!twd_clk)
- twd_clk = twd_get_clock();
+ /*
+ * If the basic setup has been done before, don't bother
+ * with yet again looking up the clock and register the clock
+ * source.
+ */
+ if (initial_setup_called) {
+ if (!IS_ERR(twd_clk))
+ clk_enable(twd_clk);
+ __raw_writel(0, twd_base + TWD_TIMER_CONTROL);
+ enable_percpu_irq(clk->irq, 0);
+ return 0;
+ }
+ initial_setup_called = true;
+
+ twd_clk = twd_get_clock();
if (!IS_ERR_OR_NULL(twd_clk))
twd_timer_rate = clk_get_rate(twd_clk);
--
1.7.11.7
^ permalink raw reply related
* Do we need to fix below dump during cpu hot plug operation?
From: Linus Walleij @ 2012-10-19 9:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20121019094027.GF21164@n2100.arm.linux.org.uk>
On Fri, Oct 19, 2012 at 11:40 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Fri, Oct 19, 2012 at 11:37:48AM +0200, Linus Walleij wrote:
>> @@ -93,6 +94,8 @@ static void twd_timer_stop(struct clock_event_device *clk)
>> {
>> twd_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
>> disable_percpu_irq(clk->irq);
>> + if (twd_clk)
>> + clk_disable(twd_clk);
>
> While we're here, can we please ensure that the CLK API is properly
> respected, and use IS_ERR(twd_clk) to determine of the clock is valid
> or not (and not use IS_ERR_OR_NULL()).
OK I'll refine this thing if it works for Freescale.
I'm under the impression that the use of IS_ERR_OR_NULL() in the
setup() section below the changed code is correct however, since it'll
catch the situation where the clock API is disabled (and returns rate
0) and then proceed to calibrate the rate properly?
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH 2/2] ARM: SMP_TWD: use clk_prepare_enable()
From: Linus Walleij @ 2012-10-19 9:47 UTC (permalink / raw)
To: linux-arm-kernel
A minor code refactoring saving a few lines by merging prepare()
and enable() calls.
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
arch/arm/kernel/smp_twd.c | 12 ++----------
1 file changed, 2 insertions(+), 10 deletions(-)
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c
index b22d700..b92d524 100644
--- a/arch/arm/kernel/smp_twd.c
+++ b/arch/arm/kernel/smp_twd.c
@@ -248,17 +248,9 @@ static struct clk *twd_get_clock(void)
return clk;
}
- err = clk_prepare(clk);
+ err = clk_prepare_enable(clk);
if (err) {
- pr_err("smp_twd: clock failed to prepare: %d\n", err);
- clk_put(clk);
- return ERR_PTR(err);
- }
-
- err = clk_enable(clk);
- if (err) {
- pr_err("smp_twd: clock failed to enable: %d\n", err);
- clk_unprepare(clk);
+ pr_err("smp_twd: clock failed to prepare+enable: %d\n", err);
clk_put(clk);
return ERR_PTR(err);
}
--
1.7.11.7
^ permalink raw reply related
* [PATCH 1/1] arm: KGDB support for PXA
From: Haojian Zhuang @ 2012-10-19 9:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAHod+Gf9t6uhLvVc+yJR4vVjJA1pRq-htm1Pd_Rbb+kFkO43jw@mail.gmail.com>
On Thu, Oct 18, 2012 at 5:38 PM, Marko Kati? <dromede@gmail.com> wrote:
> I'm still not seeing this patch in arch-pxa, tty or mainline trees.
> Has it been forgotten again? Is there a problem with this patch that
> needs to be fixed?
Applied into arch-pxa now.
Greg,
If you have any comment, please let me know.
Regards
Haojian
^ permalink raw reply
* [PATCH 1/2] ARM: SMP_TWD: use DIV_ROUND_CLOSEST() for periodic mode
From: Linus Walleij @ 2012-10-19 9:47 UTC (permalink / raw)
To: linux-arm-kernel
The periodic mode is currently calculated by a simple division
but we should pay more attention to our integer arithmetics.
Also delete a comment that does not make any sense.
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
arch/arm/kernel/smp_twd.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c
index e1f9069..b22d700 100644
--- a/arch/arm/kernel/smp_twd.c
+++ b/arch/arm/kernel/smp_twd.c
@@ -42,10 +42,10 @@ static void twd_set_mode(enum clock_event_mode mode,
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
- /* timer load already set up */
ctrl = TWD_TIMER_CONTROL_ENABLE | TWD_TIMER_CONTROL_IT_ENABLE
| TWD_TIMER_CONTROL_PERIODIC;
- __raw_writel(twd_timer_rate / HZ, twd_base + TWD_TIMER_LOAD);
+ __raw_writel(DIV_ROUND_CLOSEST(twd_timer_rate, HZ),
+ twd_base + TWD_TIMER_LOAD);
break;
case CLOCK_EVT_MODE_ONESHOT:
/* period set, and timer enabled in 'next_event' hook */
--
1.7.11.7
^ permalink raw reply related
* [PATCH 5/6] ARM: OMAP2+: Make some definitions local
From: Laurent Pinchart @ 2012-10-19 9:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20121018202848.11834.17893.stgit@muffinssi.local>
Hi Tony,
On Thursday 18 October 2012 13:28:48 Tony Lindgren wrote:
> From: Ido Yariv <ido@wizery.com>
>
> Move some of the definitions in omap-iommu.h that can be made local to
> either drivers/iommu.
>
> Cc: Joerg Roedel <joerg.roedel@amd.com>
> Cc: Ohad Ben-Cohen <ohad@wizery.com>
> Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Cc: Mauro Carvalho Chehab <mchehab@infradead.org>
> Cc: Omar Ramirez Luna <omar.luna@linaro.org>
> Signed-off-by: Ido Yariv <ido@wizery.com>
> [tony at atomide.com: updated for header changes in the series]
> Signed-off-by: Tony Lindgren <tony@atomide.com>
> ---
> drivers/iommu/omap-iommu.c | 15 +++++++++++++++
> drivers/iommu/omap-iommu.h | 33 +++------------------------------
> drivers/iommu/omap-iommu2.c | 6 ++++++
> 3 files changed, 24 insertions(+), 30 deletions(-)
>
> diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c
> index 4db86e1..df84087 100644
> --- a/drivers/iommu/omap-iommu.c
> +++ b/drivers/iommu/omap-iommu.c
> @@ -54,6 +54,21 @@ struct omap_iommu_domain {
> spinlock_t lock;
> };
>
> +#define MMU_LOCK_BASE_SHIFT 10
> +#define MMU_LOCK_BASE_MASK (0x1f << MMU_LOCK_BASE_SHIFT)
> +#define MMU_LOCK_BASE(x) \
> + ((x & MMU_LOCK_BASE_MASK) >> MMU_LOCK_BASE_SHIFT)
> +
> +#define MMU_LOCK_VICT_SHIFT 4
> +#define MMU_LOCK_VICT_MASK (0x1f << MMU_LOCK_VICT_SHIFT)
> +#define MMU_LOCK_VICT(x) \
> + ((x & MMU_LOCK_VICT_MASK) >> MMU_LOCK_VICT_SHIFT)
> +
> +struct iotlb_lock {
> + short base;
> + short vict;
> +};
> +
> /* accommodate the difference between omap1 and omap2/3 */
> static const struct iommu_functions *arch_iommu;
>
> diff --git a/drivers/iommu/omap-iommu.h b/drivers/iommu/omap-iommu.h
> index 8c3378d..2b5f3c0 100644
> --- a/drivers/iommu/omap-iommu.h
> +++ b/drivers/iommu/omap-iommu.h
> @@ -72,11 +72,6 @@ struct cr_regs {
> };
> };
>
> -struct iotlb_lock {
> - short base;
> - short vict;
> -};
> -
> /* architecture specific functions */
> struct iommu_functions {
> unsigned long version;
> @@ -117,13 +112,6 @@ static inline struct omap_iommu
> *dev_to_omap_iommu(struct device *dev) }
> #endif
>
> -/* IOMMU errors */
> -#define OMAP_IOMMU_ERR_TLB_MISS (1 << 0)
> -#define OMAP_IOMMU_ERR_TRANS_FAULT (1 << 1)
> -#define OMAP_IOMMU_ERR_EMU_MISS (1 << 2)
> -#define OMAP_IOMMU_ERR_TBLWALK_FAULT (1 << 3)
> -#define OMAP_IOMMU_ERR_MULTIHIT_FAULT (1 << 4)
> -
I'll use those in the tidspbridge driver, in patches that I plan to push soon.
I will apply this patch set on top of mine, see what breaks. Would you like me
to propose a modified version of this set, or add additional patches in my set
?
> /*
> * MMU Register offsets
> */
> @@ -151,16 +139,6 @@ static inline struct omap_iommu
> *dev_to_omap_iommu(struct device *dev) /*
> * MMU Register bit definitions
> */
> -#define MMU_LOCK_BASE_SHIFT 10
> -#define MMU_LOCK_BASE_MASK (0x1f << MMU_LOCK_BASE_SHIFT)
> -#define MMU_LOCK_BASE(x) \
> - ((x & MMU_LOCK_BASE_MASK) >> MMU_LOCK_BASE_SHIFT)
> -
> -#define MMU_LOCK_VICT_SHIFT 4
> -#define MMU_LOCK_VICT_MASK (0x1f << MMU_LOCK_VICT_SHIFT)
> -#define MMU_LOCK_VICT(x) \
> - ((x & MMU_LOCK_VICT_MASK) >> MMU_LOCK_VICT_SHIFT)
> -
> #define MMU_CAM_VATAG_SHIFT 12
> #define MMU_CAM_VATAG_MASK \
> ((~0UL >> MMU_CAM_VATAG_SHIFT) << MMU_CAM_VATAG_SHIFT)
> @@ -222,20 +200,15 @@ extern void omap_iotlb_cr_to_e(struct cr_regs *cr,
> struct iotlb_entry *e); extern int
> omap_iopgtable_store_entry(struct omap_iommu *obj, struct iotlb_entry *e);
>
> -extern int omap_iommu_set_isr(const char *name,
> - int (*isr)(struct omap_iommu *obj, u32 da, u32 iommu_errs,
> - void *priv),
> - void *isr_priv);
> -
> extern void omap_iommu_save_ctx(struct device *dev);
> extern void omap_iommu_restore_ctx(struct device *dev);
>
> -extern int omap_install_iommu_arch(const struct iommu_functions *ops);
> -extern void omap_uninstall_iommu_arch(const struct iommu_functions *ops);
> -
> extern int omap_foreach_iommu_device(void *data,
> int (*fn)(struct device *, void *));
>
> +extern int omap_install_iommu_arch(const struct iommu_functions *ops);
> +extern void omap_uninstall_iommu_arch(const struct iommu_functions *ops);
> +
> extern ssize_t
> omap_iommu_dump_ctx(struct omap_iommu *obj, char *buf, ssize_t len);
> extern size_t
> diff --git a/drivers/iommu/omap-iommu2.c b/drivers/iommu/omap-iommu2.c
> index 066e6b2..4d8d91d 100644
> --- a/drivers/iommu/omap-iommu2.c
> +++ b/drivers/iommu/omap-iommu2.c
> @@ -68,6 +68,12 @@
> ((pgsz) == MMU_CAM_PGSZ_64K) ? 0xffff0000 : \
> ((pgsz) == MMU_CAM_PGSZ_4K) ? 0xfffff000 : 0)
>
> +/* IOMMU errors */
> +#define OMAP_IOMMU_ERR_TLB_MISS (1 << 0)
> +#define OMAP_IOMMU_ERR_TRANS_FAULT (1 << 1)
> +#define OMAP_IOMMU_ERR_EMU_MISS (1 << 2)
> +#define OMAP_IOMMU_ERR_TBLWALK_FAULT (1 << 3)
> +#define OMAP_IOMMU_ERR_MULTIHIT_FAULT (1 << 4)
>
> static void __iommu_set_twl(struct omap_iommu *obj, bool on)
> {
--
Regards,
Laurent Pinchart
^ permalink raw reply
* [PATCH 3/6] ARM: OMAP2+: Move plat/iovmm.h to include/linux/omap-iommu.h
From: Laurent Pinchart @ 2012-10-19 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20121018202842.11834.14375.stgit@muffinssi.local>
Hi Tony,
Thanks for the patch.
On Thursday 18 October 2012 13:28:42 Tony Lindgren wrote:
> Looks like the iommu framework does not have generic functions
> exported for all the needs yet. The hardware specific functions
> are defined in files like intel-iommu.h and amd-iommu.h. Follow
> the same standard for omap-iommu.h.
>
> This is needed because we are removing plat and mach includes
> for ARM common zImage support. Further work should continue
> in the iommu framework context as only pure platform data will
> be communicated from arch/arm/*omap*/* code to the iommu
> framework.
>
> Cc: Joerg Roedel <joerg.roedel@amd.com>
> Cc: Ohad Ben-Cohen <ohad@wizery.com>
> Cc: Ido Yariv <ido@wizery.com>
> Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Cc: Mauro Carvalho Chehab <mchehab@infradead.org>
> Cc: Omar Ramirez Luna <omar.luna@linaro.org>
> Cc: linux-media at vger.kernel.org
> Signed-off-by: Tony Lindgren <tony@atomide.com>
> ---
> arch/arm/mach-omap2/iommu2.c | 1
> arch/arm/plat-omap/include/plat/iommu.h | 10 +--
> arch/arm/plat-omap/include/plat/iovmm.h | 89 -------------------------
> drivers/iommu/omap-iommu-debug.c | 2 -
> drivers/iommu/omap-iommu.c | 1
> drivers/iommu/omap-iovmm.c | 46 ++++++++++++++
> drivers/media/platform/omap3isp/isp.c | 1
> drivers/media/platform/omap3isp/isp.h | 2 -
> drivers/media/platform/omap3isp/ispccdc.c | 1
> drivers/media/platform/omap3isp/ispstat.c | 1
> drivers/media/platform/omap3isp/ispvideo.c | 2 -
> include/linux/omap-iommu.h | 47 +++++++++++++++
> 12 files changed, 101 insertions(+), 102 deletions(-)
> delete mode 100644 arch/arm/plat-omap/include/plat/iovmm.h
> create mode 100644 include/linux/omap-iommu.h
>
> diff --git a/arch/arm/mach-omap2/iommu2.c b/arch/arm/mach-omap2/iommu2.c
> index eefc379..77cbf2f 100644
> --- a/arch/arm/mach-omap2/iommu2.c
> +++ b/arch/arm/mach-omap2/iommu2.c
> @@ -17,6 +17,7 @@
> #include <linux/module.h>
> #include <linux/slab.h>
> #include <linux/stringify.h>
> +#include <linux/omap-iommu.h>
Nitpicking, please keep the headers sorted alphabetically, here and in all
locations below (especially the OMAP3 ISP driver).
(OK, there's already one misplaced #include, but let's not make it worse :-))
> #include <plat/iommu.h>
>
> diff --git a/arch/arm/plat-omap/include/plat/iommu.h
> b/arch/arm/plat-omap/include/plat/iommu.h index 7e8c7b6..a4b71b1 100644
> --- a/arch/arm/plat-omap/include/plat/iommu.h
> +++ b/arch/arm/plat-omap/include/plat/iommu.h
> @@ -216,13 +216,10 @@ static inline struct omap_iommu
> *dev_to_omap_iommu(struct device *dev) #define MMU_RAM_PADDR_SHIFT 12
> #define MMU_RAM_PADDR_MASK \
> ((~0UL >> MMU_RAM_PADDR_SHIFT) << MMU_RAM_PADDR_SHIFT)
> -#define MMU_RAM_ENDIAN_SHIFT 9
> +
> #define MMU_RAM_ENDIAN_MASK (1 << MMU_RAM_ENDIAN_SHIFT)
> -#define MMU_RAM_ENDIAN_BIG (1 << MMU_RAM_ENDIAN_SHIFT)
> -#define MMU_RAM_ENDIAN_LITTLE (0 << MMU_RAM_ENDIAN_SHIFT)
> -#define MMU_RAM_ELSZ_SHIFT 7
> #define MMU_RAM_ELSZ_MASK (3 << MMU_RAM_ELSZ_SHIFT)
> -#define MMU_RAM_ELSZ_8 (0 << MMU_RAM_ELSZ_SHIFT)
> +
> #define MMU_RAM_ELSZ_16 (1 << MMU_RAM_ELSZ_SHIFT)
> #define MMU_RAM_ELSZ_32 (2 << MMU_RAM_ELSZ_SHIFT)
> #define MMU_RAM_ELSZ_NONE (3 << MMU_RAM_ELSZ_SHIFT)
I plan to push cleanup patches for the staging tidspbridge driver that get rid
of the local register definitions and use plat/iommu.h instead. That's
obviously an interim solution as in the long run the driver should use the
IOMMU API, but in the meantime it's a step in the right direction. Would it
then make sense to move all those definitions to include/linux/omap-iommu.h,
not just the ones used by the OMAP3 ISP driver ?
> @@ -269,9 +266,6 @@ extern int omap_iommu_set_isr(const char *name,
> void *priv),
> void *isr_priv);
>
> -extern void omap_iommu_save_ctx(struct device *dev);
> -extern void omap_iommu_restore_ctx(struct device *dev);
> -
> extern int omap_install_iommu_arch(const struct iommu_functions *ops);
> extern void omap_uninstall_iommu_arch(const struct iommu_functions *ops);
>
> diff --git a/arch/arm/plat-omap/include/plat/iovmm.h
> b/arch/arm/plat-omap/include/plat/iovmm.h deleted file mode 100644
> index 498e57c..0000000
> --- a/arch/arm/plat-omap/include/plat/iovmm.h
> +++ /dev/null
> @@ -1,89 +0,0 @@
> -/*
> - * omap iommu: simple virtual address space management
> - *
> - * Copyright (C) 2008-2009 Nokia Corporation
> - *
> - * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License version 2 as
> - * published by the Free Software Foundation.
> - */
> -
> -#ifndef __IOMMU_MMAP_H
> -#define __IOMMU_MMAP_H
> -
> -#include <linux/iommu.h>
> -
> -struct iovm_struct {
> - struct omap_iommu *iommu; /* iommu object which this belongs to */
> - u32 da_start; /* area definition */
> - u32 da_end;
> - u32 flags; /* IOVMF_: see below */
> - struct list_head list; /* linked in ascending order */
> - const struct sg_table *sgt; /* keep 'page' <-> 'da' mapping */
> - void *va; /* mpu side mapped address */
> -};
> -
> -/*
> - * IOVMF_FLAGS: attribute for iommu virtual memory area(iovma)
> - *
> - * lower 16 bit is used for h/w and upper 16 bit is for s/w.
> - */
> -#define IOVMF_SW_SHIFT 16
> -
> -/*
> - * iovma: h/w flags derived from cam and ram attribute
> - */
> -#define IOVMF_CAM_MASK (~((1 << 10) - 1))
> -#define IOVMF_RAM_MASK (~IOVMF_CAM_MASK)
> -
> -#define IOVMF_PGSZ_MASK (3 << 0)
> -#define IOVMF_PGSZ_1M MMU_CAM_PGSZ_1M
> -#define IOVMF_PGSZ_64K MMU_CAM_PGSZ_64K
> -#define IOVMF_PGSZ_4K MMU_CAM_PGSZ_4K
> -#define IOVMF_PGSZ_16M MMU_CAM_PGSZ_16M
> -
> -#define IOVMF_ENDIAN_MASK (1 << 9)
> -#define IOVMF_ENDIAN_BIG MMU_RAM_ENDIAN_BIG
> -#define IOVMF_ENDIAN_LITTLE MMU_RAM_ENDIAN_LITTLE
> -
> -#define IOVMF_ELSZ_MASK (3 << 7)
> -#define IOVMF_ELSZ_8 MMU_RAM_ELSZ_8
> -#define IOVMF_ELSZ_16 MMU_RAM_ELSZ_16
> -#define IOVMF_ELSZ_32 MMU_RAM_ELSZ_32
> -#define IOVMF_ELSZ_NONE MMU_RAM_ELSZ_NONE
> -
> -#define IOVMF_MIXED_MASK (1 << 6)
> -#define IOVMF_MIXED MMU_RAM_MIXED
> -
> -/*
> - * iovma: s/w flags, used for mapping and umapping internally.
> - */
> -#define IOVMF_MMIO (1 << IOVMF_SW_SHIFT)
> -#define IOVMF_ALLOC (2 << IOVMF_SW_SHIFT)
> -#define IOVMF_ALLOC_MASK (3 << IOVMF_SW_SHIFT)
> -
> -/* "superpages" is supported just with physically linear pages */
> -#define IOVMF_DISCONT (1 << (2 + IOVMF_SW_SHIFT))
> -#define IOVMF_LINEAR (2 << (2 + IOVMF_SW_SHIFT))
> -#define IOVMF_LINEAR_MASK (3 << (2 + IOVMF_SW_SHIFT))
> -
> -#define IOVMF_DA_FIXED (1 << (4 + IOVMF_SW_SHIFT))
> -
> -
> -extern struct iovm_struct *omap_find_iovm_area(struct device *dev, u32 da);
> -extern u32
> -omap_iommu_vmap(struct iommu_domain *domain, struct device *dev, u32 da,
> - const struct sg_table *sgt, u32 flags);
> -extern struct sg_table *omap_iommu_vunmap(struct iommu_domain *domain,
> - struct device *dev, u32 da);
> -extern u32
> -omap_iommu_vmalloc(struct iommu_domain *domain, struct device *dev,
> - u32 da, size_t bytes, u32 flags);
> -extern void
> -omap_iommu_vfree(struct iommu_domain *domain, struct device *dev,
> - const u32 da);
> -extern void *omap_da_to_va(struct device *dev, u32 da);
> -
> -#endif /* __IOMMU_MMAP_H */
> diff --git a/drivers/iommu/omap-iommu-debug.c
> b/drivers/iommu/omap-iommu-debug.c index 0cac372..cf4a0b5 100644
> --- a/drivers/iommu/omap-iommu-debug.c
> +++ b/drivers/iommu/omap-iommu-debug.c
> @@ -18,9 +18,9 @@
> #include <linux/uaccess.h>
> #include <linux/platform_device.h>
> #include <linux/debugfs.h>
> +#include <linux/omap-iommu.h>
>
> #include <plat/iommu.h>
> -#include <plat/iovmm.h>
>
> #include "omap-iopgtable.h"
>
> diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c
> index f2bbfb0..eadcfde 100644
> --- a/drivers/iommu/omap-iommu.c
> +++ b/drivers/iommu/omap-iommu.c
> @@ -19,6 +19,7 @@
> #include <linux/clk.h>
> #include <linux/platform_device.h>
> #include <linux/iommu.h>
> +#include <linux/omap-iommu.h>
> #include <linux/mutex.h>
> #include <linux/spinlock.h>
>
> diff --git a/drivers/iommu/omap-iovmm.c b/drivers/iommu/omap-iovmm.c
> index b332392..9852101 100644
> --- a/drivers/iommu/omap-iovmm.c
> +++ b/drivers/iommu/omap-iovmm.c
> @@ -17,15 +17,59 @@
> #include <linux/device.h>
> #include <linux/scatterlist.h>
> #include <linux/iommu.h>
> +#include <linux/omap-iommu.h>
>
> #include <asm/cacheflush.h>
> #include <asm/mach/map.h>
>
> #include <plat/iommu.h>
> -#include <plat/iovmm.h>
>
> #include "omap-iopgtable.h"
>
> +/*
> + * IOVMF_FLAGS: attribute for iommu virtual memory area(iovma)
> + *
> + * lower 16 bit is used for h/w and upper 16 bit is for s/w.
> + */
> +#define IOVMF_SW_SHIFT 16
> +
> +/*
> + * iovma: h/w flags derived from cam and ram attribute
> + */
> +#define IOVMF_CAM_MASK (~((1 << 10) - 1))
> +#define IOVMF_RAM_MASK (~IOVMF_CAM_MASK)
> +
> +#define IOVMF_PGSZ_MASK (3 << 0)
> +#define IOVMF_PGSZ_1M MMU_CAM_PGSZ_1M
> +#define IOVMF_PGSZ_64K MMU_CAM_PGSZ_64K
> +#define IOVMF_PGSZ_4K MMU_CAM_PGSZ_4K
> +#define IOVMF_PGSZ_16M MMU_CAM_PGSZ_16M
> +
> +#define IOVMF_ENDIAN_MASK (1 << 9)
> +#define IOVMF_ENDIAN_BIG MMU_RAM_ENDIAN_BIG
> +
> +#define IOVMF_ELSZ_MASK (3 << 7)
> +#define IOVMF_ELSZ_16 MMU_RAM_ELSZ_16
> +#define IOVMF_ELSZ_32 MMU_RAM_ELSZ_32
> +#define IOVMF_ELSZ_NONE MMU_RAM_ELSZ_NONE
> +
> +#define IOVMF_MIXED_MASK (1 << 6)
> +#define IOVMF_MIXED MMU_RAM_MIXED
> +
> +/*
> + * iovma: s/w flags, used for mapping and umapping internally.
> + */
> +#define IOVMF_MMIO (1 << IOVMF_SW_SHIFT)
> +#define IOVMF_ALLOC (2 << IOVMF_SW_SHIFT)
> +#define IOVMF_ALLOC_MASK (3 << IOVMF_SW_SHIFT)
> +
> +/* "superpages" is supported just with physically linear pages */
> +#define IOVMF_DISCONT (1 << (2 + IOVMF_SW_SHIFT))
> +#define IOVMF_LINEAR (2 << (2 + IOVMF_SW_SHIFT))
> +#define IOVMF_LINEAR_MASK (3 << (2 + IOVMF_SW_SHIFT))
> +
> +#define IOVMF_DA_FIXED (1 << (4 + IOVMF_SW_SHIFT))
> +
> static struct kmem_cache *iovm_area_cachep;
>
> /* return the offset of the first scatterlist entry in a sg table */
> diff --git a/drivers/media/platform/omap3isp/isp.c
> b/drivers/media/platform/omap3isp/isp.c index 99640d8..d72bd38 100644
> --- a/drivers/media/platform/omap3isp/isp.c
> +++ b/drivers/media/platform/omap3isp/isp.c
> @@ -66,6 +66,7 @@
> #include <linux/slab.h>
> #include <linux/sched.h>
> #include <linux/vmalloc.h>
> +#include <linux/omap-iommu.h>
>
> #include <media/v4l2-common.h>
> #include <media/v4l2-device.h>
> diff --git a/drivers/media/platform/omap3isp/isp.h
> b/drivers/media/platform/omap3isp/isp.h index 8be7487..50be8c2 100644
> --- a/drivers/media/platform/omap3isp/isp.h
> +++ b/drivers/media/platform/omap3isp/isp.h
> @@ -34,8 +34,6 @@
> #include <linux/platform_device.h>
> #include <linux/wait.h>
> #include <linux/iommu.h>
> -#include <plat/iommu.h>
> -#include <plat/iovmm.h>
>
> #include "ispstat.h"
> #include "ispccdc.h"
> diff --git a/drivers/media/platform/omap3isp/ispccdc.c
> b/drivers/media/platform/omap3isp/ispccdc.c index 60181ab..b2d4976 100644
> --- a/drivers/media/platform/omap3isp/ispccdc.c
> +++ b/drivers/media/platform/omap3isp/ispccdc.c
> @@ -32,6 +32,7 @@
> #include <linux/mm.h>
> #include <linux/sched.h>
> #include <linux/slab.h>
> +#include <linux/omap-iommu.h>
> #include <media/v4l2-event.h>
>
> #include "isp.h"
> diff --git a/drivers/media/platform/omap3isp/ispstat.c
> b/drivers/media/platform/omap3isp/ispstat.c index d7ac76b..e135774 100644
> --- a/drivers/media/platform/omap3isp/ispstat.c
> +++ b/drivers/media/platform/omap3isp/ispstat.c
> @@ -28,6 +28,7 @@
> #include <linux/dma-mapping.h>
> #include <linux/slab.h>
> #include <linux/uaccess.h>
> +#include <linux/omap-iommu.h>
>
> #include "isp.h"
>
> diff --git a/drivers/media/platform/omap3isp/ispvideo.c
> b/drivers/media/platform/omap3isp/ispvideo.c index a0b737fe..2853bef 100644
> --- a/drivers/media/platform/omap3isp/ispvideo.c
> +++ b/drivers/media/platform/omap3isp/ispvideo.c
> @@ -32,10 +32,10 @@
> #include <linux/sched.h>
> #include <linux/slab.h>
> #include <linux/vmalloc.h>
> +#include <linux/omap-iommu.h>
> #include <media/v4l2-dev.h>
> #include <media/v4l2-ioctl.h>
> #include <plat/iommu.h>
> -#include <plat/iovmm.h>
> #include <plat/omap-pm.h>
>
> #include "ispvideo.h"
> diff --git a/include/linux/omap-iommu.h b/include/linux/omap-iommu.h
> new file mode 100644
> index 0000000..bd12665
> --- /dev/null
> +++ b/include/linux/omap-iommu.h
> @@ -0,0 +1,47 @@
> +/*
> + * omap iommu: simple virtual address space management
> + *
> + * Copyright (C) 2008-2009 Nokia Corporation
> + *
> + * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
Missing #ifndef #define ... #endif
> +
> +struct iovm_struct {
> + struct omap_iommu *iommu; /* iommu object which this belongs to */
> + u32 da_start; /* area definition */
> + u32 da_end;
> + u32 flags; /* IOVMF_: see below */
> + struct list_head list; /* linked in ascending order */
> + const struct sg_table *sgt; /* keep 'page' <-> 'da' mapping */
> + void *va; /* mpu side mapped address */
> +};
> +
> +#define MMU_RAM_ENDIAN_SHIFT 9
> +#define MMU_RAM_ENDIAN_LITTLE (0 << MMU_RAM_ENDIAN_SHIFT)
> +#define MMU_RAM_ELSZ_8 (0 << MMU_RAM_ELSZ_SHIFT)
> +#define IOVMF_ENDIAN_LITTLE MMU_RAM_ENDIAN_LITTLE
> +#define MMU_RAM_ELSZ_SHIFT 7
> +#define IOVMF_ELSZ_8 MMU_RAM_ELSZ_8
Shouldn't this header be split in include/linux/omap-iommu.h and
include/linux/omap-iovmm.h ? I would also move all the hardware IOVMF flags to
include/linux/omap-iovmm.h, not just the two currently used by the OMAP3 ISP
driver. The software flags can be kept in drivers/iommu/omap-iovmm.c.
> +struct iommu_domain;
> +
> +extern struct iovm_struct *omap_find_iovm_area(struct device *dev, u32 da);
> +extern u32
> +omap_iommu_vmap(struct iommu_domain *domain, struct device *dev, u32 da,
> + const struct sg_table *sgt, u32 flags);
> +extern struct sg_table *omap_iommu_vunmap(struct iommu_domain *domain,
> + struct device *dev, u32 da);
> +extern u32
> +omap_iommu_vmalloc(struct iommu_domain *domain, struct device *dev,
> + u32 da, size_t bytes, u32 flags);
> +extern void
> +omap_iommu_vfree(struct iommu_domain *domain, struct device *dev,
> + const u32 da);
> +extern void *omap_da_to_va(struct device *dev, u32 da);
> +
> +extern void omap_iommu_save_ctx(struct device *dev);
> +extern void omap_iommu_restore_ctx(struct device *dev);
Do we really need to prefix functions with 'extern' ?
--
Regards,
Laurent Pinchart
^ permalink raw reply
* Do we need to fix below dump during cpu hot plug operation?
From: Russell King - ARM Linux @ 2012-10-19 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CACRpkdaS3bJwnJMJHRSEukiD4r+62h-0HzL7bKF_Ppa4TFy-xw@mail.gmail.com>
On Fri, Oct 19, 2012 at 11:37:48AM +0200, Linus Walleij wrote:
> @@ -93,6 +94,8 @@ static void twd_timer_stop(struct clock_event_device *clk)
> {
> twd_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
> disable_percpu_irq(clk->irq);
> + if (twd_clk)
> + clk_disable(twd_clk);
While we're here, can we please ensure that the CLK API is properly
respected, and use IS_ERR(twd_clk) to determine of the clock is valid
or not (and not use IS_ERR_OR_NULL()).
^ permalink raw reply
* Do we need to fix below dump during cpu hot plug operation?
From: Linus Walleij @ 2012-10-19 9:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20121019045802.GA21078@S2101-09.ap.freescale.net>
On Fri, Oct 19, 2012 at 6:58 AM, Shawn Guo <shawn.guo@linaro.org> wrote:
> Linus, Russell,
> Does it seem to be a valid fix?
Yes, but this patch is very incomplete. It only accounts for handling
the fact that the *twd_clk has already been fetched.
If the clock .setup() and .stop() is going to be called repeatedly, look over
the entire .setup() and .stop() function pair and make it
reentrant, right now it's pure luck if it works.
There are obviously more things that need fixing here.
You need to move this_cpu_clk = __this_cpu_ptr(twd_evt);
and check that before trying to register the clockevent right,
because if setup() is called repeatedly, you will add a new
clockevent every time, which is a massive memory leak.
Isn't the easiest and most straigt-forward fix something
like this:
>From 202c411f3212f74a5d2525ca291b249e1599b64e Mon Sep 17 00:00:00 2001
From: Linus Walleij <linus.walleij@linaro.org>
Date: Fri, 19 Oct 2012 11:30:47 +0200
Subject: [PATCH] ARM: SMP_TWD: make setup()/stop() reentrant
This makes the SMP_TWD clock .setup()/.stop() pair reentrant by
not re-fetching the clk and re-registering the clock event every
time .setup() is called. We also make sure to call the
clk_enable()/clk_disable() pair on subsequent calls.
As it has been brought to my knowledge that this pair is going
to be called from atomic contexts for CPU clusters coming and
going, the clk_prepare()/clk_unprepare() calls cannot be called
on subsequent .setup()/.stop() iterations.
The patch assumes that the code will make sure that
twd_set_mode() is called through .set_mode() on the clock
event *after* the .setup() call, so that the timer registers
are fully re-programmed after a new .setup() cycle.
Reported-by: Peter Chen <peter.chen@freescale.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
arch/arm/kernel/smp_twd.c | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c
index e1f9069..1ac637b 100644
--- a/arch/arm/kernel/smp_twd.c
+++ b/arch/arm/kernel/smp_twd.c
@@ -31,6 +31,7 @@ static void __iomem *twd_base;
static struct clk *twd_clk;
static unsigned long twd_timer_rate;
+static bool initial_setup_called;
static struct clock_event_device __percpu **twd_evt;
static int twd_ppi;
@@ -93,6 +94,8 @@ static void twd_timer_stop(struct clock_event_device *clk)
{
twd_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
disable_percpu_irq(clk->irq);
+ if (twd_clk)
+ clk_disable(twd_clk);
}
#ifdef CONFIG_COMMON_CLK
@@ -273,8 +276,21 @@ static int __cpuinit twd_timer_setup(struct
clock_event_device *clk)
{
struct clock_event_device **this_cpu_clk;
- if (!twd_clk)
- twd_clk = twd_get_clock();
+ /*
+ * If the basic setup has been done before, don't bother
+ * with yet again looking up the clock and register the clock
+ * source.
+ */
+ if (initial_setup_called) {
+ if (twd_clk)
+ clk_enable(twd_clk);
+ __raw_writel(0, twd_base + TWD_TIMER_CONTROL);
+ enable_percpu_irq(clk->irq, 0);
+ return 0;
+ }
+ initial_setup_called = true;
+
+ twd_clk = twd_get_clock();
if (!IS_ERR_OR_NULL(twd_clk))
twd_timer_rate = clk_get_rate(twd_clk);
--
1.7.11.7
Can you please see if this solves your problem?
Yours,
Linus Walleij
^ permalink raw reply related
* [PATCH 3/5] ARM: tegra: fix clock entry of slink controller
From: Laxman Dewangan @ 2012-10-19 9:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <5080862C.9030203@wwwdotorg.org>
On Friday 19 October 2012 04:13 AM, Stephen Warren wrote:
>
> The patch subject isn't entirely accurate here; this patch isn't just
> about fixing clock entries.
>
OK, then will break in two patches.
>
>> + OF_DEV_AUXDATA("nvidia,tegra20-slink", TEGRA_SLINK1_BASE, "spi-tegra-slink.0", NULL),
> Here, can't we just use the existing device names in the clock files...
>
> So we can completely drop the changes to these two files:
>
I want to name the driver as spi-tegra-slink. When we add the sflash spi
driver for tegra20, the driver name will be spi-tegra-sflash.
Also current name is "spi_tegra" I do not want to name with "_". So it
is require to convert as "-".
>> diff --git a/arch/arm/mach-tegra/tegra20_clocks_data.c b/arch/arm/mach-tegra/tegra20_clocks_data.c
>> diff --git a/arch/arm/mach-tegra/tegra30_clocks_data.c b/arch/arm/mach-tegra/tegra30_clocks_data.c
> The device names will hopefully be irrelevant once we're able to use
> DT-based clock lookups.
Agree, but currently we can add this to have progress.
^ permalink raw reply
* [PATCH 2/5] ARM: tegra: dts: add slink controller dt entry
From: Laxman Dewangan @ 2012-10-19 9:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <50808595.8070505@wwwdotorg.org>
On Friday 19 October 2012 04:11 AM, Stephen Warren wrote:
> On 10/18/2012 04:56 AM, Laxman Dewangan wrote:
>> Add slink controller details in the dts file of
>> Tegra20 and Tegra30.
>> diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi
>> + slink at 7000d400 {
>> + compatible = "nvidia,tegra20-slink";
>> + reg =<0x7000d400 0x200>;
>> + interrupts =<0 59 0x04>;
>> + nvidia,dma-req-sel =<15>;
> I thought the common DT DMA bindings were going to be in 3.7, and hence
> we could just use them here rather than inventing another custom
> property for this purpose?
Adding Vinod here.
I looked the dma devicetree bingind document and did not found the
generic binding name. Howvere, for arm-pl330.txt, it is explained as
Example:
pdma0: pdma at 12680000 {
compatible = "arm,pl330", "arm,primecell";
reg = <0x12680000 0x1000>;
interrupts = <99>;
};
Client drivers (device nodes requiring dma transfers from dev-to-mem or
mem-to-dev) should specify the DMA channel numbers using a two-value pair
as shown below.
[property name] = <[phandle of the dma controller] [dma request id]>;
where 'dma request id' is the dma request number which is connected
to the client controller. The 'property name' is recommended to be
of the form <name>-dma-channel.
Example: tx-dma-channel = <&pdma0 12>;
So here I can also add the name like same.
Vinod,
Do you see any issue in follow the above mechanism for Tegra dma client
driver?
Thanks,
Laxman
^ permalink raw reply
* [PATCH 1/5] ARM: tegra: Add slink controller base address
From: Laxman Dewangan @ 2012-10-19 9:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <50808526.1080504@wwwdotorg.org>
On Friday 19 October 2012 04:09 AM, Stephen Warren wrote:
> On 10/18/2012 04:56 AM, Laxman Dewangan wrote:
>> Add base address of all slink controller of Tegra20
>> and tegra30.
> Lets not add anything to iomap.h; we're trying to remove it. Instead,
> just put the raw address in the AUXDATA; I assume that's the only place
> these defines end up being used...
OK, I will drop this change and add the base address definition in the
AUXDATA file only.
^ permalink raw reply
* [PATCH v2 1/5] ARM: bcm476x: Add platform infrastructure
From: Arnd Bergmann @ 2012-10-19 9:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20121018154731.GA9344@glitch>
On Thursday 18 October 2012, Domenico Andreoli wrote:
> On Thu, Oct 18, 2012 at 01:48:01PM +0000, Arnd Bergmann wrote:
> > On Sunday 14 October 2012, Domenico Andreoli wrote:
> +
> +Boards with the BCM4760 SoC shall have the following properties:
> +
> +Required root node property:
> +
> +compatible = "brcm,bcm4760";
> +
> +
> +Boards with the BCM4761 SoC shall have the following properties:
> +
> +Required root node property:
> +
> +compatible = "brcm,bcm4760", "brcm,bcm4761";
>
> and the dt_mach in the board file is left only with "brcm,bcm4760" until
> required otherwise. The same applies to drivers.
Right.
> Does the order matter?
Yes, you have to have the most specific one first, and the most generic
one last, as documented in Documentation/devicetree/booting-without-of.txt.
If one chip has a functionality that the other one doesn't but is otherwise
completely compatible, then the less capable one should be put last.
For the root node, you might actually want to keep both "compatible" strings
separate as you have in the version you posted, at least if the chips are
not completely backwards compatible. For the other devices inside of the
soc, just use one.
> > For the patches that go into different directories like the clk
> > and the clocksource drivers, please Cc the respective subsystem
> > maintainers and ask them for an Ack. It certainly makes sense
> > for a new platform port to get merged through the arm-soc tree,
> > but any future improvements should normally just go through the
> > subsystem trees.
>
> I'd prefer patches but only because I've not any public git repository. If
> the git pull is much more preferred, I surely can manage it.
Ok, no problem. In the long run, it can be useful for you to set up
your own git tree, for for now, we can manage with patches just fine.
Arnd
^ permalink raw reply
* [PATCH 1/2] i2c: mux: Add dt support to i2c-mux-gpio driver
From: Maxime Ripard @ 2012-10-19 8:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <50808481.6040501@wwwdotorg.org>
Le 19/10/2012 00:36, Stephen Warren a ?crit :
> On 10/18/2012 08:13 AM, Maxime Ripard wrote:
>> Allow the i2c-mux-gpio to be used by a device tree enabled device. The
>> bindings are inspired by the one found in the i2c-mux-pinctrl driver.
>
>> +++ b/Documentation/devicetree/bindings/i2c/i2c-mux-gpio.txt
>
>> +Optional properties:
>> +- idle-state: value to set to the muxer when idle. When no value is
>> + given, it defaults to the first value in the array.
>
> That's inconsistent with the following text that appears later (and
> describes what the driver actually does):
Ah, right, sorry
Maxime
--
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
^ permalink raw reply
* [PATCH V2 7/7] ARM: tegra30: cpuidle: add LP2 driver for CPU0
From: Joseph Lo @ 2012-10-19 8:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1350636526-25920-1-git-send-email-josephl@nvidia.com>
The cpuidle LP2 is a power gating idle mode. It support power gating
vdd_cpu rail after all cpu cores in LP2. For Tegra30, the CPU0 must
be last one to go into LP2. We need to take care and make sure whole
secondary CPUs were in LP2 by checking CPU and power gate status.
After that, the CPU0 can go into LP2 safely. Then power gating the
CPU rail.
Base on the work by:
Scott Williams <scwilliams@nvidia.com>
Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
V2:
* refine the pclk usage in "set_power_timers"
arch/arm/mach-tegra/cpuidle-tegra30.c | 41 +++++++++-
arch/arm/mach-tegra/pm.c | 145 +++++++++++++++++++++++++++++++++
arch/arm/mach-tegra/pm.h | 3 +
arch/arm/mach-tegra/sleep-tegra30.S | 44 ++++++++++
arch/arm/mach-tegra/sleep.S | 42 ++++++++++
arch/arm/mach-tegra/sleep.h | 2 +
6 files changed, 273 insertions(+), 4 deletions(-)
diff --git a/arch/arm/mach-tegra/cpuidle-tegra30.c b/arch/arm/mach-tegra/cpuidle-tegra30.c
index b574abd..317dc28 100644
--- a/arch/arm/mach-tegra/cpuidle-tegra30.c
+++ b/arch/arm/mach-tegra/cpuidle-tegra30.c
@@ -32,6 +32,7 @@
#include "pm.h"
#include "sleep.h"
+#include "tegra_cpu_car.h"
#ifdef CONFIG_PM_SLEEP
static int tegra30_idle_lp2(struct cpuidle_device *dev,
@@ -67,6 +68,28 @@ static struct cpuidle_driver tegra_idle_driver = {
static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device);
#ifdef CONFIG_PM_SLEEP
+static bool tegra30_idle_enter_lp2_cpu_0(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ struct cpuidle_state *state = &drv->states[index];
+ u32 cpu_on_time = state->exit_latency;
+ u32 cpu_off_time = state->target_residency - state->exit_latency;
+
+ if (num_online_cpus() > 1 && !tegra_cpu_rail_off_ready()) {
+ cpu_do_idle();
+ return false;
+ }
+
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
+
+ tegra_idle_lp2_last(cpu_on_time, cpu_off_time);
+
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
+
+ return true;
+}
+
#ifdef CONFIG_SMP
static bool tegra30_idle_enter_lp2_cpu_n(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
@@ -101,16 +124,22 @@ static int __cpuinit tegra30_idle_lp2(struct cpuidle_device *dev,
{
u32 cpu = is_smp() ? cpu_logical_map(dev->cpu) : dev->cpu;
bool entered_lp2 = false;
+ bool last_cpu;
local_fiq_disable();
- tegra_set_cpu_in_lp2(cpu);
+ last_cpu = tegra_set_cpu_in_lp2(cpu);
cpu_pm_enter();
- if (cpu == 0)
- cpu_do_idle();
- else
+ if (cpu == 0) {
+ if (last_cpu)
+ entered_lp2 = tegra30_idle_enter_lp2_cpu_0(dev, drv,
+ index);
+ else
+ cpu_do_idle();
+ } else {
entered_lp2 = tegra30_idle_enter_lp2_cpu_n(dev, drv, index);
+ }
cpu_pm_exit();
tegra_clear_cpu_in_lp2(cpu);
@@ -130,6 +159,10 @@ int __init tegra30_cpuidle_init(void)
struct cpuidle_device *dev;
struct cpuidle_driver *drv = &tegra_idle_driver;
+#ifdef CONFIG_PM_SLEEP
+ tegra_tear_down_cpu = tegra30_tear_down_cpu;
+#endif
+
ret = cpuidle_register_driver(&tegra_idle_driver);
if (ret) {
pr_err("CPUidle driver registration failed\n");
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index 39ee557..f899b06 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -20,14 +20,37 @@
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/cpumask.h>
+#include <linux/delay.h>
+#include <linux/cpu_pm.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <asm/smp_plat.h>
+#include <asm/cacheflush.h>
+#include <asm/suspend.h>
+#include <asm/idmap.h>
+#include <asm/proc-fns.h>
+#include <asm/tlbflush.h>
#include <mach/iomap.h>
#include "reset.h"
+#include "flowctrl.h"
+#include "sleep.h"
+#include "tegra_cpu_car.h"
+
+#define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */
+
+#define PMC_CTRL 0x0
+#define PMC_CPUPWRGOOD_TIMER 0xc8
+#define PMC_CPUPWROFF_TIMER 0xcc
#ifdef CONFIG_PM_SLEEP
static unsigned int g_diag_reg;
static DEFINE_SPINLOCK(tegra_lp2_lock);
+static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+static struct clk *tegra_pclk;
+void (*tegra_tear_down_cpu)(void);
void save_cpu_arch_register(void)
{
@@ -43,6 +66,89 @@ void restore_cpu_arch_register(void)
return;
}
+static void set_power_timers(unsigned long us_on, unsigned long us_off)
+{
+ unsigned long long ticks;
+ unsigned long long pclk;
+ unsigned long rate;
+ static unsigned long tegra_last_pclk;
+
+ if (tegra_pclk == NULL) {
+ tegra_pclk = clk_get_sys(NULL, "pclk");
+ WARN_ON(IS_ERR(tegra_pclk));
+ }
+
+ rate = clk_get_rate(tegra_pclk);
+
+ if (WARN_ON_ONCE(rate <= 0))
+ pclk = 100000000;
+ else
+ pclk = rate;
+
+ if ((rate != tegra_last_pclk)) {
+ ticks = (us_on * pclk) + 999999ull;
+ do_div(ticks, 1000000);
+ writel((unsigned long)ticks, pmc + PMC_CPUPWRGOOD_TIMER);
+
+ ticks = (us_off * pclk) + 999999ull;
+ do_div(ticks, 1000000);
+ writel((unsigned long)ticks, pmc + PMC_CPUPWROFF_TIMER);
+ wmb();
+ }
+ tegra_last_pclk = pclk;
+}
+
+/*
+ * restore_cpu_complex
+ *
+ * restores cpu clock setting, clears flow controller
+ *
+ * Always called on CPU 0.
+ */
+static void restore_cpu_complex(void)
+{
+ int cpu = smp_processor_id();
+
+ BUG_ON(cpu != 0);
+
+#ifdef CONFIG_SMP
+ cpu = cpu_logical_map(cpu);
+#endif
+
+ /* Restore the CPU clock settings */
+ tegra_cpu_clock_resume();
+
+ flowctrl_cpu_suspend_exit(cpu);
+
+ restore_cpu_arch_register();
+}
+
+/*
+ * suspend_cpu_complex
+ *
+ * saves pll state for use by restart_plls, prepares flow controller for
+ * transition to suspend state
+ *
+ * Must always be called on cpu 0.
+ */
+static void suspend_cpu_complex(void)
+{
+ int cpu = smp_processor_id();
+
+ BUG_ON(cpu != 0);
+
+#ifdef CONFIG_SMP
+ cpu = cpu_logical_map(cpu);
+#endif
+
+ /* Save the CPU clock settings */
+ tegra_cpu_clock_suspend();
+
+ flowctrl_cpu_suspend_enter(cpu);
+
+ save_cpu_arch_register();
+}
+
void __cpuinit tegra_clear_cpu_in_lp2(int phy_cpu_id)
{
u32 *cpu_in_lp2 = tegra_cpu_lp2_mask;
@@ -72,4 +178,43 @@ bool __cpuinit tegra_set_cpu_in_lp2(int phy_cpu_id)
spin_unlock(&tegra_lp2_lock);
return last_cpu;
}
+
+static int tegra_sleep_cpu(unsigned long v2p)
+{
+ /* Switch to the identity mapping. */
+ cpu_switch_mm(idmap_pgd, &init_mm);
+
+ /* Flush the TLB. */
+ local_flush_tlb_all();
+
+ tegra_sleep_cpu_finish(v2p);
+
+ /* should never here */
+ BUG();
+
+ return 0;
+}
+
+void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time)
+{
+ u32 mode;
+
+ /* Only the last cpu down does the final suspend steps */
+ mode = readl(pmc + PMC_CTRL);
+ mode |= TEGRA_POWER_CPU_PWRREQ_OE;
+ writel(mode, pmc + PMC_CTRL);
+
+ set_power_timers(cpu_on_time, cpu_off_time);
+
+ cpu_cluster_pm_enter();
+ suspend_cpu_complex();
+ flush_cache_all();
+ outer_disable();
+
+ cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu);
+
+ outer_resume();
+ restore_cpu_complex();
+ cpu_cluster_pm_exit();
+}
#endif
diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h
index bcfc45f..512345c 100644
--- a/arch/arm/mach-tegra/pm.h
+++ b/arch/arm/mach-tegra/pm.h
@@ -27,4 +27,7 @@ void restore_cpu_arch_register(void);
void tegra_clear_cpu_in_lp2(int phy_cpu_id);
bool tegra_set_cpu_in_lp2(int phy_cpu_id);
+void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time);
+extern void (*tegra_tear_down_cpu)(void);
+
#endif /* _MACH_TEGRA_PM_H_ */
diff --git a/arch/arm/mach-tegra/sleep-tegra30.S b/arch/arm/mach-tegra/sleep-tegra30.S
index 5b4f54c..f4b5e80 100644
--- a/arch/arm/mach-tegra/sleep-tegra30.S
+++ b/arch/arm/mach-tegra/sleep-tegra30.S
@@ -125,4 +125,48 @@ ENTRY(tegra30_sleep_cpu_secondary_finish)
mov r0, #1 @ never return here
mov pc, r7
ENDPROC(tegra30_sleep_cpu_secondary_finish)
+
+/*
+ * tegra30_tear_down_cpu
+ *
+ * Switches the CPU to enter sleep.
+ */
+ENTRY(tegra30_tear_down_cpu)
+ mov32 r6, TEGRA_FLOW_CTRL_BASE
+
+ b tegra30_enter_sleep
+ENDPROC(tegra30_tear_down_cpu)
+
+/*
+ * tegra30_enter_sleep
+ *
+ * uses flow controller to enter sleep state
+ * executes from IRAM with SDRAM in selfrefresh when target state is LP0 or LP1
+ * executes from SDRAM with target state is LP2
+ * r6 = TEGRA_FLOW_CTRL_BASE
+ */
+tegra30_enter_sleep:
+ cpu_id r1
+
+ cpu_to_csr_reg r2, r1
+ ldr r0, [r6, r2]
+ orr r0, r0, #FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG
+ orr r0, r0, #FLOW_CTRL_CSR_ENABLE
+ str r0, [r6, r2]
+
+ mov r0, #FLOW_CTRL_WAIT_FOR_INTERRUPT
+ orr r0, r0, #FLOW_CTRL_HALT_CPU_IRQ | FLOW_CTRL_HALT_CPU_FIQ
+ cpu_to_halt_reg r2, r1
+ str r0, [r6, r2]
+ dsb
+ ldr r0, [r6, r2] /* memory barrier */
+
+halted:
+ isb
+ dsb
+ wfi /* CPU should be power gated here */
+
+ /* !!!FIXME!!! Implement halt failure handler */
+ b halted
+
#endif
diff --git a/arch/arm/mach-tegra/sleep.S b/arch/arm/mach-tegra/sleep.S
index cba7a52..b7541e2 100644
--- a/arch/arm/mach-tegra/sleep.S
+++ b/arch/arm/mach-tegra/sleep.S
@@ -25,6 +25,7 @@
#include <linux/linkage.h>
#include <asm/assembler.h>
+#include <asm/cache.h>
#include <asm/cp15.h>
#include <mach/iomap.h>
@@ -59,4 +60,45 @@ ENTRY(tegra_disable_clean_inv_dcache)
ldmfd sp!, {r0, r4-r5, r7, r9-r11, pc}
ENDPROC(tegra_disable_clean_inv_dcache)
+/*
+ * tegra_sleep_cpu_finish(unsigned long v2p)
+ *
+ * enters suspend in LP2 by turning off the mmu and jumping to
+ * tegra?_tear_down_cpu
+ */
+ENTRY(tegra_sleep_cpu_finish)
+ /* Flush and disable the L1 data cache */
+ bl tegra_disable_clean_inv_dcache
+
+ mov32 r6, tegra_tear_down_cpu
+ ldr r1, [r6]
+ add r1, r1, r0
+
+ mov32 r3, tegra_shut_off_mmu
+ add r3, r3, r0
+ mov r0, r1
+
+ mov pc, r3
+ENDPROC(tegra_sleep_cpu_finish)
+
+/*
+ * tegra_shut_off_mmu
+ *
+ * r0 = physical address to jump to with mmu off
+ *
+ * called with VA=PA mapping
+ * turns off MMU, icache, dcache and branch prediction
+ */
+ .align L1_CACHE_SHIFT
+ .pushsection .idmap.text, "ax"
+ENTRY(tegra_shut_off_mmu)
+ mrc p15, 0, r3, c1, c0, 0
+ movw r2, #CR_I | CR_Z | CR_C | CR_M
+ bic r3, r3, r2
+ dsb
+ mcr p15, 0, r3, c1, c0, 0
+ isb
+ mov pc, r0
+ENDPROC(tegra_shut_off_mmu)
+ .popsection
#endif
diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h
index 220fbd1..001920f 100644
--- a/arch/arm/mach-tegra/sleep.h
+++ b/arch/arm/mach-tegra/sleep.h
@@ -73,6 +73,7 @@
.endm
#else
void tegra_resume(void);
+int tegra_sleep_cpu_finish(unsigned long);
#ifdef CONFIG_HOTPLUG_CPU
void tegra20_hotplug_init(void);
@@ -83,6 +84,7 @@ static inline void tegra30_hotplug_init(void) {}
#endif
int tegra30_sleep_cpu_secondary_finish(unsigned long);
+void tegra30_tear_down_cpu(void);
#endif
#endif
--
1.7.0.4
^ permalink raw reply related
* [PATCH V2 6/7] ARM: tegra30: flowctrl: add cpu_suspend_exter/exit function
From: Joseph Lo @ 2012-10-19 8:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1350636526-25920-1-git-send-email-josephl@nvidia.com>
The flow controller can help CPU to go into suspend mode (deep power
saving mode). When CPU go into deep power saving mode, it needs some
careful settings before getting into and after leaving. The enter and
exit functions do that by configuring appropriate mode for flow
controller.
Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
V2:
* same with V1
arch/arm/mach-tegra/flowctrl.c | 47 ++++++++++++++++++++++++++++++++++++++++
arch/arm/mach-tegra/flowctrl.h | 8 ++++++
2 files changed, 55 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-tegra/flowctrl.c b/arch/arm/mach-tegra/flowctrl.c
index f07488e..5967b08 100644
--- a/arch/arm/mach-tegra/flowctrl.c
+++ b/arch/arm/mach-tegra/flowctrl.c
@@ -21,6 +21,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/io.h>
+#include <linux/cpumask.h>
#include <mach/iomap.h>
@@ -51,6 +52,14 @@ static void flowctrl_update(u8 offset, u32 value)
readl_relaxed(addr);
}
+u32 flowctrl_read_cpu_csr(unsigned int cpuid)
+{
+ u8 offset = flowctrl_offset_cpu_csr[cpuid];
+ void __iomem *addr = IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + offset;
+
+ return readl(addr);
+}
+
void flowctrl_write_cpu_csr(unsigned int cpuid, u32 value)
{
return flowctrl_update(flowctrl_offset_cpu_csr[cpuid], value);
@@ -60,3 +69,41 @@ void flowctrl_write_cpu_halt(unsigned int cpuid, u32 value)
{
return flowctrl_update(flowctrl_offset_halt_cpu[cpuid], value);
}
+
+void flowctrl_cpu_suspend_enter(unsigned int cpuid)
+{
+ unsigned int reg;
+ int i;
+
+ reg = flowctrl_read_cpu_csr(cpuid);
+ reg &= ~TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP; /* clear wfe bitmap */
+ reg &= ~TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP; /* clear wfi bitmap */
+ reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr flag */
+ reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event flag */
+ reg |= TEGRA30_FLOW_CTRL_CSR_WFI_CPU0 << cpuid; /* pwr gating on wfi */
+ reg |= FLOW_CTRL_CSR_ENABLE; /* pwr gating */
+ flowctrl_write_cpu_csr(cpuid, reg);
+
+ for (i = 0; i < num_possible_cpus(); i++) {
+ if (i == cpuid)
+ continue;
+ reg = flowctrl_read_cpu_csr(i);
+ reg |= FLOW_CTRL_CSR_EVENT_FLAG;
+ reg |= FLOW_CTRL_CSR_INTR_FLAG;
+ flowctrl_write_cpu_csr(i, reg);
+ }
+}
+
+void flowctrl_cpu_suspend_exit(unsigned int cpuid)
+{
+ unsigned int reg;
+
+ /* Disable powergating via flow controller for CPU0 */
+ reg = flowctrl_read_cpu_csr(cpuid);
+ reg &= ~TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP; /* clear wfe bitmap */
+ reg &= ~TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP; /* clear wfi bitmap */
+ reg &= ~FLOW_CTRL_CSR_ENABLE; /* clear enable */
+ reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr */
+ reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event */
+ flowctrl_write_cpu_csr(cpuid, reg);
+}
diff --git a/arch/arm/mach-tegra/flowctrl.h b/arch/arm/mach-tegra/flowctrl.h
index 1942817..0798dec 100644
--- a/arch/arm/mach-tegra/flowctrl.h
+++ b/arch/arm/mach-tegra/flowctrl.h
@@ -34,9 +34,17 @@
#define FLOW_CTRL_HALT_CPU1_EVENTS 0x14
#define FLOW_CTRL_CPU1_CSR 0x18
+#define TEGRA30_FLOW_CTRL_CSR_WFI_CPU0 (1 << 8)
+#define TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP (0xF << 4)
+#define TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP (0xF << 8)
+
#ifndef __ASSEMBLY__
+u32 flowctrl_read_cpu_csr(unsigned int cpuid);
void flowctrl_write_cpu_csr(unsigned int cpuid, u32 value);
void flowctrl_write_cpu_halt(unsigned int cpuid, u32 value);
+
+void flowctrl_cpu_suspend_enter(unsigned int cpuid);
+void flowctrl_cpu_suspend_exit(unsigned int cpuid);
#endif
#endif
--
1.7.0.4
^ permalink raw reply related
* [PATCH V2 5/7] ARM: tegra30: clocks: add CPU low-power function into tegra_cpu_car_ops
From: Joseph Lo @ 2012-10-19 8:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1350636526-25920-1-git-send-email-josephl@nvidia.com>
Add suspend, resume and rail_off_ready API into tegra_cpu_car_ops. These
functions were used for CPU low-power state maintenance (e.g., LP2). One
thing needs to notice the rail_off_ready API only availalbe for cpu_g
cluster not cpu_lp cluster.
Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
V2:
* same with V1
arch/arm/mach-tegra/tegra30_clocks.c | 107 ++++++++++++++++++++++++++++++++++
arch/arm/mach-tegra/tegra_cpu_car.h | 37 ++++++++++++
2 files changed, 144 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-tegra/tegra30_clocks.c b/arch/arm/mach-tegra/tegra30_clocks.c
index 875744f..552ce89 100644
--- a/arch/arm/mach-tegra/tegra30_clocks.c
+++ b/arch/arm/mach-tegra/tegra30_clocks.c
@@ -32,6 +32,7 @@
#include <asm/clkdev.h>
#include <mach/iomap.h>
+#include <mach/powergate.h>
#include "clock.h"
#include "fuse.h"
@@ -310,6 +311,31 @@
#define CPU_CLOCK(cpu) (0x1 << (8 + cpu))
#define CPU_RESET(cpu) (0x1111ul << (cpu))
+#define CLK_RESET_CCLK_BURST 0x20
+#define CLK_RESET_CCLK_DIVIDER 0x24
+#define CLK_RESET_PLLX_BASE 0xe0
+#define CLK_RESET_PLLX_MISC 0xe4
+
+#define CLK_RESET_SOURCE_CSITE 0x1d4
+
+#define CLK_RESET_CCLK_BURST_POLICY_SHIFT 28
+#define CLK_RESET_CCLK_RUN_POLICY_SHIFT 4
+#define CLK_RESET_CCLK_IDLE_POLICY_SHIFT 0
+#define CLK_RESET_CCLK_IDLE_POLICY 1
+#define CLK_RESET_CCLK_RUN_POLICY 2
+#define CLK_RESET_CCLK_BURST_POLICY_PLLX 8
+
+#ifdef CONFIG_PM_SLEEP
+static struct cpu_clk_suspend_context {
+ u32 pllx_misc;
+ u32 pllx_base;
+
+ u32 cpu_burst;
+ u32 clk_csite_src;
+ u32 cclk_divider;
+} tegra30_cpu_clk_sctx;
+#endif
+
/**
* Structure defining the fields for USB UTMI clocks Parameters.
*/
@@ -2387,12 +2413,93 @@ static void tegra30_disable_cpu_clock(u32 cpu)
reg_clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
}
+#ifdef CONFIG_PM_SLEEP
+static bool tegra30_cpu_rail_off_ready(void)
+{
+ unsigned int cpu_rst_status;
+ int cpu_pwr_status;
+
+ cpu_rst_status = readl(reg_clk_base +
+ TEGRA30_CLK_RST_CONTROLLER_CPU_CMPLX_STATUS);
+ cpu_pwr_status = tegra_powergate_is_powered(TEGRA_POWERGATE_CPU1) ||
+ tegra_powergate_is_powered(TEGRA_POWERGATE_CPU2) ||
+ tegra_powergate_is_powered(TEGRA_POWERGATE_CPU3);
+
+ if (((cpu_rst_status & 0xE) != 0xE) || cpu_pwr_status)
+ return false;
+
+ return true;
+}
+
+static void tegra30_cpu_clock_suspend(void)
+{
+ /* switch coresite to clk_m, save off original source */
+ tegra30_cpu_clk_sctx.clk_csite_src =
+ readl(reg_clk_base + CLK_RESET_SOURCE_CSITE);
+ writel(3<<30, reg_clk_base + CLK_RESET_SOURCE_CSITE);
+
+ tegra30_cpu_clk_sctx.cpu_burst =
+ readl(reg_clk_base + CLK_RESET_CCLK_BURST);
+ tegra30_cpu_clk_sctx.pllx_base =
+ readl(reg_clk_base + CLK_RESET_PLLX_BASE);
+ tegra30_cpu_clk_sctx.pllx_misc =
+ readl(reg_clk_base + CLK_RESET_PLLX_MISC);
+ tegra30_cpu_clk_sctx.cclk_divider =
+ readl(reg_clk_base + CLK_RESET_CCLK_DIVIDER);
+}
+
+static void tegra30_cpu_clock_resume(void)
+{
+ unsigned int reg, policy;
+
+ /* Is CPU complex already running on PLLX? */
+ reg = readl(reg_clk_base + CLK_RESET_CCLK_BURST);
+ policy = (reg >> CLK_RESET_CCLK_BURST_POLICY_SHIFT) & 0xF;
+
+ if (policy == CLK_RESET_CCLK_IDLE_POLICY)
+ reg = (reg >> CLK_RESET_CCLK_IDLE_POLICY_SHIFT) & 0xF;
+ else if (policy == CLK_RESET_CCLK_RUN_POLICY)
+ reg = (reg >> CLK_RESET_CCLK_RUN_POLICY_SHIFT) & 0xF;
+ else
+ BUG();
+
+ if (reg != CLK_RESET_CCLK_BURST_POLICY_PLLX) {
+ /* restore PLLX settings if CPU is on different PLL */
+ writel(tegra30_cpu_clk_sctx.pllx_misc,
+ reg_clk_base + CLK_RESET_PLLX_MISC);
+ writel(tegra30_cpu_clk_sctx.pllx_base,
+ reg_clk_base + CLK_RESET_PLLX_BASE);
+
+ /* wait for PLL stabilization if PLLX was enabled */
+ if (tegra30_cpu_clk_sctx.pllx_base & (1 << 30))
+ udelay(300);
+ }
+
+ /*
+ * Restore original burst policy setting for calls resulting from CPU
+ * LP2 in idle or system suspend.
+ */
+ writel(tegra30_cpu_clk_sctx.cclk_divider,
+ reg_clk_base + CLK_RESET_CCLK_DIVIDER);
+ writel(tegra30_cpu_clk_sctx.cpu_burst,
+ reg_clk_base + CLK_RESET_CCLK_BURST);
+
+ writel(tegra30_cpu_clk_sctx.clk_csite_src,
+ reg_clk_base + CLK_RESET_SOURCE_CSITE);
+}
+#endif
+
static struct tegra_cpu_car_ops tegra30_cpu_car_ops = {
.wait_for_reset = tegra30_wait_cpu_in_reset,
.put_in_reset = tegra30_put_cpu_in_reset,
.out_of_reset = tegra30_cpu_out_of_reset,
.enable_clock = tegra30_enable_cpu_clock,
.disable_clock = tegra30_disable_cpu_clock,
+#ifdef CONFIG_PM_SLEEP
+ .rail_off_ready = tegra30_cpu_rail_off_ready,
+ .suspend = tegra30_cpu_clock_suspend,
+ .resume = tegra30_cpu_clock_resume,
+#endif
};
void __init tegra30_cpu_car_ops_init(void)
diff --git a/arch/arm/mach-tegra/tegra_cpu_car.h b/arch/arm/mach-tegra/tegra_cpu_car.h
index 30d063a..9764d31 100644
--- a/arch/arm/mach-tegra/tegra_cpu_car.h
+++ b/arch/arm/mach-tegra/tegra_cpu_car.h
@@ -30,6 +30,12 @@
* CPU clock un-gate
* disable_clock:
* CPU clock gate
+ * rail_off_ready:
+ * CPU is ready for rail off
+ * suspend:
+ * save the clock settings when CPU go into low-power state
+ * resume:
+ * restore the clock settings when CPU exit low-power state
*/
struct tegra_cpu_car_ops {
void (*wait_for_reset)(u32 cpu);
@@ -37,6 +43,11 @@ struct tegra_cpu_car_ops {
void (*out_of_reset)(u32 cpu);
void (*enable_clock)(u32 cpu);
void (*disable_clock)(u32 cpu);
+#ifdef CONFIG_PM_SLEEP
+ bool (*rail_off_ready)(void);
+ void (*suspend)(void);
+ void (*resume)(void);
+#endif
};
extern struct tegra_cpu_car_ops *tegra_cpu_car_ops;
@@ -81,6 +92,32 @@ static inline void tegra_disable_cpu_clock(u32 cpu)
tegra_cpu_car_ops->disable_clock(cpu);
}
+#ifdef CONFIG_PM_SLEEP
+static inline bool tegra_cpu_rail_off_ready(void)
+{
+ if (WARN_ON(!tegra_cpu_car_ops->rail_off_ready))
+ return false;
+
+ return tegra_cpu_car_ops->rail_off_ready();
+}
+
+static inline void tegra_cpu_clock_suspend(void)
+{
+ if (WARN_ON(!tegra_cpu_car_ops->suspend))
+ return;
+
+ tegra_cpu_car_ops->suspend();
+}
+
+static inline void tegra_cpu_clock_resume(void)
+{
+ if (WARN_ON(!tegra_cpu_car_ops->resume))
+ return;
+
+ tegra_cpu_car_ops->resume();
+}
+#endif
+
void tegra20_cpu_car_ops_init(void);
void tegra30_cpu_car_ops_init(void);
--
1.7.0.4
^ permalink raw reply related
* [PATCH V2 4/7] ARM: tegra30: common: enable csite clock
From: Joseph Lo @ 2012-10-19 8:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1350636526-25920-1-git-send-email-josephl@nvidia.com>
Enable csite (debug and trace controller) clock at init to prevent it
be disabled. And this also the necessary clock for CPU be brought up or
resumed from a power-gate low power state (e.g., LP2).
Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
V2:
* same with V1
arch/arm/mach-tegra/common.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c
index 177f164..0e31f8c 100644
--- a/arch/arm/mach-tegra/common.c
+++ b/arch/arm/mach-tegra/common.c
@@ -108,6 +108,7 @@ static __initdata struct tegra_clk_init_table tegra30_clk_init_table[] = {
{ "sclk", "pll_p_out4", 102000000, true },
{ "hclk", "sclk", 102000000, true },
{ "pclk", "hclk", 51000000, true },
+ { "csite", NULL, 0, true },
{ NULL, NULL, 0, 0},
};
#endif
--
1.7.0.4
^ permalink raw reply related
* [PATCH V2 3/7] ARM: tegra30: cpuidle: add LP2 driver for secondary CPUs
From: Joseph Lo @ 2012-10-19 8:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1350636526-25920-1-git-send-email-josephl@nvidia.com>
This supports power-gated (LP2) idle on secondary CPUs for Tegra30.
The secondary CPUs can go into LP2 state independently. When CPU goes
into LP2 state, it saves it's state and puts itself to flow controlled
WFI state. After that, it will been power gated.
Based on the work by:
Scott Williams <scwilliams@nvidia.com>
Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
V2:
* static initialization for idle states when PM_SLEEP is true or not
* using inline fuction to replace the empty fuction when #ifdef false
* convert the phy cpu number by cpu_logical_map
* update the usage of tegra_cpu_lp2_mask (only one copy in the IRAM)
* update the usage of data cache maintenance API for LP2
* disable L1 data cache
* v7_flush_dcache_louis
* exit SMP coherency
arch/arm/mach-tegra/Makefile | 1 +
arch/arm/mach-tegra/cpuidle-tegra30.c | 86 +++++++++++++++++++++++++++++++++
arch/arm/mach-tegra/pm.c | 75 ++++++++++++++++++++++++++++
arch/arm/mach-tegra/pm.h | 30 +++++++++++
arch/arm/mach-tegra/reset.h | 9 ++++
arch/arm/mach-tegra/sleep-tegra30.S | 23 +++++++++
arch/arm/mach-tegra/sleep.S | 29 +++++++++++
arch/arm/mach-tegra/sleep.h | 2 +
8 files changed, 255 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/mach-tegra/pm.c
create mode 100644 arch/arm/mach-tegra/pm.h
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 9b80c1e..6f224f7 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -8,6 +8,7 @@ obj-y += pmc.o
obj-y += flowctrl.o
obj-y += powergate.o
obj-y += apbio.o
+obj-y += pm.o
obj-$(CONFIG_CPU_IDLE) += cpuidle.o
obj-$(CONFIG_CPU_IDLE) += sleep.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_clocks.o
diff --git a/arch/arm/mach-tegra/cpuidle-tegra30.c b/arch/arm/mach-tegra/cpuidle-tegra30.c
index 37e7551..b574abd 100644
--- a/arch/arm/mach-tegra/cpuidle-tegra30.c
+++ b/arch/arm/mach-tegra/cpuidle-tegra30.c
@@ -22,21 +22,107 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cpuidle.h>
+#include <linux/cpu_pm.h>
+#include <linux/clockchips.h>
#include <asm/cpuidle.h>
+#include <asm/proc-fns.h>
+#include <asm/suspend.h>
+#include <asm/smp_plat.h>
+
+#include "pm.h"
+#include "sleep.h"
+
+#ifdef CONFIG_PM_SLEEP
+static int tegra30_idle_lp2(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index);
+#endif
static struct cpuidle_driver tegra_idle_driver = {
.name = "tegra_idle",
.owner = THIS_MODULE,
.en_core_tk_irqen = 1,
+#ifdef CONFIG_PM_SLEEP
+ .state_count = 2,
+#else
.state_count = 1,
+#endif
.states = {
[0] = ARM_CPUIDLE_WFI_STATE_PWR(600),
+#ifdef CONFIG_PM_SLEEP
+ [1] = {
+ .enter = tegra30_idle_lp2,
+ .exit_latency = 2000,
+ .target_residency = 2200,
+ .power_usage = 0,
+ .flags = CPUIDLE_FLAG_TIME_VALID,
+ .name = "LP2",
+ .desc = "CPU power-gate",
+ },
+#endif
},
};
static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device);
+#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_SMP
+static bool tegra30_idle_enter_lp2_cpu_n(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
+
+ smp_wmb();
+
+ save_cpu_arch_register();
+
+ cpu_suspend(0, tegra30_sleep_cpu_secondary_finish);
+
+ restore_cpu_arch_register();
+
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
+
+ return true;
+}
+#else
+static inline bool tegra30_idle_enter_lp2_cpu_n(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ return true;
+}
+#endif
+
+static int __cpuinit tegra30_idle_lp2(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ u32 cpu = is_smp() ? cpu_logical_map(dev->cpu) : dev->cpu;
+ bool entered_lp2 = false;
+
+ local_fiq_disable();
+
+ tegra_set_cpu_in_lp2(cpu);
+ cpu_pm_enter();
+
+ if (cpu == 0)
+ cpu_do_idle();
+ else
+ entered_lp2 = tegra30_idle_enter_lp2_cpu_n(dev, drv, index);
+
+ cpu_pm_exit();
+ tegra_clear_cpu_in_lp2(cpu);
+
+ local_fiq_enable();
+
+ smp_rmb();
+
+ return (entered_lp2) ? index : 0;
+}
+#endif
+
int __init tegra30_cpuidle_init(void)
{
int ret;
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
new file mode 100644
index 0000000..39ee557
--- /dev/null
+++ b/arch/arm/mach-tegra/pm.c
@@ -0,0 +1,75 @@
+/*
+ * CPU complex suspend & resume functions for Tegra SoCs
+ *
+ * Copyright (c) 2009-2012, NVIDIA Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/cpumask.h>
+
+#include <mach/iomap.h>
+
+#include "reset.h"
+
+#ifdef CONFIG_PM_SLEEP
+static unsigned int g_diag_reg;
+static DEFINE_SPINLOCK(tegra_lp2_lock);
+
+void save_cpu_arch_register(void)
+{
+ /* read diagnostic register */
+ asm("mrc p15, 0, %0, c15, c0, 1" : "=r"(g_diag_reg) : : "cc");
+ return;
+}
+
+void restore_cpu_arch_register(void)
+{
+ /* write diagnostic register */
+ asm("mcr p15, 0, %0, c15, c0, 1" : : "r"(g_diag_reg) : "cc");
+ return;
+}
+
+void __cpuinit tegra_clear_cpu_in_lp2(int phy_cpu_id)
+{
+ u32 *cpu_in_lp2 = tegra_cpu_lp2_mask;
+
+ spin_lock(&tegra_lp2_lock);
+
+ BUG_ON(!(*cpu_in_lp2 & BIT(phy_cpu_id)));
+ *cpu_in_lp2 &= ~BIT(phy_cpu_id);
+
+ spin_unlock(&tegra_lp2_lock);
+}
+
+bool __cpuinit tegra_set_cpu_in_lp2(int phy_cpu_id)
+{
+ bool last_cpu = false;
+ cpumask_t *cpu_lp2_mask = tegra_cpu_lp2_mask;
+ u32 *cpu_in_lp2 = tegra_cpu_lp2_mask;
+
+ spin_lock(&tegra_lp2_lock);
+
+ BUG_ON((*cpu_in_lp2 & BIT(phy_cpu_id)));
+ *cpu_in_lp2 |= BIT(phy_cpu_id);
+
+ if ((phy_cpu_id == 0) && cpumask_equal(cpu_lp2_mask, cpu_online_mask))
+ last_cpu = true;
+
+ spin_unlock(&tegra_lp2_lock);
+ return last_cpu;
+}
+#endif
diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h
new file mode 100644
index 0000000..bcfc45f
--- /dev/null
+++ b/arch/arm/mach-tegra/pm.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (c) 2010-2012 NVIDIA Corporation. All rights reserved.
+ *
+ * Author:
+ * Colin Cross <ccross@google.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MACH_TEGRA_PM_H_
+#define _MACH_TEGRA_PM_H_
+
+void save_cpu_arch_register(void);
+void restore_cpu_arch_register(void);
+
+void tegra_clear_cpu_in_lp2(int phy_cpu_id);
+bool tegra_set_cpu_in_lp2(int phy_cpu_id);
+
+#endif /* _MACH_TEGRA_PM_H_ */
diff --git a/arch/arm/mach-tegra/reset.h b/arch/arm/mach-tegra/reset.h
index de88bf8..234cd6b 100644
--- a/arch/arm/mach-tegra/reset.h
+++ b/arch/arm/mach-tegra/reset.h
@@ -29,6 +29,8 @@
#ifndef __ASSEMBLY__
+#include <mach/irammap.h>
+
extern unsigned long __tegra_cpu_reset_handler_data[TEGRA_RESET_DATA_SIZE];
void __tegra_cpu_reset_handler_start(void);
@@ -36,6 +38,13 @@ void __tegra_cpu_reset_handler(void);
void __tegra_cpu_reset_handler_end(void);
void tegra_secondary_startup(void);
+#ifdef CONFIG_PM_SLEEP
+#define tegra_cpu_lp2_mask \
+ (IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \
+ ((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP2] - \
+ (u32)__tegra_cpu_reset_handler_start)))
+#endif
+
#define tegra_cpu_reset_handler_offset \
((u32)__tegra_cpu_reset_handler - \
(u32)__tegra_cpu_reset_handler_start)
diff --git a/arch/arm/mach-tegra/sleep-tegra30.S b/arch/arm/mach-tegra/sleep-tegra30.S
index be7614b..5b4f54c 100644
--- a/arch/arm/mach-tegra/sleep-tegra30.S
+++ b/arch/arm/mach-tegra/sleep-tegra30.S
@@ -17,6 +17,8 @@
#include <linux/linkage.h>
#include <asm/assembler.h>
+#include <asm/asm-offsets.h>
+#include <asm/glue-cache.h>
#include "sleep.h"
#include "flowctrl.h"
@@ -80,6 +82,7 @@ delay_1:
ldr r3, [r1] @ read CSR
str r3, [r1] @ clear CSR
tst r0, #TEGRA30_POWER_HOTPLUG_SHUTDOWN
+ moveq r3, #FLOW_CTRL_WAIT_FOR_INTERRUPT @ For LP2
movne r3, #FLOW_CTRL_WAITEVENT @ For hotplug
str r3, [r2]
ldr r0, [r2]
@@ -103,3 +106,23 @@ wfe_war:
ENDPROC(tegra30_cpu_shutdown)
#endif
+
+#ifdef CONFIG_PM_SLEEP
+/*
+ * tegra30_sleep_cpu_secondary_finish(unsigned long v2p)
+ *
+ * Enters LP2 on secondary CPU by exiting coherency and powergating the CPU.
+ */
+ENTRY(tegra30_sleep_cpu_secondary_finish)
+ mov r7, lr
+
+ /* Flush and disable the L1 data cache */
+ bl tegra_disable_clean_inv_dcache
+
+ /* Powergate this CPU. */
+ mov r0, #0 @ power mode flags (!hotplug)
+ bl tegra30_cpu_shutdown
+ mov r0, #1 @ never return here
+ mov pc, r7
+ENDPROC(tegra30_sleep_cpu_secondary_finish)
+#endif
diff --git a/arch/arm/mach-tegra/sleep.S b/arch/arm/mach-tegra/sleep.S
index ea81554..cba7a52 100644
--- a/arch/arm/mach-tegra/sleep.S
+++ b/arch/arm/mach-tegra/sleep.S
@@ -25,9 +25,38 @@
#include <linux/linkage.h>
#include <asm/assembler.h>
+#include <asm/cp15.h>
#include <mach/iomap.h>
#include "flowctrl.h"
#include "sleep.h"
+#ifdef CONFIG_PM_SLEEP
+/*
+ * tegra_disable_clean_inv_dcache
+ *
+ * disable, clean & invalidate the D-cache
+ *
+ * Corrupted registers: r1-r3, r6, r8, r9-r11
+ */
+ENTRY(tegra_disable_clean_inv_dcache)
+ stmfd sp!, {r0, r4-r5, r7, r9-r11, lr}
+ dmb @ ensure ordering
+
+ /* Disable the D-cache */
+ mrc p15, 0, r2, c1, c0, 0
+ bic r2, r2, #CR_C
+ mcr p15, 0, r2, c1, c0, 0
+ isb
+
+ /* Flush the D-cache */
+ bl v7_flush_dcache_louis
+
+ /* Trun off coherency */
+ exit_smp r4, r5
+
+ ldmfd sp!, {r0, r4-r5, r7, r9-r11, pc}
+ENDPROC(tegra_disable_clean_inv_dcache)
+
+#endif
diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h
index c9dec37..220fbd1 100644
--- a/arch/arm/mach-tegra/sleep.h
+++ b/arch/arm/mach-tegra/sleep.h
@@ -82,5 +82,7 @@ static inline void tegra20_hotplug_init(void) {}
static inline void tegra30_hotplug_init(void) {}
#endif
+int tegra30_sleep_cpu_secondary_finish(unsigned long);
+
#endif
#endif
--
1.7.0.4
^ permalink raw reply related
* [PATCH V2 2/7] ARM: tegra: cpuidle: add LP2 resume function
From: Joseph Lo @ 2012-10-19 8:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1350636526-25920-1-git-send-email-josephl@nvidia.com>
LP2 is one of the Tegra low power states that supports power gating both
CPU cores and GICs. Adding a resume function for taking care the CPUs that
resume from LP2. This function was been hooked to reset handler. We take
care everything here before go into kernel.
Based on the work by:
Scott Williams <scwilliams@nvidia.com>
Colin Cross <ccross@android.com>
Gary King <gking@nvidia.com>
Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
V2:
* moving the code that only for Tegra30 inside the ifdef in the tegra_resume
arch/arm/mach-tegra/headsmp.S | 58 +++++++++++++++++++++++++++++++++++++++++
arch/arm/mach-tegra/reset.c | 6 ++++
arch/arm/mach-tegra/sleep.h | 1 +
3 files changed, 65 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-tegra/headsmp.S b/arch/arm/mach-tegra/headsmp.S
index 6addc78..36066f2 100644
--- a/arch/arm/mach-tegra/headsmp.S
+++ b/arch/arm/mach-tegra/headsmp.S
@@ -69,6 +69,53 @@ ENTRY(tegra_secondary_startup)
b secondary_startup
ENDPROC(tegra_secondary_startup)
+#ifdef CONFIG_PM_SLEEP
+/*
+ * tegra_resume
+ *
+ * CPU boot vector when restarting the a CPU following
+ * an LP2 transition. Also branched to by LP0 and LP1 resume after
+ * re-enabling sdram.
+ */
+ENTRY(tegra_resume)
+ bl v7_invalidate_l1
+ /* Enable coresight */
+ mov32 r0, 0xC5ACCE55
+ mcr p14, 0, r0, c7, c12, 6
+
+ cpu_id r0
+ cmp r0, #0 @ CPU0?
+ bne cpu_resume @ no
+
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+ /* Are we on Tegra20? */
+ mov32 r6, TEGRA_APB_MISC_BASE
+ ldr r0, [r6, #APB_MISC_GP_HIDREV]
+ and r0, r0, #0xff00
+ cmp r0, #(0x20 << 8)
+ beq 1f @ Yes
+ /* Clear the flow controller flags for this CPU. */
+ mov32 r2, TEGRA_FLOW_CTRL_BASE + FLOW_CTRL_CPU0_CSR @ CPU0 CSR
+ ldr r1, [r2]
+ /* Clear event & intr flag */
+ orr r1, r1, \
+ #FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG
+ movw r0, #0x0FFD @ enable, cluster_switch, immed, & bitmaps
+ bic r1, r1, r0
+ str r1, [r2]
+1:
+#endif
+
+ /* enable SCU */
+ mov32 r0, TEGRA_ARM_PERIF_BASE
+ ldr r1, [r0]
+ orr r1, r1, #1
+ str r1, [r0]
+
+ b cpu_resume
+ENDPROC(tegra_resume)
+#endif
+
.align L1_CACHE_SHIFT
ENTRY(__tegra_cpu_reset_handler_start)
@@ -122,6 +169,17 @@ ENTRY(__tegra_cpu_reset_handler)
1:
#endif
+ /* Waking up from LP2? */
+ ldr r9, [r12, #RESET_DATA(MASK_LP2)]
+ tst r9, r11 @ if in_lp2
+ beq __is_not_lp2
+ ldr lr, [r12, #RESET_DATA(STARTUP_LP2)]
+ cmp lr, #0
+ bleq __die @ no LP2 startup handler
+ bx lr
+
+__is_not_lp2:
+
#ifdef CONFIG_SMP
/*
* Can only be secondary boot (initial or hotplug) but CPU 0
diff --git a/arch/arm/mach-tegra/reset.c b/arch/arm/mach-tegra/reset.c
index 5beb7eb..c48548f 100644
--- a/arch/arm/mach-tegra/reset.c
+++ b/arch/arm/mach-tegra/reset.c
@@ -26,6 +26,7 @@
#include <mach/irammap.h>
#include "reset.h"
+#include "sleep.h"
#include "fuse.h"
#define TEGRA_IRAM_RESET_BASE (TEGRA_IRAM_BASE + \
@@ -80,5 +81,10 @@ void __init tegra_cpu_reset_handler_init(void)
virt_to_phys((void *)tegra_secondary_startup);
#endif
+#ifdef CONFIG_PM_SLEEP
+ __tegra_cpu_reset_handler_data[TEGRA_RESET_STARTUP_LP2] =
+ virt_to_phys((void *)tegra_resume);
+#endif
+
tegra_cpu_reset_handler_enable();
}
diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h
index e25a7cd..c9dec37 100644
--- a/arch/arm/mach-tegra/sleep.h
+++ b/arch/arm/mach-tegra/sleep.h
@@ -72,6 +72,7 @@
dsb
.endm
#else
+void tegra_resume(void);
#ifdef CONFIG_HOTPLUG_CPU
void tegra20_hotplug_init(void);
--
1.7.0.4
^ permalink raw reply related
* [PATCH V2 1/7] ARM: tegra: cpuidle: separate cpuidle driver for different chips
From: Joseph Lo @ 2012-10-19 8:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1350636526-25920-1-git-send-email-josephl@nvidia.com>
The different Tegra chips may have different CPU idle states and data.
Individual CPU idle driver make it more easy to maintain.
Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
V2:
* only remove the line of file name and path in the (c) header
arch/arm/mach-tegra/Makefile | 6 +++
.../mach-tegra/{cpuidle.c => cpuidle-tegra20.c} | 7 +--
.../mach-tegra/{cpuidle.c => cpuidle-tegra30.c} | 7 +--
arch/arm/mach-tegra/cpuidle.c | 47 +++++--------------
arch/arm/mach-tegra/cpuidle.h | 32 +++++++++++++
5 files changed, 55 insertions(+), 44 deletions(-)
copy arch/arm/mach-tegra/{cpuidle.c => cpuidle-tegra20.c} (91%)
copy arch/arm/mach-tegra/{cpuidle.c => cpuidle-tegra30.c} (91%)
create mode 100644 arch/arm/mach-tegra/cpuidle.h
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index e6929c6..9b80c1e 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -14,9 +14,15 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_clocks.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_clocks_data.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += sleep-tegra20.o
+ifeq ($(CONFIG_CPU_IDLE),y)
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += cpuidle-tegra20.o
+endif
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_clocks.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_clocks_data.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += sleep-tegra30.o
+ifeq ($(CONFIG_CPU_IDLE),y)
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += cpuidle-tegra30.o
+endif
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
obj-$(CONFIG_SMP) += reset.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle-tegra20.c
similarity index 91%
copy from arch/arm/mach-tegra/cpuidle.c
copy to arch/arm/mach-tegra/cpuidle-tegra20.c
index 4e0b07c..d32e8b0 100644
--- a/arch/arm/mach-tegra/cpuidle.c
+++ b/arch/arm/mach-tegra/cpuidle-tegra20.c
@@ -1,6 +1,4 @@
/*
- * arch/arm/mach-tegra/cpuidle.c
- *
* CPU idle driver for Tegra CPUs
*
* Copyright (c) 2010-2012, NVIDIA Corporation.
@@ -27,7 +25,7 @@
#include <asm/cpuidle.h>
-struct cpuidle_driver tegra_idle_driver = {
+static struct cpuidle_driver tegra_idle_driver = {
.name = "tegra_idle",
.owner = THIS_MODULE,
.en_core_tk_irqen = 1,
@@ -39,7 +37,7 @@ struct cpuidle_driver tegra_idle_driver = {
static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device);
-static int __init tegra_cpuidle_init(void)
+int __init tegra20_cpuidle_init(void)
{
int ret;
unsigned int cpu;
@@ -66,4 +64,3 @@ static int __init tegra_cpuidle_init(void)
}
return 0;
}
-device_initcall(tegra_cpuidle_init);
diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle-tegra30.c
similarity index 91%
copy from arch/arm/mach-tegra/cpuidle.c
copy to arch/arm/mach-tegra/cpuidle-tegra30.c
index 4e0b07c..37e7551 100644
--- a/arch/arm/mach-tegra/cpuidle.c
+++ b/arch/arm/mach-tegra/cpuidle-tegra30.c
@@ -1,6 +1,4 @@
/*
- * arch/arm/mach-tegra/cpuidle.c
- *
* CPU idle driver for Tegra CPUs
*
* Copyright (c) 2010-2012, NVIDIA Corporation.
@@ -27,7 +25,7 @@
#include <asm/cpuidle.h>
-struct cpuidle_driver tegra_idle_driver = {
+static struct cpuidle_driver tegra_idle_driver = {
.name = "tegra_idle",
.owner = THIS_MODULE,
.en_core_tk_irqen = 1,
@@ -39,7 +37,7 @@ struct cpuidle_driver tegra_idle_driver = {
static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device);
-static int __init tegra_cpuidle_init(void)
+int __init tegra30_cpuidle_init(void)
{
int ret;
unsigned int cpu;
@@ -66,4 +64,3 @@ static int __init tegra_cpuidle_init(void)
}
return 0;
}
-device_initcall(tegra_cpuidle_init);
diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle.c
index 4e0b07c..d065139 100644
--- a/arch/arm/mach-tegra/cpuidle.c
+++ b/arch/arm/mach-tegra/cpuidle.c
@@ -23,47 +23,26 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/cpuidle.h>
-#include <asm/cpuidle.h>
-
-struct cpuidle_driver tegra_idle_driver = {
- .name = "tegra_idle",
- .owner = THIS_MODULE,
- .en_core_tk_irqen = 1,
- .state_count = 1,
- .states = {
- [0] = ARM_CPUIDLE_WFI_STATE_PWR(600),
- },
-};
-
-static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device);
+#include "fuse.h"
+#include "cpuidle.h"
static int __init tegra_cpuidle_init(void)
{
int ret;
- unsigned int cpu;
- struct cpuidle_device *dev;
- struct cpuidle_driver *drv = &tegra_idle_driver;
- ret = cpuidle_register_driver(&tegra_idle_driver);
- if (ret) {
- pr_err("CPUidle driver registration failed\n");
- return ret;
+ switch (tegra_chip_id) {
+ case TEGRA20:
+ ret = tegra20_cpuidle_init();
+ break;
+ case TEGRA30:
+ ret = tegra30_cpuidle_init();
+ break;
+ default:
+ ret = -ENODEV;
+ break;
}
- for_each_possible_cpu(cpu) {
- dev = &per_cpu(tegra_idle_device, cpu);
- dev->cpu = cpu;
-
- dev->state_count = drv->state_count;
- ret = cpuidle_register_device(dev);
- if (ret) {
- pr_err("CPU%u: CPUidle device registration failed\n",
- cpu);
- return ret;
- }
- }
- return 0;
+ return ret;
}
device_initcall(tegra_cpuidle_init);
diff --git a/arch/arm/mach-tegra/cpuidle.h b/arch/arm/mach-tegra/cpuidle.h
new file mode 100644
index 0000000..496204d
--- /dev/null
+++ b/arch/arm/mach-tegra/cpuidle.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2012, NVIDIA Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __MACH_TEGRA_CPUIDLE_H
+#define __MACH_TEGRA_CPUIDLE_H
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+int tegra20_cpuidle_init(void);
+#else
+static inline int tegra20_cpuidle_init(void) { return -ENODEV; }
+#endif
+
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+int tegra30_cpuidle_init(void);
+#else
+static inline int tegra30_cpuidle_init(void) { return -ENODEV; }
+#endif
+
+#endif
--
1.7.0.4
^ permalink raw reply related
* [PATCH V2 0/7] ARM: tegra30: cpuidle: add LP2 support
From: Joseph Lo @ 2012-10-19 8:48 UTC (permalink / raw)
To: linux-arm-kernel
The CPU idle LP2 is a power gating idle mode for Tegra30. It supports the
secondary CPUs (i.e., CPU1-CPU3) to go into LP2 dynamically. When any of
the secondary CPUs go into LP2, it can be power gated alone. There is a
limitation on CPU0. The CPU0 can go into LP2 only when all secondary CPUs
are already in LP2. After CPU0 is in LP2, the CPU rail can be turned off.
Verified on Seaboard(Tegra20) and Cardhu(Tegra30).
This patch set should depend on these two patches:
d8be3dc ARM: tegra: rename the file of "sleep-tXX" to "sleep-tegraXX"
01b176e ARM: tegra30: clocks: add AHB and APB clocks
Previous work can be found at:
V1:
http://www.mail-archive.com/linux-tegra at vger.kernel.org/msg06319.html
Joseph Lo (7):
ARM: tegra: cpuidle: separate cpuidle driver for different chips
ARM: tegra: cpuidle: add LP2 resume function
ARM: tegra30: cpuidle: add LP2 driver for secondary CPUs
ARM: tegra30: common: enable csite clock
ARM: tegra30: clocks: add CPU low-power function into
tegra_cpu_car_ops
ARM: tegra30: flowctrl: add cpu_suspend_exter/exit function
ARM: tegra30: cpuidle: add LP2 driver for CPU0
arch/arm/mach-tegra/Makefile | 7 +
arch/arm/mach-tegra/common.c | 1 +
.../mach-tegra/{cpuidle.c => cpuidle-tegra20.c} | 7 +-
arch/arm/mach-tegra/cpuidle-tegra30.c | 185 ++++++++++++++++
arch/arm/mach-tegra/cpuidle.c | 47 ++---
arch/arm/mach-tegra/cpuidle.h | 32 +++
arch/arm/mach-tegra/flowctrl.c | 47 ++++
arch/arm/mach-tegra/flowctrl.h | 8 +
arch/arm/mach-tegra/headsmp.S | 58 +++++
arch/arm/mach-tegra/pm.c | 220 ++++++++++++++++++++
arch/arm/mach-tegra/pm.h | 33 +++
arch/arm/mach-tegra/reset.c | 6 +
arch/arm/mach-tegra/reset.h | 9 +
arch/arm/mach-tegra/sleep-tegra30.S | 67 ++++++
arch/arm/mach-tegra/sleep.S | 71 +++++++
arch/arm/mach-tegra/sleep.h | 5 +
arch/arm/mach-tegra/tegra30_clocks.c | 107 ++++++++++
arch/arm/mach-tegra/tegra_cpu_car.h | 37 ++++
18 files changed, 908 insertions(+), 39 deletions(-)
copy arch/arm/mach-tegra/{cpuidle.c => cpuidle-tegra20.c} (91%)
create mode 100644 arch/arm/mach-tegra/cpuidle-tegra30.c
create mode 100644 arch/arm/mach-tegra/cpuidle.h
create mode 100644 arch/arm/mach-tegra/pm.c
create mode 100644 arch/arm/mach-tegra/pm.h
^ permalink raw reply
* [PATCH v3] ARM: mxs: Add support for the Armadeus Systems APF28 module
From: Shawn Guo @ 2012-10-19 8:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1350553826-28057-1-git-send-email-julien.boibessot@free.fr>
On Thu, Oct 18, 2012 at 11:50:26AM +0200, julien.boibessot at free.fr wrote:
> From: Julien Boibessot <julien.boibessot@armadeus.com>
>
> The APF28 is a small SOM built around an i.MX28 processor with 128MBytes DDR2,
> 256MBytes NAND Flash and an Ethernet PHY.
>
> Signed-off-by: Julien Boibessot <julien.boibessot@armadeus.com>
> Signed-off-by: Gwenhael Goavec-Merou <gwenhael.goavec-merou@armadeus.com>
Applied, thanks.
^ 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