* [PATCH 09/17] omap4: pm: Add WakeupGen save/restore support
From: Santosh Shilimkar @ 2011-02-19 10:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298112158-28469-1-git-send-email-santosh.shilimkar@ti.com>
WakeupGen is lost only when device hits off-mode. Though the register
context is retained in MPUSS OFF/OSWR state, hardware recommondation is
to save/restore WakeupGen along with GIC to have consistent interrupt
state at both the blocks. The ROM code restore mechinism also does
restore of wakeupgen on mpu OFF/OSWR
Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
arch/arm/mach-omap2/include/mach/omap-wakeupgen.h | 1 +
arch/arm/mach-omap2/omap-wakeupgen.c | 74 +++++++++++++++++++++
arch/arm/mach-omap2/omap4-mpuss-lowpower.c | 2 +
arch/arm/mach-omap2/omap4-sar-layout.h | 11 +++
4 files changed, 88 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-omap2/include/mach/omap-wakeupgen.h b/arch/arm/mach-omap2/include/mach/omap-wakeupgen.h
index f10d106..66f31c3 100644
--- a/arch/arm/mach-omap2/include/mach/omap-wakeupgen.h
+++ b/arch/arm/mach-omap2/include/mach/omap-wakeupgen.h
@@ -37,4 +37,5 @@
extern int __init omap_wakeupgen_init(void);
extern void omap_wakeupgen_irqmask_all(unsigned int cpu, unsigned int set);
+extern void omap_wakeupgen_save(void);
#endif
diff --git a/arch/arm/mach-omap2/omap-wakeupgen.c b/arch/arm/mach-omap2/omap-wakeupgen.c
index e26a0ed..0f0a5ed 100644
--- a/arch/arm/mach-omap2/omap-wakeupgen.c
+++ b/arch/arm/mach-omap2/omap-wakeupgen.c
@@ -24,6 +24,9 @@
#include <asm/hardware/gic.h>
#include <mach/omap-wakeupgen.h>
+#include <mach/omap4-common.h>
+
+#include "omap4-sar-layout.h"
#define NR_BANKS 4
#define MAX_IRQS 128
@@ -54,6 +57,11 @@ static inline void cpu_writel(u32 val, u8 idx, u32 cpu)
(cpu * CPU_ENA_OFFSET) + (idx * 4));
}
+static inline void sar_writel(u32 val, u32 offset, u8 idx)
+{
+ __raw_writel(val, sar_ram_base + offset + (idx * 4));
+}
+
static void _wakeupgen_set_all(unsigned int cpu, unsigned int reg)
{
u8 i;
@@ -211,3 +219,69 @@ int __init omap_wakeupgen_init(void)
return 0;
}
+
+/**
+ * omap_wakeupgen_save() - WakeupGen context save function
+ *
+ * Save WakewupGen context in SAR BANK3. Restore is done by ROM code.
+ * WakeupGen is lost only when DEVICE hits OFF. Though the register
+ * context is retained in MPU OFF/OSWR state, hw recommondation is to
+ * save/restore WakeupGen along with GIC to have consistent interrupt
+ * state at both the blocks.
+ *
+ * During normal operation, WakeupGen delivers external interrupts
+ * directly to the GIC. When the CPU asserts StandbyWFI, indicating
+ * it wants to enter lowpower state, the Standby Controller checks
+ * with the WakeupGen unit using the idlereq/idleack handshake to make
+ * sure there is no incoming interrupts.
+ */
+
+void omap_wakeupgen_save(void)
+{
+ u8 i;
+ u32 val;
+
+ if (omap_rev() == OMAP4430_REV_ES1_0)
+ return;
+
+ for (i = 0; i < NR_BANKS; i++) {
+ /* Save the CPUx interrupt mask for IRQ 0 to 127 */
+ val = cpu_readl(i, 0);
+ sar_writel(val, WAKEUPGENENB_OFFSET_CPU0, i);
+ val = cpu_readl(i, 1);
+ sar_writel(val, WAKEUPGENENB_OFFSET_CPU1, i);
+
+ /*
+ * Disable the secure interrupts for CPUx. The restore
+ * code blindly restores secure and non-secure interrupt
+ * masks from SAR RAM. Secure interrupts are not suppose
+ * to be enabled from HLOS. So overwrite the SAR location
+ * so that the secure interrupt remains disabled.
+ */
+ sar_writel(0x0, WAKEUPGENENB_SECURE_OFFSET_CPU0, i);
+ sar_writel(0x0, WAKEUPGENENB_SECURE_OFFSET_CPU1, i);
+ }
+
+ /* Save AuxBoot* registers */
+ val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
+ __raw_writel(val, sar_ram_base + AUXCOREBOOT0_OFFSET);
+ val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
+ __raw_writel(val, sar_ram_base + AUXCOREBOOT1_OFFSET);
+
+ /* Save SyncReq generation logic */
+ val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
+ __raw_writel(val, sar_ram_base + AUXCOREBOOT0_OFFSET);
+ val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
+ __raw_writel(val, sar_ram_base + AUXCOREBOOT1_OFFSET);
+
+ /* Save SyncReq generation logic */
+ val = __raw_readl(wakeupgen_base + OMAP_PTMSYNCREQ_MASK);
+ __raw_writel(val, sar_ram_base + PTMSYNCREQ_MASK_OFFSET);
+ val = __raw_readl(wakeupgen_base + OMAP_PTMSYNCREQ_EN);
+ __raw_writel(val, sar_ram_base + PTMSYNCREQ_EN_OFFSET);
+
+ /* Set the Backup Bit Mask status */
+ val = __raw_readl(sar_ram_base + SAR_BACKUP_STATUS_OFFSET);
+ val |= SAR_BACKUP_STATUS_WAKEUPGEN;
+ __raw_writel(val, sar_ram_base + SAR_BACKUP_STATUS_OFFSET);
+}
diff --git a/arch/arm/mach-omap2/omap4-mpuss-lowpower.c b/arch/arm/mach-omap2/omap4-mpuss-lowpower.c
index 4140251..a30f19b 100644
--- a/arch/arm/mach-omap2/omap4-mpuss-lowpower.c
+++ b/arch/arm/mach-omap2/omap4-mpuss-lowpower.c
@@ -52,6 +52,7 @@
#include <plat/omap44xx.h>
#include <mach/omap4-common.h>
+#include <mach/omap-wakeupgen.h>
#include "omap4-sar-layout.h"
#include "pm.h"
@@ -290,6 +291,7 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
* GIC lost during MPU OFF and OSWR
*/
if (pwrdm_read_next_pwrst(mpuss_pd) == PWRDM_POWER_OFF) {
+ omap_wakeupgen_save();
gic_save_context();
save_state = 3;
}
diff --git a/arch/arm/mach-omap2/omap4-sar-layout.h b/arch/arm/mach-omap2/omap4-sar-layout.h
index 0a19f49..5a815c4 100644
--- a/arch/arm/mach-omap2/omap4-sar-layout.h
+++ b/arch/arm/mach-omap2/omap4-sar-layout.h
@@ -52,6 +52,17 @@
#ifndef __ASSEMBLER__
+/* WakeUpGen save restore offset from OMAP44XX_SAR_RAM_BASE */
+#define WAKEUPGENENB_OFFSET_CPU0 (SAR_BANK3_OFFSET + 0x684)
+#define WAKEUPGENENB_SECURE_OFFSET_CPU0 (SAR_BANK3_OFFSET + 0x694)
+#define WAKEUPGENENB_OFFSET_CPU1 (SAR_BANK3_OFFSET + 0x6a4)
+#define WAKEUPGENENB_SECURE_OFFSET_CPU1 (SAR_BANK3_OFFSET + 0x6b4)
+#define AUXCOREBOOT0_OFFSET (SAR_BANK3_OFFSET + 0x6c4)
+#define AUXCOREBOOT1_OFFSET (SAR_BANK3_OFFSET + 0x6c8)
+#define PTMSYNCREQ_MASK_OFFSET (SAR_BANK3_OFFSET + 0x6cc)
+#define PTMSYNCREQ_EN_OFFSET (SAR_BANK3_OFFSET + 0x6d0)
+#define SAR_BACKUP_STATUS_WAKEUPGEN 0x10
+
extern void __iomem *sar_ram_base;
#endif
--
1.6.0.4
^ permalink raw reply related
* [PATCH 10/17] omap4: pm: Add L2 cache lowpower support
From: Santosh Shilimkar @ 2011-02-19 10:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298112158-28469-1-git-send-email-santosh.shilimkar@ti.com>
When MPUSS hits off-mode e, L2 cache is lost. This patch adds L2X0
necessary maintenance operations and context restoration in the
low power code.
Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
arch/arm/mach-omap2/omap4-mpuss-lowpower.c | 11 +++++
arch/arm/mach-omap2/omap4-sar-layout.h | 2 +
arch/arm/mach-omap2/sleep44xx.S | 64 ++++++++++++++++++++++++++++
3 files changed, 77 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-omap2/omap4-mpuss-lowpower.c b/arch/arm/mach-omap2/omap4-mpuss-lowpower.c
index a30f19b..bff768f 100644
--- a/arch/arm/mach-omap2/omap4-mpuss-lowpower.c
+++ b/arch/arm/mach-omap2/omap4-mpuss-lowpower.c
@@ -49,6 +49,7 @@
#include <asm/system.h>
#include <asm/irq.h>
#include <asm/hardware/gic.h>
+#include <asm/hardware/cache-l2x0.h>
#include <plat/omap44xx.h>
#include <mach/omap4-common.h>
@@ -341,6 +342,7 @@ int __init omap4_mpuss_init(void)
{
struct omap4_cpu_pm_info *pm_info;
u8 i;
+ u32 val;
if (omap_rev() == OMAP4430_REV_ES1_0) {
WARN(1, "Power Management not supported on OMAP4430 ES1.0\n");
@@ -410,6 +412,15 @@ int __init omap4_mpuss_init(void)
for (i = 0; i < max_spi_reg; i++)
sar_writel(GIC_ISR_NON_SECURE, ICDISR_SPI_OFFSET, i);
+#ifdef CONFIG_CACHE_L2X0
+ /*
+ * Save the L2X0 AUXCTRL value to SAR memory. Its used to
+ * in every restore patch MPUSS OFF path.
+ */
+ val = __raw_readl(l2cache_base + L2X0_AUX_CTRL);
+ __raw_writel(val, sar_ram_base + L2X0_AUXCTRL_OFFSET);
+#endif
+
return 0;
}
diff --git a/arch/arm/mach-omap2/omap4-sar-layout.h b/arch/arm/mach-omap2/omap4-sar-layout.h
index 5a815c4..eb2d53b 100644
--- a/arch/arm/mach-omap2/omap4-sar-layout.h
+++ b/arch/arm/mach-omap2/omap4-sar-layout.h
@@ -25,6 +25,8 @@
#define MMU_OFFSET 0xd00
#define SCU_OFFSET0 0xd20
#define SCU_OFFSET1 0xd24
+#define L2X0_OFFSET 0xd28
+#define L2X0_AUXCTRL_OFFSET 0xd2c
/* CPUx Wakeup Non-Secure Physical Address offsets in SAR_BANK3 */
#define CPU0_WAKEUP_NS_PA_ADDR_OFFSET 0xa04
diff --git a/arch/arm/mach-omap2/sleep44xx.S b/arch/arm/mach-omap2/sleep44xx.S
index bb42a7a..e683f78 100644
--- a/arch/arm/mach-omap2/sleep44xx.S
+++ b/arch/arm/mach-omap2/sleep44xx.S
@@ -13,6 +13,7 @@
#include <asm/system.h>
#include <asm/smp_scu.h>
#include <asm/memory.h>
+#include <asm/hardware/cache-l2x0.h>
#include <plat/omap44xx.h>
#include <mach/omap4-common.h>
@@ -56,6 +57,7 @@ ENTRY(omap4_cpu_suspend)
beq do_WFI @ Nothing to save, jump to WFI
ldr r8, =sar_ram_base
ldr r8, [r8]
+ str r1, [r8, #L2X0_OFFSET] @ Store save state
ands r0, r0, #0x0f
orreq r8, r8, #CPU0_SAVE_OFFSET
orrne r8, r8, #CPU1_SAVE_OFFSET
@@ -131,6 +133,42 @@ ENTRY(omap4_cpu_suspend)
ldr r0, =scu_base
ldr r0, [r0]
bl scu_power_mode
+ isb
+ dsb
+
+#ifdef CONFIG_CACHE_L2X0
+ /*
+ * Clean and invalidate the L2 cache.
+ * Common cache-l2x0.c functions can't be used here since it
+ * uses spinlocks. We are out of coherency here with data cache
+ * disabled. The spinlock implementation uses exclusive load/store
+ * instruction which can fail without data cache being enabled.
+ * OMAP4 hardware doesn't support exclusive monitor which can
+ * overcome exclusive access issue. Because of this, CPU can
+ * lead to deadlock.
+ */
+l2x_clean_inv:
+ ldr r8, =sar_ram_base
+ ldr r8, [r8]
+ ldr r0, [r8, #L2X0_OFFSET]
+ cmp r0, #3
+ bne do_WFI
+ ldr r2, =l2cache_base
+ ldr r2, [r2]
+ mov r0, #0xff
+ str r0, [r2, #L2X0_CLEAN_WAY]
+wait:
+ ldr r0, [r2, #L2X0_CLEAN_WAY]
+ ands r0, r0, #0xff
+ bne wait
+l2x_sync:
+ mov r0, #0x0
+ str r0, [r2, #L2X0_CACHE_SYNC]
+sync:
+ ldr r0, [r2, #L2X0_CACHE_SYNC]
+ ands r0, r0, #0x1
+ bne sync
+#endif
do_WFI:
/*
@@ -193,6 +231,32 @@ ENDPROC(omap4_cpu_suspend)
*/
ENTRY(omap4_cpu_resume)
+#ifdef CONFIG_CACHE_L2X0
+ /*
+ * Restore the L2 AUXCTRL and enable the L2 cache.
+ * 0x109 = Program the L2X0 AUXCTRL
+ * 0x102 = Enable the L2 using L2X0 CTRL
+ * register r0 contains value to be programmed.
+ * L2 cache is already invalidate by ROM code as part
+ * of MPUSS OFF wakeup path.
+ */
+ ldr r2, =OMAP44XX_L2CACHE_BASE
+ ldr r0, [r2, #L2X0_CTRL]
+ and r0, #0x0f
+ cmp r0, #1
+ beq skip_l2en @ Skip if already enabled
+ ldr r3, =OMAP44XX_SAR_RAM_BASE
+ ldr r0, [r3, #L2X0_AUXCTRL_OFFSET]
+ ldr r12, =0x109 @ Setup L2 AUXCTRL value
+ dsb
+ smc #0
+ mov r0, #0x1
+ ldr r12, =0x102 @ Enable L2 Cache controller
+ dsb
+ smc #0
+skip_l2en:
+#endif
+
/*
* Check the wakeup cpuid and use appropriate
* SAR BANK location for context restore.
--
1.6.0.4
^ permalink raw reply related
* [PATCH 11/17] omap4: suspend: Add MPUSS RET and OFF support
From: Santosh Shilimkar @ 2011-02-19 10:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298112158-28469-1-git-send-email-santosh.shilimkar@ti.com>
This patch adds MPUSS(MPU Sub System) RET and OFF mode support
to suspend path. For both MPUSS RET and OFF support, CPUs are
programmed to OFF state.
Only MPUSS RET and OFF supported at this point of time. CORE RET
will be added subsequently.
Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
arch/arm/mach-omap2/omap4-mpuss-lowpower.c | 8 ++-
arch/arm/mach-omap2/pm-debug.c | 2 +
arch/arm/mach-omap2/pm.h | 1 +
arch/arm/mach-omap2/pm44xx.c | 88 ++++++++++++++++++++++++++--
4 files changed, 93 insertions(+), 6 deletions(-)
diff --git a/arch/arm/mach-omap2/omap4-mpuss-lowpower.c b/arch/arm/mach-omap2/omap4-mpuss-lowpower.c
index bff768f..ab2e101 100644
--- a/arch/arm/mach-omap2/omap4-mpuss-lowpower.c
+++ b/arch/arm/mach-omap2/omap4-mpuss-lowpower.c
@@ -25,7 +25,7 @@
* ON(Inactive) OFF ON(Inactive)
* OFF OFF CSWR
* OFF OFF OSWR (*TBD)
- * OFF OFF OFF* (*TBD)
+ * OFF OFF OFF
* ----------------------------------------------
*
* Note: CPU0 is the master core and it is the last CPU to go down
@@ -291,6 +291,7 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
* Check MPUSS next state and save GIC if needed
* GIC lost during MPU OFF and OSWR
*/
+ pwrdm_clear_all_prev_pwrst(mpuss_pd);
if (pwrdm_read_next_pwrst(mpuss_pd) == PWRDM_POWER_OFF) {
omap_wakeupgen_save();
gic_save_context();
@@ -357,6 +358,8 @@ int __init omap4_mpuss_init(void)
pr_err("Lookup failed for CPU0 pwrdm\n");
return -ENODEV;
}
+ /* Clear CPU previous power domain state */
+ pwrdm_clear_all_prev_pwrst(pm_info->pwrdm);
/* Clear CPU previous power domain state */
pwrdm_clear_all_prev_pwrst(pm_info->pwrdm);
@@ -394,6 +397,9 @@ int __init omap4_mpuss_init(void)
return -ENODEV;
}
+ /* Clear CPU previous power domain state */
+ pwrdm_clear_all_prev_pwrst(mpuss_pd);
+
/*
* Find out how many interrupts are supported.
* OMAP4 supports max of 128 SPIs where as GIC can support
diff --git a/arch/arm/mach-omap2/pm-debug.c b/arch/arm/mach-omap2/pm-debug.c
index 125f565..9b46b3e 100644
--- a/arch/arm/mach-omap2/pm-debug.c
+++ b/arch/arm/mach-omap2/pm-debug.c
@@ -588,6 +588,8 @@ static int option_set(void *data, u64 val)
omap_pm_disable_off_mode();
if (cpu_is_omap34xx())
omap3_pm_off_mode_enable(val);
+ else if (cpu_is_omap44xx())
+ omap4_pm_off_mode_enable(val);
}
return 0;
diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h
index 1c1b0ab..f557407 100644
--- a/arch/arm/mach-omap2/pm.h
+++ b/arch/arm/mach-omap2/pm.h
@@ -17,6 +17,7 @@
extern void *omap3_secure_ram_storage;
extern void omap3_pm_off_mode_enable(int);
+extern void omap4_pm_off_mode_enable(int);
extern void omap_sram_idle(void);
extern int omap3_can_sleep(void);
extern int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state);
diff --git a/arch/arm/mach-omap2/pm44xx.c b/arch/arm/mach-omap2/pm44xx.c
index 9a9dc41..f527e33 100644
--- a/arch/arm/mach-omap2/pm44xx.c
+++ b/arch/arm/mach-omap2/pm44xx.c
@@ -1,8 +1,9 @@
/*
* OMAP4 Power Management Routines
*
- * Copyright (C) 2010 Texas Instruments, Inc.
+ * Copyright (C) 2010-2011 Texas Instruments, Inc.
* Rajendra Nayak <rnayak@ti.com>
+ * Santosh Shilimkar <santosh.shilimkar@ti.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
@@ -21,6 +22,7 @@
#include "powerdomain.h"
#include "clockdomain.h"
+#include "pm.h"
struct power_state {
struct powerdomain *pwrdm;
@@ -36,7 +38,50 @@ static LIST_HEAD(pwrst_list);
#ifdef CONFIG_SUSPEND
static int omap4_pm_suspend(void)
{
- do_wfi();
+ struct power_state *pwrst;
+ int state, ret = 0;
+ u32 cpu_id = smp_processor_id();
+
+ /* Save current powerdomain state */
+ list_for_each_entry(pwrst, &pwrst_list, node) {
+ pwrst->saved_state = pwrdm_read_next_pwrst(pwrst->pwrdm);
+ }
+
+ /* Set targeted power domain states by suspend */
+ list_for_each_entry(pwrst, &pwrst_list, node) {
+ /* FIXME: Remove this check when CORE retention is supported */
+ if (!strcmp(pwrst->pwrdm->name, "mpu_pwrdm"))
+ omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state);
+ }
+
+ /*
+ * For MPUSS to hit power domain retention(CSWR or OSWR),
+ * CPU0 and CPU1 power domain needs to be in OFF or DORMANT
+ * state. For MPUSS to reach off-mode. CPU0 and CPU1 power domain
+ * should be in off state.
+ * Only master CPU followes suspend path. All other CPUs follow
+ * cpu-hotplug path in system wide suspend. On OMAP4, CPU power
+ * domain CSWR is not supported by hardware.
+ * More details can be found in OMAP4430 TRM section 4.3.4.2.
+ */
+ omap4_enter_lowpower(cpu_id, PWRDM_POWER_OFF);
+
+ /* Restore next powerdomain state */
+ list_for_each_entry(pwrst, &pwrst_list, node) {
+ state = pwrdm_read_prev_pwrst(pwrst->pwrdm);
+ if (state > pwrst->next_state) {
+ pr_info("Powerdomain (%s) didn't enter "
+ "target state %d\n",
+ pwrst->pwrdm->name, pwrst->next_state);
+ ret = -1;
+ }
+ omap_set_pwrdm_state(pwrst->pwrdm, pwrst->saved_state);
+ }
+ if (ret)
+ pr_err("Could not enter target state in pm_suspend\n");
+ else
+ pr_err("Successfully put all powerdomains to target state\n");
+
return 0;
}
@@ -91,7 +136,6 @@ static int __init clkdms_setup(struct clockdomain *clkdm, void *unused)
return 0;
}
-
static int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused)
{
struct power_state *pwrst;
@@ -99,14 +143,48 @@ static int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused)
if (!pwrdm->pwrsts)
return 0;
+ /*
+ * Skip CPU0 and CPU1 power domains. CPU1 is programmed
+ * through hotplug path and CPU0 explicitly programmed
+ * further down in the code path
+ */
+ if ((!strcmp(pwrdm->name, "cpu0_pwrdm")) ||
+ (!strcmp(pwrdm->name, "cpu1_pwrdm")))
+ return 0;
+
pwrst = kmalloc(sizeof(struct power_state), GFP_ATOMIC);
if (!pwrst)
return -ENOMEM;
pwrst->pwrdm = pwrdm;
- pwrst->next_state = PWRDM_POWER_ON;
+
+ /* FIXME: Remove this check when core retention is supported */
+ if (!strcmp(pwrst->pwrdm->name, "mpu_pwrdm"))
+ pwrst->next_state = PWRDM_POWER_RET;
+ else
+ pwrst->next_state = PWRDM_POWER_ON;
+
list_add(&pwrst->node, &pwrst_list);
- return pwrdm_set_next_pwrst(pwrst->pwrdm, pwrst->next_state);
+ return omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state);
+}
+
+void omap4_pm_off_mode_enable(int enable)
+{
+ struct power_state *pwrst;
+ u32 state;
+
+ if (enable)
+ state = PWRDM_POWER_OFF;
+ else
+ state = PWRDM_POWER_RET;
+
+ list_for_each_entry(pwrst, &pwrst_list, node) {
+ /* FIXME: Remove this check when core retention is supported */
+ if (!strcmp(pwrst->pwrdm->name, "mpu_pwrdm")) {
+ pwrst->next_state = state;
+ omap_set_pwrdm_state(pwrst->pwrdm, state);
+ }
+ }
}
/**
--
1.6.0.4
^ permalink raw reply related
* [PATCH 12/17] omap4: pm-debug: Add wakeup timer and debug counters
From: Santosh Shilimkar @ 2011-02-19 10:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298112158-28469-1-git-send-email-santosh.shilimkar@ti.com>
This patch adds configurable wakeup timer support in suspend. Also
for statistics pm counter support is added.
Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
arch/arm/mach-omap2/omap4-mpuss-lowpower.c | 8 ++++++++
arch/arm/mach-omap2/pm-debug.c | 6 ++++--
arch/arm/mach-omap2/pm44xx.c | 5 +++++
3 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-omap2/omap4-mpuss-lowpower.c b/arch/arm/mach-omap2/omap4-mpuss-lowpower.c
index ab2e101..5e0141e 100644
--- a/arch/arm/mach-omap2/omap4-mpuss-lowpower.c
+++ b/arch/arm/mach-omap2/omap4-mpuss-lowpower.c
@@ -287,6 +287,10 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
if (cpu)
goto cpu_prepare;
+#ifdef CONFIG_PM_DEBUG
+ pwrdm_pre_transition();
+#endif
+
/*
* Check MPUSS next state and save GIC if needed
* GIC lost during MPU OFF and OSWR
@@ -332,6 +336,10 @@ cpu_prepare:
gic_dist_set(CPU0_ID, 1);
}
+#ifdef CONFIG_PM_DEBUG
+ pwrdm_post_transition();
+#endif
+
ret:
return 0;
}
diff --git a/arch/arm/mach-omap2/pm-debug.c b/arch/arm/mach-omap2/pm-debug.c
index 9b46b3e..7d6dd5b 100644
--- a/arch/arm/mach-omap2/pm-debug.c
+++ b/arch/arm/mach-omap2/pm-debug.c
@@ -606,9 +606,11 @@ static int __init pm_dbg_init(void)
if (pm_dbg_init_done)
return 0;
- if (cpu_is_omap34xx())
+ if (cpu_is_omap34xx()) {
pm_dbg_reg_modules = omap3_pm_reg_modules;
- else {
+ } else if (cpu_is_omap44xx()) {
+ /* Allow pm_dbg_init on OMAP4. */
+ } else {
printk(KERN_ERR "%s: only OMAP3 supported\n", __func__);
return -ENODEV;
}
diff --git a/arch/arm/mach-omap2/pm44xx.c b/arch/arm/mach-omap2/pm44xx.c
index f527e33..8e57b42 100644
--- a/arch/arm/mach-omap2/pm44xx.c
+++ b/arch/arm/mach-omap2/pm44xx.c
@@ -42,6 +42,11 @@ static int omap4_pm_suspend(void)
int state, ret = 0;
u32 cpu_id = smp_processor_id();
+ /* Wakeup timer from suspend */
+ if (wakeup_timer_seconds || wakeup_timer_milliseconds)
+ omap2_pm_wakeup_on_timer(wakeup_timer_seconds,
+ wakeup_timer_milliseconds);
+
/* Save current powerdomain state */
list_for_each_entry(pwrst, &pwrst_list, node) {
pwrst->saved_state = pwrdm_read_next_pwrst(pwrst->pwrdm);
--
1.6.0.4
^ permalink raw reply related
* [PATCH 13/17] omap4: cpuidle: Basic CPUidle support
From: Santosh Shilimkar @ 2011-02-19 10:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298112158-28469-1-git-send-email-santosh.shilimkar@ti.com>
From: Rajendra Nayak <rnayak@ti.com>
The patch adds a basic CPUidle driver for OMAP4. Just
one C state is registered for both CPU cores which
does a wfi.
Signed-off-by: Rajendra Nayak <rnayak@ti.com>
Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
arch/arm/mach-omap2/Makefile | 3 +-
arch/arm/mach-omap2/cpuidle44xx.c | 165 +++++++++++++++++++++++++++++++++++++
arch/arm/mach-omap2/pm.h | 1 +
arch/arm/mach-omap2/pm44xx.c | 2 +
4 files changed, 170 insertions(+), 1 deletions(-)
create mode 100644 arch/arm/mach-omap2/cpuidle44xx.c
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 5d94f7e..2b4fe44 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -64,7 +64,8 @@ obj-$(CONFIG_ARCH_OMAP2) += sleep24xx.o pm_bus.o voltage.o
obj-$(CONFIG_ARCH_OMAP3) += pm34xx.o sleep34xx.o voltage.o \
cpuidle34xx.o pm_bus.o
obj-$(CONFIG_ARCH_OMAP4) += pm44xx.o voltage.o pm_bus.o \
- omap4-mpuss-lowpower.o sleep44xx.o
+ omap4-mpuss-lowpower.o sleep44xx.o \
+ cpuidle44xx.o
obj-$(CONFIG_PM_DEBUG) += pm-debug.o
obj-$(CONFIG_OMAP_SMARTREFLEX) += sr_device.o smartreflex.o
obj-$(CONFIG_OMAP_SMARTREFLEX_CLASS3) += smartreflex-class3.o
diff --git a/arch/arm/mach-omap2/cpuidle44xx.c b/arch/arm/mach-omap2/cpuidle44xx.c
new file mode 100644
index 0000000..6c3c69d
--- /dev/null
+++ b/arch/arm/mach-omap2/cpuidle44xx.c
@@ -0,0 +1,165 @@
+/*
+ * OMAP4 CPU IDLE Routines
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Rajendra Nayak <rnayak@ti.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.
+ */
+
+#include <linux/sched.h>
+#include <linux/cpuidle.h>
+
+#include <asm/proc-fns.h>
+
+#include <mach/omap4-common.h>
+
+#include "pm.h"
+
+#ifdef CONFIG_CPU_IDLE
+
+#define OMAP4_MAX_STATES 1
+/* C1 - CPUx wfi + MPU inactive + CORE inactive */
+#define OMAP4_STATE_C1 0
+
+struct omap4_processor_cx {
+ u8 valid;
+ u8 type;
+ u32 sleep_latency;
+ u32 wakeup_latency;
+ u32 cpu0_state;
+ u32 cpu1_state;
+ u32 mpu_state;
+ u32 core_state;
+ u32 threshold;
+ u32 flags;
+};
+
+struct omap4_processor_cx omap4_power_states[OMAP4_MAX_STATES];
+struct omap4_processor_cx current_cx_state;
+
+static struct cpuidle_params cpuidle_params_table[] = {
+ /* C1 */
+ {1, 2, 2, 5},
+};
+
+/**
+ * omap4_enter_idle - Programs OMAP4 to enter the specified state
+ * @dev: cpuidle device
+ * @state: The target state to be programmed
+ *
+ * Called from the CPUidle framework to program the device to the
+ * specified low power state selected by the governor.
+ * Returns the amount of time spent in the low power state.
+ */
+static int omap4_enter_idle(struct cpuidle_device *dev,
+ struct cpuidle_state *state)
+{
+ struct timespec ts_preidle, ts_postidle, ts_idle;
+
+ /* Used to keep track of the total time in idle */
+ getnstimeofday(&ts_preidle);
+
+ local_irq_disable();
+ local_fiq_disable();
+
+ cpu_do_idle();
+
+ getnstimeofday(&ts_postidle);
+ ts_idle = timespec_sub(ts_postidle, ts_preidle);
+
+ local_irq_enable();
+ local_fiq_enable();
+
+ return ts_idle.tv_nsec / NSEC_PER_USEC + ts_idle.tv_sec * USEC_PER_SEC;
+}
+
+DEFINE_PER_CPU(struct cpuidle_device, omap4_idle_dev);
+
+/**
+ * omap4_init_power_states - Initialises the OMAP4 specific C states.
+ *
+ * Below is the desciption of each C state.
+ * C1 : CPUx wfi + MPU inative + Core inactive
+ */
+void omap_init_power_states(void)
+{
+ /* C1 . CPUx wfi + MPU inactive + Core inactive */
+ omap4_power_states[OMAP4_STATE_C1].valid =
+ cpuidle_params_table[OMAP4_STATE_C1].valid;
+ omap4_power_states[OMAP4_STATE_C1].type = OMAP4_STATE_C1;
+ omap4_power_states[OMAP4_STATE_C1].sleep_latency =
+ cpuidle_params_table[OMAP4_STATE_C1].sleep_latency;
+ omap4_power_states[OMAP4_STATE_C1].wakeup_latency =
+ cpuidle_params_table[OMAP4_STATE_C1].wake_latency;
+ omap4_power_states[OMAP4_STATE_C1].threshold =
+ cpuidle_params_table[OMAP4_STATE_C1].threshold;
+ omap4_power_states[OMAP4_STATE_C1].mpu_state = PWRDM_POWER_ON;
+ omap4_power_states[OMAP4_STATE_C1].core_state = PWRDM_POWER_ON;
+ omap4_power_states[OMAP4_STATE_C1].flags = CPUIDLE_FLAG_TIME_VALID;
+
+}
+
+struct cpuidle_driver omap4_idle_driver = {
+ .name = "omap4_idle",
+ .owner = THIS_MODULE,
+};
+
+/**
+ * omap4_idle_init - Init routine for OMAP4 idle
+ *
+ * Registers the OMAP4 specific cpuidle driver with the cpuidle
+ * framework with the valid set of states.
+ */
+int __init omap4_idle_init(void)
+{
+ int cpu_id, i, count = 0;
+ struct omap4_processor_cx *cx;
+ struct cpuidle_state *state;
+ struct cpuidle_device *dev;
+
+ omap_init_power_states();
+ cpuidle_register_driver(&omap4_idle_driver);
+
+ for_each_cpu(cpu_id, cpu_online_mask) {
+ pr_err("CPUidle for CPU%d registered\n", cpu_id);
+ dev = &per_cpu(omap4_idle_dev, cpu_id);
+ dev->cpu = cpu_id;
+ count = 0;
+ for (i = OMAP4_STATE_C1; i < OMAP4_MAX_STATES; i++) {
+ cx = &omap4_power_states[i];
+ state = &dev->states[count];
+
+ if (!cx->valid)
+ continue;
+ cpuidle_set_statedata(state, cx);
+ state->exit_latency = cx->sleep_latency +
+ cx->wakeup_latency;
+ state->target_residency = cx->threshold;
+ state->flags = cx->flags;
+ state->enter = omap4_enter_idle;
+ sprintf(state->name, "C%d", count+1);
+ count++;
+ }
+
+ if (!count)
+ return -EINVAL;
+ dev->state_count = count;
+
+ if (cpuidle_register_device(dev)) {
+ pr_err("%s: CPUidle register device failed\n",
+ __func__);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+#else
+int __init omap4_idle_init(void)
+{
+ return 0;
+}
+#endif /* CONFIG_CPU_IDLE */
diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h
index f557407..ce848b0 100644
--- a/arch/arm/mach-omap2/pm.h
+++ b/arch/arm/mach-omap2/pm.h
@@ -22,6 +22,7 @@ extern void omap_sram_idle(void);
extern int omap3_can_sleep(void);
extern int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state);
extern int omap3_idle_init(void);
+extern int omap4_idle_init(void);
#if defined(CONFIG_PM_OPP)
extern int omap3_opp_init(void);
diff --git a/arch/arm/mach-omap2/pm44xx.c b/arch/arm/mach-omap2/pm44xx.c
index 8e57b42..628242d 100644
--- a/arch/arm/mach-omap2/pm44xx.c
+++ b/arch/arm/mach-omap2/pm44xx.c
@@ -230,6 +230,8 @@ static int __init omap4_pm_init(void)
suspend_set_ops(&omap_pm_ops);
#endif /* CONFIG_SUSPEND */
+ omap4_idle_init();
+
err2:
return ret;
}
--
1.6.0.4
^ permalink raw reply related
* [PATCH 14/17] omap4: cpuidle: Add MPUSS RET OFF states
From: Santosh Shilimkar @ 2011-02-19 10:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298112158-28469-1-git-send-email-santosh.shilimkar@ti.com>
This patch adds MPUSS low power states in cpuidle.
C1 - CPU0 ON + CPU1 ON/OFF + MPU ON + CORE ON
C2 - CPU0 ON + CPU1 OFF + MPU ON + CORE ON
C3 - CPU0 OFF + CPU1 OFF + MPU CSWR + CORE ON
C4 - CPU0 OFF + CPU1 OFF + MPU OFF + CORE ON
MPU OSWR isn't supported yet. To support OSWR, power domain context
registers needs to be managed which are not supported yet. A patch
to address this was submitted but it's not ready for merge yet because
it was not addressing all OMAP4 power domain context registers.
More info on this issue:
http://www.mail-archive.com/linux-omap at vger.kernel.org/msg38667.html
OMAP4 powerdomain INACTIVE support is also dropped because of inconsistency
of it with OMAP3. More information on this thread.
http://www.spinics.net/lists/linux-omap/msg45370.html
CORE low power states and associated latencies will be updated as part
along with chip retention support.
On OMAP4 because of hardware constraints, no low power states are
targeted when both CPUs are online and in SMP mode. The low power
states are attempted only when secondary CPU gets offline to OFF
through hotplug infrastructure.
Thanks to Nicole Chalhoub <n-chalhoub@ti.com> for doing exhaustive
C-state latency profiling.
Signed-off-by: Rajendra Nayak <rnayak@ti.com>
Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
arch/arm/mach-omap2/cpuidle44xx.c | 190 ++++++++++++++++++++++++++++++++++---
1 files changed, 176 insertions(+), 14 deletions(-)
diff --git a/arch/arm/mach-omap2/cpuidle44xx.c b/arch/arm/mach-omap2/cpuidle44xx.c
index 6c3c69d..aa1584e 100644
--- a/arch/arm/mach-omap2/cpuidle44xx.c
+++ b/arch/arm/mach-omap2/cpuidle44xx.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 2011 Texas Instruments, Inc.
* Rajendra Nayak <rnayak@ti.com>
+ * Santosh Shilimkar <santosh.shilimkar@ti.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
@@ -17,12 +18,21 @@
#include <mach/omap4-common.h>
#include "pm.h"
+#include "prm.h"
#ifdef CONFIG_CPU_IDLE
-#define OMAP4_MAX_STATES 1
-/* C1 - CPUx wfi + MPU inactive + CORE inactive */
+#define CPUIDLE_FLAG_CHECK_BM 0x10000 /* use omap4_enter_idle_bm() */
+#define OMAP4_MAX_STATES 4
+
+/* C1 - CPU0 ON + CPU1 ON/OFF + MPU ON + CORE ON */
#define OMAP4_STATE_C1 0
+/* C2 - CPU0 ON + CPU1 OFF + MPU ON + CORE ON */
+#define OMAP4_STATE_C2 1
+/* C3 - CPU0 OFF + CPU1 OFF + MPU CSWR + CORE ON */
+#define OMAP4_STATE_C3 2
+/* C4 - CPU0 OFF + CPU1 OFF + MPU OFF + CORE ON */
+#define OMAP4_STATE_C4 3
struct omap4_processor_cx {
u8 valid;
@@ -32,19 +42,44 @@ struct omap4_processor_cx {
u32 cpu0_state;
u32 cpu1_state;
u32 mpu_state;
+ u32 mpu_logic_state;
u32 core_state;
+ u32 core_logic_state;
u32 threshold;
u32 flags;
+ const char *desc;
};
-struct omap4_processor_cx omap4_power_states[OMAP4_MAX_STATES];
-struct omap4_processor_cx current_cx_state;
+static struct omap4_processor_cx omap4_power_states[OMAP4_MAX_STATES];
+static struct powerdomain *mpu_pd, *cpu1_pd, *core_pd;
+/*
+ * FIXME: Full latenecy numbers needs to be updated as part of
+ * cpuidle CORE retention support.
+ * Currently only MPUSS latency numbers are added based on
+ * measurements done internally. The numbers for MPUSS are
+ * not board dependent and hence set directly here instead of
+ * passing it from board files.
+ */
static struct cpuidle_params cpuidle_params_table[] = {
- /* C1 */
- {1, 2, 2, 5},
+ /* C1 - CPU0 WFI + CPU1 ON/OFF + MPU ON + CORE ON */
+ {1, 2, 2, 5},
+ /* C2 - CPU0 ON + CPU1 OFF + MPU ON + CORE ON */
+ {1, 140, 160, 300},
+ /* C3 - CPU0 OFF + CPU1 OFF + MPU CSWR + CORE ON */
+ {1, 200, 300, 700},
+ /* C4 - CPU0 OFF + CPU1 OFF + MPU OFF + CORE ON */
+ {1, 1400, 600, 5000},
};
+DEFINE_PER_CPU(struct cpuidle_device, omap4_idle_dev);
+
+static int omap4_idle_bm_check(void)
+{
+ /* FIXME: Populate this with CORE retention support */
+ return 0;
+}
+
/**
* omap4_enter_idle - Programs OMAP4 to enter the specified state
* @dev: cpuidle device
@@ -57,7 +92,9 @@ static struct cpuidle_params cpuidle_params_table[] = {
static int omap4_enter_idle(struct cpuidle_device *dev,
struct cpuidle_state *state)
{
+ struct omap4_processor_cx *cx = cpuidle_get_statedata(state);
struct timespec ts_preidle, ts_postidle, ts_idle;
+ u32 cpu1_state;
/* Used to keep track of the total time in idle */
getnstimeofday(&ts_preidle);
@@ -65,28 +102,74 @@ static int omap4_enter_idle(struct cpuidle_device *dev,
local_irq_disable();
local_fiq_disable();
- cpu_do_idle();
+ /*
+ * Special hardware/software considerations:
+ * 1. Do only WFI for secondary CPU(non-boot - CPU1).
+ * Secondary cores are taken down only via hotplug path.
+ * 2. Do only a WFI as long as in SMP mode.
+ * 3. Continue to do only WFI till CPU1 hits OFF state.
+ * This is necessary to honour hardware recommondation
+ * of triggeing all the possible low power modes once CPU1 is
+ * out of coherency and in OFF mode.
+ * Update dev->last_state so that governor stats reflects right
+ * data.
+ */
+ cpu1_state = pwrdm_read_pwrst(cpu1_pd);
+ if ((dev->cpu) || (num_online_cpus() > 1) ||
+ (cpu1_state != PWRDM_POWER_OFF)) {
+ dev->last_state = dev->safe_state;
+ cpu_do_idle();
+ goto return_sleep_time;
+ }
+ pwrdm_set_logic_retst(mpu_pd, cx->mpu_logic_state);
+ omap_set_pwrdm_state(mpu_pd, cx->mpu_state);
+ pwrdm_set_logic_retst(core_pd, cx->core_logic_state);
+ omap_set_pwrdm_state(core_pd, cx->core_state);
+
+ omap4_enter_lowpower(dev->cpu, cx->cpu0_state);
+
+return_sleep_time:
getnstimeofday(&ts_postidle);
ts_idle = timespec_sub(ts_postidle, ts_preidle);
local_irq_enable();
local_fiq_enable();
+
return ts_idle.tv_nsec / NSEC_PER_USEC + ts_idle.tv_sec * USEC_PER_SEC;
}
-DEFINE_PER_CPU(struct cpuidle_device, omap4_idle_dev);
+/**
+ * omap4_enter_idle_bm - Checks for any bus activity
+ * @dev: cpuidle device
+ * @state: The target state to be programmed
+ *
+ * Used for C states with CPUIDLE_FLAG_CHECK_BM flag set. This
+ * function checks for any pending activity and then programs the
+ * device to the specified or a safer state.
+ */
+static int omap4_enter_idle_bm(struct cpuidle_device *dev,
+ struct cpuidle_state *state)
+{
+ if ((state->flags & CPUIDLE_FLAG_CHECK_BM) && omap4_idle_bm_check()) {
+ BUG_ON(!dev->safe_state);
+ state = dev->safe_state;
+ }
+
+ dev->last_state = state;
+ return omap4_enter_idle(dev, state);
+}
/**
* omap4_init_power_states - Initialises the OMAP4 specific C states.
*
- * Below is the desciption of each C state.
- * C1 : CPUx wfi + MPU inative + Core inactive
*/
-void omap_init_power_states(void)
+void omap4_init_power_states(void)
{
- /* C1 . CPUx wfi + MPU inactive + Core inactive */
+ /*
+ * C1 - CPU0 WFI + CPU1 OFF + MPU ON + CORE ON
+ */
omap4_power_states[OMAP4_STATE_C1].valid =
cpuidle_params_table[OMAP4_STATE_C1].valid;
omap4_power_states[OMAP4_STATE_C1].type = OMAP4_STATE_C1;
@@ -96,9 +179,80 @@ void omap_init_power_states(void)
cpuidle_params_table[OMAP4_STATE_C1].wake_latency;
omap4_power_states[OMAP4_STATE_C1].threshold =
cpuidle_params_table[OMAP4_STATE_C1].threshold;
+ omap4_power_states[OMAP4_STATE_C1].cpu0_state = PWRDM_POWER_ON;
+ omap4_power_states[OMAP4_STATE_C1].cpu1_state = PWRDM_POWER_OFF;
omap4_power_states[OMAP4_STATE_C1].mpu_state = PWRDM_POWER_ON;
+ omap4_power_states[OMAP4_STATE_C1].mpu_logic_state = PWRDM_POWER_RET;
omap4_power_states[OMAP4_STATE_C1].core_state = PWRDM_POWER_ON;
+ omap4_power_states[OMAP4_STATE_C1].core_logic_state = PWRDM_POWER_RET;
omap4_power_states[OMAP4_STATE_C1].flags = CPUIDLE_FLAG_TIME_VALID;
+ omap4_power_states[OMAP4_STATE_C1].desc = "MPU ON + CORE ON";
+
+ /*
+ * C2 - CPU0 ON + CPU1 OFF + MPU ON + CORE ON
+ */
+ omap4_power_states[OMAP4_STATE_C2].valid =
+ cpuidle_params_table[OMAP4_STATE_C2].valid;
+ omap4_power_states[OMAP4_STATE_C2].type = OMAP4_STATE_C2;
+ omap4_power_states[OMAP4_STATE_C2].sleep_latency =
+ cpuidle_params_table[OMAP4_STATE_C2].sleep_latency;
+ omap4_power_states[OMAP4_STATE_C2].wakeup_latency =
+ cpuidle_params_table[OMAP4_STATE_C2].wake_latency;
+ omap4_power_states[OMAP4_STATE_C2].threshold =
+ cpuidle_params_table[OMAP4_STATE_C2].threshold;
+ omap4_power_states[OMAP4_STATE_C2].cpu0_state = PWRDM_POWER_ON;
+ omap4_power_states[OMAP4_STATE_C2].cpu1_state = PWRDM_POWER_OFF;
+ omap4_power_states[OMAP4_STATE_C2].mpu_state = PWRDM_POWER_ON;
+ omap4_power_states[OMAP4_STATE_C2].mpu_logic_state = PWRDM_POWER_RET;
+ omap4_power_states[OMAP4_STATE_C2].core_state = PWRDM_POWER_ON;
+ omap4_power_states[OMAP4_STATE_C2].core_logic_state = PWRDM_POWER_RET;
+ omap4_power_states[OMAP4_STATE_C2].flags = CPUIDLE_FLAG_TIME_VALID |
+ CPUIDLE_FLAG_CHECK_BM;
+ omap4_power_states[OMAP4_STATE_C2].desc = "MPU ON + CORE ON";
+
+ /*
+ * C3 - CPU0 OFF + CPU1 OFF + MPU CSWR + CORE ON
+ */
+ omap4_power_states[OMAP4_STATE_C3].valid =
+ cpuidle_params_table[OMAP4_STATE_C3].valid;
+ omap4_power_states[OMAP4_STATE_C3].type = OMAP4_STATE_C3;
+ omap4_power_states[OMAP4_STATE_C3].sleep_latency =
+ cpuidle_params_table[OMAP4_STATE_C3].sleep_latency;
+ omap4_power_states[OMAP4_STATE_C3].wakeup_latency =
+ cpuidle_params_table[OMAP4_STATE_C3].wake_latency;
+ omap4_power_states[OMAP4_STATE_C3].threshold =
+ cpuidle_params_table[OMAP4_STATE_C3].threshold;
+ omap4_power_states[OMAP4_STATE_C3].cpu0_state = PWRDM_POWER_OFF;
+ omap4_power_states[OMAP4_STATE_C3].cpu1_state = PWRDM_POWER_OFF;
+ omap4_power_states[OMAP4_STATE_C3].mpu_state = PWRDM_POWER_RET;
+ omap4_power_states[OMAP4_STATE_C3].mpu_logic_state = PWRDM_POWER_RET;
+ omap4_power_states[OMAP4_STATE_C3].core_state = PWRDM_POWER_ON;
+ omap4_power_states[OMAP4_STATE_C3].core_logic_state = PWRDM_POWER_RET;
+ omap4_power_states[OMAP4_STATE_C3].flags = CPUIDLE_FLAG_TIME_VALID |
+ CPUIDLE_FLAG_CHECK_BM;
+ omap4_power_states[OMAP4_STATE_C3].desc = "MPU CSWR + CORE ON";
+
+ /*
+ * C4 - CPU0 OFF + CPU1 OFF + MPU OFF + CORE ON
+ */
+ omap4_power_states[OMAP4_STATE_C4].valid =
+ cpuidle_params_table[OMAP4_STATE_C4].valid;
+ omap4_power_states[OMAP4_STATE_C4].type = OMAP4_STATE_C4;
+ omap4_power_states[OMAP4_STATE_C4].sleep_latency =
+ cpuidle_params_table[OMAP4_STATE_C4].sleep_latency;
+ omap4_power_states[OMAP4_STATE_C4].wakeup_latency =
+ cpuidle_params_table[OMAP4_STATE_C4].wake_latency;
+ omap4_power_states[OMAP4_STATE_C4].threshold =
+ cpuidle_params_table[OMAP4_STATE_C4].threshold;
+ omap4_power_states[OMAP4_STATE_C4].cpu0_state = PWRDM_POWER_OFF;
+ omap4_power_states[OMAP4_STATE_C4].cpu1_state = PWRDM_POWER_OFF;
+ omap4_power_states[OMAP4_STATE_C4].mpu_state = PWRDM_POWER_OFF;
+ omap4_power_states[OMAP4_STATE_C4].mpu_logic_state = PWRDM_POWER_OFF;
+ omap4_power_states[OMAP4_STATE_C4].core_state = PWRDM_POWER_ON;
+ omap4_power_states[OMAP4_STATE_C4].core_logic_state = PWRDM_POWER_RET;
+ omap4_power_states[OMAP4_STATE_C4].flags = CPUIDLE_FLAG_TIME_VALID |
+ CPUIDLE_FLAG_CHECK_BM;
+ omap4_power_states[OMAP4_STATE_C4].desc = "MPU OFF + CORE ON";
}
@@ -120,7 +274,11 @@ int __init omap4_idle_init(void)
struct cpuidle_state *state;
struct cpuidle_device *dev;
- omap_init_power_states();
+ mpu_pd = pwrdm_lookup("mpu_pwrdm");
+ cpu1_pd = pwrdm_lookup("cpu1_pwrdm");
+ core_pd = pwrdm_lookup("core_pwrdm");
+
+ omap4_init_power_states();
cpuidle_register_driver(&omap4_idle_driver);
for_each_cpu(cpu_id, cpu_online_mask) {
@@ -139,8 +297,12 @@ int __init omap4_idle_init(void)
cx->wakeup_latency;
state->target_residency = cx->threshold;
state->flags = cx->flags;
- state->enter = omap4_enter_idle;
+ if (cx->type == OMAP4_STATE_C1)
+ dev->safe_state = state;
+ state->enter = (state->flags & CPUIDLE_FLAG_CHECK_BM) ?
+ omap4_enter_idle_bm : omap4_enter_idle;
sprintf(state->name, "C%d", count+1);
+ strncpy(state->desc, cx->desc, CPUIDLE_DESC_LEN);
count++;
}
--
1.6.0.4
^ permalink raw reply related
* [PATCH 15/17] omap4: cpuidle: Switch to gptimer from twd in deeper C-states.
From: Santosh Shilimkar @ 2011-02-19 10:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298112158-28469-1-git-send-email-santosh.shilimkar@ti.com>
CPU local timer(TWD) stops when the CPU is transitioning into
deeper C-States. Since these timers are not wakeup capable, we
need the wakeup capable global timer to program the wakeup time
depending on the next timer expiry.
It can be handled by registering a global wakeup capable timer along
with local timers marked with (mis)feature flag CLOCK_EVT_FEAT_C3STOP.
Then notify the clock events layer from idle code using
CLOCK_EVT_NOTIFY_BROADCAST_ENTER/EXIT).
Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
arch/arm/mach-omap2/cpuidle44xx.c | 8 ++++++++
1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-omap2/cpuidle44xx.c b/arch/arm/mach-omap2/cpuidle44xx.c
index aa1584e..e887eb5 100644
--- a/arch/arm/mach-omap2/cpuidle44xx.c
+++ b/arch/arm/mach-omap2/cpuidle44xx.c
@@ -12,6 +12,7 @@
#include <linux/sched.h>
#include <linux/cpuidle.h>
+#include <linux/clockchips.h>
#include <asm/proc-fns.h>
@@ -95,6 +96,7 @@ static int omap4_enter_idle(struct cpuidle_device *dev,
struct omap4_processor_cx *cx = cpuidle_get_statedata(state);
struct timespec ts_preidle, ts_postidle, ts_idle;
u32 cpu1_state;
+ int cpu_id = smp_processor_id();
/* Used to keep track of the total time in idle */
getnstimeofday(&ts_preidle);
@@ -127,8 +129,14 @@ static int omap4_enter_idle(struct cpuidle_device *dev,
pwrdm_set_logic_retst(core_pd, cx->core_logic_state);
omap_set_pwrdm_state(core_pd, cx->core_state);
+ if (cx->type > OMAP4_STATE_C1)
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu_id);
+
omap4_enter_lowpower(dev->cpu, cx->cpu0_state);
+ if (cx->type > OMAP4_STATE_C1)
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu_id);
+
return_sleep_time:
getnstimeofday(&ts_postidle);
ts_idle = timespec_sub(ts_postidle, ts_preidle);
--
1.6.0.4
^ permalink raw reply related
* [PATCH 16/17] omap4: cpuidle: Allow debugfs control through enable_off_mode
From: Santosh Shilimkar @ 2011-02-19 10:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298112158-28469-1-git-send-email-santosh.shilimkar@ti.com>
Only MPU OFF and RET is controllable. CORE state is blocked
at ON state till the CORE RET support is added.
Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
arch/arm/mach-omap2/cpuidle44xx.c | 30 ++++++++++++++++++++++++++++++
arch/arm/mach-omap2/pm.h | 1 +
arch/arm/mach-omap2/pm44xx.c | 4 ++++
3 files changed, 35 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-omap2/cpuidle44xx.c b/arch/arm/mach-omap2/cpuidle44xx.c
index e887eb5..4207862 100644
--- a/arch/arm/mach-omap2/cpuidle44xx.c
+++ b/arch/arm/mach-omap2/cpuidle44xx.c
@@ -170,6 +170,31 @@ static int omap4_enter_idle_bm(struct cpuidle_device *dev,
}
/**
+ * omap4_cpuidle_update_states() - Update the cpuidle states
+ * @mpu_deepest_state: Enable states upto and including this for mpu domain
+ * @core_deepest_state: Enable states upto and including this for core domain
+ *
+ * This goes through the list of states available and enables and disables the
+ * validity of C states based on deepest state that can be achieved for the
+ * variable domain
+ */
+void omap4_cpuidle_update_states(u32 mpu_deepest_state, u32 core_deepest_state)
+{
+ int i;
+
+ for (i = OMAP4_STATE_C1; i < OMAP4_MAX_STATES; i++) {
+ struct omap4_processor_cx *cx = &omap4_power_states[i];
+
+ if ((cx->mpu_state >= mpu_deepest_state) &&
+ (cx->core_state >= core_deepest_state)) {
+ cx->valid = 1;
+ } else {
+ cx->valid = 0;
+ }
+ }
+}
+
+/**
* omap4_init_power_states - Initialises the OMAP4 specific C states.
*
*/
@@ -325,6 +350,11 @@ int __init omap4_idle_init(void)
}
}
+ if (enable_off_mode)
+ omap4_cpuidle_update_states(PWRDM_POWER_OFF, PWRDM_POWER_OFF);
+ else
+ omap4_cpuidle_update_states(PWRDM_POWER_RET, PWRDM_POWER_RET);
+
return 0;
}
#else
diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h
index ce848b0..4df89d1 100644
--- a/arch/arm/mach-omap2/pm.h
+++ b/arch/arm/mach-omap2/pm.h
@@ -77,6 +77,7 @@ extern u32 sleep_while_idle;
#if defined(CONFIG_CPU_IDLE)
extern void omap3_cpuidle_update_states(u32, u32);
+extern void omap4_cpuidle_update_states(u32, u32);
#endif
#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS)
diff --git a/arch/arm/mach-omap2/pm44xx.c b/arch/arm/mach-omap2/pm44xx.c
index 628242d..6244ab2 100644
--- a/arch/arm/mach-omap2/pm44xx.c
+++ b/arch/arm/mach-omap2/pm44xx.c
@@ -183,6 +183,10 @@ void omap4_pm_off_mode_enable(int enable)
else
state = PWRDM_POWER_RET;
+#ifdef CONFIG_CPU_IDLE
+ omap4_cpuidle_update_states(state, PWRDM_POWER_ON);
+#endif
+
list_for_each_entry(pwrst, &pwrst_list, node) {
/* FIXME: Remove this check when core retention is supported */
if (!strcmp(pwrst->pwrdm->name, "mpu_pwrdm")) {
--
1.6.0.4
^ permalink raw reply related
* [PATCH 17/17] omap4: Remove un-used do_wfi() macro.
From: Santosh Shilimkar @ 2011-02-19 10:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298112158-28469-1-git-send-email-santosh.shilimkar@ti.com>
With OMAP4 suspend, idle and hotplug series, we no longer need
do_wfi() macro.
Remove the same.
Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
---
arch/arm/mach-omap2/include/mach/omap4-common.h | 6 ------
1 files changed, 0 insertions(+), 6 deletions(-)
diff --git a/arch/arm/mach-omap2/include/mach/omap4-common.h b/arch/arm/mach-omap2/include/mach/omap4-common.h
index 74c9aa7..ca20333 100644
--- a/arch/arm/mach-omap2/include/mach/omap4-common.h
+++ b/arch/arm/mach-omap2/include/mach/omap4-common.h
@@ -16,12 +16,6 @@
#include <asm/proc-fns.h>
#ifndef __ASSEMBLER__
-/*
- * wfi used in low power code. Directly opcode is used instead
- * of instruction to avoid mulit-omap build break
- */
-#define do_wfi() \
- __asm__ __volatile__ (".word 0xe320f003" : : : "memory")
#ifdef CONFIG_CACHE_L2X0
extern void __iomem *l2cache_base;
--
1.6.0.4
^ permalink raw reply related
* MMC quirks relating to performance/lifetime.
From: Arnd Bergmann @ 2011-02-19 11:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <AANLkTimWAnPg2sYiLbfZXAAUiu7P5T3LmNoVbPi9RTEG@mail.gmail.com>
On Saturday 19 February 2011 00:17:51 Andrei Warkentin wrote:
> # echo 0 > /sys/block/mmcblk0/device/page_size
> # ./flashbench -A -b 1024 /dev/block/mmcblk0p9
> write align 8388608 pre 3.59ms on 6.54ms post 3.65ms diff 2.92ms
> write align 4194304 pre 4.13ms on 7.37ms post 4.27ms diff 3.17ms
> write align 2097152 pre 3.62ms on 6.81ms post 3.94ms diff 3.03ms
> write align 1048576 pre 3.62ms on 6.53ms post 3.55ms diff 2.95ms
> write align 524288 pre 3.62ms on 6.51ms post 3.63ms diff 2.88ms
> write align 262144 pre 3.62ms on 6.51ms post 3.63ms diff 2.89ms
> write align 131072 pre 3.62ms on 6.5ms post 3.63ms diff 2.88ms
> write align 65536 pre 3.61ms on 6.49ms post 3.62ms diff 2.88ms
> write align 32768 pre 3.61ms on 6.49ms post 3.61ms diff 2.88ms
> write align 16384 pre 3.68ms on 107ms post 3.51ms diff 103ms
> write align 8192 pre 3.74ms on 121ms post 3.91ms diff 117ms
> write align 4096 pre 3.88ms on 3.87ms post 3.87ms diff -2937ns
> write align 2048 pre 3.89ms on 3.88ms post 3.88ms diff -8734ns
> # fjnh84 at fjnh84-desktop:~/src/n/src/flash$ adb -s 17006185428011d7 shell
> # echo 8192 > /sys/block/mmcblk0/device/page_size
> # cd data
> # ./flashbench -A -b 1024 /dev/block/mmcblk0p9
> write align 8388608 pre 3.33ms on 6.8ms post 3.65ms diff 3.31ms
> write align 4194304 pre 4.34ms on 8.14ms post 4.53ms diff 3.71ms
> write align 2097152 pre 3.64ms on 7.31ms post 4.09ms diff 3.44ms
> write align 1048576 pre 3.65ms on 7.52ms post 3.65ms diff 3.87ms
> write align 524288 pre 3.62ms on 6.8ms post 3.63ms diff 3.17ms
> write align 262144 pre 3.62ms on 6.84ms post 3.63ms diff 3.22ms
> write align 131072 pre 3.62ms on 6.85ms post 3.44ms diff 3.32ms
> write align 65536 pre 3.39ms on 6.8ms post 3.66ms diff 3.28ms
> write align 32768 pre 3.64ms on 6.86ms post 3.66ms diff 3.21ms
> write align 16384 pre 3.67ms on 6.86ms post 3.65ms diff 3.2ms
> write align 8192 pre 3.66ms on 6.84ms post 3.64ms diff 3.19ms
> write align 4096 pre 3.71ms on 3.71ms post 3.64ms diff 38.6?s
> write align 2048 pre 3.71ms on 3.71ms post 3.72ms diff -656ns
>
> This was with the split unaligned accesses patch... Which I am
> attaching for comments.
I agree, this is very fascinating behavior. 100ms second latency for a
single 2KB access is definitely something we should try to avoid, and I
wonder why the drive decides to do that. It must get into a state where
it requires an extra garbage collection (you mentioned that earlier).
The numbers you see here are taken over multiple runs. Do you see a lot
of fluctuation when doing this with --count=1?
Also, does the same happen with other blocksizes, e.g. 4096 or 8192, passed
to flashbench?
Arnd
^ permalink raw reply
* [PATCH 1/2] PRUSS UIO driver support
From: TK, Pratheesh Gangadhar @ 2011-02-19 11:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110218163147.GD4684@local>
> -----Original Message-----
> From: Hans J. Koch [mailto:hjk at hansjkoch.de]
> Sent: Friday, February 18, 2011 10:02 PM
> To: TK, Pratheesh Gangadhar
> Cc: davinci-linux-open-source at linux.davincidsp.com; hjk at hansjkoch.de;
> gregkh at suse.de; Chatterjee, Amit; linux-kernel at vger.kernel.org; linux-arm-
> kernel at lists.infradead.org
> Subject: Re: [PATCH 1/2] PRUSS UIO driver support
>
> On Fri, Feb 18, 2011 at 08:35:29PM +0530, Pratheesh Gangadhar wrote:
> > Signed-off-by: Pratheesh Gangadhar <pratheesh@ti.com>
> >
> > This patch implements PRUSS (Programmable Real-time Unit Sub System)
> > UIO driver which exports SOC resources associated with PRUSS like
> > I/O, memories and IRQs to user space. PRUSS is dual 32-bit RISC
> > processors which is efficient in performing embedded tasks that
> > require manipulation of packed memory mapped data structures and
> > efficient in handling system events that have tight real time
> > constraints. This driver is currently supported on Texas Instruments
> > DA850, AM18xx and OMAPL1-38 devices.
> > For example, PRUSS runs firmware for real-time critical industrial
> > communication data link layer and communicates with application stack
> > running in user space via shared memory and IRQs.
>
> I see a few issues, comments below.
>
> Thanks,
> Hans
>
> > ---
> > drivers/uio/Kconfig | 10 ++
> > drivers/uio/Makefile | 1 +
> > drivers/uio/uio_pruss.c | 250
> +++++++++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 261 insertions(+), 0 deletions(-)
> > create mode 100644 drivers/uio/uio_pruss.c
> >
> > diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig
> > index bb44079..631ffe3 100644
> > --- a/drivers/uio/Kconfig
> > +++ b/drivers/uio/Kconfig
> > @@ -94,4 +94,14 @@ config UIO_NETX
> > To compile this driver as a module, choose M here; the module
> > will be called uio_netx.
> >
> > +config UIO_PRUSS
> > + tristate "Texas Instruments PRUSS driver"
> > + depends on ARCH_DAVINCI_DA850
> > + default n
>
> That line is unneccessary, "n" is already the default.
>
Ok, will fix in the next version.
> > + help
> > + PRUSS driver for OMAPL138/DA850/AM18XX devices
> > + PRUSS driver requires user space components
> > + To compile this driver as a module, choose M here: the module
> > + will be called uio_pruss.
> > +
> > endif
> > +#define MAX_PRUSS_EVTOUT_INSTANCE (8)
>
> The brackets are not needed.
>
Will fix in the next version.
> > +
> > +static struct clk *pruss_clk;
> > +static struct uio_info *info[MAX_PRUSS_EVTOUT_INSTANCE];
>
> is it really neccessary to allocate that statically?
>
> > +static void *ddr_virt_addr;
> > +static dma_addr_t ddr_phy_addr;
> > +
Agree - not necessary - will fix.
> > +static irqreturn_t pruss_handler(int irq, struct uio_info *dev_info)
> > +{
> > + return IRQ_HANDLED;
> > +}
>
> ROTFL. That reminds me of an old story. The last time I wrote this, and
> Greg dared to post it, we received this reply:
>
> http://marc.info/?l=linux-kernel&m=116604101232144&w=2
>
> So, if you really have a _very_ good reason why this _always_ works on
> _any_ DA850 board, add a comment that explains why. Otherwise the whole
> patch set will be doomed.
>
It always worked for me during the tests on the h/w. So did not bother to dig into the details then. From AM1808/AM1810 ARM Microprocessor System Reference Guide (http://focus.ti.com/general/docs/lit/getliterature.tsp?literatureNumber=sprugm9a&fileType=pdf), section 11.3.1 Interrupt processing
The interrupt processing block does the following tasks:
-Synchronization of slower and asynchronous interrupts
- Conversion of polarity to active high
- Conversion of interrupt type to pulse interrupts
After the processing block, all interrupts will be active-high pulses
Interrupt processing is the first step in INTC h/w which maps system interrupts to ARM (host) interrupts (FIQ, IRQ).
However I am willing to clean this up to meet the kernel guidelines and good practices...
> > +
> > +static int __devinit pruss_probe(struct platform_device *dev)
> > +{
> > + int ret = -ENODEV;
> > + int count = 0;
> > + struct resource *regs_pruram, *regs_l3ram, *regs_ddr;
> > + char *string;
> > + for (count = 0; count < MAX_PRUSS_EVTOUT_INSTANCE; count++) {
> > + info[count]->mem[0].addr = regs_pruram->start;
> > + info[count]->mem[0].size =
> > + regs_pruram->end - regs_pruram->start + 1;
> > + if (!info[count]->mem[0].addr
> > + || !(info[count]->mem[0].size - 1)) {
>
> That size check looks fishy. If somebody forgot to set the size it's OK ?
>
size is set just previous line, right? If regs_prum_ram->end== regs_prum_ram->start - then there is something wrong...
> > + dev_err(&dev->dev, "Invalid memory resource\n");
> > + break;
> > + }
> > + info[count]->mem[0].internal_addr =
> > + ioremap(regs_pruram->start, info[count]->mem[0].size);
> > + if (!info[count]->mem[0].internal_addr) {
> > + dev_err(&dev->dev,
> > + "Can't remap memory address range\n");
> > + break;
> > + }
> > + /* Register PRUSS IRQ lines */
> > + info[count]->irq = IRQ_DA8XX_EVTOUT0 + count;
> > +
> > + info[count]->irq_flags = IRQF_SHARED;
>
> How do you handle shared interrupts with the handler above?
>
> > + info[count]->handler = pruss_handler;
>
> And how do you make sure your interrupts are not level triggered? The
> handler above will hang for level triggered interrupts.
As mentioned above interrupt controller (ARM INTC) converts interrupt type to pulse. After required processing is complete - user space handler clears the interrupt from PRUSS.
Thanks,
Pratheesh
^ permalink raw reply
* [PATCH 1/2] PRUSS UIO driver support
From: TK, Pratheesh Gangadhar @ 2011-02-19 12:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <alpine.LFD.2.00.1102181715260.2701@localhost6.localdomain6>
> -----Original Message-----
> From: Thomas Gleixner [mailto:tglx at linutronix.de]
> Sent: Friday, February 18, 2011 10:22 PM
> To: TK, Pratheesh Gangadhar
> Cc: davinci-linux-open-source at linux.davincidsp.com; Hans J. Koch;
> gregkh at suse.de; Chatterjee, Amit; LKML; linux-arm-
> kernel at lists.infradead.org
> Subject: Re: [PATCH 1/2] PRUSS UIO driver support
>
> On Fri, 18 Feb 2011, Pratheesh Gangadhar wrote:
> > +/*
> > + * Host event IRQ numbers from PRUSS
> > + * 3 PRU_EVTOUT0 PRUSS Interrupt
> > + * 4 PRU_EVTOUT1 PRUSS Interrupt
> > + * 5 PRU_EVTOUT2 PRUSS Interrupt
> > + * 6 PRU_EVTOUT3 PRUSS Interrupt
> > + * 7 PRU_EVTOUT4 PRUSS Interrupt
> > + * 8 PRU_EVTOUT5 PRUSS Interrupt
> > + * 9 PRU_EVTOUT6 PRUSS Interrupt
> > + * 10 PRU_EVTOUT7 PRUSS Interrupt
> > +*/
> > +
> > +#define MAX_PRUSS_EVTOUT_INSTANCE (8)
> > +
> > +static struct clk *pruss_clk;
> > +static struct uio_info *info[MAX_PRUSS_EVTOUT_INSTANCE];
> > +static void *ddr_virt_addr;
> > +static dma_addr_t ddr_phy_addr;
> > +
> > +static irqreturn_t pruss_handler(int irq, struct uio_info *dev_info)
> > +{
> > + return IRQ_HANDLED;
>
> See other mail.
>
I responded in the other mail.
> > +}
> > +
> > +static int __devinit pruss_probe(struct platform_device *dev)
> > +{
> > + int ret = -ENODEV;
> > + int count = 0;
>
> Please make this one line.
>
Sure, will do.
> > + struct resource *regs_pruram, *regs_l3ram, *regs_ddr;
> > + char *string;
> > +
> > + /* Power on PRU in case its not done as part of boot-loader */
> > + pruss_clk = clk_get(&dev->dev, "pruss");
> > + if (IS_ERR(pruss_clk)) {
> > + dev_err(&dev->dev, "Failed to get clock\n");
> > + ret = PTR_ERR(pruss_clk);
> > + pruss_clk = NULL;
> > + return ret;
> > + } else {
> > + clk_enable(pruss_clk);
> > + }
> > +
> > + for (count = 0; count < MAX_PRUSS_EVTOUT_INSTANCE; count++) {
> > + info[count] = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
> > + if (!info[count])
> > + return -ENOMEM;
>
> That leaks memory in case of count > 0
>
Correct...
> And this whole loop is crap:
>
> struct uio_info *info = kzalloc(sizeof(struct uio_info) *
> MAX_PRUSS_EVTOUT_INSTANCE,
> GFP_KERNEL);
>
> perhaps ?
>
Will do.
> > + }
> > +
> > + regs_pruram = platform_get_resource(dev, IORESOURCE_MEM, 0);
> > + if (!regs_pruram) {
> > + dev_err(&dev->dev, "No memory resource specified\n");
> > + goto out_free;
> > + }
> > +
> > + regs_l3ram = platform_get_resource(dev, IORESOURCE_MEM, 1);
> > + if (!regs_l3ram) {
> > + dev_err(&dev->dev, "No memory resource specified\n");
> > + goto out_free;
> > + }
> > +
> > + regs_ddr = platform_get_resource(dev, IORESOURCE_MEM, 2);
> > + if (!regs_ddr) {
> > + dev_err(&dev->dev, "No memory resource specified\n");
> > + goto out_free;
> > + }
> > + ddr_virt_addr =
> > + dma_alloc_coherent(&dev->dev, regs_ddr->end - regs_ddr->start +
> 1,
> > + &ddr_phy_addr, GFP_KERNEL | GFP_DMA);
> > + if (!ddr_virt_addr) {
> > + dev_err(&dev->dev, "Could not allocate external memory\n");
> > + goto out_free;
> > + }
> > +
> > + for (count = 0; count < MAX_PRUSS_EVTOUT_INSTANCE; count++) {
> > + info[count]->mem[0].addr = regs_pruram->start;
> > + info[count]->mem[0].size =
> > + regs_pruram->end - regs_pruram->start + 1;
> > + if (!info[count]->mem[0].addr
> > + || !(info[count]->mem[0].size - 1)) {
> > + dev_err(&dev->dev, "Invalid memory resource\n");
> > + break;
>
> Is this break intentional ? Assume you have registered one uio
> instance already then ret = 0 and you fall into the good path below.
>
This is a bug. Will fix...
> > + }
> > + info[count]->mem[0].internal_addr =
> > + ioremap(regs_pruram->start, info[count]->mem[0].size);
> > + if (!info[count]->mem[0].internal_addr) {
> > + dev_err(&dev->dev,
> > + "Can't remap memory address range\n");
> > + break;
>
> Ditto
>
> > + }
> > + info[count]->mem[0].memtype = UIO_MEM_PHYS;
> > +
> > + info[count]->mem[1].addr = regs_l3ram->start;
> > + info[count]->mem[1].size =
> > + regs_l3ram->end - regs_l3ram->start + 1;
> > + if (!info[count]->mem[1].addr
> > + || !(info[count]->mem[1].size - 1)) {
> > + dev_err(&dev->dev, "Invalid memory resource\n");
> > + break;
> > + }
> > + info[count]->mem[1].internal_addr =
> > + ioremap(regs_l3ram->start, info[count]->mem[1].size);
> > + if (!info[count]->mem[1].internal_addr) {
> > + dev_err(&dev->dev,
> > + "Can't remap memory address range\n");
> > + break;
> > + }
> > + info[count]->mem[1].memtype = UIO_MEM_PHYS;
> > +
> > + info[count]->mem[2].addr = ddr_phy_addr;
> > + info[count]->mem[2].size = regs_ddr->end - regs_ddr->start +
> 1;
> > + if (!info[count]->mem[2].addr
> > + || !(info[count]->mem[2].size - 1)) {
> > + dev_err(&dev->dev, "Invalid memory resource\n");
> > + break;
>
> This is silly. If ddr_virt_addr == NULL you never reach that code.
>
Right... Will fix.
>
> > + }
> > + info[count]->mem[2].internal_addr = ddr_virt_addr;
> > + if (!info[count]->mem[2].internal_addr) {
> > + dev_err(&dev->dev,
> > + "Can't remap memory address range\n");
>
> This is silly. If ddr_virt_addr == NULL you never reach that code.
>
Right... Will fix.
> > + break;
> > + }
> > + info[count]->mem[2].memtype = UIO_MEM_PHYS;
> > +
> > + string = kzalloc(20, GFP_KERNEL);
> > + sprintf(string, "pruss_evt%d", count);
> > + info[count]->name = string;
> > + info[count]->version = "0.50";
> > +
> > + /* Register PRUSS IRQ lines */
> > + info[count]->irq = IRQ_DA8XX_EVTOUT0 + count;
> > +
> > + info[count]->irq_flags = IRQF_SHARED;
>
> Huch. That can be shared ? Then you must in the interrupt handler
>
> 1) check whether the interrupt is originated from your device
> 2) mask at the device level.
This interrupt is not shared. Must admit I am newbie to linux interrupt framework.
>
> > + info[count]->handler = pruss_handler;
> > +
> > + ret = uio_register_device(&dev->dev, info[count]);
> > +
> > + if (ret < 0)
> > + break;
> > + }
> > +
> > + if (ret < 0) {
> > + if (ddr_virt_addr)
> > + dma_free_coherent(&dev->dev,
> > + regs_ddr->end - regs_ddr->start + 1,
> > + ddr_virt_addr, ddr_phy_addr);
> > + while (count--) {
> > + uio_unregister_device(info[count]);
> > + kfree(info[count]->name);
> > + iounmap(info[count]->mem[0].internal_addr);
>
> Please move that section below the return 0 and use goto out_uio;
> instead of break;
>
> This is real horrible.
>
Ok, will do.
> > + }
> > + } else {
> > + platform_set_drvdata(dev, info);
> > + return 0;
> > + }
> > +
> > +out_free:
> > + for (count = 0; count < MAX_PRUSS_EVTOUT_INSTANCE; count++)
> > + kfree(info[count]);
> > +
> > + if (pruss_clk != NULL)
> > + clk_put(pruss_clk);
> > +
> > + return ret;
> > +}
> > +
> > +static int __devexit pruss_remove(struct platform_device *dev)
> > +{
> > + int count = 0;
> > + struct uio_info **info;
> > +
> > + info = (struct uio_info **)platform_get_drvdata(dev);
> > +
> > + for (count = 0; count < MAX_PRUSS_EVTOUT_INSTANCE; count++) {
> > + uio_unregister_device(info[count]);
> > + kfree(info[count]->name);
>
> In the above error exit path you do:
>
> iounmap(info[count]->mem[0].internal_addr);
>
> And in both cases you dont unmap info[count]->mem[1].internal_addr
>
> Why do you have those kernel mappings at all if you do not access
> the device from this code ?
>
I got this from existing UIO drivers which I used as reference. Should have paid more attention as this h/w is quite different...Was under the impression that ioremap is necessary for the user space access to those memory regions.
> > +
> > + }
> > + iounmap(info[0]->mem[0].internal_addr);
> > + iounmap(info[0]->mem[1].internal_addr);
>
> Sigh
>
> > + if (ddr_virt_addr)
> > + dma_free_coherent(&dev->dev, info[0]->mem[2].size,
> > + info[0]->mem[2].internal_addr,
> > + info[0]->mem[2].addr);
> > +
> > + for (count = 0; count < MAX_PRUSS_EVTOUT_INSTANCE; count++)
> > + kfree(info[count]);
> > +
> > + platform_set_drvdata(dev, NULL);
> > +
> > + if (pruss_clk != NULL)
> > + clk_put(pruss_clk);
> > + return 0;
>
> So you have basically the same cleanup code twice with different bugs.
> Please avoid this kind of mistake which will happen with duplicated
> code. The right way to do that is creating a cleanup function and call
> them from both places.
>
Agree and will fix this.
> You can call uio_unregister_device on a non registered info struct as
> well. So all it takes is:
>
> if (ddr_virt_addr)
> dma_free_coherent(&dev->dev, info[0]->mem[2].size,
> info[0]->mem[2].internal_addr,
> info[0]->mem[2].addr);
>
> for (count = 0; count < MAX_PRUSS_EVTOUT_INSTANCE; count++) {
> if (!info[count])
> break;
>
> uio_unregister_device(info[count]);
> kfree(info[count]->name);
> ...
> kfree(info[count]);
> info[count] = NULL;
> }
>
> platform_set_drvdata(dev, NULL);
>
> if (pruss_clk)
> clk_put(pruss_clk);
>
Thanks a lot for a detailed review. I will address these comments in the next revision of the patch...
Thanks,
Pratheesh
^ permalink raw reply
* [PATCH 1/2] PRUSS UIO driver support
From: Russell King - ARM Linux @ 2011-02-19 13:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298041530-26855-2-git-send-email-pratheesh@ti.com>
On Fri, Feb 18, 2011 at 08:35:29PM +0530, Pratheesh Gangadhar wrote:
> +static int __devinit pruss_probe(struct platform_device *dev)
> +{
> + int ret = -ENODEV;
> + int count = 0;
> + struct resource *regs_pruram, *regs_l3ram, *regs_ddr;
> + char *string;
> +
> + /* Power on PRU in case its not done as part of boot-loader */
> + pruss_clk = clk_get(&dev->dev, "pruss");
> + if (IS_ERR(pruss_clk)) {
> + dev_err(&dev->dev, "Failed to get clock\n");
> + ret = PTR_ERR(pruss_clk);
> + pruss_clk = NULL;
Delete this line.
...
> +out_free:
> + for (count = 0; count < MAX_PRUSS_EVTOUT_INSTANCE; count++)
> + kfree(info[count]);
> +
> + if (pruss_clk != NULL)
if (!IS_ERR(pruss_clk))
> + clk_put(pruss_clk);
...
> +static int __devexit pruss_remove(struct platform_device *dev)
...
> + platform_set_drvdata(dev, NULL);
> +
> + if (pruss_clk != NULL)
if (!IS_ERR(pruss_clk))
> + clk_put(pruss_clk);
^ permalink raw reply
* [PATCH v2] OMAP: PM: DMA: Enable runtime pm
From: G, Manjunath Kondaiah @ 2011-02-19 13:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <87ipwm1daa.fsf@ti.com>
On Mon, Feb 14, 2011 at 02:06:53PM -0800, Kevin Hilman wrote:
> "G, Manjunath Kondaiah" <manjugk@ti.com> writes:
>
> > From: Manjunath G Kondaiah <manjugk@ti.com>
> >
> > Enable runtime pm and use pm_runtime_get_sync and pm_runtime_put_autosuspend
> > for OMAP DMA driver.
> >
> > The DMA driver uses auto suspend feature of runtime pm framework through
> > which the clock gets disabled automatically if there is no activity for
> > more than one second.
> >
> > Testing:
> > Compile: omap1_defconfig and omap2plus_defconfig
> > Boot: OMAP1710(H3), OMAP2420(H4), OMAP3630(Zoom3), OMAP4(Blaze)
>
> The normal DMA tests should also be run on these platforms. Based on
> the above, I can't tell any DMA tests were run. Based on my tests,
> this isn't working for chained xfers.
>
> Using the runtime PM sysfs interface, you can check the runtime status
> of the device:
>
> # cat /sys/devices/platform/omap/omap_dma_system.0/power/runtime_status
>
> It should show 'active' during transfer, and after timeout expires it
> will show 'suspended'.
>
> Doing some tests using my dmatest module:
>
> git://gitorious.org/omap-test/dmatest.git
>
> I noticed that it gets stuck in 'active' and never gets suspended when I
> used DMA channel linking (load module using 'linking=1' as load-time option)
>
> I'm not sure exactly why, but I will guess that the reason is that there
> is an imbalance in get/put calls when using chaining, since 'get' is
> only called once upon omap_start_dma() but 'put' is called for every
> channel in the callback.
found this issue and fixed. It was due to chain handling in interrupt
handler. Since get_sync used only in omap_start_dma for 1st transfer and there
will be no omap_start_dma for second transfer onwards since it will be linked
through chaining in interrupt handler. Here is the fix.
@@ -1895,8 +2002,11 @@ static int omap2_dma_handle_ch(int ch)
OMAP_DMA_DYNAMIC_CHAIN)
disable_lnk(ch);
- if (!OMAP_DMA_CHAIN_QEMPTY(chain_id))
+ if (!OMAP_DMA_CHAIN_QEMPTY(chain_id)) {
OMAP_DMA_CHAIN_INCQHEAD(chain_id);
+ pm_runtime_get_sync(dev);
+ }
+
status = p->dma_read(CSR, ch);
p->dma_write(status, CSR, ch);
-Manjunath
>
> > On zoom3 core retention is tested with following steps:
> > echo 1 > /debug/pm_debug/sleep_while_idle
> > echo 1 > /debug/pm_debug/enable_off_mode
> > echo 5 > /sys/devices/platform/omap/omap_uart.0/sleep_timeout
> > echo 5 > /sys/devices/platform/omap/omap_uart.1/sleep_timeout
> > echo 5 > /sys/devices/platform/omap/omap_uart.2/sleep_timeout
> > echo 5 > /sys/devices/platform/omap/omap_uart.3/sleep_timeout
> >
> > It is observed that(on pm branch), core retention count gets increasing if the
> > board is left idle for more than 5 seconds. However, it doesnot enter off mode
> > (even without DMA runtime changes).
>
> What silicon rev is on your Zoom3? Mainline kernels now disable core
> off-mode for 3630 revs < ES2.1 due to erratum i583.
>
> If this happens, you should see something like this on the console:
>
> Core OFF disabled due to errata i583
>
> Kevin
>
> > Signed-off-by: G, Manjunath Kondaiah <manjugk@ti.com>
> > ---
> > arch/arm/plat-omap/dma.c | 27 +++++++++++++++++++++++++++
> > 1 files changed, 27 insertions(+), 0 deletions(-)
> >
> > diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c
> > index 2ec3b5d..6bfe25c 100644
> > --- a/arch/arm/plat-omap/dma.c
> > +++ b/arch/arm/plat-omap/dma.c
> > @@ -35,6 +35,7 @@
> > #include <linux/io.h>
> > #include <linux/slab.h>
> > #include <linux/delay.h>
> > +#include <linux/pm_runtime.h>
> >
> > #include <asm/system.h>
> > #include <mach/hardware.h>
> > @@ -59,6 +60,7 @@ enum { DMA_CHAIN_STARTED, DMA_CHAIN_NOTSTARTED };
> >
> > static struct omap_system_dma_plat_info *p;
> > static struct omap_dma_dev_attr *d;
> > +static struct device *dev;
> >
> > static int enable_1510_mode;
> > static u32 errata;
> > @@ -676,6 +678,7 @@ int omap_request_dma(int dev_id, const char *dev_name,
> > unsigned long flags;
> > struct omap_dma_lch *chan;
> >
> > + pm_runtime_get_sync(dev);
> > spin_lock_irqsave(&dma_chan_lock, flags);
> > for (ch = 0; ch < dma_chan_count; ch++) {
> > if (free_ch == -1 && dma_chan[ch].dev_id == -1) {
> > @@ -686,6 +689,7 @@ int omap_request_dma(int dev_id, const char *dev_name,
> > }
> > if (free_ch == -1) {
> > spin_unlock_irqrestore(&dma_chan_lock, flags);
> > + pm_runtime_put_autosuspend(dev);
> > return -EBUSY;
> > }
> > chan = dma_chan + free_ch;
> > @@ -743,6 +747,7 @@ int omap_request_dma(int dev_id, const char *dev_name,
> > }
> >
> > *dma_ch_out = free_ch;
> > + pm_runtime_put_autosuspend(dev);
> >
> > return 0;
> > }
> > @@ -871,6 +876,8 @@ void omap_start_dma(int lch)
> > {
> > u32 l;
> >
> > + pm_runtime_get_sync(dev);
> > +
> > /*
> > * The CPC/CDAC register needs to be initialized to zero
> > * before starting dma transfer.
> > @@ -1805,6 +1812,8 @@ static int omap1_dma_handle_ch(int ch)
> > if (likely(dma_chan[ch].callback != NULL))
> > dma_chan[ch].callback(ch, csr, dma_chan[ch].data);
> >
> > + pm_runtime_mark_last_busy(dev);
> > + pm_runtime_put_autosuspend(dev);
> > return 1;
> > }
> >
> > @@ -1899,6 +1908,8 @@ static int omap2_dma_handle_ch(int ch)
> > if (likely(dma_chan[ch].callback != NULL))
> > dma_chan[ch].callback(ch, status, dma_chan[ch].data);
> >
> > + pm_runtime_mark_last_busy(dev);
> > + pm_runtime_put_autosuspend(dev);
> > return 0;
> > }
> >
> > @@ -1978,6 +1989,7 @@ static int __devinit omap_system_dma_probe(struct platform_device *pdev)
> > return -EINVAL;
> > }
> >
> > + dev = &pdev->dev;
> > d = p->dma_attr;
> > errata = p->errata;
> >
> > @@ -1999,6 +2011,11 @@ static int __devinit omap_system_dma_probe(struct platform_device *pdev)
> > }
> > }
> >
> > + pm_runtime_use_autosuspend(dev);
> > + pm_runtime_set_autosuspend_delay(dev, 1000);
> > + pm_runtime_enable(dev);
> > + pm_runtime_get_sync(dev);
> > +
> > spin_lock_init(&dma_chan_lock);
> > for (ch = 0; ch < dma_chan_count; ch++) {
> > omap_clear_dma(ch);
> > @@ -2064,6 +2081,16 @@ static int __devinit omap_system_dma_probe(struct platform_device *pdev)
> > dma_chan[1].dev_id = 1;
> > }
> > p->show_dma_caps();
> > +
> > + /*
> > + * Note: If dma channels are reserved through boot paramters,
> > + * then dma device is always enabled.
> > + */
> > + if (omap_dma_reserve_channels)
> > + pm_runtime_get(dev);
> > +
> > + pm_runtime_put_autosuspend(dev);
> > +
> > return 0;
> >
> > exit_dma_irq_fail:
^ permalink raw reply
* [PATCH V5 00/63] Updating SPEAr Support
From: Russell King - ARM Linux @ 2011-02-19 13:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1295499392.git.viresh.kumar@st.com>
I don't understand why people think that it's a good idea to post a
patch series which adds new support and then has a whole series of
patches which modify then modifies both new and old stuff.
It makes it a lot harder to apply, a lot harder to review, and all
round is a bad idea.
The problem is that mixing it up in the way you've done means that
it's impossible to apply the cleanup patches before the new support.
Another issue worth pointing out is that submission of patch series
by email has to be done _carefully_ - which basically means slowly.
The email system on this planet will randomly re-order messages -
there is no guarantee that the order in which you send them is the
order in which they're received at the remote end. If you want them
to arrive in a certain order, you're out of luck.
You can help by not dumping 63 patches into the mail system within a
few seconds or so, but leaving a greater period of time between each.
The longer that period, the more likely they are to arrive in order -
but it's something that will never be guaranteed.
As it is, I'm going to have to spend something like a few _hours_
over sorting out how to merge these Spear patches because they're all
out of order and they're all interdependent.
That's another argument for not waiting until you have a huge patch
set before sending stuff out - keep patch sets small and targetted to
specific areas. For instance, with the Versatile updates which form a
total of 21 individual patches, I split them up into one set of 10
patches which changed the way CLCD stuff was handled, and another
separate set of 11 patches of platform specific stuff.
So, why aren't all the padmux changes together? Why aren't the clk
API patches together?
And another thing: separate out the 'fixes' from the new developments.
Things like the addition of UL to VMALLOC_END counts as a warning fix.
Using readl_relaxed/writel_relaxed also counts as a fix as things in
the decompressor break. As these are technically bug fixes, they go
upstream faster than new work.
Bug fixes should _always_ be at the very start of any patch series,
then cleanups, then new stuff.
In summary, I hate large patch sets. I tend to ignore large patch
sets. I have very little motivation to review large patch sets. I
have very little motivation to apply large patch sets. Don't create
them in the first place. Spend time sorting the patches into smaller
sets and things all round go much easier.
I'll work through what you've already submitted, but it will be a
slow and erratic job.
^ permalink raw reply
* [PATCH V5 03/63] sp810 Fix: Switch to slow mode before sysctl_soft_reset
From: Russell King - ARM Linux @ 2011-02-19 14:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <25134317a9ba259df7189151daddbaf4440774c5.1295499394.git.viresh.kumar@st.com>
On Thu, Jan 20, 2011 at 12:55:42PM +0530, Viresh Kumar wrote:
> From: Shiraz Hashim <shiraz.hashim@st.com>
>
> Before resetting system from system controller, switching to slow
> mode is required.
This comment could do with some expansion about why it is required.
Eg, this comment could become:
"sp810: switch to slow mode before reset
In sysctl_soft_reset(), switch to slow mode before resetting the system
via the system controller. This is required by the SPEAr reference
manual."
^ permalink raw reply
* [PATCH V5 41/63] ST SPEAr: replace readl, writel with readl_relaxed, writel_relaxed in uncompress.h
From: Russell King - ARM Linux @ 2011-02-19 14:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <c792c9602552d0650cd55c4f724257ad46b79cc8.1295499395.git.viresh.kumar@st.com>
On Thu, Jan 20, 2011 at 12:56:19PM +0530, Viresh Kumar wrote:
> readl also calls outer cache maintainance operations which are not available
> during Linux uncompression. This patch replaces readl, writel with readl_relaxed
> and writel_relaxed.
Shouldn't this be near the start of the series?
Also, would:
"SPEAr: replace readl, writel with relaxed versions in uncompress.h
readl() and writel() calls the outer cache maintainance operations
which are not available during Linux uncompression. This patch replaces
readl() and writel() with readl_relaxed() and writel_relaxed() to avoid
the link time errors."
be a better patch description?
^ permalink raw reply
* [RFC] MMC: error handling improvements
From: Russell King - ARM Linux @ 2011-02-19 15:00 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <8yaoc6c7tan.fsf@huya.qualcomm.com>
On Tue, Feb 15, 2011 at 03:49:04PM -0800, David Brown wrote:
> On Tue, Feb 15 2011, Russell King - ARM Linux wrote:
>
> > This patch is for RFC only; it needs splitting up somewhat. However, I
> > wanted to get it out there for some comment.
>
> Just for kicks, I applied this and ran it on an MSM target (8x50). It
> seems to cause lots of:
>
> mmc0: Data timeout
> mmc0: Controller has been re-initialized
> ...
> mmc0: Data CRC error
>
> and I can post more if you would find them interesting. Eventually the
> MSM mmc driver derefernces a null pointer in the interrupt code.
That doesn't look right. If resetting the MMC controller results in all
state being lost, it presumably means the card gets powered down too.
If that happens, the card needs to be reinitialized from scratch again,
and I don't see how that happens in this driver.
Nothing in this patch should cause data timeouts to appear where they
weren't already appearing before the patch - the patch only changes
what happens after an error occurs, so you must already be encountering
errors on the interface before you applied this patch.
^ permalink raw reply
* [PATCH 1/2] PRUSS UIO driver support
From: TK, Pratheesh Gangadhar @ 2011-02-19 15:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <alpine.LFD.2.00.1102181802580.2701@localhost6.localdomain6>
> -----Original Message-----
> From: Thomas Gleixner [mailto:tglx at linutronix.de]
> Sent: Friday, February 18, 2011 10:33 PM
> To: Arnd Bergmann
> Cc: linux-arm-kernel at lists.infradead.org; TK, Pratheesh Gangadhar;
> davinci-linux-open-source at linux.davincidsp.com; gregkh at suse.de;
> Chatterjee, Amit; Hans J. Koch; LKML
> Subject: Re: [PATCH 1/2] PRUSS UIO driver support
>
> On Fri, 18 Feb 2011, Arnd Bergmann wrote:
>
> > On Friday 18 February 2011, Thomas Gleixner wrote:
> > > On Fri, 18 Feb 2011, Arnd Bergmann wrote:
> > > > On Friday 18 February 2011, Pratheesh Gangadhar wrote:
> > > > > Signed-off-by: Pratheesh Gangadhar <pratheesh@ti.com>
> > > > > +static irqreturn_t pruss_handler(int irq, struct uio_info
> *dev_info)
> > > > > +{
> > > > > + return IRQ_HANDLED;
> > > > > +}
> > > >
> > > > An empty interrupt handler is rather pointless. I guess you really
> > > > notify user space when the interrupt handler gets called, as this
> > > > is the main point of a UIO driver as far as I understand it.
> > >
> > > The UIO core code does this for you when the driver handler returns
> > > IRQ_HANDLED
> >
> > Ah, right.
> >
> > > but the empty handler raises a different questions:
> > >
> > > Is the interrupt edge triggerd or how do you avoid an irq storm here?
> > > Usually UIO drivers are requested to mask the interrupt in the device
> > > itself.
> >
> > If it's edge triggered, it should not advertise IRQF_SHARED, right?
>
> Nope. And the handler needs a fat comment why this works.
For my understanding - if the interrupt is not shared and not level triggered - is this okay to have empty handler? In this specific case, these interrupt lines are internal to SOC and hooked to ARM INTC from PRUSS. PRUSS has another INTC to handle system events to PRUSS as well as to generate system events to host ARM. These generated events are used for IPC between user application and PRU firmware and for async notifications from PRU firmware to user space. I don't see a reason to make it shared as we have 8 lines available for use. As mentioned before ARM INTC interrupt processing logic converts interrupts to active high pulses.
I also looked at the interrupt handling in existing UIO drivers
static irqreturn_t my_uio_handler(int irq, struct uio_info *dev_info)
{
if (no interrupt is enabled and no interrupt is active) /For shared interrupt handling
return IRQ_NONE;
disable interrupt; // For level triggered interrupts
return IRQ_HANDLED;
}
It's not clear how and where interrupts are re-enabled. Is this expected to be done from user space?
Uio_secos3.c has an irqcontrol function to enable/disable interrupts. Is this the recommended approach?
Thanks and Regards,
Pratheesh
^ permalink raw reply
* [PATCH 1/3] amba: make probe() functions take const id tables
From: Russell King - ARM Linux @ 2011-02-19 15:46 UTC (permalink / raw)
To: linux-arm-kernel
Make Primecell driver probe functions take a const pointer to their
ID tables. Drivers should never modify their ID tables in their
probe handler.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
arch/arm/kernel/etm.c | 4 ++--
drivers/char/hw_random/nomadik-rng.c | 2 +-
drivers/dma/amba-pl08x.c | 2 +-
drivers/dma/pl330.c | 2 +-
drivers/gpio/pl061.c | 2 +-
drivers/input/serio/ambakmi.c | 3 ++-
drivers/mmc/host/mmci.c | 3 ++-
drivers/rtc/rtc-pl030.c | 2 +-
drivers/rtc/rtc-pl031.c | 2 +-
drivers/spi/amba-pl022.c | 2 +-
drivers/tty/serial/amba-pl010.c | 2 +-
drivers/tty/serial/amba-pl011.c | 2 +-
drivers/video/amba-clcd.c | 2 +-
drivers/watchdog/sp805_wdt.c | 2 +-
include/linux/amba/bus.h | 2 +-
sound/arm/aaci.c | 3 ++-
16 files changed, 20 insertions(+), 17 deletions(-)
diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c
index 11db628..052b509 100644
--- a/arch/arm/kernel/etm.c
+++ b/arch/arm/kernel/etm.c
@@ -338,7 +338,7 @@ static struct miscdevice etb_miscdev = {
.fops = &etb_fops,
};
-static int __init etb_probe(struct amba_device *dev, struct amba_id *id)
+static int __init etb_probe(struct amba_device *dev, const struct amba_id *id)
{
struct tracectx *t = &tracer;
int ret = 0;
@@ -530,7 +530,7 @@ static ssize_t trace_mode_store(struct kobject *kobj,
static struct kobj_attribute trace_mode_attr =
__ATTR(trace_mode, 0644, trace_mode_show, trace_mode_store);
-static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
+static int __init etm_probe(struct amba_device *dev, const struct amba_id *id)
{
struct tracectx *t = &tracer;
int ret = 0;
diff --git a/drivers/char/hw_random/nomadik-rng.c b/drivers/char/hw_random/nomadik-rng.c
index a348c7e..dd1d143 100644
--- a/drivers/char/hw_random/nomadik-rng.c
+++ b/drivers/char/hw_random/nomadik-rng.c
@@ -39,7 +39,7 @@ static struct hwrng nmk_rng = {
.read = nmk_rng_read,
};
-static int nmk_rng_probe(struct amba_device *dev, struct amba_id *id)
+static int nmk_rng_probe(struct amba_device *dev, const struct amba_id *id)
{
void __iomem *base;
int ret;
diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c
index 07bca49..e6d7228 100644
--- a/drivers/dma/amba-pl08x.c
+++ b/drivers/dma/amba-pl08x.c
@@ -1845,7 +1845,7 @@ static inline void init_pl08x_debugfs(struct pl08x_driver_data *pl08x)
}
#endif
-static int pl08x_probe(struct amba_device *adev, struct amba_id *id)
+static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
{
struct pl08x_driver_data *pl08x;
const struct vendor_data *vd = id->data;
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 7c50f6d..6abe1ec 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -657,7 +657,7 @@ static irqreturn_t pl330_irq_handler(int irq, void *data)
}
static int __devinit
-pl330_probe(struct amba_device *adev, struct amba_id *id)
+pl330_probe(struct amba_device *adev, const struct amba_id *id)
{
struct dma_pl330_platdata *pdat;
struct dma_pl330_dmac *pdmac;
diff --git a/drivers/gpio/pl061.c b/drivers/gpio/pl061.c
index 2975d22..838ddbd 100644
--- a/drivers/gpio/pl061.c
+++ b/drivers/gpio/pl061.c
@@ -232,7 +232,7 @@ static void pl061_irq_handler(unsigned irq, struct irq_desc *desc)
desc->irq_data.chip->irq_unmask(&desc->irq_data);
}
-static int pl061_probe(struct amba_device *dev, struct amba_id *id)
+static int pl061_probe(struct amba_device *dev, const struct amba_id *id)
{
struct pl061_platform_data *pdata;
struct pl061_gpio *chip;
diff --git a/drivers/input/serio/ambakmi.c b/drivers/input/serio/ambakmi.c
index 92563a6..12abc50 100644
--- a/drivers/input/serio/ambakmi.c
+++ b/drivers/input/serio/ambakmi.c
@@ -107,7 +107,8 @@ static void amba_kmi_close(struct serio *io)
clk_disable(kmi->clk);
}
-static int __devinit amba_kmi_probe(struct amba_device *dev, struct amba_id *id)
+static int __devinit amba_kmi_probe(struct amba_device *dev,
+ const struct amba_id *id)
{
struct amba_kmi_port *kmi;
struct serio *io;
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 8a29c9f..5bbb87d 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -965,7 +965,8 @@ static const struct mmc_host_ops mmci_ops = {
.get_cd = mmci_get_cd,
};
-static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
+static int __devinit mmci_probe(struct amba_device *dev,
+ const struct amba_id *id)
{
struct mmci_platform_data *plat = dev->dev.platform_data;
struct variant_data *variant = id->data;
diff --git a/drivers/rtc/rtc-pl030.c b/drivers/rtc/rtc-pl030.c
index bbdb2f0..baff724 100644
--- a/drivers/rtc/rtc-pl030.c
+++ b/drivers/rtc/rtc-pl030.c
@@ -103,7 +103,7 @@ static const struct rtc_class_ops pl030_ops = {
.set_alarm = pl030_set_alarm,
};
-static int pl030_probe(struct amba_device *dev, struct amba_id *id)
+static int pl030_probe(struct amba_device *dev, const struct amba_id *id)
{
struct pl030_rtc *rtc;
int ret;
diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
index b7a6690..6568f4b 100644
--- a/drivers/rtc/rtc-pl031.c
+++ b/drivers/rtc/rtc-pl031.c
@@ -358,7 +358,7 @@ static int pl031_remove(struct amba_device *adev)
return 0;
}
-static int pl031_probe(struct amba_device *adev, struct amba_id *id)
+static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
{
int ret;
struct pl031_local *ldata;
diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c
index 71a1219..95e58c7 100644
--- a/drivers/spi/amba-pl022.c
+++ b/drivers/spi/amba-pl022.c
@@ -2021,7 +2021,7 @@ static void pl022_cleanup(struct spi_device *spi)
static int __devinit
-pl022_probe(struct amba_device *adev, struct amba_id *id)
+pl022_probe(struct amba_device *adev, const struct amba_id *id)
{
struct device *dev = &adev->dev;
struct pl022_ssp_controller *platform_info = adev->dev.platform_data;
diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c
index 2904aa0..d742dd2 100644
--- a/drivers/tty/serial/amba-pl010.c
+++ b/drivers/tty/serial/amba-pl010.c
@@ -676,7 +676,7 @@ static struct uart_driver amba_reg = {
.cons = AMBA_CONSOLE,
};
-static int pl010_probe(struct amba_device *dev, struct amba_id *id)
+static int pl010_probe(struct amba_device *dev, const struct amba_id *id)
{
struct uart_amba_port *uap;
void __iomem *base;
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index e76d7d0..0dae46f 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -1349,7 +1349,7 @@ static struct uart_driver amba_reg = {
.cons = AMBA_CONSOLE,
};
-static int pl011_probe(struct amba_device *dev, struct amba_id *id)
+static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
{
struct uart_amba_port *uap;
struct vendor_data *vendor = id->data;
diff --git a/drivers/video/amba-clcd.c b/drivers/video/amba-clcd.c
index 8bd3706..5fc983c 100644
--- a/drivers/video/amba-clcd.c
+++ b/drivers/video/amba-clcd.c
@@ -534,7 +534,7 @@ static int clcdfb_register(struct clcd_fb *fb)
return ret;
}
-static int clcdfb_probe(struct amba_device *dev, struct amba_id *id)
+static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id)
{
struct clcd_board *board = dev->dev.platform_data;
struct clcd_fb *fb;
diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c
index 9127eda..0a0efe7 100644
--- a/drivers/watchdog/sp805_wdt.c
+++ b/drivers/watchdog/sp805_wdt.c
@@ -278,7 +278,7 @@ static struct miscdevice sp805_wdt_miscdev = {
};
static int __devinit
-sp805_wdt_probe(struct amba_device *adev, struct amba_id *id)
+sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
{
int ret = 0;
diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h
index a0ccf28..07b6374 100644
--- a/include/linux/amba/bus.h
+++ b/include/linux/amba/bus.h
@@ -43,7 +43,7 @@ struct amba_id {
struct amba_driver {
struct device_driver drv;
- int (*probe)(struct amba_device *, struct amba_id *);
+ int (*probe)(struct amba_device *, const struct amba_id *);
int (*remove)(struct amba_device *);
void (*shutdown)(struct amba_device *);
int (*suspend)(struct amba_device *, pm_message_t);
diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c
index af5a152..d0cead3 100644
--- a/sound/arm/aaci.c
+++ b/sound/arm/aaci.c
@@ -984,7 +984,8 @@ static unsigned int __devinit aaci_size_fifo(struct aaci *aaci)
return i;
}
-static int __devinit aaci_probe(struct amba_device *dev, struct amba_id *id)
+static int __devinit aaci_probe(struct amba_device *dev,
+ const struct amba_id *id)
{
struct aaci *aaci;
int ret, i;
^ permalink raw reply related
* [PATCH 2/3] amba: make internal ID table handling const
From: Russell King - ARM Linux @ 2011-02-19 15:47 UTC (permalink / raw)
To: linux-arm-kernel
As all probe() functions now take a const pointer, we can make the bus
level code deal with const pointers too.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/amba/bus.c | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
index ca96b0a..4308899 100644
--- a/drivers/amba/bus.c
+++ b/drivers/amba/bus.c
@@ -21,8 +21,8 @@
#define to_amba_device(d) container_of(d, struct amba_device, dev)
#define to_amba_driver(d) container_of(d, struct amba_driver, drv)
-static struct amba_id *
-amba_lookup(struct amba_id *table, struct amba_device *dev)
+static const struct amba_id *
+amba_lookup(const struct amba_id *table, struct amba_device *dev)
{
int ret = 0;
@@ -188,7 +188,7 @@ static int amba_probe(struct device *dev)
{
struct amba_device *pcdev = to_amba_device(dev);
struct amba_driver *pcdrv = to_amba_driver(dev->driver);
- struct amba_id *id = amba_lookup(pcdrv->id_table, pcdev);
+ const struct amba_id *id = amba_lookup(pcdrv->id_table, pcdev);
int ret;
do {
^ permalink raw reply related
* [PATCH 3/3] Make amba_driver id_table const
From: Russell King - ARM Linux @ 2011-02-19 15:48 UTC (permalink / raw)
To: linux-arm-kernel
Now that the bus level code deals with a const id table, we can also
make the ID table in the amba_driver structure also const.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
include/linux/amba/bus.h | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h
index a0ccf28..9cd7f8e 100644
--- a/include/linux/amba/bus.h
+++ b/include/linux/amba/bus.h
@@ -48,7 +48,7 @@ struct amba_driver {
void (*shutdown)(struct amba_device *);
int (*suspend)(struct amba_device *, pm_message_t);
int (*resume)(struct amba_device *);
- struct amba_id *id_table;
+ const struct amba_id *id_table;
};
enum amba_vendor {
^ permalink raw reply related
* [PATCH 1/2] PRUSS UIO driver support
From: TK, Pratheesh Gangadhar @ 2011-02-19 16:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110218170538.GB11773@suse.de>
> -----Original Message-----
> From: Greg KH [mailto:gregkh at suse.de]
> Sent: Friday, February 18, 2011 10:36 PM
> To: TK, Pratheesh Gangadhar
> Cc: Hans J. Koch; davinci-linux-open-source at linux.davincidsp.com;
> Chatterjee, Amit; linux-kernel at vger.kernel.org; linux-arm-
> kernel at lists.infradead.org
> Subject: Re: [PATCH 1/2] PRUSS UIO driver support
>
> On Fri, Feb 18, 2011 at 05:31:47PM +0100, Hans J. Koch wrote:
> > On Fri, Feb 18, 2011 at 08:35:29PM +0530, Pratheesh Gangadhar wrote:
> > > Signed-off-by: Pratheesh Gangadhar <pratheesh@ti.com>
>
> As noted by others, this needs to go at the end of the changelog
> comment.
>
> Also, always run your patches through the scripts/checkpatch.pl script
> and fix the warnings and errors it finds. To not do so is just rude as
> you are asking us to do the basic review work that you yourself did not
> do in the first place.
I actually ran checkpatch multiple times, but made some more changes before sending and missed to run again. Sorry about this. Will make sure these issues are fixed in the next version.
>
> > > +static irqreturn_t pruss_handler(int irq, struct uio_info *dev_info)
> > > +{
> > > + return IRQ_HANDLED;
> > > +}
> >
> > ROTFL. That reminds me of an old story. The last time I wrote this, and
> > Greg dared to post it, we received this reply:
> >
> > http://marc.info/?l=linux-kernel&m=116604101232144&w=2
> >
> > So, if you really have a _very_ good reason why this _always_ works on
> > _any_ DA850 board, add a comment that explains why. Otherwise the whole
> > patch set will be doomed.
>
> Nope, this whole patch set is doomed if this isn't fixed, I'm not going
> to accept it no matter how much you want to try to say this is ok on
> this hardware.
Okay. I had some queries in the previous mail. Will fix this once I am clear about the options.
Thanks,
Pratheesh
^ permalink raw reply
* [PATCH V5 15/63] ST SPEAr: Correcting SOC Config base address for spear320
From: Russell King - ARM Linux @ 2011-02-19 16:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cac5d194ea4be06a5a10dcf8059e87426ae1ee9d.1295499394.git.viresh.kumar@st.com>
On Thu, Jan 20, 2011 at 12:55:54PM +0530, Viresh Kumar wrote:
> Signed-off-by: Viresh Kumar <viresh.kumar@st.com>
This looks to me like another bug fix, and I'm treating it as such. It
should be submitted separately from this patch set along with other bug
fixes, and clearly labelled as such.
I've also changed the comments on this patch to:
"SPEAr: Correct SOC config base address for spear320
SPEAR320_SOC_CONFIG_BASE was wrong, causing the wrong registers to be
accessed."
^ permalink raw reply
* [PATCH V5 57/63] ST SPEAr3xx: Updating plgpio and emi source to make it compliant with single image strategy
From: Russell King - ARM Linux @ 2011-02-19 16:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <29f3e319e062a9f2f65ea9683fb9663f938dfa3b.1295499395.git.viresh.kumar@st.com>
On Thu, Jan 20, 2011 at 12:56:35PM +0530, Viresh Kumar wrote:
> Signed-off-by: Viresh Kumar <viresh.kumar@st.com>
Why aren't the plgpio bits of this in patch 16?
> +static u32 plgpio_enb, plgpio_wdata, plgpio_dir, plgpio_rdata, plgpio_ie,
> + plgpio_mis;
'int' or 'unsigned int' will do for these. There's no pressing need for
these to be exactly 32-bit quantities.
> static int __init plgpio_init(void)
> {
> + if (machine_is_spear310()) {
> + plgpio_enb = SPEAR310_PLGPIO_ENB;
> + plgpio_wdata = SPEAR310_PLGPIO_WDATA;
> + plgpio_dir = SPEAR310_PLGPIO_DIR;
> + plgpio_rdata = SPEAR310_PLGPIO_IE;
> + plgpio_ie = SPEAR310_PLGPIO_RDATA;
> + plgpio_mis = SPEAR310_PLGPIO_MIS;
> + } else if (machine_is_spear320()) {
> + plgpio_enb = SPEAR320_PLGPIO_ENB;
> + plgpio_wdata = SPEAR320_PLGPIO_WDATA;
> + plgpio_dir = SPEAR320_PLGPIO_DIR;
> + plgpio_rdata = SPEAR320_PLGPIO_IE;
> + plgpio_ie = SPEAR320_PLGPIO_RDATA;
> + plgpio_mis = SPEAR320_PLGPIO_MIS;
> + } else {
> + return 0;
If it's not supported, then what about returning an error instead of
zero?
Can you supersede this patch and patch 16 in the patch system with a
single patch which adds PLGPIO support with no further updates.
You may also consider combining the EMI updates with the patch which
creates EMI support in the first place, which I think is patch 36.
^ 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