From: Keshava Munegowda <[email protected]>
These set of patches
- defines the hwmod structures of ehci and ohci of omap3 and omap4 socs.
- Implements the Run time pm
- global suspend/resume of EHCI and OHCI
Since, existing omap usbhs core driver of v3.0-rc1 release uses
the run time PM APIs, these patches enables the ehci and ohci
functionalaties.
Keshava Munegowda (4):
arm: omap: usb: ehci and ohci hwmod structures for omap3 and omap4
arm: omap: usb: register hwmods of usbhs
arm: omap: usb: device name change for the clk names of usbhs
mfd: global Suspend and resume support of ehci and ohci
arch/arm/mach-omap2/clock3xxx_data.c | 28 ++--
arch/arm/mach-omap2/clock44xx_data.c | 10 +-
arch/arm/mach-omap2/omap_hwmod_3xxx_data.c | 184 ++++++++++++++++++++++++++++
arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 153 +++++++++++++++++++++++
arch/arm/mach-omap2/usb-host.c | 99 ++++++----------
drivers/mfd/omap-usb-host.c | 105 ++++++++++++++++-
6 files changed, 495 insertions(+), 84 deletions(-)
Following 2 hwmod strcuture are added:
UHH hwmod of usbhs with uhh base address and
EHCI , OHCI irq and base addresses.
TLL hwmod of usbhs with the TLL base address and irq.
Signed-off-by: Keshava Munegowda <[email protected]>
---
arch/arm/mach-omap2/omap_hwmod_3xxx_data.c | 184 ++++++++++++++++++++++++++++
arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 153 +++++++++++++++++++++++
2 files changed, 337 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
index 909a84d..fe9a176 100644
--- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
@@ -84,6 +84,8 @@ static struct omap_hwmod omap3xxx_mcbsp4_hwmod;
static struct omap_hwmod omap3xxx_mcbsp5_hwmod;
static struct omap_hwmod omap3xxx_mcbsp2_sidetone_hwmod;
static struct omap_hwmod omap3xxx_mcbsp3_sidetone_hwmod;
+static struct omap_hwmod omap34xx_usb_host_hs_hwmod;
+static struct omap_hwmod omap34xx_usb_tll_hs_hwmod;
/* L3 -> L4_CORE interface */
static struct omap_hwmod_ocp_if omap3xxx_l3_main__l4_core = {
@@ -3574,6 +3576,185 @@ static struct omap_hwmod omap3xxx_mmc3_hwmod = {
.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
};
+/*
+ * 'usb_host_hs' class
+ * high-speed multi-port usb host controller
+ */
+static struct omap_hwmod_ocp_if omap34xx_usb_host_hs__l3_main_2 = {
+ .master = &omap34xx_usb_host_hs_hwmod,
+ .slave = &omap3xxx_l3_main_hwmod,
+ .clk = "core_l3_ick",
+ .user = OCP_USER_MPU,
+};
+
+static struct omap_hwmod_class_sysconfig omap34xx_usb_host_hs_sysc = {
+ .rev_offs = 0x0000,
+ .sysc_offs = 0x0010,
+ .syss_offs = 0x0014,
+ .sysc_flags = (SYSC_HAS_MIDLEMODE | SYSC_HAS_SIDLEMODE),
+ .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+ MSTANDBY_FORCE | MSTANDBY_NO | MSTANDBY_SMART),
+ .sysc_fields = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap34xx_usb_host_hs_hwmod_class = {
+ .name = "usbhs_uhh",
+ .sysc = &omap34xx_usb_host_hs_sysc,
+};
+
+static struct omap_hwmod_ocp_if *omap34xx_usb_host_hs_masters[] = {
+ &omap34xx_usb_host_hs__l3_main_2,
+};
+
+static struct omap_hwmod_irq_info omap34xx_usb_host_hs_irqs[] = {
+ { .name = "ohci-irq", .irq = 76 },
+ { .name = "ehci-irq", .irq = 77 },
+};
+
+static struct omap_hwmod_addr_space omap34xx_usb_host_hs_addrs[] = {
+ {
+ .name = "uhh",
+ .pa_start = 0x48064000,
+ .pa_end = 0x480643ff,
+ .flags = ADDR_TYPE_RT
+ },
+ {
+ .name = "ohci",
+ .pa_start = 0x48064400,
+ .pa_end = 0x480647FF,
+ .flags = ADDR_MAP_ON_INIT
+ },
+ {
+ .name = "ehci",
+ .pa_start = 0x48064800,
+ .pa_end = 0x48064CFF,
+ .flags = ADDR_MAP_ON_INIT
+ }
+};
+
+static struct omap_hwmod_ocp_if omap34xx_l4_cfg__usb_host_hs = {
+ .master = &omap3xxx_l4_core_hwmod,
+ .slave = &omap34xx_usb_host_hs_hwmod,
+ .clk = "l4_ick",
+ .addr = omap34xx_usb_host_hs_addrs,
+ .addr_cnt = ARRAY_SIZE(omap34xx_usb_host_hs_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_ocp_if omap34xx_f128m_cfg__usb_host_hs = {
+ .clk = "usbhost_120m_fck",
+ .user = OCP_USER_MPU,
+ .flags = OCPIF_SWSUP_IDLE,
+};
+
+static struct omap_hwmod_ocp_if omap34xx_f48m_cfg__usb_host_hs = {
+ .clk = "usbhost_48m_fck",
+ .user = OCP_USER_MPU,
+ .flags = OCPIF_SWSUP_IDLE,
+};
+
+static struct omap_hwmod_ocp_if *omap34xx_usb_host_hs_slaves[] = {
+ &omap34xx_l4_cfg__usb_host_hs,
+ &omap34xx_f128m_cfg__usb_host_hs,
+ &omap34xx_f48m_cfg__usb_host_hs,
+};
+
+static struct omap_hwmod omap34xx_usb_host_hs_hwmod = {
+ .name = "usbhs_uhh",
+ .class = &omap34xx_usb_host_hs_hwmod_class,
+ .mpu_irqs = omap34xx_usb_host_hs_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap34xx_usb_host_hs_irqs),
+ .main_clk = "usbhost_ick",
+ .prcm = {
+ .omap2 = {
+ .module_offs = OMAP3430ES2_USBHOST_MOD,
+ .prcm_reg_id = 1,
+ .module_bit = 0,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = 1,
+ .idlest_stdby_bit = 0,
+ },
+ },
+ .slaves = omap34xx_usb_host_hs_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap34xx_usb_host_hs_slaves),
+ .masters = omap34xx_usb_host_hs_masters,
+ .masters_cnt = ARRAY_SIZE(omap34xx_usb_host_hs_masters),
+ .flags = HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+};
+
+/*
+ * 'usb_tll_hs' class
+ * usb_tll_hs module is the adapter on the usb_host_hs ports
+ */
+static struct omap_hwmod_class_sysconfig omap34xx_usb_tll_hs_sysc = {
+ .rev_offs = 0x0000,
+ .sysc_offs = 0x0010,
+ .syss_offs = 0x0014,
+ .sysc_flags = (SYSC_HAS_AUTOIDLE | SYSC_HAS_SIDLEMODE),
+ .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
+ .sysc_fields = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap34xx_usb_tll_hs_hwmod_class = {
+ .name = "usbhs_tll",
+ .sysc = &omap34xx_usb_tll_hs_sysc,
+};
+
+static struct omap_hwmod_irq_info omap34xx_usb_tll_hs_irqs[] = {
+ { .name = "tll-irq", .irq = 78 },
+};
+
+static struct omap_hwmod_addr_space omap34xx_usb_tll_hs_addrs[] = {
+ {
+ .name = "tll",
+ .pa_start = 0x48062000,
+ .pa_end = 0x48062fff,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+static struct omap_hwmod_ocp_if omap34xx_f_cfg__usb_tll_hs = {
+ .clk = "usbtll_fck",
+ .user = OCP_USER_MPU,
+ .flags = OCPIF_SWSUP_IDLE,
+};
+
+static struct omap_hwmod_ocp_if omap34xx_l4_cfg__usb_tll_hs = {
+ .master = &omap3xxx_l4_core_hwmod,
+ .slave = &omap34xx_usb_tll_hs_hwmod,
+ .clk = "l4_ick",
+ .addr = omap34xx_usb_tll_hs_addrs,
+ .addr_cnt = ARRAY_SIZE(omap34xx_usb_tll_hs_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_ocp_if *omap34xx_usb_tll_hs_slaves[] = {
+ &omap34xx_l4_cfg__usb_tll_hs,
+ &omap34xx_f_cfg__usb_tll_hs,
+};
+
+static struct omap_hwmod omap34xx_usb_tll_hs_hwmod = {
+ .name = "usbhs_tll",
+ .class = &omap34xx_usb_tll_hs_hwmod_class,
+ .mpu_irqs = omap34xx_usb_tll_hs_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap34xx_usb_tll_hs_irqs),
+ .main_clk = "usbtll_ick",
+ .prcm = {
+ .omap2 = {
+ .module_offs = CORE_MOD,
+ .prcm_reg_id = 3,
+ .module_bit = 2,
+ .idlest_reg_id = 3,
+ .idlest_idle_bit = 2,
+ },
+ },
+ .slaves = omap34xx_usb_tll_hs_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap34xx_usb_tll_hs_slaves),
+ .flags = HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+};
+
static __initdata struct omap_hwmod *omap3xxx_hwmods[] = {
&omap3xxx_l3_main_hwmod,
&omap3xxx_l4_core_hwmod,
@@ -3656,6 +3837,9 @@ static __initdata struct omap_hwmod *omap3xxx_hwmods[] = {
/* usbotg for am35x */
&am35xx_usbhsotg_hwmod,
+ &omap34xx_usb_host_hs_hwmod,
+ &omap34xx_usb_tll_hs_hwmod,
+
NULL,
};
diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
index abc548a..d7112b0 100644
--- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
@@ -66,6 +66,8 @@ static struct omap_hwmod omap44xx_mmc2_hwmod;
static struct omap_hwmod omap44xx_mpu_hwmod;
static struct omap_hwmod omap44xx_mpu_private_hwmod;
static struct omap_hwmod omap44xx_usb_otg_hs_hwmod;
+static struct omap_hwmod omap44xx_usb_host_hs_hwmod;
+static struct omap_hwmod omap44xx_usb_tll_hs_hwmod;
/*
* Interconnects omap_hwmod structures
@@ -5027,6 +5029,155 @@ static struct omap_hwmod omap44xx_wd_timer3_hwmod = {
.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
};
+/*
+ * 'usb_host_hs' class
+ * high-speed multi-port usb host controller
+ */
+static struct omap_hwmod_ocp_if omap44xx_usb_host_hs__l3_main_2 = {
+ .master = &omap44xx_usb_host_hs_hwmod,
+ .slave = &omap44xx_l3_main_2_hwmod,
+ .clk = "l3_div_ck",
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_class_sysconfig omap44xx_usb_host_hs_sysc = {
+ .rev_offs = 0x0000,
+ .sysc_offs = 0x0010,
+ .syss_offs = 0x0014,
+ .sysc_flags = (SYSC_HAS_MIDLEMODE | SYSC_HAS_SIDLEMODE),
+ .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+ MSTANDBY_FORCE | MSTANDBY_NO | MSTANDBY_SMART),
+ .sysc_fields = &omap_hwmod_sysc_type2,
+};
+
+static struct omap_hwmod_class omap44xx_usb_host_hs_hwmod_class = {
+ .name = "usbhs_uhh",
+ .sysc = &omap44xx_usb_host_hs_sysc,
+};
+
+static struct omap_hwmod_ocp_if *omap44xx_usb_host_hs_masters[] = {
+ &omap44xx_usb_host_hs__l3_main_2,
+};
+
+static struct omap_hwmod_irq_info omap44xx_usb_host_hs_irqs[] = {
+ { .name = "ohci-irq", .irq = 76 + OMAP44XX_IRQ_GIC_START },
+ { .name = "ehci-irq", .irq = 77 + OMAP44XX_IRQ_GIC_START },
+};
+
+static struct omap_hwmod_addr_space omap44xx_usb_host_hs_addrs[] = {
+ {
+ .name = "uhh",
+ .pa_start = 0x4a064000,
+ .pa_end = 0x4a0647ff,
+ .flags = ADDR_TYPE_RT
+ },
+ {
+ .name = "ohci",
+ .pa_start = 0x4A064800,
+ .pa_end = 0x4A064BFF,
+ .flags = ADDR_MAP_ON_INIT
+ },
+ {
+ .name = "ehci",
+ .pa_start = 0x4A064C00,
+ .pa_end = 0x4A064FFF,
+ .flags = ADDR_MAP_ON_INIT
+ }
+};
+
+static struct omap_hwmod_ocp_if omap44xx_l4_cfg__usb_host_hs = {
+ .master = &omap44xx_l4_cfg_hwmod,
+ .slave = &omap44xx_usb_host_hs_hwmod,
+ .clk = "l4_div_ck",
+ .addr = omap44xx_usb_host_hs_addrs,
+ .addr_cnt = ARRAY_SIZE(omap44xx_usb_host_hs_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_ocp_if *omap44xx_usb_host_hs_slaves[] = {
+ &omap44xx_l4_cfg__usb_host_hs,
+};
+
+static struct omap_hwmod omap44xx_usb_host_hs_hwmod = {
+ .name = "usbhs_uhh",
+ .class = &omap44xx_usb_host_hs_hwmod_class,
+ .mpu_irqs = omap44xx_usb_host_hs_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap44xx_usb_host_hs_irqs),
+ .main_clk = "usb_host_hs_fck",
+ .prcm = {
+ .omap4 = {
+ .clkctrl_reg = OMAP4430_CM_L3INIT_USB_HOST_CLKCTRL,
+ },
+ },
+ .slaves = omap44xx_usb_host_hs_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap44xx_usb_host_hs_slaves),
+ .masters = omap44xx_usb_host_hs_masters,
+ .masters_cnt = ARRAY_SIZE(omap44xx_usb_host_hs_masters),
+ .flags = HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
+};
+
+/*
+ * 'usb_tll_hs' class
+ * usb_tll_hs module is the adapter on the usb_host_hs ports
+ */
+static struct omap_hwmod_class_sysconfig omap44xx_usb_tll_hs_sysc = {
+ .rev_offs = 0x0000,
+ .sysc_offs = 0x0010,
+ .syss_offs = 0x0014,
+ .sysc_flags = (SYSC_HAS_AUTOIDLE | SYSC_HAS_SIDLEMODE),
+ .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
+ .sysc_fields = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap44xx_usb_tll_hs_hwmod_class = {
+ .name = "usbhs_tll",
+ .sysc = &omap44xx_usb_tll_hs_sysc,
+};
+
+static struct omap_hwmod_irq_info omap44xx_usb_tll_hs_irqs[] = {
+ { .name = "tll-irq", .irq = 78 + OMAP44XX_IRQ_GIC_START },
+};
+
+static struct omap_hwmod_addr_space omap44xx_usb_tll_hs_addrs[] = {
+ {
+ .name = "tll",
+ .pa_start = 0x4a062000,
+ .pa_end = 0x4a063fff,
+ .flags = ADDR_TYPE_RT
+ },
+};
+
+static struct omap_hwmod_ocp_if omap44xx_l4_cfg__usb_tll_hs = {
+ .master = &omap44xx_l4_cfg_hwmod,
+ .slave = &omap44xx_usb_tll_hs_hwmod,
+ .clk = "l4_div_ck",
+ .addr = omap44xx_usb_tll_hs_addrs,
+ .addr_cnt = ARRAY_SIZE(omap44xx_usb_tll_hs_addrs),
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_ocp_if *omap44xx_usb_tll_hs_slaves[] = {
+ &omap44xx_l4_cfg__usb_tll_hs,
+};
+
+static struct omap_hwmod omap44xx_usb_tll_hs_hwmod = {
+ .name = "usbhs_tll",
+ .class = &omap44xx_usb_tll_hs_hwmod_class,
+ .mpu_irqs = omap44xx_usb_tll_hs_irqs,
+ .mpu_irqs_cnt = ARRAY_SIZE(omap44xx_usb_tll_hs_irqs),
+ .main_clk = "usb_tll_hs_ick",
+ .prcm = {
+ .omap4 = {
+ .clkctrl_reg = OMAP4430_CM_L3INIT_USB_TLL_CLKCTRL,
+ },
+ },
+ .slaves = omap44xx_usb_tll_hs_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap44xx_usb_tll_hs_slaves),
+ .flags = HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
+};
+
static __initdata struct omap_hwmod *omap44xx_hwmods[] = {
/* dmm class */
@@ -5173,6 +5324,8 @@ static __initdata struct omap_hwmod *omap44xx_hwmods[] = {
&omap44xx_wd_timer2_hwmod,
&omap44xx_wd_timer3_hwmod,
+ &omap44xx_usb_host_hs_hwmod,
+ &omap44xx_usb_tll_hs_hwmod,
NULL,
};
--
1.6.0.4
The hwmod structure of uhh and tll are retrived
and registered with omap device
Signed-off-by: Keshava Munegowda <[email protected]>
---
arch/arm/mach-omap2/usb-host.c | 99 ++++++++++++++--------------------------
1 files changed, 35 insertions(+), 64 deletions(-)
diff --git a/arch/arm/mach-omap2/usb-host.c b/arch/arm/mach-omap2/usb-host.c
index 89ae298..9d762c4 100644
--- a/arch/arm/mach-omap2/usb-host.c
+++ b/arch/arm/mach-omap2/usb-host.c
@@ -28,51 +28,28 @@
#include <mach/hardware.h>
#include <mach/irqs.h>
#include <plat/usb.h>
+#include <plat/omap_device.h>
#include "mux.h"
#ifdef CONFIG_MFD_OMAP_USB_HOST
-#define OMAP_USBHS_DEVICE "usbhs-omap"
-
-static struct resource usbhs_resources[] = {
- {
- .name = "uhh",
- .flags = IORESOURCE_MEM,
- },
- {
- .name = "tll",
- .flags = IORESOURCE_MEM,
- },
- {
- .name = "ehci",
- .flags = IORESOURCE_MEM,
- },
- {
- .name = "ehci-irq",
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "ohci",
- .flags = IORESOURCE_MEM,
- },
- {
- .name = "ohci-irq",
- .flags = IORESOURCE_IRQ,
- }
-};
-
-static struct platform_device usbhs_device = {
- .name = OMAP_USBHS_DEVICE,
- .id = 0,
- .num_resources = ARRAY_SIZE(usbhs_resources),
- .resource = usbhs_resources,
-};
+#define OMAP_USBHS_DEVICE "usbhs_omap"
+#define USBHS_UHH_HWMODNAME "usbhs_uhh"
+#define USBHS_TLL_HWMODNAME "usbhs_tll"
static struct usbhs_omap_platform_data usbhs_data;
static struct ehci_hcd_omap_platform_data ehci_data;
static struct ohci_hcd_omap_platform_data ohci_data;
+static struct omap_device_pm_latency omap_uhhtll_latency[] = {
+ {
+ .deactivate_func = omap_device_idle_hwmods,
+ .activate_func = omap_device_enable_hwmods,
+ .flags = OMAP_DEVICE_LATENCY_AUTO_ADJUST,
+ },
+};
+
/* MUX settings for EHCI pins */
/*
* setup_ehci_io_mux - initialize IO pad mux for USBHOST
@@ -508,7 +485,10 @@ static void setup_4430ohci_io_mux(const enum usbhs_omap_port_mode *port_mode)
void __init usbhs_init(const struct usbhs_omap_board_data *pdata)
{
- int i;
+ struct omap_hwmod *oh[2];
+ struct omap_device *od;
+ int bus_id = -1;
+ int i;
for (i = 0; i < OMAP3_HS_USB_PORTS; i++) {
usbhs_data.port_mode[i] = pdata->port_mode[i];
@@ -523,44 +503,35 @@ void __init usbhs_init(const struct usbhs_omap_board_data *pdata)
usbhs_data.ohci_data = &ohci_data;
if (cpu_is_omap34xx()) {
- usbhs_resources[0].start = OMAP34XX_UHH_CONFIG_BASE;
- usbhs_resources[0].end = OMAP34XX_UHH_CONFIG_BASE + SZ_1K - 1;
- usbhs_resources[1].start = OMAP34XX_USBTLL_BASE;
- usbhs_resources[1].end = OMAP34XX_USBTLL_BASE + SZ_4K - 1;
- usbhs_resources[2].start = OMAP34XX_EHCI_BASE;
- usbhs_resources[2].end = OMAP34XX_EHCI_BASE + SZ_1K - 1;
- usbhs_resources[3].start = INT_34XX_EHCI_IRQ;
- usbhs_resources[4].start = OMAP34XX_OHCI_BASE;
- usbhs_resources[4].end = OMAP34XX_OHCI_BASE + SZ_1K - 1;
- usbhs_resources[5].start = INT_34XX_OHCI_IRQ;
setup_ehci_io_mux(pdata->port_mode);
setup_ohci_io_mux(pdata->port_mode);
} else if (cpu_is_omap44xx()) {
- usbhs_resources[0].start = OMAP44XX_UHH_CONFIG_BASE;
- usbhs_resources[0].end = OMAP44XX_UHH_CONFIG_BASE + SZ_1K - 1;
- usbhs_resources[1].start = OMAP44XX_USBTLL_BASE;
- usbhs_resources[1].end = OMAP44XX_USBTLL_BASE + SZ_4K - 1;
- usbhs_resources[2].start = OMAP44XX_HSUSB_EHCI_BASE;
- usbhs_resources[2].end = OMAP44XX_HSUSB_EHCI_BASE + SZ_1K - 1;
- usbhs_resources[3].start = OMAP44XX_IRQ_EHCI;
- usbhs_resources[4].start = OMAP44XX_HSUSB_OHCI_BASE;
- usbhs_resources[4].end = OMAP44XX_HSUSB_OHCI_BASE + SZ_1K - 1;
- usbhs_resources[5].start = OMAP44XX_IRQ_OHCI;
setup_4430ehci_io_mux(pdata->port_mode);
setup_4430ohci_io_mux(pdata->port_mode);
}
- if (platform_device_add_data(&usbhs_device,
- &usbhs_data, sizeof(usbhs_data)) < 0) {
- printk(KERN_ERR "USBHS platform_device_add_data failed\n");
- goto init_end;
+ oh[0] = omap_hwmod_lookup(USBHS_UHH_HWMODNAME);
+ if (!oh[0]) {
+ pr_err("Could not look up %s\n", USBHS_UHH_HWMODNAME);
+ return;
}
- if (platform_device_register(&usbhs_device) < 0)
- printk(KERN_ERR "USBHS platform_device_register failed\n");
+ oh[1] = omap_hwmod_lookup(USBHS_TLL_HWMODNAME);
+ if (!oh[1]) {
+ pr_err("Could not look up %s\n", USBHS_TLL_HWMODNAME);
+ return;
+ }
-init_end:
- return;
+ od = omap_device_build_ss(OMAP_USBHS_DEVICE, bus_id, oh, 2,
+ (void *)&usbhs_data, sizeof(usbhs_data),
+ omap_uhhtll_latency,
+ ARRAY_SIZE(omap_uhhtll_latency), false);
+
+ if (IS_ERR(od)) {
+ pr_err("Could not build hwmod devices %s, %s\n",
+ USBHS_UHH_HWMODNAME, USBHS_TLL_HWMODNAME);
+ return;
+ }
}
#else
--
1.6.0.4
From: Keshava Munegowda <[email protected]>
device name usbhs clocks are changed from
usbhs-omap.0 to usbhs_omap; this is because
in the hwmod registration the device name is set
as usbhs_omap
Signed-off-by: Keshava Munegowda <[email protected]>
---
arch/arm/mach-omap2/clock3xxx_data.c | 28 ++++++++++++++--------------
arch/arm/mach-omap2/clock44xx_data.c | 10 +++++-----
drivers/mfd/omap-usb-host.c | 2 +-
3 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/arch/arm/mach-omap2/clock3xxx_data.c b/arch/arm/mach-omap2/clock3xxx_data.c
index 75b119b..fabe482 100644
--- a/arch/arm/mach-omap2/clock3xxx_data.c
+++ b/arch/arm/mach-omap2/clock3xxx_data.c
@@ -3285,7 +3285,7 @@ static struct omap_clk omap3xxx_clks[] = {
CLK(NULL, "cpefuse_fck", &cpefuse_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
CLK(NULL, "ts_fck", &ts_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
CLK(NULL, "usbtll_fck", &usbtll_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
- CLK("usbhs-omap.0", "usbtll_fck", &usbtll_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
+ CLK("usbhs_omap", "usbtll_fck", &usbtll_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
CLK("omap-mcbsp.1", "prcm_fck", &core_96m_fck, CK_3XXX),
CLK("omap-mcbsp.5", "prcm_fck", &core_96m_fck, CK_3XXX),
CLK(NULL, "core_96m_fck", &core_96m_fck, CK_3XXX),
@@ -3321,7 +3321,7 @@ static struct omap_clk omap3xxx_clks[] = {
CLK(NULL, "pka_ick", &pka_ick, CK_34XX | CK_36XX),
CLK(NULL, "core_l4_ick", &core_l4_ick, CK_3XXX),
CLK(NULL, "usbtll_ick", &usbtll_ick, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
- CLK("usbhs-omap.0", "usbtll_ick", &usbtll_ick, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
+ CLK("usbhs_omap", "usbtll_ick", &usbtll_ick, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
CLK("omap_hsmmc.2", "ick", &mmchs3_ick, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
CLK(NULL, "icr_ick", &icr_ick, CK_34XX | CK_36XX),
CLK("omap-aes", "ick", &aes2_ick, CK_34XX | CK_36XX),
@@ -3367,20 +3367,20 @@ static struct omap_clk omap3xxx_clks[] = {
CLK(NULL, "cam_ick", &cam_ick, CK_34XX | CK_36XX),
CLK(NULL, "csi2_96m_fck", &csi2_96m_fck, CK_34XX | CK_36XX),
CLK(NULL, "usbhost_120m_fck", &usbhost_120m_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
- CLK("usbhs-omap.0", "hs_fck", &usbhost_120m_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
+ CLK("usbhs_omap", "hs_fck", &usbhost_120m_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
CLK(NULL, "usbhost_48m_fck", &usbhost_48m_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
- CLK("usbhs-omap.0", "fs_fck", &usbhost_48m_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
+ CLK("usbhs_omap", "fs_fck", &usbhost_48m_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
CLK(NULL, "usbhost_ick", &usbhost_ick, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
- CLK("usbhs-omap.0", "usbhost_ick", &usbhost_ick, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
- CLK("usbhs-omap.0", "utmi_p1_gfclk", &dummy_ck, CK_3XXX),
- CLK("usbhs-omap.0", "utmi_p2_gfclk", &dummy_ck, CK_3XXX),
- CLK("usbhs-omap.0", "xclk60mhsp1_ck", &dummy_ck, CK_3XXX),
- CLK("usbhs-omap.0", "xclk60mhsp2_ck", &dummy_ck, CK_3XXX),
- CLK("usbhs-omap.0", "usb_host_hs_utmi_p1_clk", &dummy_ck, CK_3XXX),
- CLK("usbhs-omap.0", "usb_host_hs_utmi_p2_clk", &dummy_ck, CK_3XXX),
- CLK("usbhs-omap.0", "usb_tll_hs_usb_ch0_clk", &dummy_ck, CK_3XXX),
- CLK("usbhs-omap.0", "usb_tll_hs_usb_ch1_clk", &dummy_ck, CK_3XXX),
- CLK("usbhs-omap.0", "init_60m_fclk", &dummy_ck, CK_3XXX),
+ CLK("usbhs_omap", "usbhost_ick", &usbhost_ick, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
+ CLK("usbhs_omap", "utmi_p1_gfclk", &dummy_ck, CK_3XXX),
+ CLK("usbhs_omap", "utmi_p2_gfclk", &dummy_ck, CK_3XXX),
+ CLK("usbhs_omap", "xclk60mhsp1_ck", &dummy_ck, CK_3XXX),
+ CLK("usbhs_omap", "xclk60mhsp2_ck", &dummy_ck, CK_3XXX),
+ CLK("usbhs_omap", "usb_host_hs_utmi_p1_clk", &dummy_ck, CK_3XXX),
+ CLK("usbhs_omap", "usb_host_hs_utmi_p2_clk", &dummy_ck, CK_3XXX),
+ CLK("usbhs_omap", "usb_tll_hs_usb_ch0_clk", &dummy_ck, CK_3XXX),
+ CLK("usbhs_omap", "usb_tll_hs_usb_ch1_clk", &dummy_ck, CK_3XXX),
+ CLK("usbhs_omap", "init_60m_fclk", &dummy_ck, CK_3XXX),
CLK(NULL, "usim_fck", &usim_fck, CK_3430ES2PLUS | CK_36XX),
CLK(NULL, "gpt1_fck", &gpt1_fck, CK_3XXX),
CLK(NULL, "wkup_32k_fck", &wkup_32k_fck, CK_3XXX),
diff --git a/arch/arm/mach-omap2/clock44xx_data.c b/arch/arm/mach-omap2/clock44xx_data.c
index 8c96567..34e91eb 100644
--- a/arch/arm/mach-omap2/clock44xx_data.c
+++ b/arch/arm/mach-omap2/clock44xx_data.c
@@ -3205,7 +3205,7 @@ static struct omap_clk omap44xx_clks[] = {
CLK(NULL, "uart3_fck", &uart3_fck, CK_443X),
CLK(NULL, "uart4_fck", &uart4_fck, CK_443X),
CLK(NULL, "usb_host_fs_fck", &usb_host_fs_fck, CK_443X),
- CLK("usbhs-omap.0", "fs_fck", &usb_host_fs_fck, CK_443X),
+ CLK("usbhs_omap", "fs_fck", &usb_host_fs_fck, CK_443X),
CLK(NULL, "utmi_p1_gfclk", &utmi_p1_gfclk, CK_443X),
CLK(NULL, "usb_host_hs_utmi_p1_clk", &usb_host_hs_utmi_p1_clk, CK_443X),
CLK(NULL, "utmi_p2_gfclk", &utmi_p2_gfclk, CK_443X),
@@ -3217,8 +3217,8 @@ static struct omap_clk omap44xx_clks[] = {
CLK(NULL, "usb_host_hs_hsic480m_p2_clk", &usb_host_hs_hsic480m_p2_clk, CK_443X),
CLK(NULL, "usb_host_hs_func48mclk", &usb_host_hs_func48mclk, CK_443X),
CLK(NULL, "usb_host_hs_fck", &usb_host_hs_fck, CK_443X),
- CLK("usbhs-omap.0", "hs_fck", &usb_host_hs_fck, CK_443X),
- CLK("usbhs-omap.0", "usbhost_ick", &dummy_ck, CK_443X),
+ CLK("usbhs_omap", "hs_fck", &usb_host_hs_fck, CK_443X),
+ CLK("usbhs_omap", "usbhost_ick", &dummy_ck, CK_443X),
CLK(NULL, "otg_60m_gfclk", &otg_60m_gfclk, CK_443X),
CLK(NULL, "usb_otg_hs_xclk", &usb_otg_hs_xclk, CK_443X),
CLK("musb-omap2430", "ick", &usb_otg_hs_ick, CK_443X),
@@ -3227,8 +3227,8 @@ static struct omap_clk omap44xx_clks[] = {
CLK(NULL, "usb_tll_hs_usb_ch0_clk", &usb_tll_hs_usb_ch0_clk, CK_443X),
CLK(NULL, "usb_tll_hs_usb_ch1_clk", &usb_tll_hs_usb_ch1_clk, CK_443X),
CLK(NULL, "usb_tll_hs_ick", &usb_tll_hs_ick, CK_443X),
- CLK("usbhs-omap.0", "usbtll_ick", &usb_tll_hs_ick, CK_443X),
- CLK("usbhs-omap.0", "usbtll_fck", &dummy_ck, CK_443X),
+ CLK("usbhs_omap", "usbtll_ick", &usb_tll_hs_ick, CK_443X),
+ CLK("usbhs_omap", "usbtll_fck", &dummy_ck, CK_443X),
CLK(NULL, "usim_ck", &usim_ck, CK_443X),
CLK(NULL, "usim_fclk", &usim_fclk, CK_443X),
CLK(NULL, "usim_fck", &usim_fck, CK_443X),
diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
index 8552195..43de12a 100644
--- a/drivers/mfd/omap-usb-host.c
+++ b/drivers/mfd/omap-usb-host.c
@@ -28,7 +28,7 @@
#include <plat/usb.h>
#include <linux/pm_runtime.h>
-#define USBHS_DRIVER_NAME "usbhs-omap"
+#define USBHS_DRIVER_NAME "usbhs_omap"
#define OMAP_EHCI_DEVICE "ehci-omap"
#define OMAP_OHCI_DEVICE "ohci-omap3"
--
1.6.0.4
From: Keshava Munegowda <[email protected]>
The global suspend and resume functions for usbhs core driver
are implemented.These routine are called when the global suspend
and resume occurs. Before calling these functions, the
bus suspend and resume of ehci and ohci drivers are called
from runtime pm.
Signed-off-by: Keshava Munegowda <[email protected]>
---
drivers/mfd/omap-usb-host.c | 103 +++++++++++++++++++++++++++++++++++++++++++
1 files changed, 103 insertions(+), 0 deletions(-)
diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
index 43de12a..32d19e2 100644
--- a/drivers/mfd/omap-usb-host.c
+++ b/drivers/mfd/omap-usb-host.c
@@ -146,6 +146,10 @@
#define is_ehci_hsic_mode(x) (x == OMAP_EHCI_PORT_MODE_HSIC)
+/* USBHS state bits */
+#define OMAP_USBHS_INIT 0
+#define OMAP_USBHS_SUSPEND 4
+
struct usbhs_hcd_omap {
struct clk *xclk60mhsp1_ck;
struct clk *xclk60mhsp2_ck;
@@ -165,6 +169,7 @@ struct usbhs_hcd_omap {
u32 usbhs_rev;
spinlock_t lock;
int count;
+ unsigned long state;
};
/*-------------------------------------------------------------------------*/
@@ -809,6 +814,8 @@ static int usbhs_enable(struct device *dev)
(pdata->ehci_data->reset_gpio_port[1], 1);
}
+ set_bit(OMAP_USBHS_INIT, &omap->state);
+
end_count:
omap->count++;
spin_unlock_irqrestore(&omap->lock, flags);
@@ -897,6 +904,7 @@ static void usbhs_disable(struct device *dev)
}
pm_runtime_put_sync(dev);
+ clear_bit(OMAP_USBHS_INIT, &omap->state);
/* The gpio_free migh sleep; so unlock the spinlock */
spin_unlock_irqrestore(&omap->lock, flags);
@@ -926,10 +934,105 @@ void omap_usbhs_disable(struct device *dev)
}
EXPORT_SYMBOL_GPL(omap_usbhs_disable);
+#ifdef CONFIG_PM
+
+static int usbhs_resume(struct device *dev)
+{
+ struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
+ struct usbhs_omap_platform_data *pdata = &omap->platdata;
+ unsigned long flags = 0;
+
+ dev_dbg(dev, "Resuming TI HSUSB Controller\n");
+
+ if (!pdata) {
+ dev_dbg(dev, "missing platform_data\n");
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&omap->lock, flags);
+
+ if (!test_bit(OMAP_USBHS_INIT, &omap->state) ||
+ !test_bit(OMAP_USBHS_SUSPEND, &omap->state))
+ goto end_resume;
+
+ pm_runtime_get_sync(dev);
+
+ if (is_omap_usbhs_rev2(omap)) {
+ if (is_ehci_tll_mode(pdata->port_mode[0])) {
+ clk_enable(omap->usbhost_p1_fck);
+ clk_enable(omap->usbtll_p1_fck);
+ }
+ if (is_ehci_tll_mode(pdata->port_mode[1])) {
+ clk_enable(omap->usbhost_p2_fck);
+ clk_enable(omap->usbtll_p2_fck);
+ }
+ clk_enable(omap->utmi_p1_fck);
+ clk_enable(omap->utmi_p2_fck);
+ }
+ clear_bit(OMAP_USBHS_SUSPEND, &omap->state);
+
+end_resume:
+ spin_unlock_irqrestore(&omap->lock, flags);
+ return 0;
+}
+
+
+static int usbhs_suspend(struct device *dev)
+{
+ struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
+ struct usbhs_omap_platform_data *pdata = &omap->platdata;
+ unsigned long flags = 0;
+
+ dev_dbg(dev, "Suspending TI HSUSB Controller\n");
+
+ if (!pdata) {
+ dev_dbg(dev, "missing platform_data\n");
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&omap->lock, flags);
+
+ if (!test_bit(OMAP_USBHS_INIT, &omap->state) ||
+ test_bit(OMAP_USBHS_SUSPEND, &omap->state))
+ goto end_suspend;
+
+ if (is_omap_usbhs_rev2(omap)) {
+ if (is_ehci_tll_mode(pdata->port_mode[0])) {
+ clk_disable(omap->usbhost_p1_fck);
+ clk_disable(omap->usbtll_p1_fck);
+ }
+ if (is_ehci_tll_mode(pdata->port_mode[1])) {
+ clk_disable(omap->usbhost_p2_fck);
+ clk_disable(omap->usbtll_p2_fck);
+ }
+ clk_disable(omap->utmi_p2_fck);
+ clk_disable(omap->utmi_p1_fck);
+ }
+
+ set_bit(OMAP_USBHS_SUSPEND, &omap->state);
+ pm_runtime_put_sync(dev);
+
+end_suspend:
+ spin_unlock_irqrestore(&omap->lock, flags);
+ return 0;
+}
+
+
+static const struct dev_pm_ops usbhsomap_dev_pm_ops = {
+ .suspend = usbhs_suspend,
+ .resume = usbhs_resume,
+};
+
+#define USBHS_OMAP_DEV_PM_OPS (&usbhsomap_dev_pm_ops)
+#else
+#define USBHS_OMAP_DEV_PM_OPS NULL
+#endif
+
static struct platform_driver usbhs_omap_driver = {
.driver = {
.name = (char *)usbhs_driver_name,
.owner = THIS_MODULE,
+ .pm = USBHS_OMAP_DEV_PM_OPS,
},
.remove = __exit_p(usbhs_omap_remove),
};
--
1.6.0.4
Hi,
On Wed, Jun 01, 2011 at 06:57:27PM +0530, Keshava Munegowda wrote:
> diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
> index 43de12a..32d19e2 100644
> --- a/drivers/mfd/omap-usb-host.c
> +++ b/drivers/mfd/omap-usb-host.c
> @@ -146,6 +146,10 @@
> #define is_ehci_hsic_mode(x) (x == OMAP_EHCI_PORT_MODE_HSIC)
>
>
> +/* USBHS state bits */
> +#define OMAP_USBHS_INIT 0
> +#define OMAP_USBHS_SUSPEND 4
#define OMAP_USBHS_INIT BIT(0)
#define OMAP_USBHS_SUSPEND BIT(1)
???
--
balbi
On Wed, Jun 1, 2011 at 7:01 PM, Felipe Balbi <[email protected]> wrote:
> Hi,
>
> On Wed, Jun 01, 2011 at 06:57:27PM +0530, Keshava Munegowda wrote:
>> diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
>> index 43de12a..32d19e2 100644
>> --- a/drivers/mfd/omap-usb-host.c
>> +++ b/drivers/mfd/omap-usb-host.c
>> @@ -146,6 +146,10 @@
>> ?#define is_ehci_hsic_mode(x) (x == OMAP_EHCI_PORT_MODE_HSIC)
>>
>>
>> +/* USBHS state bits */
>> +#define OMAP_USBHS_INIT ? ? ? ? ? ? ?0
>> +#define OMAP_USBHS_SUSPEND ? 4
>
> #define OMAP_USBHS_INIT ? ? ? ? BIT(0)
> #define OMAP_USBHS_SUSPEND ? ? ?BIT(1)
>
> ???
yes, I will do.. please comment on other patches too
keshava
On Wednesday, June 01, 2011, Keshava Munegowda wrote:
> From: Keshava Munegowda <[email protected]>
>
> The global suspend and resume functions for usbhs core driver
> are implemented.These routine are called when the global suspend
> and resume occurs. Before calling these functions, the
> bus suspend and resume of ehci and ohci drivers are called
> from runtime pm.
>
> Signed-off-by: Keshava Munegowda <[email protected]>
> ---
> drivers/mfd/omap-usb-host.c | 103 +++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 103 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
> index 43de12a..32d19e2 100644
> --- a/drivers/mfd/omap-usb-host.c
> +++ b/drivers/mfd/omap-usb-host.c
> @@ -146,6 +146,10 @@
> #define is_ehci_hsic_mode(x) (x == OMAP_EHCI_PORT_MODE_HSIC)
>
>
> +/* USBHS state bits */
> +#define OMAP_USBHS_INIT 0
> +#define OMAP_USBHS_SUSPEND 4
> +
> struct usbhs_hcd_omap {
> struct clk *xclk60mhsp1_ck;
> struct clk *xclk60mhsp2_ck;
> @@ -165,6 +169,7 @@ struct usbhs_hcd_omap {
> u32 usbhs_rev;
> spinlock_t lock;
> int count;
> + unsigned long state;
> };
> /*-------------------------------------------------------------------------*/
>
> @@ -809,6 +814,8 @@ static int usbhs_enable(struct device *dev)
> (pdata->ehci_data->reset_gpio_port[1], 1);
> }
>
> + set_bit(OMAP_USBHS_INIT, &omap->state);
> +
> end_count:
> omap->count++;
> spin_unlock_irqrestore(&omap->lock, flags);
> @@ -897,6 +904,7 @@ static void usbhs_disable(struct device *dev)
> }
>
> pm_runtime_put_sync(dev);
> + clear_bit(OMAP_USBHS_INIT, &omap->state);
>
> /* The gpio_free migh sleep; so unlock the spinlock */
> spin_unlock_irqrestore(&omap->lock, flags);
> @@ -926,10 +934,105 @@ void omap_usbhs_disable(struct device *dev)
> }
> EXPORT_SYMBOL_GPL(omap_usbhs_disable);
>
> +#ifdef CONFIG_PM
> +
> +static int usbhs_resume(struct device *dev)
> +{
> + struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
> + struct usbhs_omap_platform_data *pdata = &omap->platdata;
> + unsigned long flags = 0;
> +
> + dev_dbg(dev, "Resuming TI HSUSB Controller\n");
> +
> + if (!pdata) {
> + dev_dbg(dev, "missing platform_data\n");
> + return -ENODEV;
> + }
> +
> + spin_lock_irqsave(&omap->lock, flags);
> +
> + if (!test_bit(OMAP_USBHS_INIT, &omap->state) ||
> + !test_bit(OMAP_USBHS_SUSPEND, &omap->state))
> + goto end_resume;
> +
> + pm_runtime_get_sync(dev);
> +
> + if (is_omap_usbhs_rev2(omap)) {
> + if (is_ehci_tll_mode(pdata->port_mode[0])) {
> + clk_enable(omap->usbhost_p1_fck);
> + clk_enable(omap->usbtll_p1_fck);
> + }
> + if (is_ehci_tll_mode(pdata->port_mode[1])) {
> + clk_enable(omap->usbhost_p2_fck);
> + clk_enable(omap->usbtll_p2_fck);
> + }
> + clk_enable(omap->utmi_p1_fck);
> + clk_enable(omap->utmi_p2_fck);
> + }
> + clear_bit(OMAP_USBHS_SUSPEND, &omap->state);
> +
> +end_resume:
> + spin_unlock_irqrestore(&omap->lock, flags);
> + return 0;
> +}
> +
> +
> +static int usbhs_suspend(struct device *dev)
> +{
> + struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
> + struct usbhs_omap_platform_data *pdata = &omap->platdata;
> + unsigned long flags = 0;
> +
> + dev_dbg(dev, "Suspending TI HSUSB Controller\n");
> +
> + if (!pdata) {
> + dev_dbg(dev, "missing platform_data\n");
> + return -ENODEV;
> + }
> +
> + spin_lock_irqsave(&omap->lock, flags);
> +
> + if (!test_bit(OMAP_USBHS_INIT, &omap->state) ||
> + test_bit(OMAP_USBHS_SUSPEND, &omap->state))
> + goto end_suspend;
> +
> + if (is_omap_usbhs_rev2(omap)) {
> + if (is_ehci_tll_mode(pdata->port_mode[0])) {
> + clk_disable(omap->usbhost_p1_fck);
> + clk_disable(omap->usbtll_p1_fck);
> + }
> + if (is_ehci_tll_mode(pdata->port_mode[1])) {
> + clk_disable(omap->usbhost_p2_fck);
> + clk_disable(omap->usbtll_p2_fck);
> + }
> + clk_disable(omap->utmi_p2_fck);
> + clk_disable(omap->utmi_p1_fck);
> + }
> +
> + set_bit(OMAP_USBHS_SUSPEND, &omap->state);
> + pm_runtime_put_sync(dev);
> +
> +end_suspend:
> + spin_unlock_irqrestore(&omap->lock, flags);
> + return 0;
> +}
> +
> +
> +static const struct dev_pm_ops usbhsomap_dev_pm_ops = {
> + .suspend = usbhs_suspend,
> + .resume = usbhs_resume,
> +};
Please add .freeze()/.thaw() and .poweroff()/.restore() callbacks too.
They may point to the same routines, but must be present.
You can actually use the SIMPLE_DEV_PM_OPS() convenience macro for this
purpose.
> +
> +#define USBHS_OMAP_DEV_PM_OPS (&usbhsomap_dev_pm_ops)
> +#else
> +#define USBHS_OMAP_DEV_PM_OPS NULL
> +#endif
> +
> static struct platform_driver usbhs_omap_driver = {
> .driver = {
> .name = (char *)usbhs_driver_name,
> .owner = THIS_MODULE,
> + .pm = USBHS_OMAP_DEV_PM_OPS,
> },
> .remove = __exit_p(usbhs_omap_remove),
> };
>
Thanks,
Rafael
Hi,
On Wed, Jun 01, 2011 at 03:54:38PM +0200, Rafael J. Wysocki wrote:
> > +static const struct dev_pm_ops usbhsomap_dev_pm_ops = {
> > + .suspend = usbhs_suspend,
> > + .resume = usbhs_resume,
> > +};
>
> Please add .freeze()/.thaw() and .poweroff()/.restore() callbacks too.
> They may point to the same routines, but must be present.
>
> You can actually use the SIMPLE_DEV_PM_OPS() convenience macro for this
> purpose.
good point. BTW, do we need this #ifdef CONFIG_PM stuff which has been
popping on most drivers recently ? To me it looks like driver.pm field
is always available even if PM is disabled, so what's the point ? Saving
a few bytes ?
--
balbi
Keshava Munegowda <[email protected]> writes:
> Following 2 hwmod strcuture are added:
> UHH hwmod of usbhs with uhh base address and
> EHCI , OHCI irq and base addresses.
> TLL hwmod of usbhs with the TLL base address and irq.
>
> Signed-off-by: Keshava Munegowda <[email protected]>
I believe the OMAP4 data came from Benoit, so should be in a patch with
his authorship and signoff. OMAP3 can be a separate patch with your
authorship.
Kevin
Hi Kesheva,
Keshava Munegowda <[email protected]> writes:
> The hwmod structure of uhh and tll are retrived
> and registered with omap device
>
> Signed-off-by: Keshava Munegowda <[email protected]>
Minor comment below...
> ---
> arch/arm/mach-omap2/usb-host.c | 99 ++++++++++++++--------------------------
> 1 files changed, 35 insertions(+), 64 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/usb-host.c b/arch/arm/mach-omap2/usb-host.c
> index 89ae298..9d762c4 100644
> --- a/arch/arm/mach-omap2/usb-host.c
> +++ b/arch/arm/mach-omap2/usb-host.c
> @@ -28,51 +28,28 @@
> #include <mach/hardware.h>
> #include <mach/irqs.h>
> #include <plat/usb.h>
> +#include <plat/omap_device.h>
>
> #include "mux.h"
>
> #ifdef CONFIG_MFD_OMAP_USB_HOST
>
> -#define OMAP_USBHS_DEVICE "usbhs-omap"
> -
> -static struct resource usbhs_resources[] = {
> - {
> - .name = "uhh",
> - .flags = IORESOURCE_MEM,
> - },
> - {
> - .name = "tll",
> - .flags = IORESOURCE_MEM,
> - },
> - {
> - .name = "ehci",
> - .flags = IORESOURCE_MEM,
> - },
> - {
> - .name = "ehci-irq",
> - .flags = IORESOURCE_IRQ,
> - },
> - {
> - .name = "ohci",
> - .flags = IORESOURCE_MEM,
> - },
> - {
> - .name = "ohci-irq",
> - .flags = IORESOURCE_IRQ,
> - }
> -};
> -
> -static struct platform_device usbhs_device = {
> - .name = OMAP_USBHS_DEVICE,
> - .id = 0,
> - .num_resources = ARRAY_SIZE(usbhs_resources),
> - .resource = usbhs_resources,
> -};
> +#define OMAP_USBHS_DEVICE "usbhs_omap"
> +#define USBHS_UHH_HWMODNAME "usbhs_uhh"
> +#define USBHS_TLL_HWMODNAME "usbhs_tll"
>
> static struct usbhs_omap_platform_data usbhs_data;
> static struct ehci_hcd_omap_platform_data ehci_data;
> static struct ohci_hcd_omap_platform_data ohci_data;
While changing the platform_data registration, these platform_data
structs should be alloc'd and then free'd after omap_device_build, since
a copy of them is made during device registration.
Kevin
> +static struct omap_device_pm_latency omap_uhhtll_latency[] = {
> + {
> + .deactivate_func = omap_device_idle_hwmods,
> + .activate_func = omap_device_enable_hwmods,
> + .flags = OMAP_DEVICE_LATENCY_AUTO_ADJUST,
> + },
> +};
> +
> /* MUX settings for EHCI pins */
> /*
> * setup_ehci_io_mux - initialize IO pad mux for USBHOST
> @@ -508,7 +485,10 @@ static void setup_4430ohci_io_mux(const enum usbhs_omap_port_mode *port_mode)
>
> void __init usbhs_init(const struct usbhs_omap_board_data *pdata)
> {
> - int i;
> + struct omap_hwmod *oh[2];
> + struct omap_device *od;
> + int bus_id = -1;
> + int i;
>
> for (i = 0; i < OMAP3_HS_USB_PORTS; i++) {
> usbhs_data.port_mode[i] = pdata->port_mode[i];
> @@ -523,44 +503,35 @@ void __init usbhs_init(const struct usbhs_omap_board_data *pdata)
> usbhs_data.ohci_data = &ohci_data;
>
> if (cpu_is_omap34xx()) {
> - usbhs_resources[0].start = OMAP34XX_UHH_CONFIG_BASE;
> - usbhs_resources[0].end = OMAP34XX_UHH_CONFIG_BASE + SZ_1K - 1;
> - usbhs_resources[1].start = OMAP34XX_USBTLL_BASE;
> - usbhs_resources[1].end = OMAP34XX_USBTLL_BASE + SZ_4K - 1;
> - usbhs_resources[2].start = OMAP34XX_EHCI_BASE;
> - usbhs_resources[2].end = OMAP34XX_EHCI_BASE + SZ_1K - 1;
> - usbhs_resources[3].start = INT_34XX_EHCI_IRQ;
> - usbhs_resources[4].start = OMAP34XX_OHCI_BASE;
> - usbhs_resources[4].end = OMAP34XX_OHCI_BASE + SZ_1K - 1;
> - usbhs_resources[5].start = INT_34XX_OHCI_IRQ;
> setup_ehci_io_mux(pdata->port_mode);
> setup_ohci_io_mux(pdata->port_mode);
> } else if (cpu_is_omap44xx()) {
> - usbhs_resources[0].start = OMAP44XX_UHH_CONFIG_BASE;
> - usbhs_resources[0].end = OMAP44XX_UHH_CONFIG_BASE + SZ_1K - 1;
> - usbhs_resources[1].start = OMAP44XX_USBTLL_BASE;
> - usbhs_resources[1].end = OMAP44XX_USBTLL_BASE + SZ_4K - 1;
> - usbhs_resources[2].start = OMAP44XX_HSUSB_EHCI_BASE;
> - usbhs_resources[2].end = OMAP44XX_HSUSB_EHCI_BASE + SZ_1K - 1;
> - usbhs_resources[3].start = OMAP44XX_IRQ_EHCI;
> - usbhs_resources[4].start = OMAP44XX_HSUSB_OHCI_BASE;
> - usbhs_resources[4].end = OMAP44XX_HSUSB_OHCI_BASE + SZ_1K - 1;
> - usbhs_resources[5].start = OMAP44XX_IRQ_OHCI;
> setup_4430ehci_io_mux(pdata->port_mode);
> setup_4430ohci_io_mux(pdata->port_mode);
> }
>
> - if (platform_device_add_data(&usbhs_device,
> - &usbhs_data, sizeof(usbhs_data)) < 0) {
> - printk(KERN_ERR "USBHS platform_device_add_data failed\n");
> - goto init_end;
> + oh[0] = omap_hwmod_lookup(USBHS_UHH_HWMODNAME);
> + if (!oh[0]) {
> + pr_err("Could not look up %s\n", USBHS_UHH_HWMODNAME);
> + return;
> }
>
> - if (platform_device_register(&usbhs_device) < 0)
> - printk(KERN_ERR "USBHS platform_device_register failed\n");
> + oh[1] = omap_hwmod_lookup(USBHS_TLL_HWMODNAME);
> + if (!oh[1]) {
> + pr_err("Could not look up %s\n", USBHS_TLL_HWMODNAME);
> + return;
> + }
>
> -init_end:
> - return;
> + od = omap_device_build_ss(OMAP_USBHS_DEVICE, bus_id, oh, 2,
> + (void *)&usbhs_data, sizeof(usbhs_data),
> + omap_uhhtll_latency,
> + ARRAY_SIZE(omap_uhhtll_latency), false);
> +
> + if (IS_ERR(od)) {
> + pr_err("Could not build hwmod devices %s, %s\n",
> + USBHS_UHH_HWMODNAME, USBHS_TLL_HWMODNAME);
> + return;
> + }
> }
>
> #else
Keshava Munegowda <[email protected]> writes:
> The hwmod structure of uhh and tll are retrived
> and registered with omap device
And the name strings are changed as well as the device identifier
changed from zero to -1. Please comment and justify.
Kevin
Keshava Munegowda <[email protected]> writes:
> From: Keshava Munegowda <[email protected]>
>
> device name usbhs clocks are changed from
> usbhs-omap.0 to usbhs_omap; this is because
> in the hwmod registration the device name is set
> as usbhs_omap
..and because the identifier is changed to -1 in the previous patch
without being documented.
Kevin
Keshava Munegowda <[email protected]> writes:
> From: Keshava Munegowda <[email protected]>
>
> The global suspend and resume functions for usbhs core driver
> are implemented.These routine are called when the global suspend
> and resume occurs. Before calling these functions, the
> bus suspend and resume of ehci and ohci drivers are called
> from runtime pm.
>
> Signed-off-by: Keshava Munegowda <[email protected]>
First, from what I can see, this is only a partial implementation of
runtime PM. What I mean is that the runtime PM methods are used only
during the suspend path. The rest of the time the USB host IP block is
left enabled, even when nothing is connected.
I tested this on my 3530/Overo board, and verified that indeed the
usbhost powerdomain hits retention on suspend, but while idle, when
nothing is connected, I would expect the driver could be clever enough
to use runtime PM (probably using autosuspend timeouts) to disable the
hardware as well.
> ---
> drivers/mfd/omap-usb-host.c | 103 +++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 103 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
> index 43de12a..32d19e2 100644
> --- a/drivers/mfd/omap-usb-host.c
> +++ b/drivers/mfd/omap-usb-host.c
> @@ -146,6 +146,10 @@
> #define is_ehci_hsic_mode(x) (x == OMAP_EHCI_PORT_MODE_HSIC)
>
>
> +/* USBHS state bits */
> +#define OMAP_USBHS_INIT 0
> +#define OMAP_USBHS_SUSPEND 4
These additional state bits don't seem to be necessary.
For suspend, just check 'pm_runtime_is_suspended()'
The init flag is only used in the suspend/resume hooks, but the need for
it is a side effect of not correctly using the runtime PM callbacks.
Remember that the runtime PM get/put hooks have usage counting. Only
when the usage count transitions to/from zero is the actual
hardware-level enable/disable (via omap_hwmod) being done.
The current code is making the assumption that every call to get/put is
going to result in an enable/disable of the hardware.
Instead, all of the code that needs to be run only upon actual
enable/disable of the hardware should be done in the driver's
runtime_suspend/runtime_resume callbacks. These are only called when
the hardware actually changes state.
Not knowing that much about the EHCI block, upon first glance, it looks
like mmuch of what is done in usbhs_enable() should actually be done in
the ->runtime_resume() callback, and similarily, much of what is done in
usbhs_disable() should be done in the ->runtime_suspend() callback.
Another thing to be aware of is that runtime PM can be disabled from
userspace. For example, try this:
echo on > /sys/devices/platform/omap/usbhs_omap/power/control
This disables runtime PM for the device. After doing this and
suspending, you'll notice that usbhost powerdomain no longer hits
retention on suspend. Setting it back to 'auto' allows it to work
again.
Because of this, you can not simply call pm_runtime_put() from the
static suspend callback. You should check pm_runtime_is_suspended().
If it is, there's nothing to do. If not, the runtime PM callbacks for
the subsystem need to manually be called. See drivers/i2c/i2c-omap.c
for an example (and check the version in my PM branch, which has a fix
required starting with kernel v3.0.)
While I'm preaching on runtime PM here, some other things that should be
cleaned up because they duplicate what other frameworks are doing:
- drivers should not be touching their SYSCONFIG register. This
is managed by omap_hwmod
- current driver is doing usage counting, but runtime PM core is
already handling usage counting.
My apologies for not reviewing the runtime PM work in this driver
earlier. Some of the problems above come from code that's already in
mainline (which I should've reviewed earlier), and some are added with
this series. All of them should be cleaned up before merging this.
Kevin
> struct usbhs_hcd_omap {
> struct clk *xclk60mhsp1_ck;
> struct clk *xclk60mhsp2_ck;
> @@ -165,6 +169,7 @@ struct usbhs_hcd_omap {
> u32 usbhs_rev;
> spinlock_t lock;
> int count;
> + unsigned long state;
> };
> /*-------------------------------------------------------------------------*/
>
> @@ -809,6 +814,8 @@ static int usbhs_enable(struct device *dev)
> (pdata->ehci_data->reset_gpio_port[1], 1);
> }
>
> + set_bit(OMAP_USBHS_INIT, &omap->state);
> +
> end_count:
> omap->count++;
> spin_unlock_irqrestore(&omap->lock, flags);
> @@ -897,6 +904,7 @@ static void usbhs_disable(struct device *dev)
> }
>
> pm_runtime_put_sync(dev);
> + clear_bit(OMAP_USBHS_INIT, &omap->state);
>
> /* The gpio_free migh sleep; so unlock the spinlock */
> spin_unlock_irqrestore(&omap->lock, flags);
> @@ -926,10 +934,105 @@ void omap_usbhs_disable(struct device *dev)
> }
> EXPORT_SYMBOL_GPL(omap_usbhs_disable);
>
> +#ifdef CONFIG_PM
> +
> +static int usbhs_resume(struct device *dev)
> +{
> + struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
> + struct usbhs_omap_platform_data *pdata = &omap->platdata;
> + unsigned long flags = 0;
> +
> + dev_dbg(dev, "Resuming TI HSUSB Controller\n");
> +
> + if (!pdata) {
> + dev_dbg(dev, "missing platform_data\n");
> + return -ENODEV;
> + }
> +
> + spin_lock_irqsave(&omap->lock, flags);
> +
> + if (!test_bit(OMAP_USBHS_INIT, &omap->state) ||
> + !test_bit(OMAP_USBHS_SUSPEND, &omap->state))
> + goto end_resume;
> +
> + pm_runtime_get_sync(dev);
> +
> + if (is_omap_usbhs_rev2(omap)) {
> + if (is_ehci_tll_mode(pdata->port_mode[0])) {
> + clk_enable(omap->usbhost_p1_fck);
> + clk_enable(omap->usbtll_p1_fck);
> + }
> + if (is_ehci_tll_mode(pdata->port_mode[1])) {
> + clk_enable(omap->usbhost_p2_fck);
> + clk_enable(omap->usbtll_p2_fck);
> + }
> + clk_enable(omap->utmi_p1_fck);
> + clk_enable(omap->utmi_p2_fck);
> + }
> + clear_bit(OMAP_USBHS_SUSPEND, &omap->state);
> +
> +end_resume:
> + spin_unlock_irqrestore(&omap->lock, flags);
> + return 0;
> +}
> +
> +
> +static int usbhs_suspend(struct device *dev)
> +{
> + struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
> + struct usbhs_omap_platform_data *pdata = &omap->platdata;
> + unsigned long flags = 0;
> +
> + dev_dbg(dev, "Suspending TI HSUSB Controller\n");
> +
> + if (!pdata) {
> + dev_dbg(dev, "missing platform_data\n");
> + return -ENODEV;
> + }
> +
> + spin_lock_irqsave(&omap->lock, flags);
> +
> + if (!test_bit(OMAP_USBHS_INIT, &omap->state) ||
> + test_bit(OMAP_USBHS_SUSPEND, &omap->state))
> + goto end_suspend;
> +
> + if (is_omap_usbhs_rev2(omap)) {
> + if (is_ehci_tll_mode(pdata->port_mode[0])) {
> + clk_disable(omap->usbhost_p1_fck);
> + clk_disable(omap->usbtll_p1_fck);
> + }
> + if (is_ehci_tll_mode(pdata->port_mode[1])) {
> + clk_disable(omap->usbhost_p2_fck);
> + clk_disable(omap->usbtll_p2_fck);
> + }
> + clk_disable(omap->utmi_p2_fck);
> + clk_disable(omap->utmi_p1_fck);
> + }
> +
> + set_bit(OMAP_USBHS_SUSPEND, &omap->state);
> + pm_runtime_put_sync(dev);
> +
> +end_suspend:
> + spin_unlock_irqrestore(&omap->lock, flags);
> + return 0;
> +}
> +
> +
> +static const struct dev_pm_ops usbhsomap_dev_pm_ops = {
> + .suspend = usbhs_suspend,
> + .resume = usbhs_resume,
> +};
> +
> +#define USBHS_OMAP_DEV_PM_OPS (&usbhsomap_dev_pm_ops)
> +#else
> +#define USBHS_OMAP_DEV_PM_OPS NULL
> +#endif
> +
> static struct platform_driver usbhs_omap_driver = {
> .driver = {
> .name = (char *)usbhs_driver_name,
> .owner = THIS_MODULE,
> + .pm = USBHS_OMAP_DEV_PM_OPS,
> },
> .remove = __exit_p(usbhs_omap_remove),
> };
On Thu, Jun 2, 2011 at 1:26 AM, Kevin Hilman <[email protected]> wrote:
> Keshava Munegowda <[email protected]> writes:
>
>> Following 2 hwmod strcuture are added:
>> UHH hwmod of usbhs with uhh base address and
>> EHCI , OHCI irq and base addresses.
>> TLL hwmod of usbhs with the TLL base address and irq.
>>
>> Signed-off-by: Keshava Munegowda <[email protected]>
>
> I believe the OMAP4 data came from Benoit, so should be in a patch with
> his authorship and signoff. ?OMAP3 can be a separate patch with your
> authorship.
yes , you are right.
I apologizes for this. Benoit should be author.
>
> Kevin
>
On Wednesday, June 01, 2011, Felipe Balbi wrote:
> Hi,
>
> On Wed, Jun 01, 2011 at 03:54:38PM +0200, Rafael J. Wysocki wrote:
> > > +static const struct dev_pm_ops usbhsomap_dev_pm_ops = {
> > > + .suspend = usbhs_suspend,
> > > + .resume = usbhs_resume,
> > > +};
> >
> > Please add .freeze()/.thaw() and .poweroff()/.restore() callbacks too.
> > They may point to the same routines, but must be present.
> >
> > You can actually use the SIMPLE_DEV_PM_OPS() convenience macro for this
> > purpose.
>
> good point. BTW, do we need this #ifdef CONFIG_PM stuff which has been
> popping on most drivers recently ? To me it looks like driver.pm field
> is always available even if PM is disabled, so what's the point ? Saving
> a few bytes ?
Basically, yes (you may want to avoid defining the object this points to if
CONFIG_PM is unset).
Thanks,
Rafael
Hi,
On Sun, Jun 05, 2011 at 07:19:38PM +0200, Rafael J. Wysocki wrote:
> > On Wed, Jun 01, 2011 at 03:54:38PM +0200, Rafael J. Wysocki wrote:
> > > > +static const struct dev_pm_ops usbhsomap_dev_pm_ops = {
> > > > + .suspend = usbhs_suspend,
> > > > + .resume = usbhs_resume,
> > > > +};
> > >
> > > Please add .freeze()/.thaw() and .poweroff()/.restore() callbacks too.
> > > They may point to the same routines, but must be present.
> > >
> > > You can actually use the SIMPLE_DEV_PM_OPS() convenience macro for this
> > > purpose.
> >
> > good point. BTW, do we need this #ifdef CONFIG_PM stuff which has been
> > popping on most drivers recently ? To me it looks like driver.pm field
> > is always available even if PM is disabled, so what's the point ? Saving
> > a few bytes ?
>
> Basically, yes (you may want to avoid defining the object this points to if
> CONFIG_PM is unset).
wouldn't it look nicer to have a specific section for dev_pm_ops which
gets automatically freed if CONFIG_PM isn't set ? I mean, there are a
few drivers which don't have the ifdeferry around dev_pm_ops anyway.
So, something like:
#define __pm_ops __section(.pm.ops)
static const struct dev_pm_ops my_driver_pm_ops __pm_ops = {
.suspend = my_driver_suspend,
.resume = my_driver_resume,
[ blablabla ]
};
to simplify things, you could:
#define DEFINE_DEV_PM_OPS(_ops) \
const struct dev_pm_ops _ops __pm_ops
that would mean changes to all linker scripts, though and a utility call
that only does anything ifndef CONFIG_PM to free the .pm.ops section.
--
balbi
On Sun, 5 Jun 2011, Felipe Balbi wrote:
> > > good point. BTW, do we need this #ifdef CONFIG_PM stuff which has been
> > > popping on most drivers recently ? To me it looks like driver.pm field
> > > is always available even if PM is disabled, so what's the point ? Saving
> > > a few bytes ?
> >
> > Basically, yes (you may want to avoid defining the object this points to if
> > CONFIG_PM is unset).
>
> wouldn't it look nicer to have a specific section for dev_pm_ops which
> gets automatically freed if CONFIG_PM isn't set ? I mean, there are a
> few drivers which don't have the ifdeferry around dev_pm_ops anyway.
>
> So, something like:
>
> #define __pm_ops __section(.pm.ops)
>
> static const struct dev_pm_ops my_driver_pm_ops __pm_ops = {
> .suspend = my_driver_suspend,
> .resume = my_driver_resume,
> [ blablabla ]
> };
>
> to simplify things, you could:
>
> #define DEFINE_DEV_PM_OPS(_ops) \
> const struct dev_pm_ops _ops __pm_ops
>
> that would mean changes to all linker scripts, though and a utility call
> that only does anything ifndef CONFIG_PM to free the .pm.ops section.
In my opinion this would make programming harder, not easier. It's
very easy to understand "#ifdef" followed by "#endif"; people see them
all the time. The new tags you propose would force people to go
searching through tons of source files to see what they mean, and then
readers would still have to figure out when these tags should be used
or what advantage they might bring.
It's a little like "typedef struct foo foo_t;" -- doing this forces
people to remember one extra piece of information that serves no real
purpose except perhaps a minimal reduction in the amount of typing.
Since the limiting factor in kernel programming is human brainpower,
not source file length, this is a bad tradeoff. (Not to mention that
it also obscures an important fact: A foo_t is an extended structure
rather than a single value.)
Alan Stern
Hi,
On Sun, Jun 05, 2011 at 03:30:55PM -0400, Alan Stern wrote:
> > > > good point. BTW, do we need this #ifdef CONFIG_PM stuff which has been
> > > > popping on most drivers recently ? To me it looks like driver.pm field
> > > > is always available even if PM is disabled, so what's the point ? Saving
> > > > a few bytes ?
> > >
> > > Basically, yes (you may want to avoid defining the object this points to if
> > > CONFIG_PM is unset).
> >
> > wouldn't it look nicer to have a specific section for dev_pm_ops which
> > gets automatically freed if CONFIG_PM isn't set ? I mean, there are a
> > few drivers which don't have the ifdeferry around dev_pm_ops anyway.
> >
> > So, something like:
> >
> > #define __pm_ops __section(.pm.ops)
> >
> > static const struct dev_pm_ops my_driver_pm_ops __pm_ops = {
> > .suspend = my_driver_suspend,
> > .resume = my_driver_resume,
> > [ blablabla ]
> > };
> >
> > to simplify things, you could:
> >
> > #define DEFINE_DEV_PM_OPS(_ops) \
> > const struct dev_pm_ops _ops __pm_ops
> >
> > that would mean changes to all linker scripts, though and a utility call
> > that only does anything ifndef CONFIG_PM to free the .pm.ops section.
>
> In my opinion this would make programming harder, not easier. It's
I tend to disagree with this statement, see below.
> very easy to understand "#ifdef" followed by "#endif"; people see them
very true... Still everybody has to put them in place.
> all the time. The new tags you propose would force people to go
> searching through tons of source files to see what they mean, and then
only those who want to see "how things work" would be forced to do that,
other people would be allowed to "assume it's doing the right thing".
> readers would still have to figure out when these tags should be used
> or what advantage they might bring.
not really, if you add a macro which adds that correctly and during
review we only accept drivers using that particular macro, things
wouldn't go bad at all.
> It's a little like "typedef struct foo foo_t;" -- doing this forces
hey c'mon. Then you're saying that all __initdata, __devinitdata,
__initconst and all of those are "typedef struct foo foo_t" ;-)
> people to remember one extra piece of information that serves no real
> purpose except perhaps a minimal reduction in the amount of typing.
and a guarantee that the unused data will be freed when it's really not
needed ;-)
> Since the limiting factor in kernel programming is human brainpower,
> not source file length, this is a bad tradeoff. (Not to mention that
OTOH we are going through a big re-factor of the ARM port to reduce the
amount of code. Not that these few characters would change much but my
point is that amount of code also matters. So does uniformity, coding
style, etc...
> it also obscures an important fact: A foo_t is an extended structure
> rather than a single value.)
then it would make sense to have dev_pm_ops only defined when CONFIG_PM
is set to force all drivers stick to a common way of handling this.
Besides, currently, everybody who wants to keep the ifdeferry, needs to
define a macro for &my_dev_pm_ops or have two #ifdef..#endif blocks.
Either you do:
#ifdef CONFIG_PM
static int my_driver_suspend(struct device *dev)
{
...
return 0;
}
....
static const struct dev_pm_ops my_driver_pm_ops = {
.suspend = my_driver_suspend,
...
};
#define DEV_PM_OPS (&my_driver_pm_ops)
#else
#define DEV_PM_OPS NULL
#endif
static struct platform_driver my_driver = {
...
.driver = {
.pm = DEV_PM_OPS
},
};
or you do:
#ifdef CONFIG_PM
static int my_driver_suspend(struct device *dev)
{
...
return 0;
}
....
static const struct dev_pm_ops my_driver_pm_ops = {
.suspend = my_driver_suspend,
...
};
#endif
static struct platform_driver my_driver = {
...
.driver = {
#ifdef CONFIG_PM
.pm = &my_driver_pm_ops,
#endif
},
};
So, while this is a small thing which is easy to understand, it's still
yet another thing that all drivers have to remember to add. And when
everybody needs to remember that, I'd rather have it done
"automatically" by other means.
I mean, we already free .init.* sections after __init anyway, so what's
the problem in freeing another section ? I don't see it as obfuscation
at all. I see it as if the kernel is smart enough to free all unused
data by itself, without myself having to add ifdefs or freeing it by my
own.
On top of all that, today, we have driver with both ways of ifdefs plus
drivers with no ifdeferry at all, leaving dev_pm_ops floating around for
nothing.
IMHO, if things aren't uniform, we will have small problems, such as
this, proliferate because new drivers are based on other drivers,
generally.
--
balbi
On Sun, Jun 05, 2011 at 03:30:55PM -0400, Alan Stern wrote:
> On Sun, 5 Jun 2011, Felipe Balbi wrote:
> > that would mean changes to all linker scripts, though and a utility call
> > that only does anything ifndef CONFIG_PM to free the .pm.ops section.
> In my opinion this would make programming harder, not easier. It's
> very easy to understand "#ifdef" followed by "#endif"; people see them
> all the time. The new tags you propose would force people to go
> searching through tons of source files to see what they mean, and then
> readers would still have to figure out when these tags should be used
> or what advantage they might bring.
The big advantage is that they make it much harder to introduce random
build breakage and they're an awful lot less fiddly to do - it used to
be not so bad when it was just the function pointers that needed to be
defined to NULL but now we need to faff around with both the functions
and the dev_pm_ops.
The annotation approach is already familiar from the init stuff so it
wouldn't be so hard for people to understand.
On Sun, 5 Jun 2011, Felipe Balbi wrote:
> Hi,
>
> On Sun, Jun 05, 2011 at 03:30:55PM -0400, Alan Stern wrote:
> > > > > good point. BTW, do we need this #ifdef CONFIG_PM stuff which has been
> > > > > popping on most drivers recently ? To me it looks like driver.pm field
> > > > > is always available even if PM is disabled, so what's the point ? Saving
> > > > > a few bytes ?
> > > >
> > > > Basically, yes (you may want to avoid defining the object this points to if
> > > > CONFIG_PM is unset).
> > >
> > > wouldn't it look nicer to have a specific section for dev_pm_ops which
> > > gets automatically freed if CONFIG_PM isn't set ? I mean, there are a
> > > few drivers which don't have the ifdeferry around dev_pm_ops anyway.
> > >
> > > So, something like:
> > >
> > > #define __pm_ops __section(.pm.ops)
> > >
> > > static const struct dev_pm_ops my_driver_pm_ops __pm_ops = {
> > > .suspend = my_driver_suspend,
> > > .resume = my_driver_resume,
> > > [ blablabla ]
> > > };
> > >
> > > to simplify things, you could:
> > >
> > > #define DEFINE_DEV_PM_OPS(_ops) \
> > > const struct dev_pm_ops _ops __pm_ops
> > >
> > > that would mean changes to all linker scripts, though and a utility call
> > > that only does anything ifndef CONFIG_PM to free the .pm.ops section.
> >
> > In my opinion this would make programming harder, not easier. It's
>
> I tend to disagree with this statement, see below.
>
> > very easy to understand "#ifdef" followed by "#endif"; people see them
>
> very true... Still everybody has to put them in place.
True. But with your suggestion, people have to remember to use
__pm_ops and DEFINE_DEV_PM_OPS.
> > all the time. The new tags you propose would force people to go
> > searching through tons of source files to see what they mean, and then
>
> only those who want to see "how things work" would be forced to do that,
> other people would be allowed to "assume it's doing the right thing".
But what is the "right thing"? Suppose you want to have conditional
support for dev_pm_ops whenever CONFIG_PM is enabled and you _also_
want to have conditional support for runtime PM whenever
CONFIG_PM_RUNTIME is enabled?
> > readers would still have to figure out when these tags should be used
> > or what advantage they might bring.
>
> not really, if you add a macro which adds that correctly and during
> review we only accept drivers using that particular macro, things
> wouldn't go bad at all.
>
> > It's a little like "typedef struct foo foo_t;" -- doing this forces
>
> hey c'mon. Then you're saying that all __initdata, __devinitdata,
> __initconst and all of those are "typedef struct foo foo_t" ;-)
No. Unlike foo_t, they don't obscure important information and they do
provide a significant gain in simplicity. On the other hand, they also
provide a certain degree of confusion. Remember all the difficulty we
had with intialization code sections in the gadget framework.
> > people to remember one extra piece of information that serves no real
> > purpose except perhaps a minimal reduction in the amount of typing.
>
> and a guarantee that the unused data will be freed when it's really not
> needed ;-)
You can obtain that same guarantee by using #ifdef ... #endif. Even
better, you can guarantee that the unused data won't be present at all,
as opposed to loaded and then freed.
> > Since the limiting factor in kernel programming is human brainpower,
> > not source file length, this is a bad tradeoff. (Not to mention that
>
> OTOH we are going through a big re-factor of the ARM port to reduce the
> amount of code. Not that these few characters would change much but my
> point is that amount of code also matters. So does uniformity, coding
> style, etc...
>
> > it also obscures an important fact: A foo_t is an extended structure
> > rather than a single value.)
>
> then it would make sense to have dev_pm_ops only defined when CONFIG_PM
> is set to force all drivers stick to a common way of handling this.
>
> Besides, currently, everybody who wants to keep the ifdeferry, needs to
> define a macro for &my_dev_pm_ops or have two #ifdef..#endif blocks.
>
> Either you do:
>
> #ifdef CONFIG_PM
> static int my_driver_suspend(struct device *dev)
> {
> ...
>
> return 0;
> }
> ....
>
> static const struct dev_pm_ops my_driver_pm_ops = {
> .suspend = my_driver_suspend,
> ...
> };
>
> #define DEV_PM_OPS (&my_driver_pm_ops)
> #else
> #define DEV_PM_OPS NULL
> #endif
>
> static struct platform_driver my_driver = {
> ...
> .driver = {
> .pm = DEV_PM_OPS
> },
> };
>
> or you do:
>
> #ifdef CONFIG_PM
> static int my_driver_suspend(struct device *dev)
> {
> ...
>
> return 0;
> }
> ....
>
> static const struct dev_pm_ops my_driver_pm_ops = {
> .suspend = my_driver_suspend,
> ...
> };
>
> #endif
>
> static struct platform_driver my_driver = {
> ...
> .driver = {
> #ifdef CONFIG_PM
> .pm = &my_driver_pm_ops,
> #endif
> },
> };
Whereas your way people write:
static int __pm_ops my_driver_suspend(struct device *dev)
{
...
return 0;
}
....
static DEFINE_DEV_PM_OPS(my_driver_pm_ops) = {
.suspend = my_driver_suspend,
...
};
static struct platform_driver my_driver = {
...
.driver = {
.pm = &my_driver_pm_ops,
},
};
It doesn't seem like a good idea to keep the invalid pointer to
my_driver_pm_ops, even though it should never get used.
An approach that might work better would be for the PM core to define a
suitable macro:
#ifdef CONFIG_PM
#define DEV_PM_OPS_REF(my_pm_ops) &(my_pm_ops)
#else
#define DEV_PM_OPS_REF(my_pm_ops) NULL
#endif
Then people could write
static struct platform_driver my_driver = {
...
.driver = {
.pm = DEV_PM_OPS_REF(my_driver_pm_ops),
},
};
without worrying about whether or not my_driver_pm_ops was defined.
And only one #ifdef block would be needed.
> So, while this is a small thing which is easy to understand, it's still
> yet another thing that all drivers have to remember to add. And when
> everybody needs to remember that, I'd rather have it done
> "automatically" by other means.
>
> I mean, we already free .init.* sections after __init anyway, so what's
> the problem in freeing another section ? I don't see it as obfuscation
> at all. I see it as if the kernel is smart enough to free all unused
> data by itself, without myself having to add ifdefs or freeing it by my
> own.
>
> On top of all that, today, we have driver with both ways of ifdefs plus
> drivers with no ifdeferry at all, leaving dev_pm_ops floating around for
> nothing.
>
> IMHO, if things aren't uniform, we will have small problems, such as
> this, proliferate because new drivers are based on other drivers,
> generally.
I have to agree that uniformity is to be desired. And it's probably
already way too late, because we can't prevent new drivers from being
based on the existing drivers -- even if all the existing drivers get
changed over (which seems unlikely).
Alan Stern
Hi,
On Mon, Jun 06, 2011 at 12:06:44PM -0400, Alan Stern wrote:
> > > > So, something like:
> > > >
> > > > #define __pm_ops __section(.pm.ops)
> > > >
> > > > static const struct dev_pm_ops my_driver_pm_ops __pm_ops = {
> > > > .suspend = my_driver_suspend,
> > > > .resume = my_driver_resume,
> > > > [ blablabla ]
> > > > };
> > > >
> > > > to simplify things, you could:
> > > >
> > > > #define DEFINE_DEV_PM_OPS(_ops) \
> > > > const struct dev_pm_ops _ops __pm_ops
> > > >
> > > > that would mean changes to all linker scripts, though and a utility call
> > > > that only does anything ifndef CONFIG_PM to free the .pm.ops section.
> > >
> > > In my opinion this would make programming harder, not easier. It's
> >
> > I tend to disagree with this statement, see below.
> >
> > > very easy to understand "#ifdef" followed by "#endif"; people see them
> >
> > very true... Still everybody has to put them in place.
>
> True. But with your suggestion, people have to remember to use
> __pm_ops and DEFINE_DEV_PM_OPS.
Ok, I see your point here.
> > > all the time. The new tags you propose would force people to go
> > > searching through tons of source files to see what they mean, and then
> >
> > only those who want to see "how things work" would be forced to do that,
> > other people would be allowed to "assume it's doing the right thing".
>
> But what is the "right thing"? Suppose you want to have conditional
> support for dev_pm_ops whenever CONFIG_PM is enabled and you _also_
> want to have conditional support for runtime PM whenever
> CONFIG_PM_RUNTIME is enabled?
we don't have this today either. Currently everybody does #ifdef
CONFIG_PM, so either all the data is available, or none is and adding
another #ifdef CONFIG_PM_RUNTIME for the runtime_* friends, would just
look even uglier :-)
> > > readers would still have to figure out when these tags should be used
> > > or what advantage they might bring.
> >
> > not really, if you add a macro which adds that correctly and during
> > review we only accept drivers using that particular macro, things
> > wouldn't go bad at all.
> >
> > > It's a little like "typedef struct foo foo_t;" -- doing this forces
> >
> > hey c'mon. Then you're saying that all __initdata, __devinitdata,
> > __initconst and all of those are "typedef struct foo foo_t" ;-)
>
> No. Unlike foo_t, they don't obscure important information and they do
> provide a significant gain in simplicity. On the other hand, they also
> provide a certain degree of confusion. Remember all the difficulty we
> had with intialization code sections in the gadget framework.
this is fairly true, but only because the gadget framework isn't really
a framework. It's just an agreement that all UDCs will export a
particular function. It's a great infrastructure for the function
drivers, but not for UDCs, so I think this isn't a great example :-)
> > > people to remember one extra piece of information that serves no real
> > > purpose except perhaps a minimal reduction in the amount of typing.
> >
> > and a guarantee that the unused data will be freed when it's really not
> > needed ;-)
>
> You can obtain that same guarantee by using #ifdef ... #endif. Even
> better, you can guarantee that the unused data won't be present at all,
> as opposed to loaded and then freed.
I agree with you here, but I give you the same question as you gave me.
How will you have conditional on CONFIG_RUNTIME_PM and CONFIG_PM ? you'd
need two levels of ifdefs.
> > > Since the limiting factor in kernel programming is human brainpower,
> > > not source file length, this is a bad tradeoff. (Not to mention that
> >
> > OTOH we are going through a big re-factor of the ARM port to reduce the
> > amount of code. Not that these few characters would change much but my
> > point is that amount of code also matters. So does uniformity, coding
> > style, etc...
> >
> > > it also obscures an important fact: A foo_t is an extended structure
> > > rather than a single value.)
> >
> > then it would make sense to have dev_pm_ops only defined when CONFIG_PM
> > is set to force all drivers stick to a common way of handling this.
> >
> > Besides, currently, everybody who wants to keep the ifdeferry, needs to
> > define a macro for &my_dev_pm_ops or have two #ifdef..#endif blocks.
> >
> > Either you do:
> >
> > #ifdef CONFIG_PM
> > static int my_driver_suspend(struct device *dev)
> > {
> > ...
> >
> > return 0;
> > }
> > ....
> >
> > static const struct dev_pm_ops my_driver_pm_ops = {
> > .suspend = my_driver_suspend,
> > ...
> > };
> >
> > #define DEV_PM_OPS (&my_driver_pm_ops)
> > #else
> > #define DEV_PM_OPS NULL
> > #endif
> >
> > static struct platform_driver my_driver = {
> > ...
> > .driver = {
> > .pm = DEV_PM_OPS
> > },
> > };
> >
> > or you do:
> >
> > #ifdef CONFIG_PM
> > static int my_driver_suspend(struct device *dev)
> > {
> > ...
> >
> > return 0;
> > }
> > ....
> >
> > static const struct dev_pm_ops my_driver_pm_ops = {
> > .suspend = my_driver_suspend,
> > ...
> > };
> >
> > #endif
> >
> > static struct platform_driver my_driver = {
> > ...
> > .driver = {
> > #ifdef CONFIG_PM
> > .pm = &my_driver_pm_ops,
> > #endif
> > },
> > };
>
> Whereas your way people write:
>
> static int __pm_ops my_driver_suspend(struct device *dev)
> {
> ...
>
> return 0;
> }
> ....
>
> static DEFINE_DEV_PM_OPS(my_driver_pm_ops) = {
> .suspend = my_driver_suspend,
> ...
> };
>
> static struct platform_driver my_driver = {
> ...
> .driver = {
> .pm = &my_driver_pm_ops,
> },
> };
>
> It doesn't seem like a good idea to keep the invalid pointer to
> my_driver_pm_ops, even though it should never get used.
true, I agree.
> An approach that might work better would be for the PM core to define a
> suitable macro:
>
> #ifdef CONFIG_PM
> #define DEV_PM_OPS_REF(my_pm_ops) &(my_pm_ops)
> #else
> #define DEV_PM_OPS_REF(my_pm_ops) NULL
> #endif
>
> Then people could write
>
> static struct platform_driver my_driver = {
> ...
> .driver = {
> .pm = DEV_PM_OPS_REF(my_driver_pm_ops),
> },
> };
>
> without worrying about whether or not my_driver_pm_ops was defined.
> And only one #ifdef block would be needed.
that'd be nice. Something similar to __exit_p() and __devexit_p()
> > So, while this is a small thing which is easy to understand, it's still
> > yet another thing that all drivers have to remember to add. And when
> > everybody needs to remember that, I'd rather have it done
> > "automatically" by other means.
> >
> > I mean, we already free .init.* sections after __init anyway, so what's
> > the problem in freeing another section ? I don't see it as obfuscation
> > at all. I see it as if the kernel is smart enough to free all unused
> > data by itself, without myself having to add ifdefs or freeing it by my
> > own.
> >
> > On top of all that, today, we have driver with both ways of ifdefs plus
> > drivers with no ifdeferry at all, leaving dev_pm_ops floating around for
> > nothing.
> >
> > IMHO, if things aren't uniform, we will have small problems, such as
> > this, proliferate because new drivers are based on other drivers,
> > generally.
>
> I have to agree that uniformity is to be desired. And it's probably
> already way too late, because we can't prevent new drivers from being
I wouldn't call it late. Such small convertions can be done by simple
scripts, but when patches switching drivers over are rejected [1] then
we loose the opportunity to give good example to newcomers.
> based on the existing drivers -- even if all the existing drivers get
> changed over (which seems unlikely).
Well, it might work out if pm core makes dev_pm_ops only available on
CONFIG_PM builds.
[1] http://marc.info/?l=linux-usb&m=129733927804315&w=2
--
balbi
On Mon, 6 Jun 2011, Felipe Balbi wrote:
> > But what is the "right thing"? Suppose you want to have conditional
> > support for dev_pm_ops whenever CONFIG_PM is enabled and you _also_
> > want to have conditional support for runtime PM whenever
> > CONFIG_PM_RUNTIME is enabled?
>
> we don't have this today either. Currently everybody does #ifdef
> CONFIG_PM, so either all the data is available, or none is and adding
> another #ifdef CONFIG_PM_RUNTIME for the runtime_* friends, would just
> look even uglier :-)
Like in hcd-pci.c? :-)
> > You can obtain that same guarantee by using #ifdef ... #endif. Even
> > better, you can guarantee that the unused data won't be present at all,
> > as opposed to loaded and then freed.
>
> I agree with you here, but I give you the same question as you gave me.
> How will you have conditional on CONFIG_RUNTIME_PM and CONFIG_PM ? you'd
> need two levels of ifdefs.
Well, you'd need more #ifdefs, no question about that. Whether you
need more _levels_ of #ifdefs is unclear.
> > #ifdef CONFIG_PM
> > #define DEV_PM_OPS_REF(my_pm_ops) &(my_pm_ops)
> > #else
> > #define DEV_PM_OPS_REF(my_pm_ops) NULL
> > #endif
> >
> > Then people could write
> >
> > static struct platform_driver my_driver = {
> > ...
> > .driver = {
> > .pm = DEV_PM_OPS_REF(my_driver_pm_ops),
> > },
> > };
> >
> > without worrying about whether or not my_driver_pm_ops was defined.
> > And only one #ifdef block would be needed.
>
> that'd be nice. Something similar to __exit_p() and __devexit_p()
Right. Maybe even call it __pm_ops_p().
In fact, rather than tying this specifically to dev_pm_ops, it would
make sense to have a general-purpose memory section for code that won't
be used, and an appropriate macro (such as "__unused") to specify that
section attribute. Then the PM core could do:
#ifdef CONFIG_PM
#define __pm_ops
#else
#define __pm_ops __unused
#endif
and that would (I think) put less of a mental burden on people.
> Well, it might work out if pm core makes dev_pm_ops only available on
> CONFIG_PM builds.
Currently the .pm member is part of struct bus_type, struct
device_driver, and others whether CONFIG_PM is enabled or not. I don't
know if removing it when CONFIG_PM is disabled would cause build
problems -- it might.
Alan Stern
On Thu, Jun 2, 2011 at 5:36 AM, Kevin Hilman <[email protected]> wrote:
> Keshava Munegowda <[email protected]> writes:
>
>> From: Keshava Munegowda <[email protected]>
>>
>> The global suspend and resume functions for usbhs core driver
>> are implemented.These routine are called when the global suspend
>> and resume occurs. Before calling these functions, the
>> bus suspend and resume of ehci and ohci drivers are called
>> from runtime pm.
>>
>> Signed-off-by: Keshava Munegowda <[email protected]>
>
> First, from what I can see, this is only a partial implementation of
> runtime PM. ?What I mean is that the runtime PM methods are used only
> during the suspend path. ?The rest of the time the USB host IP block is
> left enabled, even when nothing is connected.
>
> I tested this on my 3530/Overo board, and verified that indeed the
> usbhost powerdomain hits retention on suspend, but while idle, when
> nothing is connected, I would expect the driver could be clever enough
> to use runtime PM (probably using autosuspend timeouts) to disable the
> hardware as well.
>
>> ---
>> ?drivers/mfd/omap-usb-host.c | ?103 +++++++++++++++++++++++++++++++++++++++++++
>> ?1 files changed, 103 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
>> index 43de12a..32d19e2 100644
>> --- a/drivers/mfd/omap-usb-host.c
>> +++ b/drivers/mfd/omap-usb-host.c
>> @@ -146,6 +146,10 @@
>> ?#define is_ehci_hsic_mode(x) (x == OMAP_EHCI_PORT_MODE_HSIC)
>>
>>
>> +/* USBHS state bits */
>> +#define OMAP_USBHS_INIT ? ? ? ? ? ? ?0
>> +#define OMAP_USBHS_SUSPEND ? 4
>
> These additional state bits don't seem to be necessary.
>
> For suspend, just check 'pm_runtime_is_suspended()'
>
> The init flag is only used in the suspend/resume hooks, but the need for
> it is a side effect of not correctly using the runtime PM callbacks.
>
> Remember that the runtime PM get/put hooks have usage counting. ?Only
> when the usage count transitions to/from zero is the actual
> hardware-level enable/disable (via omap_hwmod) being done.
>
> The current code is making the assumption that every call to get/put is
> going to result in an enable/disable of the hardware.
>
> Instead, all of the code that needs to be run only upon actual
> enable/disable of the hardware should be done in the driver's
> runtime_suspend/runtime_resume callbacks. ?These are only called when
> the hardware actually changes state.
>
> Not knowing that much about the EHCI block, upon first glance, it looks
> like mmuch of what is done in usbhs_enable() should actually be done in
> the ->runtime_resume() callback, and similarily, much of what is done in
> usbhs_disable() should be done in the ->runtime_suspend() callback.
Kevin,
do you mean driver->runtime_resume and driver->runtime_resume call backs.
are these call backs from pm_runtime_get_sync and pm_runtime_put_sync?
On Wed, Jun 29, 2011 at 8:52 PM, Munegowda, Keshava
<[email protected]> wrote:
> On Thu, Jun 2, 2011 at 5:36 AM, Kevin Hilman <[email protected]> wrote:
>> Keshava Munegowda <[email protected]> writes:
>>
>>> From: Keshava Munegowda <[email protected]>
>>>
>>> The global suspend and resume functions for usbhs core driver
>>> are implemented.These routine are called when the global suspend
>>> and resume occurs. Before calling these functions, the
>>> bus suspend and resume of ehci and ohci drivers are called
>>> from runtime pm.
>>>
>>> Signed-off-by: Keshava Munegowda <[email protected]>
>>
>> First, from what I can see, this is only a partial implementation of
>> runtime PM. ?What I mean is that the runtime PM methods are used only
>> during the suspend path. ?The rest of the time the USB host IP block is
>> left enabled, even when nothing is connected.
>>
>> I tested this on my 3530/Overo board, and verified that indeed the
>> usbhost powerdomain hits retention on suspend, but while idle, when
>> nothing is connected, I would expect the driver could be clever enough
>> to use runtime PM (probably using autosuspend timeouts) to disable the
>> hardware as well.
>>
>>> ---
>>> ?drivers/mfd/omap-usb-host.c | ?103 +++++++++++++++++++++++++++++++++++++++++++
>>> ?1 files changed, 103 insertions(+), 0 deletions(-)
>>>
>>> diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
>>> index 43de12a..32d19e2 100644
>>> --- a/drivers/mfd/omap-usb-host.c
>>> +++ b/drivers/mfd/omap-usb-host.c
>>> @@ -146,6 +146,10 @@
>>> ?#define is_ehci_hsic_mode(x) (x == OMAP_EHCI_PORT_MODE_HSIC)
>>>
>>>
>>> +/* USBHS state bits */
>>> +#define OMAP_USBHS_INIT ? ? ? ? ? ? ?0
>>> +#define OMAP_USBHS_SUSPEND ? 4
>>
>> These additional state bits don't seem to be necessary.
>>
>> For suspend, just check 'pm_runtime_is_suspended()'
>>
>> The init flag is only used in the suspend/resume hooks, but the need for
>> it is a side effect of not correctly using the runtime PM callbacks.
>>
>> Remember that the runtime PM get/put hooks have usage counting. ?Only
>> when the usage count transitions to/from zero is the actual
>> hardware-level enable/disable (via omap_hwmod) being done.
>>
>> The current code is making the assumption that every call to get/put is
>> going to result in an enable/disable of the hardware.
>>
>> Instead, all of the code that needs to be run only upon actual
>> enable/disable of the hardware should be done in the driver's
>> runtime_suspend/runtime_resume callbacks. ?These are only called when
>> the hardware actually changes state.
>>
>> Not knowing that much about the EHCI block, upon first glance, it looks
>> like mmuch of what is done in usbhs_enable() should actually be done in
>> the ->runtime_resume() callback, and similarily, much of what is done in
>> usbhs_disable() should be done in the ->runtime_suspend() callback.
>
> Kevin,
> ? do you mean driver->runtime_resume and driver->runtime_resume call backs.
> are these call backs from pm_runtime_get_sync and pm_runtime_put_sync?
for usb host case , I am seeing that the pm_runtime_get_sync
static int rpm_resume(struct device *dev, int rpmflags)
{
............
..........
if (dev->pwr_domain) {
callback = dev->pwr_domain->ops.runtime_resume;
if(!strcmp(dev_name(dev),"usbhs_omap"))
pr_err("dev->pwr_domain->ops.runtime_resume");
}
else if (dev->type && dev->type->pm) {
callback = dev->type->pm->runtime_resume;
if(!strcmp(dev_name(dev),"usbhs_omap"))
pr_err("dev->type->pm->runtime_resume");
}
else if (dev->class && dev->class->pm) {
callback = dev->class->pm->runtime_resume;
if(!strcmp(dev_name(dev),"usbhs_omap"))
pr_err("ev->class->pm->runtime_resume");
}
else if (dev->bus && dev->bus->pm) {
callback = dev->bus->pm->runtime_resume;
if(!strcmp(dev_name(dev),"usbhs_omap"))
pr_err("dev->bus->pm->runtime_resume");
}
else
callback = NULL;
}
I am seeing that below if statement was hitting true:
if (dev->pwr_domain) {
callback = dev->pwr_domain->ops.runtime_resume;
if(!strcmp(dev_name(dev),"usbhs_omap"))
pr_err("dev->pwr_domain->ops.runtime_resume");
due to this; the driver->runtime_resume was not getting called.
Any idea on why I am seeing only the dev->pwr_domain is set not
dev->bus && dev->bus->pm is hitting here?
On Wed, 29 Jun 2011, Munegowda, Keshava wrote:
> for usb host case , I am seeing that the pm_runtime_get_sync
>
>
> static int rpm_resume(struct device *dev, int rpmflags)
> {
> ............
> ..........
> if (dev->pwr_domain) {
> callback = dev->pwr_domain->ops.runtime_resume;
> if(!strcmp(dev_name(dev),"usbhs_omap"))
> pr_err("dev->pwr_domain->ops.runtime_resume");
> }
> else if (dev->type && dev->type->pm) {
> callback = dev->type->pm->runtime_resume;
> if(!strcmp(dev_name(dev),"usbhs_omap"))
> pr_err("dev->type->pm->runtime_resume");
> }
> else if (dev->class && dev->class->pm) {
> callback = dev->class->pm->runtime_resume;
> if(!strcmp(dev_name(dev),"usbhs_omap"))
> pr_err("ev->class->pm->runtime_resume");
> }
> else if (dev->bus && dev->bus->pm) {
> callback = dev->bus->pm->runtime_resume;
> if(!strcmp(dev_name(dev),"usbhs_omap"))
> pr_err("dev->bus->pm->runtime_resume");
> }
> else
> callback = NULL;
> }
>
>
> I am seeing that below if statement was hitting true:
>
> if (dev->pwr_domain) {
> callback = dev->pwr_domain->ops.runtime_resume;
> if(!strcmp(dev_name(dev),"usbhs_omap"))
> pr_err("dev->pwr_domain->ops.runtime_resume");
>
>
> due to this; the driver->runtime_resume was not getting called.
>
> Any idea on why I am seeing only the dev->pwr_domain is set not
> dev->bus && dev->bus->pm is hitting here?
Because the PM domain takes precedence over the subsystem for PM
callbacks. If the subsystem routine should be called then the PM
domain code has to call it.
Alan Stern
>-----Original Message-----
>From: Alan Stern [mailto:[email protected]]
>Sent: Wednesday, June 29, 2011 11:03 PM
>To: Munegowda, Keshava
>Cc: Kevin Hilman; [email protected]; [email protected];
>[email protected]; [email protected]; [email protected];
>[email protected]; [email protected]; [email protected]; b-
>[email protected]; [email protected]
>Subject: Re: [PATCH 4/4] mfd: global Suspend and resume support of ehci
>and ohci
>
>On Wed, 29 Jun 2011, Munegowda, Keshava wrote:
>
>> for usb host case , I am seeing that the pm_runtime_get_sync
>>
>>
>> static int rpm_resume(struct device *dev, int rpmflags)
>> {
>> ............
>> ..........
>> if (dev->pwr_domain) {
>> callback = dev->pwr_domain->ops.runtime_resume;
>> if(!strcmp(dev_name(dev),"usbhs_omap"))
>> pr_err("dev->pwr_domain->ops.runtime_resume");
>> }
>> else if (dev->type && dev->type->pm) {
>> callback = dev->type->pm->runtime_resume;
>> if(!strcmp(dev_name(dev),"usbhs_omap"))
>> pr_err("dev->type->pm->runtime_resume");
>> }
>> else if (dev->class && dev->class->pm) {
>> callback = dev->class->pm->runtime_resume;
>> if(!strcmp(dev_name(dev),"usbhs_omap"))
>> pr_err("ev->class->pm->runtime_resume");
>> }
>> else if (dev->bus && dev->bus->pm) {
>> callback = dev->bus->pm->runtime_resume;
>> if(!strcmp(dev_name(dev),"usbhs_omap"))
>> pr_err("dev->bus->pm->runtime_resume");
>> }
>> else
>> callback = NULL;
>> }
>>
>>
>> I am seeing that below if statement was hitting true:
>>
>> if (dev->pwr_domain) {
>> callback = dev->pwr_domain->ops.runtime_resume;
>> if(!strcmp(dev_name(dev),"usbhs_omap"))
>> pr_err("dev->pwr_domain->ops.runtime_resume");
>>
>>
>> due to this; the driver->runtime_resume was not getting called.
>>
>> Any idea on why I am seeing only the dev->pwr_domain is set not
>> dev->bus && dev->bus->pm is hitting here?
>
>Because the PM domain takes precedence over the subsystem for PM
>callbacks. If the subsystem routine should be called then the PM
>domain code has to call it.
This is taken care of in the pm-domain code:
static int _od_runtime_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
omap_device_enable(pdev);
return pm_generic_runtime_resume(dev);
}
pm_generic_runtime_resume will in turn call the driver call back.
int pm_generic_runtime_resume(struct device *dev)
{
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm :
NULL;
int ret;
ret = pm && pm->runtime_resume ? pm->runtime_resume(dev) : 0;
return ret;
}
>
>Alan Stern
On Wed, 29 Jun 2011, Partha Basak wrote:
> >-----Original Message-----
> >From: Alan Stern [mailto:[email protected]]
> >Sent: Wednesday, June 29, 2011 11:03 PM
> >To: Munegowda, Keshava
> >Cc: Kevin Hilman; [email protected]; [email protected];
> >[email protected]; [email protected]; [email protected];
> >[email protected]; [email protected]; [email protected]; b-
> >[email protected]; [email protected]
> >Subject: Re: [PATCH 4/4] mfd: global Suspend and resume support of ehci
> >and ohci
> >
> >On Wed, 29 Jun 2011, Munegowda, Keshava wrote:
> >
> >> for usb host case , I am seeing that the pm_runtime_get_sync
> >>
> >>
> >> static int rpm_resume(struct device *dev, int rpmflags)
> >> {
> >> ............
> >> ..........
> >> if (dev->pwr_domain) {
> >> callback = dev->pwr_domain->ops.runtime_resume;
> >> if(!strcmp(dev_name(dev),"usbhs_omap"))
> >> pr_err("dev->pwr_domain->ops.runtime_resume");
> >> }
> >> else if (dev->type && dev->type->pm) {
> >> callback = dev->type->pm->runtime_resume;
> >> if(!strcmp(dev_name(dev),"usbhs_omap"))
> >> pr_err("dev->type->pm->runtime_resume");
> >> }
> >> else if (dev->class && dev->class->pm) {
> >> callback = dev->class->pm->runtime_resume;
> >> if(!strcmp(dev_name(dev),"usbhs_omap"))
> >> pr_err("ev->class->pm->runtime_resume");
> >> }
> >> else if (dev->bus && dev->bus->pm) {
> >> callback = dev->bus->pm->runtime_resume;
> >> if(!strcmp(dev_name(dev),"usbhs_omap"))
> >> pr_err("dev->bus->pm->runtime_resume");
> >> }
> >> else
> >> callback = NULL;
> >> }
> >>
> >>
> >> I am seeing that below if statement was hitting true:
> >>
> >> if (dev->pwr_domain) {
> >> callback = dev->pwr_domain->ops.runtime_resume;
> >> if(!strcmp(dev_name(dev),"usbhs_omap"))
> >> pr_err("dev->pwr_domain->ops.runtime_resume");
> >>
> >>
> >> due to this; the driver->runtime_resume was not getting called.
> >>
> >> Any idea on why I am seeing only the dev->pwr_domain is set not
> >> dev->bus && dev->bus->pm is hitting here?
> >
> >Because the PM domain takes precedence over the subsystem for PM
> >callbacks. If the subsystem routine should be called then the PM
> >domain code has to call it.
>
> This is taken care of in the pm-domain code:
> static int _od_runtime_resume(struct device *dev)
> {
> struct platform_device *pdev = to_platform_device(dev);
>
> omap_device_enable(pdev);
>
> return pm_generic_runtime_resume(dev);
> }
> pm_generic_runtime_resume will in turn call the driver call back.
>
> int pm_generic_runtime_resume(struct device *dev)
> {
> const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm :
> NULL;
> int ret;
>
> ret = pm && pm->runtime_resume ? pm->runtime_resume(dev) : 0;
>
> return ret;
> }
You appear to be contradicting what Keshava wrote: "due to this; the
driver->runtime_resume was not getting called."
You can't both be right.
Alan Stern
"Munegowda, Keshava" <[email protected]> writes:
> On Wed, Jun 29, 2011 at 8:52 PM, Munegowda, Keshava
> <[email protected]> wrote:
>> On Thu, Jun 2, 2011 at 5:36 AM, Kevin Hilman <[email protected]> wrote:
>>> Keshava Munegowda <[email protected]> writes:
>>>
>>>> From: Keshava Munegowda <[email protected]>
>>>>
>>>> The global suspend and resume functions for usbhs core driver
>>>> are implemented.These routine are called when the global suspend
>>>> and resume occurs. Before calling these functions, the
>>>> bus suspend and resume of ehci and ohci drivers are called
>>>> from runtime pm.
>>>>
>>>> Signed-off-by: Keshava Munegowda <[email protected]>
>>>
>>> First, from what I can see, this is only a partial implementation of
>>> runtime PM. What I mean is that the runtime PM methods are used only
>>> during the suspend path. The rest of the time the USB host IP block is
>>> left enabled, even when nothing is connected.
>>>
>>> I tested this on my 3530/Overo board, and verified that indeed the
>>> usbhost powerdomain hits retention on suspend, but while idle, when
>>> nothing is connected, I would expect the driver could be clever enough
>>> to use runtime PM (probably using autosuspend timeouts) to disable the
>>> hardware as well.
>>>
>>>> ---
>>>> drivers/mfd/omap-usb-host.c | 103 +++++++++++++++++++++++++++++++++++++++++++
>>>> 1 files changed, 103 insertions(+), 0 deletions(-)
>>>>
>>>> diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
>>>> index 43de12a..32d19e2 100644
>>>> --- a/drivers/mfd/omap-usb-host.c
>>>> +++ b/drivers/mfd/omap-usb-host.c
>>>> @@ -146,6 +146,10 @@
>>>> #define is_ehci_hsic_mode(x) (x == OMAP_EHCI_PORT_MODE_HSIC)
>>>>
>>>>
>>>> +/* USBHS state bits */
>>>> +#define OMAP_USBHS_INIT 0
>>>> +#define OMAP_USBHS_SUSPEND 4
>>>
>>> These additional state bits don't seem to be necessary.
>>>
>>> For suspend, just check 'pm_runtime_is_suspended()'
>>>
>>> The init flag is only used in the suspend/resume hooks, but the need for
>>> it is a side effect of not correctly using the runtime PM callbacks.
>>>
>>> Remember that the runtime PM get/put hooks have usage counting. Only
>>> when the usage count transitions to/from zero is the actual
>>> hardware-level enable/disable (via omap_hwmod) being done.
>>>
>>> The current code is making the assumption that every call to get/put is
>>> going to result in an enable/disable of the hardware.
>>>
>>> Instead, all of the code that needs to be run only upon actual
>>> enable/disable of the hardware should be done in the driver's
>>> runtime_suspend/runtime_resume callbacks. These are only called when
>>> the hardware actually changes state.
>>>
>>> Not knowing that much about the EHCI block, upon first glance, it looks
>>> like mmuch of what is done in usbhs_enable() should actually be done in
>>> the ->runtime_resume() callback, and similarily, much of what is done in
>>> usbhs_disable() should be done in the ->runtime_suspend() callback.
>>
>> Kevin,
>> do you mean driver->runtime_resume and driver->runtime_resume call backs.
>> are these call backs from pm_runtime_get_sync and pm_runtime_put_sync?
>
> for usb host case , I am seeing that the pm_runtime_get_sync
>
>
> static int rpm_resume(struct device *dev, int rpmflags)
> {
> ............
> ..........
> if (dev->pwr_domain) {
> callback = dev->pwr_domain->ops.runtime_resume;
> if(!strcmp(dev_name(dev),"usbhs_omap"))
> pr_err("dev->pwr_domain->ops.runtime_resume");
> }
> else if (dev->type && dev->type->pm) {
> callback = dev->type->pm->runtime_resume;
> if(!strcmp(dev_name(dev),"usbhs_omap"))
> pr_err("dev->type->pm->runtime_resume");
> }
> else if (dev->class && dev->class->pm) {
> callback = dev->class->pm->runtime_resume;
> if(!strcmp(dev_name(dev),"usbhs_omap"))
> pr_err("ev->class->pm->runtime_resume");
> }
> else if (dev->bus && dev->bus->pm) {
> callback = dev->bus->pm->runtime_resume;
> if(!strcmp(dev_name(dev),"usbhs_omap"))
> pr_err("dev->bus->pm->runtime_resume");
> }
> else
> callback = NULL;
> }
>
>
> I am seeing that below if statement was hitting true:
>
> if (dev->pwr_domain) {
> callback = dev->pwr_domain->ops.runtime_resume;
> if(!strcmp(dev_name(dev),"usbhs_omap"))
> pr_err("dev->pwr_domain->ops.runtime_resume");
>
>
> due to this; the driver->runtime_resume was not getting called.
>
> Any idea on why I am seeing only the dev->pwr_domain is set not
> dev->bus && dev->bus->pm is hitting here?
Because that's how it was designed. :)
On OMAP, for on-chip devices (omap_devices) we use PM domains
(pwr_domain) and not the subsystem (bus) to implment runtime PM, and as
Alan pointed out, PM domains have precedence over subsystem callbacks.
I'm curious why you determined the driver's runtime resume was not
getting called?
The PM domain callback will call your driver's runtime_resume (assuming
it exists.)
rpm_resume()
dev->pwr_domain->ops.runtime_resume()
omap_device_enable()
pm_generic_runtime_resume()
dev->driver->pm->runtime_resume()
Note that the PM domain implementation is done at the omap_device
level. Specifically, see plat-omap/omap_device.c:_od_runtime_resume()
Kevin
On Thu, Jun 30, 2011 at 12:50 AM, Kevin Hilman <[email protected]> wrote:
> "Munegowda, Keshava" <[email protected]> writes:
>
>> On Wed, Jun 29, 2011 at 8:52 PM, Munegowda, Keshava
>> <[email protected]> wrote:
>>> On Thu, Jun 2, 2011 at 5:36 AM, Kevin Hilman <[email protected]> wrote:
>>>> Keshava Munegowda <[email protected]> writes:
>>>>
>>>>> From: Keshava Munegowda <[email protected]>
>>>>>
>>>>> The global suspend and resume functions for usbhs core driver
>>>>> are implemented.These routine are called when the global suspend
>>>>> and resume occurs. Before calling these functions, the
>>>>> bus suspend and resume of ehci and ohci drivers are called
>>>>> from runtime pm.
>>>>>
>>>>> Signed-off-by: Keshava Munegowda <[email protected]>
>>>>
>>>> First, from what I can see, this is only a partial implementation of
>>>> runtime PM. ?What I mean is that the runtime PM methods are used only
>>>> during the suspend path. ?The rest of the time the USB host IP block is
>>>> left enabled, even when nothing is connected.
>>>>
>>>> I tested this on my 3530/Overo board, and verified that indeed the
>>>> usbhost powerdomain hits retention on suspend, but while idle, when
>>>> nothing is connected, I would expect the driver could be clever enough
>>>> to use runtime PM (probably using autosuspend timeouts) to disable the
>>>> hardware as well.
>>>>
>>>>> ---
>>>>> ?drivers/mfd/omap-usb-host.c | ?103 +++++++++++++++++++++++++++++++++++++++++++
>>>>> ?1 files changed, 103 insertions(+), 0 deletions(-)
>>>>>
>>>>> diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
>>>>> index 43de12a..32d19e2 100644
>>>>> --- a/drivers/mfd/omap-usb-host.c
>>>>> +++ b/drivers/mfd/omap-usb-host.c
>>>>> @@ -146,6 +146,10 @@
>>>>> ?#define is_ehci_hsic_mode(x) (x == OMAP_EHCI_PORT_MODE_HSIC)
>>>>>
>>>>>
>>>>> +/* USBHS state bits */
>>>>> +#define OMAP_USBHS_INIT ? ? ? ? ? ? ?0
>>>>> +#define OMAP_USBHS_SUSPEND ? 4
>>>>
>>>> These additional state bits don't seem to be necessary.
>>>>
>>>> For suspend, just check 'pm_runtime_is_suspended()'
>>>>
>>>> The init flag is only used in the suspend/resume hooks, but the need for
>>>> it is a side effect of not correctly using the runtime PM callbacks.
>>>>
>>>> Remember that the runtime PM get/put hooks have usage counting. ?Only
>>>> when the usage count transitions to/from zero is the actual
>>>> hardware-level enable/disable (via omap_hwmod) being done.
>>>>
>>>> The current code is making the assumption that every call to get/put is
>>>> going to result in an enable/disable of the hardware.
>>>>
>>>> Instead, all of the code that needs to be run only upon actual
>>>> enable/disable of the hardware should be done in the driver's
>>>> runtime_suspend/runtime_resume callbacks. ?These are only called when
>>>> the hardware actually changes state.
>>>>
>>>> Not knowing that much about the EHCI block, upon first glance, it looks
>>>> like mmuch of what is done in usbhs_enable() should actually be done in
>>>> the ->runtime_resume() callback, and similarily, much of what is done in
>>>> usbhs_disable() should be done in the ->runtime_suspend() callback.
>>>
>>> Kevin,
>>> ? do you mean driver->runtime_resume and driver->runtime_resume call backs.
>>> are these call backs from pm_runtime_get_sync and pm_runtime_put_sync?
>>
>> for usb host case , I am seeing that the pm_runtime_get_sync
>>
>>
>> static int rpm_resume(struct device *dev, int rpmflags)
>> {
>> ? ............
>> ?..........
>> ? ? ? if (dev->pwr_domain) {
>> ? ? ? ? ? ? ? callback = dev->pwr_domain->ops.runtime_resume;
>> ? ? ? ? ? ? ? if(!strcmp(dev_name(dev),"usbhs_omap"))
>> ? ? ? ? ? ? ? ? ? ? ? ?pr_err("dev->pwr_domain->ops.runtime_resume");
>> ? ? ? }
>> ? ? ? else if (dev->type && dev->type->pm) {
>> ? ? ? ? ? ? ? callback = dev->type->pm->runtime_resume;
>> ? ? ? ? ? ? ? if(!strcmp(dev_name(dev),"usbhs_omap"))
>> ? ? ? ? ? ? ? ? ? ? ? ?pr_err("dev->type->pm->runtime_resume");
>> ? ? ? }
>> ? ? ? else if (dev->class && dev->class->pm) {
>> ? ? ? ? ? ? ? callback = dev->class->pm->runtime_resume;
>> ? ? ? ? ? ? ? if(!strcmp(dev_name(dev),"usbhs_omap"))
>> ? ? ? ? ? ? ? ? ? ? ? ?pr_err("ev->class->pm->runtime_resume");
>> ? ? ? }
>> ? ? ? else if (dev->bus && dev->bus->pm) {
>> ? ? ? ? ? ? ? callback = dev->bus->pm->runtime_resume;
>> ? ? ? if(!strcmp(dev_name(dev),"usbhs_omap"))
>> ? ? ? ? ? ? ? ?pr_err("dev->bus->pm->runtime_resume");
>> ? ? ? }
>> ? ? ? else
>> ? ? ? ? ? ? ? callback = NULL;
>> }
>>
>>
>> I am seeing that below if statement was hitting true:
>>
>> ? ? ? if (dev->pwr_domain) {
>> ? ? ? ? ? ? ? callback = dev->pwr_domain->ops.runtime_resume;
>> ? ? ? ? ? ? ? if(!strcmp(dev_name(dev),"usbhs_omap"))
>> ? ? ? ? ? ? ? ? ? ? ? ?pr_err("dev->pwr_domain->ops.runtime_resume");
>>
>>
>> due to this; the driver->runtime_resume was not getting called.
>>
>> Any idea on why I am seeing only the dev->pwr_domain is set not
>> dev->bus && dev->bus->pm is hitting here?
>
> Because that's how it was designed. :)
>
> On OMAP, for on-chip devices (omap_devices) we use PM domains
> (pwr_domain) and not the subsystem (bus) to implment runtime PM, and as
> Alan pointed out, PM domains have precedence over subsystem callbacks.
>
> I'm curious why you determined the driver's runtime resume was not
> getting called?
>
> The PM domain callback will call your driver's runtime_resume (assuming
> it exists.)
>
> rpm_resume()
> ? dev->pwr_domain->ops.runtime_resume()
> ? ? ? omap_device_enable()
> ? ? ? pm_generic_runtime_resume()
> ? ? ? ? ?dev->driver->pm->runtime_resume()
>
> Note that the PM domain implementation is done at the omap_device
> level. ?Specifically, see plat-omap/omap_device.c:_od_runtime_resume()
>
> Kevin
Thanks to partha and others
I was an rc issue; I migrated to latest kernel ; its working now.
driver runtime call backs are getting
called now. :-)