Skip to contentSkip to conversation

ARM: OMAP2+: AM33XX: Add suspend-resume support

RFC Superseded Vaibhav Bedia 7 participants 74 messages Dec 31, 2012 – Apr 4, 2013

Subsystems (11)
lore
Share & tools
lore
#0 lore raw

v2 cover ARM: OMAP2+: AM33XX: Add suspend-resume support

Hi,

This is the second version of the patch series for adding suspend-resume support for AM33XX. Based on the feedback received on the previous patch series [1] almost all the patches have undergone a bit a rework.

The 1st two patches depend on the changes for mailbox code migration from arch/arm/*-omap*/ to drivers/mailbox/ [2].

The patch series also depends on recent changes to the OMAP PM framework by Paul Walmsley.

I found it easiest to apply the AM33XX suspend-resume patches on top of Paul's TEST_pwrdm_post_fpwrst_devel_a_3.9 branch + the patches @ [2].

With these dependencies met, the PM code uses the firmware interface and expects the userspace to load the WKUP_M3 binary before the suspend-resume functionality is made available. The binary file (and the source-code for WKUP_M3) can be obtained from the 'next' branch at [3]. The WKUP_M3 binary can either be loaded post bootup via the sysfs entry './sys/devices/ocp.2/wkup_m3.4/firmware' or it can be included in the kernel image as part of the build process.

DDR3 specific changes have been skipped for now since mainline U-Boot exhibited stability issues on all the DDR3 based AM335x boards that i could lay my hands on.

I have done basic testing along with power measurments on the different power rails on the AM335x EVM. PER domain transition on the BeagleBone fails if the CPSW driver is included in the kernel and is yet to be root caused. Along with this issue more extensive testing on other OMAP platforms is also pending right now.

For more details on the AM335x suspend-resume support please refer to the changelog in the different patches.

Regards,
Vaibhav

[1] http://marc.info/?l=linux-arm-kernel&m=135185970731607&w=4
[2] http://marc.info/?l=linux-omap&m=135583648827089&w=4
[3] http://arago-project.org/git/projects/?p=am33x-cm3.git;a=shortlog;h=refs/heads/next

Vaibhav Bedia (18):
mailbox: OMAP2+: Add support for AM33XX
mailbox: Add an API for flushing the FIFO
memory: emif: Move EMIF related header file to include/linux/
ARM: OMAP2+: AM33XX: CM: Get rid of unncessary header inclusions
ARM: OMAP2+: AM33XX: CM/PRM: Use __ASSEMBLER__ macros in header files
ARM: OMAP2+: AM33XX: hwmod: Register OCMC RAM hwmod
ARM: OMAP2+: AM33XX: hwmod: Update TPTC0 hwmod with the right flags
ARM: OMAP2+: AM33XX: hwmod: Fixup cpgmac0 hwmod entry
ARM: OMAP2+: AM33XX: Update the hardreset API
ARM: OMAP2+: AM33XX: hwmod: Update the WKUP-M3 hwmod with reset
status bit
ARM: DTS: AM33XX: Add nodes for OCMC RAM and WKUP-M3
ARM: OMAP2+: timer: Add suspend-resume callbacks for clockevent
device
ARM: OMAP2+: AM33XX: timer: Interchance clkevt and clksrc timers
ARM: OMAP2+: AM33XX: control: Add some control module registers and
APIs
ARM: OMAP2+: AM33XX: Add assembly code for PM operations
ARM: OMAP2+: AM33XX: Basic suspend resume support
ARM: OMAP2+: AM33XX: Select Mailbox when PM is enabled
ARM: OMAP2+: AM33XX: Hookup AM33XX PM code into OMAP builds

 .../devicetree/bindings/arm/omap/timer.txt         |    2 +
 arch/arm/boot/dts/am33xx.dtsi                      |   12 +
 arch/arm/mach-omap2/Kconfig                        |    4 +-
 arch/arm/mach-omap2/Makefile                       |    2 +
 arch/arm/mach-omap2/board-generic.c                |    1 +
 arch/arm/mach-omap2/cm33xx.h                       |   10 +-
 arch/arm/mach-omap2/common.h                       |   10 +
 arch/arm/mach-omap2/control.c                      |   20 +
 arch/arm/mach-omap2/control.h                      |   41 ++
 arch/arm/mach-omap2/io.c                           |    7 +
 arch/arm/mach-omap2/omap_hwmod.c                   |    5 +-
 arch/arm/mach-omap2/omap_hwmod_33xx_data.c         |   50 +-
 arch/arm/mach-omap2/pm.h                           |    7 +
 arch/arm/mach-omap2/pm33xx.c                       |  469 ++++++++++++++++
 arch/arm/mach-omap2/pm33xx.h                       |   56 ++
 arch/arm/mach-omap2/prm33xx.c                      |   11 +-
 arch/arm/mach-omap2/prm33xx.h                      |    5 +-
 arch/arm/mach-omap2/sleep33xx.S                    |  584 +++++++++++++++++++
 arch/arm/mach-omap2/sram.c                         |   10 +-
 arch/arm/mach-omap2/sram.h                         |    2 +
 arch/arm/mach-omap2/timer.c                        |   39 ++-
 drivers/mailbox/mailbox-omap2.c                    |   54 ++-
 drivers/mailbox/mailbox.c                          |   36 ++
 drivers/mailbox/mailbox.h                          |    3 +
 drivers/memory/emif.c                              |    2 +-
 drivers/memory/emif.h                              |  589 --------------------
 include/linux/mailbox.h                            |    1 +
 include/linux/ti_emif.h                            |  589 ++++++++++++++++++++
 28 files changed, 1989 insertions(+), 632 deletions(-)
 create mode 100644 arch/arm/mach-omap2/pm33xx.c
 create mode 100644 arch/arm/mach-omap2/pm33xx.h
 create mode 100644 arch/arm/mach-omap2/sleep33xx.S
 delete mode 100644 drivers/memory/emif.h
 create mode 100644 include/linux/ti_emif.h
#1 lore raw

RFC v2 1/18 mailbox: OMAP2+: Add support for AM33XX

Mailbox IP on AM33XX is the same as that present in OMAP4. The single instance of Mailbox IP on AM33XX contains 8 sub-modules and facilitates communication between MPU, PRUs and WKUP_M3.

The first mailbox sub-module is assigned for communication between MPU and WKUP-M3.

Cc Russ Dill <[email protected]>
Cc Santosh Shilimkar <[email protected]>

---
v1->v2:
Address the comment on operator usage from Russ Dill

 drivers/mailbox/mailbox-omap2.c |   35 ++++++++++++++++++++++++++++++++++-
 1 files changed, 34 insertions(+), 1 deletions(-)
drivers/mailbox/mailbox-omap2.c +34 -1
index 7c26bed..6d61159 100644
@@ -151,7 +151,7 @@ static void omap2_mbox_disable_irq(struct mailbox *mbox,
 	struct omap_mbox2_priv *p = mbox->priv;
 	u32 bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit;
 
-	if (!cpu_is_omap44xx())
+	if (!cpu_is_omap44xx() && !soc_is_am33xx())
 		bit = mbox_read_reg(p->irqdisable) & ~bit;
 
 	mbox_write_reg(bit, p->irqdisable);
@@ -352,6 +352,32 @@ struct mailbox mbox_2_info = {
 struct mailbox *omap4_mboxes[] = { &mbox_1_info, &mbox_2_info, NULL };
 #endif
 
+#if defined(CONFIG_SOC_AM33XX)
+static struct omap_mbox2_priv omap2_mbox_wkup_m3_priv = {
+	.tx_fifo = {
+		.msg		= MAILBOX_MESSAGE(0),
+		.fifo_stat	= MAILBOX_FIFOSTATUS(0),
+		.msg_stat	= MAILBOX_MSGSTATUS(0),
+	},
+	.rx_fifo = {
+		.msg		= MAILBOX_MESSAGE(1),
+		.msg_stat	= MAILBOX_MSGSTATUS(1),
+	},
+	.irqenable	= OMAP4_MAILBOX_IRQENABLE(3),
+	.irqstatus	= OMAP4_MAILBOX_IRQSTATUS(3),
+	.irqdisable	= OMAP4_MAILBOX_IRQENABLE_CLR(3),
+	.notfull_bit	= MAILBOX_IRQ_NOTFULL(0),
+	.newmsg_bit	= MAILBOX_IRQ_NEWMSG(0),
+};
+
+struct mailbox mbox_wkup_m3_info = {
+	.name	= "wkup_m3",
+	.ops	= &omap2_mbox_ops,
+	.priv	= &omap2_mbox_wkup_m3_priv,
+};
+struct mailbox *am33xx_mboxes[] = { &mbox_wkup_m3_info, NULL };
+#endif
+
 static int __devinit omap2_mbox_probe(struct platform_device *pdev)
 {
 	struct resource *mem;
@@ -386,6 +412,13 @@ static int __devinit omap2_mbox_probe(struct platform_device *pdev)
 		list[0]->irq = list[1]->irq = platform_get_irq(pdev, 0);
 	}
 #endif
+#if defined(CONFIG_SOC_AM33XX)
+	else if (soc_is_am33xx()) {
+		list = am33xx_mboxes;
+
+		list[0]->irq = platform_get_irq(pdev, 0);
+	}
+#endif
 	else {
 		pr_err("%s: platform not supported\n", __func__);
 		return -ENODEV;
Signature · 1 line
1.7.0.4
maintainer
#2 lore raw

RFC v2 1/18 mailbox: OMAP2+: Add support for AM33XX

Quoted 23 lines · Vaibhav Bedia

Mailbox IP on AM33XX is the same as that present in OMAP4. The single instance of Mailbox IP on AM33XX contains 8 sub-modules and facilitates communication between MPU, PRUs and WKUP_M3.

The first mailbox sub-module is assigned for communication between MPU and WKUP-M3.

Cc Russ Dill <[email protected]>
Cc Santosh Shilimkar <[email protected]>

---
v1->v2:
Address the comment on operator usage from Russ Dill

 drivers/mailbox/mailbox-omap2.c |   35 ++++++++++++++++++++++++++++++++++-
 1 files changed, 34 insertions(+), 1 deletions(-)
drivers/mailbox/mailbox-omap2.c +0 -0
index 7c26bed..6d61159 100644

drivers/mailbox/mailbox-omap2.c static void omap2_mbox_disable_irq(struct mailbox *mbox, · line 157

Quoted 8 lines
drivers/mailbox/mailbox-omap2.c +1 -1
@@ -151,7 +151,7 @@ static void omap2_mbox_disable_irq(struct mailbox *mbox,
 	struct omap_mbox2_priv *p = mbox->priv;
 	u32 bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit;
 
-	if (!cpu_is_omap44xx())
+	if (!cpu_is_omap44xx() && !soc_is_am33xx())
 		bit = mbox_read_reg(p->irqdisable) & ~bit;
 
 	mbox_write_reg(bit, p->irqdisable);

The cpu_is/soc_is macros are no longer available to drivers with 8d91a42e (Merge tag 'omap-late-cleanups'...). So you'll have to pass whatever flags the driver needs in platform_data or as device tree properties.

Quoted 4 lines
drivers/mailbox/mailbox-omap2.c +0 -0
@@ -386,6 +412,13 @@ static int __devinit omap2_mbox_probe(struct platform_device *pdev)
 		list[0]->irq = list[1]->irq = platform_get_irq(pdev, 0);
 	}
 #endif

drivers/mailbox/mailbox-omap2.c static int __devinit omap2_mbox_probe(struct platform_device *pdev) · line 424

Quoted 10 lines
drivers/mailbox/mailbox-omap2.c +7 -0
+#if defined(CONFIG_SOC_AM33XX)
+	else if (soc_is_am33xx()) {
+		list = am33xx_mboxes;
+
+		list[0]->irq = platform_get_irq(pdev, 0);
+	}
+#endif
 	else {
 		pr_err("%s: platform not supported\n", __func__);
 		return -ENODEV;

Here too.

Tony

#3 lore raw

RFC v2 1/18 mailbox: OMAP2+: Add support for AM33XX

Hi Tony,

On Tue, Jan 01, 2013 at 23:55:06, Tony Lindgren wrote:
[...]

Quoted 8 lines
Quoted 2 lines

mbox_write_reg(bit, p->irqdisable);

The cpu_is/soc_is macros are no longer available to drivers with 8d91a42e (Merge tag 'omap-late-cleanups'...). So you'll have to pass whatever flags the driver needs in platform_data or as device tree properties.

Thanks for the heads-up. Will rework this patch once the mailbox driver migration is finalized.

Regards,
Vaibhav

#4 lore raw

RFC v2 1/18 mailbox: OMAP2+: Add support for AM33XX

In reply to Vaibhav Bedia
Quoted 5 lines · Vaibhav Bedia

Mailbox IP on AM33XX is the same as that present in OMAP4. The single instance of Mailbox IP on AM33XX contains 8 sub-modules and facilitates communication between MPU, PRUs and WKUP_M3.

PRUS?

Quoted 60 lines

The first mailbox sub-module is assigned for communication between MPU and WKUP-M3.

Cc Russ Dill <[email protected]>
Cc Santosh Shilimkar <[email protected]>

---
v1->v2:
Address the comment on operator usage from Russ Dill

  drivers/mailbox/mailbox-omap2.c |   35 ++++++++++++++++++++++++++++++++++-
  1 files changed, 34 insertions(+), 1 deletions(-)
drivers/mailbox/mailbox-omap2.c +27 -1
index 7c26bed..6d61159 100644
@@ -151,7 +151,7 @@ static void omap2_mbox_disable_irq(struct mailbox *mbox,
  	struct omap_mbox2_priv *p = mbox->priv;
  	u32 bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit;
 
-	if (!cpu_is_omap44xx())
+	if (!cpu_is_omap44xx() && !soc_is_am33xx())
  		bit = mbox_read_reg(p->irqdisable) & ~bit;
 
  	mbox_write_reg(bit, p->irqdisable);
@@ -352,6 +352,32 @@ struct mailbox mbox_2_info = {
  struct mailbox *omap4_mboxes[] = { &mbox_1_info, &mbox_2_info, NULL };
  #endif
 
+#if defined(CONFIG_SOC_AM33XX)
+static struct omap_mbox2_priv omap2_mbox_wkup_m3_priv = {
+	.tx_fifo = {
+		.msg		= MAILBOX_MESSAGE(0),
+		.fifo_stat	= MAILBOX_FIFOSTATUS(0),
+		.msg_stat	= MAILBOX_MSGSTATUS(0),
+	},
+	.rx_fifo = {
+		.msg		= MAILBOX_MESSAGE(1),
+		.msg_stat	= MAILBOX_MSGSTATUS(1),
+	},
+	.irqenable	= OMAP4_MAILBOX_IRQENABLE(3),
+	.irqstatus	= OMAP4_MAILBOX_IRQSTATUS(3),
+	.irqdisable	= OMAP4_MAILBOX_IRQENABLE_CLR(3),
+	.notfull_bit	= MAILBOX_IRQ_NOTFULL(0),
+	.newmsg_bit	= MAILBOX_IRQ_NEWMSG(0),
+};
+
+struct mailbox mbox_wkup_m3_info = {
+	.name	= "wkup_m3",
+	.ops	= &omap2_mbox_ops,
+	.priv	= &omap2_mbox_wkup_m3_priv,
+};
+struct mailbox *am33xx_mboxes[] = { &mbox_wkup_m3_info, NULL };
+#endif
+
  static int __devinit omap2_mbox_probe(struct platform_device *pdev)
  {
  	struct resource *mem;

drivers/mailbox/mailbox-omap2.c static int __devinit omap2_mbox_probe(struct platform_device *pdev) · line 415

Quoted 4 lines
drivers/mailbox/mailbox-omap2.c +1 -0
@@ -386,6 +412,13 @@ static int __devinit omap2_mbox_probe(struct platform_device *pdev)
  		list[0]->irq = list[1]->irq = platform_get_irq(pdev, 0);
  	}
  #endif
+#if defined(CONFIG_SOC_AM33XX)

ifdef in middle of the code. I know you are just
following what is already there.

drivers/mailbox/mailbox-omap2.c static int __devinit omap2_mbox_probe(struct platform_device *pdev) · line 418

Quoted 3 lines
drivers/mailbox/mailbox-omap2.c +3 -0
+	else if (soc_is_am33xx()) {
+		list = am33xx_mboxes;
+

UN-necessary extra line here.

drivers/mailbox/mailbox-omap2.c static int __devinit omap2_mbox_probe(struct platform_device *pdev) · line 421

Quoted 3 lines
drivers/mailbox/mailbox-omap2.c +3 -0
+		list[0]->irq = platform_get_irq(pdev, 0);
+	}
+#endif

Hopefully mailbox clean-up will kill that #ifdeffery. Apart from those comments, patch looks fine to me.

Regards
Santosh

#5 lore raw

RFC v2 1/18 mailbox: OMAP2+: Add support for AM33XX

Quoted 7 lines · Santosh
Quoted 5 lines · Vaibhav Bedia

Mailbox IP on AM33XX is the same as that present in OMAP4. The single instance of Mailbox IP on AM33XX contains 8 sub-modules and facilitates communication between MPU, PRUs and WKUP_M3.

PRUS?

Programmable Real Time Units. Will clarify in next version

Quoted 67 lines
Quoted 61 lines

The first mailbox sub-module is assigned for communication between MPU and WKUP-M3.

Cc Russ Dill <[email protected]>
Cc Santosh Shilimkar <[email protected]>

---
v1->v2:
Address the comment on operator usage from Russ Dill

  drivers/mailbox/mailbox-omap2.c |   35 ++++++++++++++++++++++++++++++++++-
  1 files changed, 34 insertions(+), 1 deletions(-)
drivers/mailbox/mailbox-omap2.c +27 -1
index 7c26bed..6d61159 100644
@@ -151,7 +151,7 @@ static void omap2_mbox_disable_irq(struct mailbox *mbox,
  	struct omap_mbox2_priv *p = mbox->priv;
  	u32 bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit;
 
-	if (!cpu_is_omap44xx())
+	if (!cpu_is_omap44xx() && !soc_is_am33xx())
  		bit = mbox_read_reg(p->irqdisable) & ~bit;
 
  	mbox_write_reg(bit, p->irqdisable);
@@ -352,6 +352,32 @@ struct mailbox mbox_2_info = {
  struct mailbox *omap4_mboxes[] = { &mbox_1_info, &mbox_2_info, NULL };
  #endif
 
+#if defined(CONFIG_SOC_AM33XX)
+static struct omap_mbox2_priv omap2_mbox_wkup_m3_priv = {
+	.tx_fifo = {
+		.msg		= MAILBOX_MESSAGE(0),
+		.fifo_stat	= MAILBOX_FIFOSTATUS(0),
+		.msg_stat	= MAILBOX_MSGSTATUS(0),
+	},
+	.rx_fifo = {
+		.msg		= MAILBOX_MESSAGE(1),
+		.msg_stat	= MAILBOX_MSGSTATUS(1),
+	},
+	.irqenable	= OMAP4_MAILBOX_IRQENABLE(3),
+	.irqstatus	= OMAP4_MAILBOX_IRQSTATUS(3),
+	.irqdisable	= OMAP4_MAILBOX_IRQENABLE_CLR(3),
+	.notfull_bit	= MAILBOX_IRQ_NOTFULL(0),
+	.newmsg_bit	= MAILBOX_IRQ_NEWMSG(0),
+};
+
+struct mailbox mbox_wkup_m3_info = {
+	.name	= "wkup_m3",
+	.ops	= &omap2_mbox_ops,
+	.priv	= &omap2_mbox_wkup_m3_priv,
+};
+struct mailbox *am33xx_mboxes[] = { &mbox_wkup_m3_info, NULL };
+#endif
+
  static int __devinit omap2_mbox_probe(struct platform_device *pdev)
  {
  	struct resource *mem;

drivers/mailbox/mailbox-omap2.c static int __devinit omap2_mbox_probe(struct platform_device *pdev) · line 415

Quoted 4 lines
drivers/mailbox/mailbox-omap2.c +1 -0
@@ -386,6 +412,13 @@ static int __devinit omap2_mbox_probe(struct platform_device *pdev)
  		list[0]->irq = list[1]->irq = platform_get_irq(pdev, 0);
  	}
  #endif
+#if defined(CONFIG_SOC_AM33XX)

ifdef in middle of the code. I know you are just
following what is already there.

Quoted 4 lines

drivers/mailbox/mailbox-omap2.c static int __devinit omap2_mbox_probe(struct platform_device *pdev) · line 418

Quoted 3 lines
drivers/mailbox/mailbox-omap2.c +3 -0
+	else if (soc_is_am33xx()) {
+		list = am33xx_mboxes;
+

UN-necessary extra line here.

Will remove

Quoted 7 lines

drivers/mailbox/mailbox-omap2.c static int __devinit omap2_mbox_probe(struct platform_device *pdev) · line 421

Quoted 3 lines
drivers/mailbox/mailbox-omap2.c +3 -0
+		list[0]->irq = platform_get_irq(pdev, 0);
+	}
+#endif

Hopefully mailbox clean-up will kill that #ifdeffery. Apart from those comments, patch looks fine to me.

Thanks,
Vaibhav

#6 lore raw

RFC v2 2/18 mailbox: Add an API for flushing the FIFO

In reply to Vaibhav Bedia

On AM33XX, the mailbox module between the MPU and the WKUP-M3 co-processor facilitates a one-way communication. MPU uses the assigned mailbox sub-module to issue the interrupt to the WKUP-M3 co-processor which then goes and reads the the IPC data from registers in the control module.

WKUP-M3 is in the L4_WKUP and does not have any access to the mailbox module. Due to this limitation, the MPU is completely responsible for FIFO maintenance and interrupt generation. MPU needs to ensure that the FIFO does not overflow by reading back the assigned mailbox sub-module.

This patch adds an API in the mailbox code which the MPU can use to empty the FIFO by issuing a readback command.

Cc Santosh Shilimkar <[email protected]>

---
Note: This patch which will be slightly reworked once the mailbox driver changes are finalized.

 drivers/mailbox/mailbox-omap2.c |   19 +++++++++++++++++++
 drivers/mailbox/mailbox.c       |   36 ++++++++++++++++++++++++++++++++++++
 drivers/mailbox/mailbox.h       |    3 +++
 include/linux/mailbox.h         |    1 +
 4 files changed, 59 insertions(+), 0 deletions(-)
drivers/mailbox/mailbox-omap2.c +19 -0
index 6d61159..c732be1 100644
@@ -125,6 +125,23 @@ static int omap2_mbox_fifo_full(struct mailbox *mbox)
 	return mbox_read_reg(fifo->fifo_stat);
 }
 
+static int omap2_mbox_fifo_needs_flush(struct mailbox *mbox)
+{
+	struct omap_mbox2_priv *p = mbox->priv;
+	struct omap_mbox2_fifo *fifo = &p->tx_fifo;
+
+	return mbox_read_reg(fifo->msg_stat);
+}
+
+static void omap2_mbox_fifo_readback(struct mailbox *mbox,
+			struct mailbox_msg *msg)
+{
+	struct omap_mbox2_priv *p = mbox->priv;
+	struct omap_mbox2_fifo *fifo = &p->tx_fifo;
+
+	msg->header = mbox_read_reg(fifo->msg);
+}
+
 static int ompa2_mbox_poll_for_space(struct mailbox *mbox)
 {
 	if (omap2_mbox_fifo_full(mbox))
@@ -221,6 +238,8 @@ static struct mailbox_ops omap2_mbox_ops = {
 	.read           = omap2_mbox_fifo_read,
 	.write          = omap2_mbox_fifo_write,
 	.empty          = omap2_mbox_fifo_empty,
+	.fifo_needs_flush	= omap2_mbox_fifo_needs_flush,
+	.fifo_readback		= omap2_mbox_fifo_readback,
 	.poll_for_space = ompa2_mbox_poll_for_space,
 	.enable_irq     = omap2_mbox_enable_irq,
 	.disable_irq    = omap2_mbox_disable_irq,
drivers/mailbox/mailbox.c +36 -0
index 2f50226..92c9f68 100644
@@ -57,6 +57,15 @@ static inline int mbox_empty(struct mailbox *mbox)
 {
 	return mbox->ops->empty(mbox);
 }
+static inline int mbox_fifo_needs_flush(struct mailbox *mbox)
+{
+	return mbox->ops->fifo_needs_flush(mbox);
+}
+static inline void mbox_fifo_readback(struct mailbox *mbox,
+				struct mailbox_msg *msg)
+{
+	mbox->ops->fifo_readback(mbox, msg);
+}
 
 /* Mailbox IRQ handle functions */
 static inline void ack_mbox_irq(struct mailbox *mbox, mailbox_irq_t irq)
@@ -110,6 +119,33 @@ out:
 }
 EXPORT_SYMBOL(mailbox_msg_send);
 
+/*
+ * Flush the Rx FIFO by reading back the messages
+ * Since the normal expectation is that the Rx will do the
+ * reading, add a debug message to indicate if we really flush
+ *
+ * Returns the no. of messages read back
+ */
+int mailbox_rx_flush(struct mailbox *mbox)
+{
+	int ret = 0;
+	struct mailbox_msg readback_msg;
+
+	while (mbox_fifo_needs_flush(mbox)) {
+		mbox_fifo_readback(mbox, &readback_msg);
+		ret++;
+	}
+
+	if (ret) {
+		/* no more messages in the fifo. clear IRQ source. */
+		ack_mbox_irq(mbox, IRQ_RX);
+		pr_debug("Flushed %s Rx FIFO by reading back\n", mbox->name);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(mailbox_rx_flush);
+
 #define TRANSFER_TIMEOUT 30000 /* Becomes ~3s timeout */
 
 static struct mailbox_msg no_irq_msg_res;
drivers/mailbox/mailbox.h +3 -0
index f103194..b841ae1 100644
@@ -24,6 +24,9 @@ struct mailbox_ops {
 	int             (*write)(struct mailbox *mbox, struct mailbox_msg *msg);
 	int             (*empty)(struct mailbox *mbox);
 	int             (*poll_for_space)(struct mailbox *mbox);
+	int		(*fifo_needs_flush)(struct mailbox *mbox);
+	void		(*fifo_readback)(struct mailbox *mbox,
+				struct mailbox_msg *msg);
 	/* irq */
 	void            (*enable_irq)(struct mailbox *mbox,
 			mailbox_irq_t irq);
include/linux/mailbox.h +1 -0
index 3f9ec1e..705def9 100644
@@ -22,6 +22,7 @@ struct mailbox_msg {
 #define MAILBOX_FILL_HEADER_MSG(_msg, _header) \
 	MAILBOX_FILL_MSG(_msg, _header, NULL, 0);
 
+int mailbox_rx_flush(struct mailbox *);
 int mailbox_msg_send(struct mailbox *, struct mailbox_msg *msg);
 struct mailbox_msg *mailbox_msg_send_receive_no_irq(struct mailbox *,
 		struct mailbox_msg *msg);
Signature · 1 line
1.7.0.4
#7 lore raw

RFC v2 2/18 mailbox: Add an API for flushing the FIFO

Quoted 13 lines · Vaibhav Bedia

On AM33XX, the mailbox module between the MPU and the WKUP-M3 co-processor facilitates a one-way communication. MPU uses the assigned mailbox sub-module to issue the interrupt to the WKUP-M3 co-processor which then goes and reads the the IPC data from registers in the control module.

WKUP-M3 is in the L4_WKUP and does not have any access to the mailbox module. Due to this limitation, the MPU is completely responsible for FIFO maintenance and interrupt generation. MPU needs to ensure that the FIFO does not overflow by reading back the assigned mailbox sub-module.

Quoted 9 lines

This patch adds an API in the mailbox code which the MPU can use to empty the FIFO by issuing a readback command.

Cc Santosh Shilimkar <[email protected]>

---
Note: This patch which will be slightly reworked once the mailbox driver changes are finalized.

Can you expand a bit please ?

Quoted 64 lines
  drivers/mailbox/mailbox-omap2.c |   19 +++++++++++++++++++
  drivers/mailbox/mailbox.c       |   36 ++++++++++++++++++++++++++++++++++++
  drivers/mailbox/mailbox.h       |    3 +++
  include/linux/mailbox.h         |    1 +
  4 files changed, 59 insertions(+), 0 deletions(-)
drivers/mailbox/mailbox-omap2.c +19 -0
index 6d61159..c732be1 100644
@@ -125,6 +125,23 @@ static int omap2_mbox_fifo_full(struct mailbox *mbox)
  	return mbox_read_reg(fifo->fifo_stat);
  }
 
+static int omap2_mbox_fifo_needs_flush(struct mailbox *mbox)
+{
+	struct omap_mbox2_priv *p = mbox->priv;
+	struct omap_mbox2_fifo *fifo = &p->tx_fifo;
+
+	return mbox_read_reg(fifo->msg_stat);
+}
+
+static void omap2_mbox_fifo_readback(struct mailbox *mbox,
+			struct mailbox_msg *msg)
+{
+	struct omap_mbox2_priv *p = mbox->priv;
+	struct omap_mbox2_fifo *fifo = &p->tx_fifo;
+
+	msg->header = mbox_read_reg(fifo->msg);
+}
+
  static int ompa2_mbox_poll_for_space(struct mailbox *mbox)
  {
  	if (omap2_mbox_fifo_full(mbox))
@@ -221,6 +238,8 @@ static struct mailbox_ops omap2_mbox_ops = {
  	.read           = omap2_mbox_fifo_read,
  	.write          = omap2_mbox_fifo_write,
  	.empty          = omap2_mbox_fifo_empty,
+	.fifo_needs_flush	= omap2_mbox_fifo_needs_flush,
+	.fifo_readback		= omap2_mbox_fifo_readback,
  	.poll_for_space = ompa2_mbox_poll_for_space,
  	.enable_irq     = omap2_mbox_enable_irq,
  	.disable_irq    = omap2_mbox_disable_irq,
drivers/mailbox/mailbox.c +9 -0
index 2f50226..92c9f68 100644
@@ -57,6 +57,15 @@ static inline int mbox_empty(struct mailbox *mbox)
  {
  	return mbox->ops->empty(mbox);
  }
+static inline int mbox_fifo_needs_flush(struct mailbox *mbox)
+{
+	return mbox->ops->fifo_needs_flush(mbox);
+}
+static inline void mbox_fifo_readback(struct mailbox *mbox,
+				struct mailbox_msg *msg)
+{
+	mbox->ops->fifo_readback(mbox, msg);
+}
 
  /* Mailbox IRQ handle functions */
  static inline void ack_mbox_irq(struct mailbox *mbox, mailbox_irq_t irq)

drivers/mailbox/mailbox.c out: · line 122

Quoted 4 lines
drivers/mailbox/mailbox.c +1 -0
@@ -110,6 +119,33 @@ out:
  }
  EXPORT_SYMBOL(mailbox_msg_send);
 
+/*

s/*/**

drivers/mailbox/mailbox.c out: · line 128

Quoted 6 lines
drivers/mailbox/mailbox.c +6 -0
+ * Flush the Rx FIFO by reading back the messages
+ * Since the normal expectation is that the Rx will do the
+ * reading, add a debug message to indicate if we really flush
+ *
+ * Returns the no. of messages read back
+ */

Just look at the kernel doc style for above

Rest looks fine.

Regards
Santosh

#8 lore raw

RFC v2 2/18 mailbox: Add an API for flushing the FIFO

Quoted 17 lines · Santosh
Quoted 16 lines · Vaibhav Bedia

On AM33XX, the mailbox module between the MPU and the WKUP-M3 co-processor facilitates a one-way communication. MPU uses the assigned mailbox sub-module to issue the interrupt to the WKUP-M3 co-processor which then goes and reads the the IPC data from registers in the control module.

WKUP-M3 is in the L4_WKUP and does not have any access to the mailbox module. Due to this limitation, the MPU is completely responsible for FIFO maintenance and interrupt generation. MPU needs to ensure that the FIFO does not overflow by reading back the assigned mailbox sub-module.

This patch adds an API in the mailbox code which the MPU can use to empty the FIFO by issuing a readback command.

Quoted 7 lines
Quoted 6 lines
Cc Santosh Shilimkar <[email protected]>

---
Note: This patch which will be slightly reworked once the mailbox driver changes are finalized.

Can you expand a bit please ?

There could be some changes in the structure names.

Quoted 62 lines
Quoted 62 lines
  drivers/mailbox/mailbox-omap2.c |   19 +++++++++++++++++++
  drivers/mailbox/mailbox.c       |   36 ++++++++++++++++++++++++++++++++++++
  drivers/mailbox/mailbox.h       |    3 +++
  include/linux/mailbox.h         |    1 +
  4 files changed, 59 insertions(+), 0 deletions(-)
drivers/mailbox/mailbox-omap2.c +19 -0
index 6d61159..c732be1 100644
@@ -125,6 +125,23 @@ static int omap2_mbox_fifo_full(struct mailbox *mbox)
  	return mbox_read_reg(fifo->fifo_stat);
  }
 
+static int omap2_mbox_fifo_needs_flush(struct mailbox *mbox)
+{
+	struct omap_mbox2_priv *p = mbox->priv;
+	struct omap_mbox2_fifo *fifo = &p->tx_fifo;
+
+	return mbox_read_reg(fifo->msg_stat);
+}
+
+static void omap2_mbox_fifo_readback(struct mailbox *mbox,
+			struct mailbox_msg *msg)
+{
+	struct omap_mbox2_priv *p = mbox->priv;
+	struct omap_mbox2_fifo *fifo = &p->tx_fifo;
+
+	msg->header = mbox_read_reg(fifo->msg);
+}
+
  static int ompa2_mbox_poll_for_space(struct mailbox *mbox)
  {
  	if (omap2_mbox_fifo_full(mbox))
@@ -221,6 +238,8 @@ static struct mailbox_ops omap2_mbox_ops = {
  	.read           = omap2_mbox_fifo_read,
  	.write          = omap2_mbox_fifo_write,
  	.empty          = omap2_mbox_fifo_empty,
+	.fifo_needs_flush	= omap2_mbox_fifo_needs_flush,
+	.fifo_readback		= omap2_mbox_fifo_readback,
  	.poll_for_space = ompa2_mbox_poll_for_space,
  	.enable_irq     = omap2_mbox_enable_irq,
  	.disable_irq    = omap2_mbox_disable_irq,
drivers/mailbox/mailbox.c +9 -0
index 2f50226..92c9f68 100644
@@ -57,6 +57,15 @@ static inline int mbox_empty(struct mailbox *mbox)
  {
  	return mbox->ops->empty(mbox);
  }
+static inline int mbox_fifo_needs_flush(struct mailbox *mbox)
+{
+	return mbox->ops->fifo_needs_flush(mbox);
+}
+static inline void mbox_fifo_readback(struct mailbox *mbox,
+				struct mailbox_msg *msg)
+{
+	mbox->ops->fifo_readback(mbox, msg);
+}
 
Quoted 8 lines

drivers/mailbox/mailbox.c out: · line 122

Quoted 7 lines
drivers/mailbox/mailbox.c +1 -0
  /* Mailbox IRQ handle functions */
  static inline void ack_mbox_irq(struct mailbox *mbox, mailbox_irq_t irq)
@@ -110,6 +119,33 @@ out:
  }
  EXPORT_SYMBOL(mailbox_msg_send);
 
+/*

s/*/**

Will do.

Quoted 10 lines

drivers/mailbox/mailbox.c out: · line 128

Quoted 6 lines
drivers/mailbox/mailbox.c +6 -0
+ * Flush the Rx FIFO by reading back the messages
+ * Since the normal expectation is that the Rx will do the
+ * reading, add a debug message to indicate if we really flush
+ *
+ * Returns the no. of messages read back
+ */

Just look at the kernel doc style for above

Rest looks fine.

Ok.

Thanks,
Vaibhav

#9 lore raw

RFC v2 3/18 memory: emif: Move EMIF related header file to include/linux/

In reply to Vaibhav Bedia

OMAP4 and AM33XX share the same EMIF controller IP. Although there are significant differences in the IP integration due to which AM33XX can't reuse the EMIF driver DVFS similar to OMAP4, it can definitely benefit by reusing the EMIF related macros defined in drivers/memory/emif.h.

In the current OMAP PM framework the PM code resides under arch/arm/mach-omap2/. To enable reuse of the register defines move the emif header file to include/linux so that both the EMIF driver and the AM33XX PM code can benefit.

Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Aneesh V <[email protected]>

---
v1->v2:
This is a new patch in the series to enable code reuse between the EMIF driver and AM33XX PM code

 drivers/memory/emif.c   |    2 +-
 drivers/memory/emif.h   |  589 -----------------------------------------------
 include/linux/ti_emif.h |  589 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 590 insertions(+), 590 deletions(-)
 delete mode 100644 drivers/memory/emif.h
 create mode 100644 include/linux/ti_emif.h
drivers/memory/emif.c +1 -1
index 06d31c9..a3d893d 100644
@@ -24,8 +24,8 @@
 #include <linux/module.h>
 #include <linux/list.h>
 #include <linux/spinlock.h>
+#include <linux/ti_emif.h>
 #include <memory/jedec_ddr.h>
-#include "emif.h"
 #include "of_memory.h"
 
 /**
drivers/memory/emif.h deleted +0 -589

589 lines not on this page · raw message

include/linux/ti_emif.h new file +589 -0

589 lines not on this page · raw message

Signature · 1 line
1.7.0.4
#10 lore raw

RFC v2 3/18 memory: emif: Move EMIF related header file to include/linux/

Quoted 15 lines · Vaibhav Bedia

OMAP4 and AM33XX share the same EMIF controller IP. Although there are significant differences in the IP integration due to which AM33XX can't reuse the EMIF driver DVFS similar to OMAP4, it can definitely benefit by reusing the EMIF related macros defined in drivers/memory/emif.h.

In the current OMAP PM framework the PM code resides under arch/arm/mach-omap2/. To enable reuse of the register defines move the emif header file to include/linux so that both the EMIF driver and the AM33XX PM code can benefit.

Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Aneesh V <[email protected]>
Quoted 8 lines

---
v1->v2:
This is a new patch in the series to enable code reuse between the EMIF driver and AM33XX PM code

  drivers/memory/emif.c   |    2 +-
  drivers/memory/emif.h   |  589 -----------------------------------------------
  include/linux/ti_emif.h |  589 +++++++++++++++++++++++++++++++++++++++++++++++

You are just moving the file. So "git mv file1 flie2; and the git format-patch -C" on committed patch should just generate few lines of patch.

Quoted 9 lines
include/linux/ti_emif.h new file +9 -0
+/* DDR_PHY_CTRL_1 - EMIF4D5 */
+#define DLL_HALF_DELAY_SHIFT_4D5			21
+#define DLL_HALF_DELAY_MASK_4D5				(1 << 21)
+#define READ_LATENCY_SHIFT_4D5				0
+#define READ_LATENCY_MASK_4D5				(0x1f << 0)
+
+/* DDR_PHY_CTRL_1_SHDW */
+#define DDR_PHY_CTRL_1_SHDW_SHIFT			5
+#define DDR_PHY_CTRL_1_SHDW_MASK			(0x7ffffff << 5)

include/linux/ti_emif.h line 564

Quoted 10 lines
include/linux/ti_emif.h new file +10 -0
+#define READ_LATENCY_SHDW_SHIFT				0
+#define READ_LATENCY_SHDW_MASK				(0x1f << 0)
+
+#ifndef __ASSEMBLY__
+/*
+ * Structure containing shadow of important registers in EMIF
+ * The calculation function fills in this structure to be later used for
+ * initialisation and DVFS
+ */
+struct emif_regs {

Are you using above struct. If not we can leave it in same place and just move the register defines.

Regards
santosh

#11 lore raw

RFC v2 3/18 memory: emif: Move EMIF related header file to include/linux/

On Tue, Jan 08, 2013 at 19:34:41, Shilimkar, Santosh wrote:
[...]

Quoted 8 lines
Quoted 4 lines
  drivers/memory/emif.c   |    2 +-
  drivers/memory/emif.h   |  589 -----------------------------------------------
  include/linux/ti_emif.h |  589 +++++++++++++++++++++++++++++++++++++++++++++++

You are just moving the file. So "git mv file1 flie2; and the git format-patch -C"
on committed patch should just generate few lines of patch.

Ok. Didn't know about this.

Quoted 13 lines
Quoted 13 lines
include/linux/ti_emif.h new file +13 -0
+/* DDR_PHY_CTRL_1 - EMIF4D5 */
+#define DLL_HALF_DELAY_SHIFT_4D5			21
+#define DLL_HALF_DELAY_MASK_4D5				(1 << 21)
+#define READ_LATENCY_SHIFT_4D5				0
+#define READ_LATENCY_MASK_4D5				(0x1f << 0)
+
+/* DDR_PHY_CTRL_1_SHDW */
+#define DDR_PHY_CTRL_1_SHDW_SHIFT			5
+#define DDR_PHY_CTRL_1_SHDW_MASK			(0x7ffffff << 5)
+#define READ_LATENCY_SHDW_SHIFT				0
+#define READ_LATENCY_SHDW_MASK				(0x1f << 0)
+
+#ifndef __ASSEMBLY__
Quoted 9 lines

include/linux/ti_emif.h line 564

Quoted 6 lines
include/linux/ti_emif.h new file +6 -0
+/*
+ * Structure containing shadow of important registers in EMIF
+ * The calculation function fills in this structure to be later used for
+ * initialisation and DVFS
+ */
+struct emif_regs {

Are you using above struct. If not we can leave it in same place and just move the register defines.

No, the struct is not used. I'll leave it here in the next version.

Regards,
Vaibhav

#12 lore raw

RFC v2 4/18 ARM: OMAP2+: AM33XX: CM: Get rid of unncessary header inclusions

In reply to Vaibhav Bedia

Some of the included header files are not needed so remove them.

Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>

---
v1->v2:
This is a new patch in the series based on Santosh's feedback on splitting the changes

 arch/arm/mach-omap2/cm33xx.h |    7 +------
 1 files changed, 1 insertions(+), 6 deletions(-)
arch/arm/mach-omap2/cm33xx.h +1 -6
index 5fa0b62..8009e13 100644
@@ -17,16 +17,11 @@
 #ifndef __ARCH_ARM_MACH_OMAP2_CM_33XX_H
 #define __ARCH_ARM_MACH_OMAP2_CM_33XX_H
 
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/err.h>
-#include <linux/io.h>
-
 #include "common.h"
 
 #include "cm.h"
 #include "cm-regbits-33xx.h"
-#include "cm33xx.h"
+#include "iomap.h"
 
 /* CM base address */
 #define AM33XX_CM_BASE		0x44e00000
Signature · 1 line
1.7.0.4
#13 lore raw

RFC v2 4/18 ARM: OMAP2+: AM33XX: CM: Get rid of unncessary header inclusions

Quoted 10 lines · Vaibhav Bedia

Some of the included header files are not needed so remove them.

Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>

---

#14 lore raw

RFC v2 5/18 ARM: OMAP2+: AM33XX: CM/PRM: Use __ASSEMBLER__ macros in header files

In reply to Vaibhav Bedia

This is necessary to ensure that macros declared here can be reused from assembly files.

Cc Santosh Shilimkar <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>

---
v1->v2:
Split out the header file changes in a separate patch based on the feedback from Santosh

 arch/arm/mach-omap2/cm33xx.h  |    3 +++
 arch/arm/mach-omap2/prm33xx.h |    3 +++
 2 files changed, 6 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/cm33xx.h +3 -0
index 8009e13..2f215cd 100644
@@ -376,6 +376,7 @@
 #define AM33XX_CM_CEFUSE_CEFUSE_CLKCTRL			AM33XX_CM_REGADDR(AM33XX_CM_CEFUSE_MOD, 0x0020)
 
 
+#ifndef __ASSEMBLER__
 extern bool am33xx_cm_is_clkdm_in_hwsup(s16 inst, u16 cdoffs);
 extern void am33xx_cm_clkdm_enable_hwsup(s16 inst, u16 cdoffs);
 extern void am33xx_cm_clkdm_disable_hwsup(s16 inst, u16 cdoffs);
@@ -412,4 +413,6 @@ static inline int am33xx_cm_wait_module_ready(u16 inst, s16 cdoffs,
 }
 #endif
 
+#endif /* ASSEMBLER */
+
 #endif
arch/arm/mach-omap2/prm33xx.h +3 -0
index 3f25c56..2f2eaa0 100644
@@ -117,6 +117,7 @@
 #define AM33XX_PM_CEFUSE_PWRSTST_OFFSET		0x0004
 #define AM33XX_PM_CEFUSE_PWRSTST		AM33XX_PRM_REGADDR(AM33XX_PRM_CEFUSE_MOD, 0x0004)
 
+#ifndef __ASSEMBLER__
 extern u32 am33xx_prm_read_reg(s16 inst, u16 idx);
 extern void am33xx_prm_write_reg(u32 val, s16 inst, u16 idx);
 extern u32 am33xx_prm_rmw_reg_bits(u32 mask, u32 bits, s16 inst, s16 idx);
@@ -126,4 +127,6 @@ extern int am33xx_prm_is_hardreset_asserted(u8 shift, s16 inst,
 extern int am33xx_prm_assert_hardreset(u8 shift, s16 inst, u16 rstctrl_offs);
 extern int am33xx_prm_deassert_hardreset(u8 shift, s16 inst,
 		u16 rstctrl_offs, u16 rstst_offs);
+#endif /* ASSEMBLER */
+
 #endif
Signature · 1 line
1.7.0.4
#15 lore raw

RFC v2 5/18 ARM: OMAP2+: AM33XX: CM/PRM: Use __ASSEMBLER__ macros in header files

Quoted 31 lines · Vaibhav Bedia

This is necessary to ensure that macros declared here can be reused from assembly files.

Cc Santosh Shilimkar <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>

---
v1->v2:
Split out the header file changes in a separate patch based on the feedback from Santosh

  arch/arm/mach-omap2/cm33xx.h  |    3 +++
  arch/arm/mach-omap2/prm33xx.h |    3 +++
  2 files changed, 6 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/cm33xx.h +1 -0
index 8009e13..2f215cd 100644
@@ -376,6 +376,7 @@
  #define AM33XX_CM_CEFUSE_CEFUSE_CLKCTRL			AM33XX_CM_REGADDR(AM33XX_CM_CEFUSE_MOD, 0x0020)
 
 
+#ifndef __ASSEMBLER__
  extern bool am33xx_cm_is_clkdm_in_hwsup(s16 inst, u16 cdoffs);
  extern void am33xx_cm_clkdm_enable_hwsup(s16 inst, u16 cdoffs);
  extern void am33xx_cm_clkdm_disable_hwsup(s16 inst, u16 cdoffs);

arch/arm/mach-omap2/cm33xx.h static inline int am33xx_cm_wait_module_ready(u16 inst, s16 cdoffs, · line 417

Quoted 5 lines
arch/arm/mach-omap2/cm33xx.h +2 -0
@@ -412,4 +413,6 @@ static inline int am33xx_cm_wait_module_ready(u16 inst, s16 cdoffs,
  }
  #endif
 
+#endif /* ASSEMBLER */
+

Drop that extra line.

Quoted 14 lines
arch/arm/mach-omap2/cm33xx.h +0 -0
  #endif
arch/arm/mach-omap2/prm33xx.h +1 -0
index 3f25c56..2f2eaa0 100644
@@ -117,6 +117,7 @@
  #define AM33XX_PM_CEFUSE_PWRSTST_OFFSET		0x0004
  #define AM33XX_PM_CEFUSE_PWRSTST		AM33XX_PRM_REGADDR(AM33XX_PRM_CEFUSE_MOD, 0x0004)
 
+#ifndef __ASSEMBLER__
  extern u32 am33xx_prm_read_reg(s16 inst, u16 idx);
  extern void am33xx_prm_write_reg(u32 val, s16 inst, u16 idx);
  extern u32 am33xx_prm_rmw_reg_bits(u32 mask, u32 bits, s16 inst, s16 idx);

arch/arm/mach-omap2/prm33xx.h extern int am33xx_prm_is_hardreset_asserted(u8 shift, s16 inst, · line 131

Quoted 5 lines
arch/arm/mach-omap2/prm33xx.h +2 -0
@@ -126,4 +127,6 @@ extern int am33xx_prm_is_hardreset_asserted(u8 shift, s16 inst,
  extern int am33xx_prm_assert_hardreset(u8 shift, s16 inst, u16 rstctrl_offs);
  extern int am33xx_prm_deassert_hardreset(u8 shift, s16 inst,
  		u16 rstctrl_offs, u16 rstst_offs);
+#endif /* ASSEMBLER */
+

ditto

Otherwise looks fine.

#16 lore raw

RFC v2 5/18 ARM: OMAP2+: AM33XX: CM/PRM: Use __ASSEMBLER__ macros in header files

On Tue, Jan 08, 2013 at 20:31:44, Shilimkar, Santosh wrote:
[...]

Quoted 3 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+#endif /* ASSEMBLER */
+

Drop that extra line.

Ok.

Quoted 9 lines
Quoted 9 lines
  #endif
arch/arm/mach-omap2/prm33xx.h +0 -0
index 3f25c56..2f2eaa0 100644
@@ -117,6 +117,7 @@
  #define AM33XX_PM_CEFUSE_PWRSTST_OFFSET		0x0004
  #define AM33XX_PM_CEFUSE_PWRSTST		AM33XX_PRM_REGADDR(AM33XX_PRM_CEFUSE_MOD, 0x0004)
 
Quoted 12 lines

arch/arm/mach-omap2/prm33xx.h extern int am33xx_prm_is_hardreset_asserted(u8 shift, s16 inst, · line 131

Quoted 10 lines
arch/arm/mach-omap2/prm33xx.h +3 -0
+#ifndef __ASSEMBLER__
  extern u32 am33xx_prm_read_reg(s16 inst, u16 idx);
  extern void am33xx_prm_write_reg(u32 val, s16 inst, u16 idx);
  extern u32 am33xx_prm_rmw_reg_bits(u32 mask, u32 bits, s16 inst, s16 idx);
@@ -126,4 +127,6 @@ extern int am33xx_prm_is_hardreset_asserted(u8 shift, s16 inst,
  extern int am33xx_prm_assert_hardreset(u8 shift, s16 inst, u16 rstctrl_offs);
  extern int am33xx_prm_deassert_hardreset(u8 shift, s16 inst,
  		u16 rstctrl_offs, u16 rstst_offs);
+#endif /* ASSEMBLER */
+

ditto

Ok.

Quoted 3 lines

Otherwise looks fine.

Thanks,
Vaibhav

#17 lore raw

RFC v2 6/18 ARM: OMAP2+: AM33XX: hwmod: Register OCMC RAM hwmod

In reply to Vaibhav Bedia

OCMC RAM lies in the PER power domain and this memory support retention.

Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>

---
v1->v2:
Move out the OCMC hwmod code from #if 0 block based on Vaibhav Hiremath's comment

 arch/arm/mach-omap2/omap_hwmod_33xx_data.c |   47 ++++++++++++++++------------
 1 files changed, 27 insertions(+), 20 deletions(-)
arch/arm/mach-omap2/omap_hwmod_33xx_data.c +27 -20
index 081c71e..5e50e68 100644
@@ -414,7 +414,6 @@ static struct omap_hwmod am33xx_adc_tsc_hwmod = {
  *    - cEFUSE (doesn't fall under any ocp_if)
  *    - clkdiv32k
  *    - debugss
- *    - ocmc ram
  *    - ocp watch point
  *    - aes0
  *    - sha0
@@ -481,25 +480,6 @@ static struct omap_hwmod am33xx_debugss_hwmod = {
 	},
 };
 
-/* ocmcram */
-static struct omap_hwmod_class am33xx_ocmcram_hwmod_class = {
-	.name = "ocmcram",
-};
-
-static struct omap_hwmod am33xx_ocmcram_hwmod = {
-	.name		= "ocmcram",
-	.class		= &am33xx_ocmcram_hwmod_class,
-	.clkdm_name	= "l3_clkdm",
-	.flags		= (HWMOD_INIT_NO_IDLE | HWMOD_INIT_NO_RESET),
-	.main_clk	= "l3_gclk",
-	.prcm		= {
-		.omap4	= {
-			.clkctrl_offs	= AM33XX_CM_PER_OCMCRAM_CLKCTRL_OFFSET,
-			.modulemode	= MODULEMODE_SWCTRL,
-		},
-	},
-};
-
 /* ocpwp */
 static struct omap_hwmod_class am33xx_ocpwp_hwmod_class = {
 	.name		= "ocpwp",
@@ -570,6 +550,25 @@ static struct omap_hwmod am33xx_sha0_hwmod = {
 
 #endif
 
+/* ocmcram */
+static struct omap_hwmod_class am33xx_ocmcram_hwmod_class = {
+	.name = "ocmcram",
+};
+
+static struct omap_hwmod am33xx_ocmcram_hwmod = {
+	.name		= "ocmcram",
+	.class		= &am33xx_ocmcram_hwmod_class,
+	.clkdm_name	= "l3_clkdm",
+	.flags		= (HWMOD_INIT_NO_IDLE | HWMOD_INIT_NO_RESET),
+	.main_clk	= "l3_gclk",
+	.prcm		= {
+		.omap4	= {
+			.clkctrl_offs	= AM33XX_CM_PER_OCMCRAM_CLKCTRL_OFFSET,
+			.modulemode	= MODULEMODE_SWCTRL,
+		},
+	},
+};
+
 /* 'smartreflex' class */
 static struct omap_hwmod_class am33xx_smartreflex_hwmod_class = {
 	.name		= "smartreflex",
@@ -3328,6 +3327,13 @@ static struct omap_hwmod_ocp_if am33xx_l3_s__usbss = {
 	.flags		= OCPIF_SWSUP_IDLE,
 };
 
+/* l3 main -> ocmc */
+static struct omap_hwmod_ocp_if am33xx_l3_main__ocmc = {
+	.master		= &am33xx_l3_main_hwmod,
+	.slave		= &am33xx_ocmcram_hwmod,
+	.user		= OCP_USER_MPU | OCP_USER_SDMA,
+};
+
 static struct omap_hwmod_ocp_if *am33xx_hwmod_ocp_ifs[] __initdata = {
 	&am33xx_l4_fw__emif_fw,
 	&am33xx_l3_main__emif,
@@ -3398,6 +3404,7 @@ static struct omap_hwmod_ocp_if *am33xx_hwmod_ocp_ifs[] __initdata = {
 	&am33xx_l3_main__tptc0,
 	&am33xx_l3_main__tptc1,
 	&am33xx_l3_main__tptc2,
+	&am33xx_l3_main__ocmc,
 	&am33xx_l3_s__usbss,
 	&am33xx_l4_hs__cpgmac0,
 	&am33xx_cpgmac0__mdio,
Signature · 1 line
1.7.0.4
#18 lore raw

RFC v2 6/18 ARM: OMAP2+: AM33XX: hwmod: Register OCMC RAM hwmod

Quoted 10 lines · Vaibhav Bedia

OCMC RAM lies in the PER power domain and this memory support retention.

Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>

---

Looks fine to me.

#19 lore raw

RFC v2 7/18 ARM: OMAP2+: AM33XX: hwmod: Update TPTC0 hwmod with the right flags

In reply to Vaibhav Bedia

TPTC0 needs to be idled and put to standby under SW control. Add the appropriate flags in the TPTC0 hwmod entry.

Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>

---
v1->v2:
No change

 arch/arm/mach-omap2/omap_hwmod_33xx_data.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/omap_hwmod_33xx_data.c +1 -0
index 5e50e68..5c34d4b 100644
@@ -1823,6 +1823,7 @@ static struct omap_hwmod am33xx_tptc0_hwmod = {
 	.class		= &am33xx_tptc_hwmod_class,
 	.clkdm_name	= "l3_clkdm",
 	.mpu_irqs	= am33xx_tptc0_irqs,
+	.flags		= (HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY),
 	.main_clk	= "l3_gclk",
 	.prcm		= {
 		.omap4	= {
Signature · 1 line
1.7.0.4
#20 lore raw

RFC v2 7/18 ARM: OMAP2+: AM33XX: hwmod: Update TPTC0 hwmod with the right flags

Quoted 3 lines · Vaibhav Bedia

TPTC0 needs to be idled and put to standby under SW control. Add the appropriate flags in the TPTC0 hwmod entry.

Can you please expand TPTC0 in chane log.

Quoted 7 lines
Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>

---

Patch is fine otherwise.

#21 lore raw

RFC v2 7/18 ARM: OMAP2+: AM33XX: hwmod: Update TPTC0 hwmod with the right flags

Quoted 5 lines · Santosh
Quoted 3 lines · Vaibhav Bedia

TPTC0 needs to be idled and put to standby under SW control. Add the appropriate flags in the TPTC0 hwmod entry.

Can you please expand TPTC0 in chane log.

Third Party Transfer Controller. It's part of the DMA IP. Will add it in the changelog.

Quoted 11 lines
Quoted 7 lines
Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>

---

Patch is fine otherwise.

#22 lore raw

RFC v2 7/18 ARM: OMAP2+: AM33XX: hwmod: Update TPTC0 hwmod with the right flags

In reply to Vaibhav Bedia
Quoted 1 line · Vaibhav Bedia

TPTC0 needs to be idled and put to standby under SW control.

Please elaborate about why (e.g. HW support not available, HW support broken/buggy, etc.) since these blocks are not well documented in the docs that I have (spruh73f.)

Thanks,

Kevin

Quoted 27 lines

Add the appropriate flags in the TPTC0 hwmod entry.

Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>

---
v1->v2:
No change

 arch/arm/mach-omap2/omap_hwmod_33xx_data.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/omap_hwmod_33xx_data.c +1 -0
index 5e50e68..5c34d4b 100644
@@ -1823,6 +1823,7 @@ static struct omap_hwmod am33xx_tptc0_hwmod = {
 	.class		= &am33xx_tptc_hwmod_class,
 	.clkdm_name	= "l3_clkdm",
 	.mpu_irqs	= am33xx_tptc0_irqs,
+	.flags		= (HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY),
 	.main_clk	= "l3_gclk",
 	.prcm		= {
 		.omap4	= {
#23 lore raw

RFC v2 7/18 ARM: OMAP2+: AM33XX: hwmod: Update TPTC0 hwmod with the right flags

Hi Kevin,

Quoted 8 lines · Kevin Hilman
Quoted 1 line · Vaibhav Bedia

TPTC0 needs to be idled and put to standby under SW control.

Please elaborate about why (e.g. HW support not available, HW support broken/buggy, etc.) since these blocks are not well documented in the docs that I have (spruh73f.)

This change was picked up for v3.9. I'll submit a follow-up patch adding in a comment in the hwmod file for this.

Regards,
Vaibhav

#24 lore raw

RFC v2 8/18 ARM: OMAP2+: AM33XX: hwmod: Fixup cpgmac0 hwmod entry

In reply to Vaibhav Bedia

The current HWMOD code expects the memory region with the IP's SYSCONFIG register to be marked with ADDR_TYPE_RT flag.

CPGMAC0 hwmod entry specifies two memory regions and marks both with the flag ADDR_TYPE_RT although only the 2nd region has the SYSCONFIG register. This leads to the HWMOD code accessing the wrong memory address for idle and standby operations. Fix this by removing the ADDR_TYPE_RT flag from the 1st memory region in CPGMAC0 hwmod entry.

Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>

---
v1->v2:
Address review comments from Santosh and
Vaibhav Hiremath. Previous discussion on the change
is @ http://lists.infradead.org/pipermail/linux-arm-kernel/2012-November/129512.html

 arch/arm/mach-omap2/omap_hwmod_33xx_data.c |    1 -
 1 files changed, 0 insertions(+), 1 deletions(-)
arch/arm/mach-omap2/omap_hwmod_33xx_data.c +0 -1
index 5c34d4b..863ce08 100644
@@ -2496,7 +2496,6 @@ static struct omap_hwmod_addr_space am33xx_cpgmac0_addr_space[] = {
 	{
 		.pa_start	= 0x4a100000,
 		.pa_end		= 0x4a100000 + SZ_2K - 1,
-		.flags		= ADDR_TYPE_RT,
 	},
 	/* cpsw wr */
 	{
Signature · 1 line
1.7.0.4
#25 lore raw

RFC v2 8/18 ARM: OMAP2+: AM33XX: hwmod: Fixup cpgmac0 hwmod entry

Quoted 11 lines · Vaibhav Bedia

The current HWMOD code expects the memory region with the IP's SYSCONFIG register to be marked with ADDR_TYPE_RT flag.

CPGMAC0 hwmod entry specifies two memory regions and marks both with the flag ADDR_TYPE_RT although only the 2nd region has the SYSCONFIG register. This leads to the HWMOD code accessing the wrong memory address for idle and standby operations. Fix this by removing the ADDR_TYPE_RT flag from the 1st memory region in CPGMAC0 hwmod entry.

Quoted 7 lines
Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>

---

Seems correct to me though Benoit, Paul can
may have comment.

Regards
Santosh

#26 lore raw

RFC v2 9/18 ARM: OMAP2+: AM33XX: hwmod: Update the WKUP-M3 hwmod with reset status bit

In reply to Vaibhav Bedia

WKUP-M3 has a reset status bit (RM_WKUP_STST.WKUP_M3_LRST) Update the WKUP-M3 hwmod data to reflect the same.

Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>

---
v1->v2:
Change the patch order to keep git bisect happy

 arch/arm/mach-omap2/omap_hwmod_33xx_data.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/omap_hwmod_33xx_data.c +1 -0
index 863ce08..bd4b171 100644
@@ -269,6 +269,7 @@ static struct omap_hwmod am33xx_wkup_m3_hwmod = {
 		.omap4	= {
 			.clkctrl_offs	= AM33XX_CM_WKUP_WKUP_M3_CLKCTRL_OFFSET,
 			.rstctrl_offs	= AM33XX_RM_WKUP_RSTCTRL_OFFSET,
+			.rstst_offs	= AM33XX_RM_WKUP_RSTST_OFFSET,
 			.modulemode	= MODULEMODE_SWCTRL,
 		},
 	},
Signature · 1 line
1.7.0.4
#27 lore raw

RFC v2 9/18 ARM: OMAP2+: AM33XX: hwmod: Update the WKUP-M3 hwmod with reset status bit

Vaibhav,

Quoted 10 lines · Vaibhav Bedia

WKUP-M3 has a reset status bit (RM_WKUP_STST.WKUP_M3_LRST) Update the WKUP-M3 hwmod data to reflect the same.

Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>

---

I think you should make a separate series for
the data file updates/fixes.

Regards
Santosh

#28 lore raw

RFC v2 10/18 ARM: OMAP2+: AM33XX: Update the hardreset API

In reply to Vaibhav Bedia

WKUP-M3 has a reset status bit (RM_WKUP_STST.WKUP_M3_LRST) Update the hardreset API to ensure that the reset line properly deasserted.

Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>

---
v1->v2:
Address comments from Vaibhav Hiremath and change the patch order as pointed out by Santosh Shilimkar

 arch/arm/mach-omap2/omap_hwmod.c |    5 +----
 arch/arm/mach-omap2/prm33xx.c    |   11 +++++++----
 arch/arm/mach-omap2/prm33xx.h    |    2 +-
 3 files changed, 9 insertions(+), 9 deletions(-)
arch/arm/mach-omap2/omap_hwmod.c +1 -4
index 4653efb..6549439 100644
@@ -3041,11 +3041,8 @@ static int _am33xx_assert_hardreset(struct omap_hwmod *oh,
 static int _am33xx_deassert_hardreset(struct omap_hwmod *oh,
 				     struct omap_hwmod_rst_info *ohri)
 {
-	if (ohri->st_shift)
-		pr_err("omap_hwmod: %s: %s: hwmod data error: OMAP4 does not support st_shift\n",
-		       oh->name, ohri->name);
-
 	return am33xx_prm_deassert_hardreset(ohri->rst_shift,
+				ohri->st_shift,
 				oh->clkdm->pwrdm.ptr->prcm_offs,
 				oh->prcm.omap4.rstctrl_offs,
 				oh->prcm.omap4.rstst_offs);
arch/arm/mach-omap2/prm33xx.c +7 -4
index 1ac7388..44c0d72 100644
@@ -110,11 +110,11 @@ int am33xx_prm_assert_hardreset(u8 shift, s16 inst, u16 rstctrl_offs)
  * -EINVAL upon an argument error, -EEXIST if the submodule was already out
  * of reset, or -EBUSY if the submodule did not exit reset promptly.
  */
-int am33xx_prm_deassert_hardreset(u8 shift, s16 inst,
+int am33xx_prm_deassert_hardreset(u8 shift, u8 st_shift, s16 inst,
 		u16 rstctrl_offs, u16 rstst_offs)
 {
 	int c;
-	u32 mask = 1 << shift;
+	u32 mask = 1 << st_shift;
 
 	/* Check the current status to avoid  de-asserting the line twice */
 	if (am33xx_prm_is_hardreset_asserted(shift, inst, rstctrl_offs) == 0)
@@ -122,11 +122,14 @@ int am33xx_prm_deassert_hardreset(u8 shift, s16 inst,
 
 	/* Clear the reset status by writing 1 to the status bit */
 	am33xx_prm_rmw_reg_bits(0xffffffff, mask, inst, rstst_offs);
+
 	/* de-assert the reset control line */
+	mask = 1 << shift;
+
 	am33xx_prm_rmw_reg_bits(mask, 0, inst, rstctrl_offs);
-	/* wait the status to be set */
 
-	omap_test_timeout(am33xx_prm_is_hardreset_asserted(shift, inst,
+	/* wait the status to be set */
+	omap_test_timeout(am33xx_prm_is_hardreset_asserted(st_shift, inst,
 							   rstst_offs),
 			  MAX_MODULE_HARDRESET_WAIT, c);
 
arch/arm/mach-omap2/prm33xx.h +1 -1
index 2f2eaa0..aefde49 100644
@@ -125,7 +125,7 @@ extern void am33xx_prm_global_warm_sw_reset(void);
 extern int am33xx_prm_is_hardreset_asserted(u8 shift, s16 inst,
 		u16 rstctrl_offs);
 extern int am33xx_prm_assert_hardreset(u8 shift, s16 inst, u16 rstctrl_offs);
-extern int am33xx_prm_deassert_hardreset(u8 shift, s16 inst,
+extern int am33xx_prm_deassert_hardreset(u8 shift, u8 st_shift, s16 inst,
 		u16 rstctrl_offs, u16 rstst_offs);
 #endif /* ASSEMBLER */
 
Signature · 1 line
1.7.0.4
#29 lore raw

RFC v2 10/18 ARM: OMAP2+: AM33XX: Update the hardreset API

Quoted 11 lines · Vaibhav Bedia

WKUP-M3 has a reset status bit (RM_WKUP_STST.WKUP_M3_LRST) Update the hardreset API to ensure that the reset line properly deasserted.

Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>

---

Looks good.

#30 lore raw

RFC v2 11/18 ARM: DTS: AM33XX: Add nodes for OCMC RAM and WKUP-M3

In reply to Vaibhav Bedia

Since AM33XX supports only DT-boot, this is needed for the appropriate device nodes to be created.

Note: OCMC RAM is part of the PER power domain and supports retention. The assembly code for low power entry/exit will run from OCMC RAM. To ensure that the OMAP PM code does not attempt to disable the clock to OCMC RAM as part of the suspend process add the no_idle_on_suspend flag.

Cc Tony Lindgren <[email protected]>
Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>

---
v1->v2:
Add a proper changelog

 arch/arm/boot/dts/am33xx.dtsi |   11 +++++++++++
 1 files changed, 11 insertions(+), 0 deletions(-)
arch/arm/boot/dts/am33xx.dtsi +11 -0
index c2f14e8..4731748 100644
@@ -385,5 +385,16 @@
 				mac-address = [ 00 00 00 00 00 00 ];
 			};
 		};
+
+		ocmcram: ocmcram at 40300000 {
+			compatible = "ti,ocmcram";
+			ti,hwmods = "ocmcram";
+			ti,no_idle_on_suspend;
+		};
+
+		wkup_m3: wkup_m3 at 44d00000 {
+			compatible = "ti,wkup_m3";
+			ti,hwmods = "wkup_m3";
+		};
 	};
 };
Signature · 1 line
1.7.0.4
#31 lore raw

RFC v2 11/18 ARM: DTS: AM33XX: Add nodes for OCMC RAM and WKUP-M3

Quoted 9 lines · Vaibhav Bedia

Since AM33XX supports only DT-boot, this is needed for the appropriate device nodes to be created.

Note: OCMC RAM is part of the PER power domain and supports retention. The assembly code for low power entry/exit will run from OCMC RAM. To ensure that the OMAP PM code does not attempt to disable the clock to OCMC RAM as part of the suspend process add the no_idle_on_suspend flag.

Quoted 8 lines
Cc Tony Lindgren <[email protected]>
Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>

---

Looks fine to me.

#32 lore raw

RFC v2 12/18 ARM: OMAP2+: timer: Add suspend-resume callbacks for clockevent device

In reply to Vaibhav Bedia

The current OMAP timer code registers two timers - one as clocksource and one as clockevent. AM33XX has only one usable timer in the WKUP domain so one of the timers needs suspend-resume support to restore the configuration to pre-suspend state.

commit adc78e6 (timekeeping: Add suspend and resume of clock event devices) introduced .suspend and .resume callbacks for clock event devices. Leverages these callbacks to have AM33XX clockevent timer which is in not in WKUP domain to behave properly across system suspend.

Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>
Cc Jon Hunter <[email protected]>

---
v1->v2:
Get rid of harcoded timer id.
Note: since a platform device is not created for these timer instances and because there's very minimal change needed for restarting the timer a full blown context save and restore has been skipped.

 arch/arm/mach-omap2/timer.c |   33 +++++++++++++++++++++++++++++++++
 1 files changed, 33 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/timer.c +33 -0
index 691aa67..38f9cbc 100644
@@ -128,6 +128,36 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
 	}
 }
 
+static void omap_clkevt_suspend(struct clock_event_device *unused)
+{
+	char name[10];
+	struct omap_hwmod *oh;
+
+	sprintf(name, "timer%d", clkev.id);
+	oh = omap_hwmod_lookup(name);
+	if (!oh)
+		return;
+
+	__omap_dm_timer_stop(&clkev, 1, clkev.rate);
+	omap_hwmod_idle(oh);
+}
+
+static void omap_clkevt_resume(struct clock_event_device *unused)
+{
+	char name[10];
+	struct omap_hwmod *oh;
+
+	sprintf(name, "timer%d", clkev.id);
+	oh = omap_hwmod_lookup(name);
+	if (!oh)
+		return;
+
+	omap_hwmod_enable(oh);
+	__omap_dm_timer_load_start(&clkev,
+			OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR, 0, 1);
+	__omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);
+}
+
 static struct clock_event_device clockevent_gpt = {
 	.name		= "gp_timer",
 	.features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
@@ -135,6 +165,8 @@ static struct clock_event_device clockevent_gpt = {
 	.rating		= 300,
 	.set_next_event	= omap2_gp_timer_set_next_event,
 	.set_mode	= omap2_gp_timer_set_mode,
+	.suspend	= omap_clkevt_suspend,
+	.resume		= omap_clkevt_resume,
 };
 
 static struct property device_disabled = {
@@ -323,6 +355,7 @@ static void __init omap2_gp_clockevent_init(int gptimer_id,
 	int res;
 
 	clkev.errata = omap_dm_timer_get_errata();
+	clkev.id = gptimer_id;
 
 	/*
 	 * For clock-event timers we never read the timer counter and
Signature · 1 line
1.7.0.4
#33 lore raw

RFC v2 12/18 ARM: OMAP2+: timer: Add suspend-resume callbacks for clockevent device

Quoted 59 lines · Vaibhav Bedia

The current OMAP timer code registers two timers - one as clocksource and one as clockevent. AM33XX has only one usable timer in the WKUP domain so one of the timers needs suspend-resume support to restore the configuration to pre-suspend state.

commit adc78e6 (timekeeping: Add suspend and resume of clock event devices) introduced .suspend and .resume callbacks for clock event devices. Leverages these callbacks to have AM33XX clockevent timer which is in not in WKUP domain to behave properly across system suspend.

Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>
Cc Jon Hunter <[email protected]>

---
v1->v2:
Get rid of harcoded timer id.
Note: since a platform device is not created for these timer instances and because there's very minimal change needed for restarting the timer a full blown context save and restore has been skipped.

  arch/arm/mach-omap2/timer.c |   33 +++++++++++++++++++++++++++++++++
  1 files changed, 33 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/timer.c +20 -0
index 691aa67..38f9cbc 100644
@@ -128,6 +128,36 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
  	}
  }
 
+static void omap_clkevt_suspend(struct clock_event_device *unused)
+{
+	char name[10];
+	struct omap_hwmod *oh;
+
+	sprintf(name, "timer%d", clkev.id);
+	oh = omap_hwmod_lookup(name);
+	if (!oh)
+		return;
+
+	__omap_dm_timer_stop(&clkev, 1, clkev.rate);
+	omap_hwmod_idle(oh);
+}
+
+static void omap_clkevt_resume(struct clock_event_device *unused)
+{
+	char name[10];
+	struct omap_hwmod *oh;
+
+	sprintf(name, "timer%d", clkev.id);

arch/arm/mach-omap2/timer.c static void omap2_gp_timer_set_mode(enum clock_event_mode mode, · line 160

Quoted 10 lines
arch/arm/mach-omap2/timer.c +10 -0
+	oh = omap_hwmod_lookup(name);
+	if (!oh)
+		return;
+
+	omap_hwmod_enable(oh);
+	__omap_dm_timer_load_start(&clkev,
+			OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR, 0, 1);
+	__omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);
+}
+

Am still bit uncomfortable with direct hwmod usage in the suspend/resmue hooks.

Jon, Any alternatives you can think of ?

Regards,
Santosh

#34 lore raw

RFC v2 12/18 ARM: OMAP2+: timer: Add suspend-resume callbacks for clockevent device

Quoted 73 lines · Santosh
Quoted 59 lines · Vaibhav Bedia

The current OMAP timer code registers two timers - one as clocksource and one as clockevent. AM33XX has only one usable timer in the WKUP domain so one of the timers needs suspend-resume support to restore the configuration to pre-suspend state.

commit adc78e6 (timekeeping: Add suspend and resume of clock event devices) introduced .suspend and .resume callbacks for clock event devices. Leverages these callbacks to have AM33XX clockevent timer which is in not in WKUP domain to behave properly across system suspend.

Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>
Cc Jon Hunter <[email protected]>

---
v1->v2:
Get rid of harcoded timer id.
Note: since a platform device is not created for these timer instances and because there's very minimal change needed for restarting the timer a full blown context save and restore has been skipped.

  arch/arm/mach-omap2/timer.c |   33 +++++++++++++++++++++++++++++++++
  1 files changed, 33 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/timer.c +20 -0
index 691aa67..38f9cbc 100644
@@ -128,6 +128,36 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
  	}
  }
 
+static void omap_clkevt_suspend(struct clock_event_device *unused)
+{
+	char name[10];
+	struct omap_hwmod *oh;
+
+	sprintf(name, "timer%d", clkev.id);
+	oh = omap_hwmod_lookup(name);
+	if (!oh)
+		return;
+
+	__omap_dm_timer_stop(&clkev, 1, clkev.rate);
+	omap_hwmod_idle(oh);
+}
+
+static void omap_clkevt_resume(struct clock_event_device *unused)
+{
+	char name[10];
+	struct omap_hwmod *oh;
+
+	sprintf(name, "timer%d", clkev.id);

arch/arm/mach-omap2/timer.c static void omap2_gp_timer_set_mode(enum clock_event_mode mode, · line 160

Quoted 10 lines
arch/arm/mach-omap2/timer.c +10 -0
+	oh = omap_hwmod_lookup(name);
+	if (!oh)
+		return;
+
+	omap_hwmod_enable(oh);
+	__omap_dm_timer_load_start(&clkev,
+			OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR, 0, 1);
+	__omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);
+}
+

Am still bit uncomfortable with direct hwmod usage in the suspend/resmue hooks.

Quoted 2 lines

Jon, Any alternatives you can think of ?

Jon,

Any suggestions here?

Regards,
Vaibhav

#35 lore raw

RFC v2 12/18 ARM: OMAP2+: timer: Add suspend-resume callbacks for clockevent device

Quoted 74 lines · Vaibhav
Quoted 73 lines · Santosh
Quoted 59 lines · Vaibhav Bedia

The current OMAP timer code registers two timers - one as clocksource and one as clockevent. AM33XX has only one usable timer in the WKUP domain so one of the timers needs suspend-resume support to restore the configuration to pre-suspend state.

commit adc78e6 (timekeeping: Add suspend and resume of clock event devices) introduced .suspend and .resume callbacks for clock event devices. Leverages these callbacks to have AM33XX clockevent timer which is in not in WKUP domain to behave properly across system suspend.

Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>
Cc Jon Hunter <[email protected]>

---
v1->v2:
Get rid of harcoded timer id.
Note: since a platform device is not created for these timer instances and because there's very minimal change needed for restarting the timer a full blown context save and restore has been skipped.

  arch/arm/mach-omap2/timer.c |   33 +++++++++++++++++++++++++++++++++
  1 files changed, 33 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/timer.c +20 -0
index 691aa67..38f9cbc 100644
@@ -128,6 +128,36 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
  	}
  }
 
+static void omap_clkevt_suspend(struct clock_event_device *unused)
+{
+	char name[10];
+	struct omap_hwmod *oh;
+
+	sprintf(name, "timer%d", clkev.id);
+	oh = omap_hwmod_lookup(name);
+	if (!oh)
+		return;
+
+	__omap_dm_timer_stop(&clkev, 1, clkev.rate);
+	omap_hwmod_idle(oh);
+}
+
+static void omap_clkevt_resume(struct clock_event_device *unused)
+{
+	char name[10];
+	struct omap_hwmod *oh;
+
+	sprintf(name, "timer%d", clkev.id);

arch/arm/mach-omap2/timer.c static void omap2_gp_timer_set_mode(enum clock_event_mode mode, · line 160

Quoted 10 lines
arch/arm/mach-omap2/timer.c +10 -0
+	oh = omap_hwmod_lookup(name);
+	if (!oh)
+		return;
+
+	omap_hwmod_enable(oh);
+	__omap_dm_timer_load_start(&clkev,
+			OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR, 0, 1);
+	__omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);
+}
+

Am still bit uncomfortable with direct hwmod usage in the suspend/resmue hooks.

Quoted 6 lines
Quoted 2 lines

Jon, Any alternatives you can think of ?

Jon,

Any suggestions here?

Sorry completed this missed this!

Unfortunately, I don't have any good suggestions here. However, I am wondering if the suspend/resume handlers can just be calls to omap_hwmod_idle/enable and we can remove these __omap_dm_timer_xxxx calls (I don't think they are needed). Furthermore, my understanding is this is only needed for AM335x (per the changelog), and so we should not add suspend/resume handlers for all OMAP2+ based devices.

Cheers
Jon

#36 lore raw

RFC v2 12/18 ARM: OMAP2+: timer: Add suspend-resume callbacks for clockevent device

Quoted 83 lines · Jon Hunter
Quoted 74 lines · Vaibhav
Quoted 73 lines · Santosh
Quoted 59 lines · Vaibhav Bedia

The current OMAP timer code registers two timers - one as clocksource and one as clockevent. AM33XX has only one usable timer in the WKUP domain so one of the timers needs suspend-resume support to restore the configuration to pre-suspend state.

commit adc78e6 (timekeeping: Add suspend and resume of clock event devices) introduced .suspend and .resume callbacks for clock event devices. Leverages these callbacks to have AM33XX clockevent timer which is in not in WKUP domain to behave properly across system suspend.

Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>
Cc Jon Hunter <[email protected]>

---
v1->v2:
Get rid of harcoded timer id.
Note: since a platform device is not created for these timer instances and because there's very minimal change needed for restarting the timer a full blown context save and restore has been skipped.

   arch/arm/mach-omap2/timer.c |   33 +++++++++++++++++++++++++++++++++
   1 files changed, 33 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/timer.c +20 -0
index 691aa67..38f9cbc 100644
@@ -128,6 +128,36 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
   	}
   }
 
+static void omap_clkevt_suspend(struct clock_event_device *unused)
+{
+	char name[10];
+	struct omap_hwmod *oh;
+
+	sprintf(name, "timer%d", clkev.id);
+	oh = omap_hwmod_lookup(name);
+	if (!oh)
+		return;
+
+	__omap_dm_timer_stop(&clkev, 1, clkev.rate);
+	omap_hwmod_idle(oh);
+}
+
+static void omap_clkevt_resume(struct clock_event_device *unused)
+{
+	char name[10];
+	struct omap_hwmod *oh;
+
+	sprintf(name, "timer%d", clkev.id);

arch/arm/mach-omap2/timer.c static void omap2_gp_timer_set_mode(enum clock_event_mode mode, · line 160

Quoted 10 lines
arch/arm/mach-omap2/timer.c +10 -0
+	oh = omap_hwmod_lookup(name);
+	if (!oh)
+		return;
+
+	omap_hwmod_enable(oh);
+	__omap_dm_timer_load_start(&clkev,
+			OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR, 0, 1);
+	__omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);
+}
+

Am still bit uncomfortable with direct hwmod usage in the suspend/resmue hooks.

Quoted 6 lines
Quoted 2 lines

Jon, Any alternatives you can think of ?

Jon,

Any suggestions here?

Quoted 9 lines

Sorry completed this missed this!

Unfortunately, I don't have any good suggestions here. However, I am wondering if the suspend/resume handlers can just be calls to omap_hwmod_idle/enable and we can remove these __omap_dm_timer_xxxx calls (I don't think they are needed). Furthermore, my understanding is this is only needed for AM335x (per the changelog), and so we should not add suspend/resume handlers for all OMAP2+ based devices.

I agree with the direction.

Regards,
Santosh

#37 lore raw

RFC v2 12/18 ARM: OMAP2+: timer: Add suspend-resume callbacks for clockevent device

Quoted 86 lines · Santosh
Quoted 85 lines · Jon Hunter
Quoted 74 lines · Vaibhav
Quoted 73 lines · Santosh
Quoted 59 lines · Vaibhav Bedia

The current OMAP timer code registers two timers - one as clocksource and one as clockevent. AM33XX has only one usable timer in the WKUP domain so one of the timers needs suspend-resume support to restore the configuration to pre-suspend state.

commit adc78e6 (timekeeping: Add suspend and resume of clock event devices) introduced .suspend and .resume callbacks for clock event devices. Leverages these callbacks to have AM33XX clockevent timer which is in not in WKUP domain to behave properly across system suspend.

Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>
Cc Jon Hunter <[email protected]>

---
v1->v2:
Get rid of harcoded timer id.
Note: since a platform device is not created for these timer instances and because there's very minimal change needed for restarting the timer a full blown context save and restore has been skipped.

   arch/arm/mach-omap2/timer.c |   33 +++++++++++++++++++++++++++++++++
   1 files changed, 33 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/timer.c +20 -0
index 691aa67..38f9cbc 100644
@@ -128,6 +128,36 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
   	}
   }
 
+static void omap_clkevt_suspend(struct clock_event_device *unused)
+{
+	char name[10];
+	struct omap_hwmod *oh;
+
+	sprintf(name, "timer%d", clkev.id);
+	oh = omap_hwmod_lookup(name);
+	if (!oh)
+		return;
+
+	__omap_dm_timer_stop(&clkev, 1, clkev.rate);
+	omap_hwmod_idle(oh);
+}
+
+static void omap_clkevt_resume(struct clock_event_device *unused)
+{
+	char name[10];
+	struct omap_hwmod *oh;
+
+	sprintf(name, "timer%d", clkev.id);

arch/arm/mach-omap2/timer.c static void omap2_gp_timer_set_mode(enum clock_event_mode mode, · line 160

Quoted 10 lines
arch/arm/mach-omap2/timer.c +10 -0
+	oh = omap_hwmod_lookup(name);
+	if (!oh)
+		return;
+
+	omap_hwmod_enable(oh);
+	__omap_dm_timer_load_start(&clkev,
+			OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR, 0, 1);
+	__omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);
+}
+

Am still bit uncomfortable with direct hwmod usage in the suspend/resmue hooks.

Quoted 6 lines
Quoted 2 lines

Jon, Any alternatives you can think of ?

Jon,

Any suggestions here?

Sorry completed this missed this!

Quoted 9 lines
Quoted 7 lines

Unfortunately, I don't have any good suggestions here. However, I am wondering if the suspend/resume handlers can just be calls to omap_hwmod_idle/enable and we can remove these __omap_dm_timer_xxxx calls (I don't think they are needed). Furthermore, my understanding is this is only needed for AM335x (per the changelog), and so we should not add suspend/resume handlers for all OMAP2+ based devices.

I agree with the direction.

I need to retain the call to enable the interrupt but the others can be dropped. Will take care of this and the comment on invoking the suspend/resume handlers only for AM335x in the next version.

Regards,
Vaibhav

#38 lore raw

RFC v2 12/18 ARM: OMAP2+: timer: Add suspend-resume callbacks for clockevent device

Quoted 94 lines · Vaibhav
Quoted 93 lines · Santosh
Quoted 92 lines · Jon Hunter
Quoted 74 lines · Vaibhav
Quoted 73 lines · Santosh
Quoted 59 lines · Vaibhav Bedia

The current OMAP timer code registers two timers - one as clocksource and one as clockevent. AM33XX has only one usable timer in the WKUP domain so one of the timers needs suspend-resume support to restore the configuration to pre-suspend state.

commit adc78e6 (timekeeping: Add suspend and resume of clock event devices) introduced .suspend and .resume callbacks for clock event devices. Leverages these callbacks to have AM33XX clockevent timer which is in not in WKUP domain to behave properly across system suspend.

Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>
Cc Jon Hunter <[email protected]>

---
v1->v2:
Get rid of harcoded timer id.
Note: since a platform device is not created for these timer instances and because there's very minimal change needed for restarting the timer a full blown context save and restore has been skipped.

   arch/arm/mach-omap2/timer.c |   33 +++++++++++++++++++++++++++++++++
   1 files changed, 33 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/timer.c +20 -0
index 691aa67..38f9cbc 100644
@@ -128,6 +128,36 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
   	}
   }
 
+static void omap_clkevt_suspend(struct clock_event_device *unused)
+{
+	char name[10];
+	struct omap_hwmod *oh;
+
+	sprintf(name, "timer%d", clkev.id);
+	oh = omap_hwmod_lookup(name);
+	if (!oh)
+		return;
+
+	__omap_dm_timer_stop(&clkev, 1, clkev.rate);
+	omap_hwmod_idle(oh);
+}
+
+static void omap_clkevt_resume(struct clock_event_device *unused)
+{
+	char name[10];
+	struct omap_hwmod *oh;
+
+	sprintf(name, "timer%d", clkev.id);

arch/arm/mach-omap2/timer.c static void omap2_gp_timer_set_mode(enum clock_event_mode mode, · line 160

Quoted 10 lines
arch/arm/mach-omap2/timer.c +10 -0
+	oh = omap_hwmod_lookup(name);
+	if (!oh)
+		return;
+
+	omap_hwmod_enable(oh);
+	__omap_dm_timer_load_start(&clkev,
+			OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR, 0, 1);
+	__omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);
+}
+

Am still bit uncomfortable with direct hwmod usage in the suspend/resmue hooks.

Quoted 6 lines
Quoted 2 lines

Jon, Any alternatives you can think of ?

Jon,

Any suggestions here?

Sorry completed this missed this!

Unfortunately, I don't have any good suggestions here. However, I am wondering if the suspend/resume handlers can just be calls to omap_hwmod_idle/enable and we can remove these __omap_dm_timer_xxxx calls (I don't think they are needed). Furthermore, my understanding is this is only needed for AM335x (per the changelog), and so we should not add suspend/resume handlers for all OMAP2+ based devices.

Quoted 6 lines
Quoted 2 lines

I agree with the direction.

I need to retain the call to enable the interrupt but the others can be dropped. Will take care of this and the comment on invoking the suspend/resume handlers only for AM335x in the next version.

Ok fair enough. By the way, I posted a patch today [1] that will use the hwmod name as the clockevent timer name. Care to try on top of that patch and then we can eliminate the sprintf.

Cheers
Jon

[1] http://www.spinics.net/lists/arm-kernel/msg221262.html

#39 lore raw

RFC v2 12/18 ARM: OMAP2+: timer: Add suspend-resume callbacks for clockevent device

Quoted 5 lines · Jon

Ok fair enough. By the way, I posted a patch today [1] that will use the hwmod name as the clockevent timer name. Care to try on top of that patch and then we can eliminate the sprintf.

Thanks. Will try it out later today.

Regards,
Vaibhav

#40 lore raw

RFC v2 12/18 ARM: OMAP2+: timer: Add suspend-resume callbacks for clockevent device

In reply to Vaibhav Bedia
Quoted 40 lines · Vaibhav Bedia

The current OMAP timer code registers two timers - one as clocksource and one as clockevent. AM33XX has only one usable timer in the WKUP domain so one of the timers needs suspend-resume support to restore the configuration to pre-suspend state.

commit adc78e6 (timekeeping: Add suspend and resume of clock event devices) introduced .suspend and .resume callbacks for clock event devices. Leverages these callbacks to have AM33XX clockevent timer which is in not in WKUP domain to behave properly across system suspend.

Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>
Cc Jon Hunter <[email protected]>

---
v1->v2:
Get rid of harcoded timer id.
Note: since a platform device is not created for these timer instances and because there's very minimal change needed for restarting the timer a full blown context save and restore has been skipped.

 arch/arm/mach-omap2/timer.c |   33 +++++++++++++++++++++++++++++++++
 1 files changed, 33 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/timer.c +1 -0
index 691aa67..38f9cbc 100644
@@ -128,6 +128,36 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
 	}
 }
 
+static void omap_clkevt_suspend(struct clock_event_device *unused)

arch/arm/mach-omap2/timer.c static void omap2_gp_timer_set_mode(enum clock_event_mode mode, · line 141

Quoted 10 lines
arch/arm/mach-omap2/timer.c +10 -0
+{
+	char name[10];
+	struct omap_hwmod *oh;
+
+	sprintf(name, "timer%d", clkev.id);
+	oh = omap_hwmod_lookup(name);
+	if (!oh)
+		return;
+
+	__omap_dm_timer_stop(&clkev, 1, clkev.rate);

I am not sure you need to call __omap_dm_timer_stop() here. This should have already been called as timekeeping_suspend() will call omap2_gp_timer_set_mode() to shutdown the timer.

Quoted 7 lines
arch/arm/mach-omap2/timer.c +7 -0
+	omap_hwmod_idle(oh);
+}
+
+static void omap_clkevt_resume(struct clock_event_device *unused)
+{
+	char name[10];
+	struct omap_hwmod *oh;

arch/arm/mach-omap2/timer.c static void omap2_gp_timer_set_mode(enum clock_event_mode mode, · line 158

Quoted 10 lines
arch/arm/mach-omap2/timer.c +10 -0
+
+	sprintf(name, "timer%d", clkev.id);
+	oh = omap_hwmod_lookup(name);
+	if (!oh)
+		return;
+
+	omap_hwmod_enable(oh);
+	__omap_dm_timer_load_start(&clkev,
+			OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR, 0, 1);
+	__omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);

Similarly here, I am not sure these __omap_dm_timer_xxxx calls are needed.

arch/arm/mach-omap2/timer.c static struct clock_event_device clockevent_gpt = { · line 169

Quoted 11 lines
arch/arm/mach-omap2/timer.c +4 -0
+}
+
 static struct clock_event_device clockevent_gpt = {
 	.name		= "gp_timer",
 	.features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
@@ -135,6 +165,8 @@ static struct clock_event_device clockevent_gpt = {
 	.rating		= 300,
 	.set_next_event	= omap2_gp_timer_set_next_event,
 	.set_mode	= omap2_gp_timer_set_mode,
+	.suspend	= omap_clkevt_suspend,
+	.resume		= omap_clkevt_resume,

AFAIK, this is only applicable for AM335x devices and so should not be added for all.

arch/arm/mach-omap2/timer.c static void __init omap2_gp_clockevent_init(int gptimer_id, · line 358

Quoted 8 lines
arch/arm/mach-omap2/timer.c +1 -0
 };
 
 static struct property device_disabled = {
@@ -323,6 +355,7 @@ static void __init omap2_gp_clockevent_init(int gptimer_id,
 	int res;
 
 	clkev.errata = omap_dm_timer_get_errata();
+	clkev.id = gptimer_id;

We should not use gptimer_id anymore. This will go away once the migration to dev-tree is completed. You may be better off storing the oh_name in the clock_event_device name field and passing to the suspend/resume handlers.

Cheers
Jon

#41 lore raw

RFC v2 12/18 ARM: OMAP2+: timer: Add suspend-resume callbacks for clockevent device

Hi Jon,

Quoted 41 lines · Jon
Quoted 39 lines · Vaibhav Bedia

The current OMAP timer code registers two timers - one as clocksource and one as clockevent. AM33XX has only one usable timer in the WKUP domain so one of the timers needs suspend-resume support to restore the configuration to pre-suspend state.

commit adc78e6 (timekeeping: Add suspend and resume of clock event devices) introduced .suspend and .resume callbacks for clock event devices. Leverages these callbacks to have AM33XX clockevent timer which is in not in WKUP domain to behave properly across system suspend.

Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>
Cc Jon Hunter <[email protected]>

---
v1->v2:
Get rid of harcoded timer id.
Note: since a platform device is not created for these timer instances and because there's very minimal change needed for restarting the timer a full blown context save and restore has been skipped.

 arch/arm/mach-omap2/timer.c |   33 +++++++++++++++++++++++++++++++++
 1 files changed, 33 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/timer.c +0 -0
index 691aa67..38f9cbc 100644
@@ -128,6 +128,36 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
 	}
 }
 
Quoted 15 lines

arch/arm/mach-omap2/timer.c static void omap2_gp_timer_set_mode(enum clock_event_mode mode, · line 141

Quoted 11 lines
arch/arm/mach-omap2/timer.c +11 -0
+static void omap_clkevt_suspend(struct clock_event_device *unused)
+{
+	char name[10];
+	struct omap_hwmod *oh;
+
+	sprintf(name, "timer%d", clkev.id);
+	oh = omap_hwmod_lookup(name);
+	if (!oh)
+		return;
+
+	__omap_dm_timer_stop(&clkev, 1, clkev.rate);

I am not sure you need to call __omap_dm_timer_stop() here. This should have already been called as timekeeping_suspend() will call omap2_gp_timer_set_mode() to shutdown the timer.

You are right, i can drop the call to __omap_dm_timer_stop().

Quoted 12 lines
Quoted 12 lines
arch/arm/mach-omap2/timer.c +11 -0
+	omap_hwmod_idle(oh);
+}
+
+static void omap_clkevt_resume(struct clock_event_device *unused)
+{
+	char name[10];
+	struct omap_hwmod *oh;
+
+	sprintf(name, "timer%d", clkev.id);
+	oh = omap_hwmod_lookup(name);
+	if (!oh)
Quoted 8 lines

arch/arm/mach-omap2/timer.c static void omap2_gp_timer_set_mode(enum clock_event_mode mode, · line 158

Quoted 6 lines
arch/arm/mach-omap2/timer.c +6 -0
+		return;
+
+	omap_hwmod_enable(oh);
+	__omap_dm_timer_load_start(&clkev,
+			OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR, 0, 1);
+	__omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);

Similarly here, I am not sure these __omap_dm_timer_xxxx calls are needed.

I went through the code again. __omap_dm_timer_load_start() is invoked from omap2_gp_timer_set_mode() with the auto-reload flag when setting the mode to CLOCK_EVT_MODE_PERIODIC and without the auto-reload flag in omap2_gp_timer_set_next_event(). So looks like this call can be dropped. But I do need the call to __omap_dm_timer_int_enable() to re-enable the interrupts from the timer.

Quoted 16 lines

arch/arm/mach-omap2/timer.c static struct clock_event_device clockevent_gpt = { · line 169

Quoted 12 lines
arch/arm/mach-omap2/timer.c +4 -0
+}
+
 static struct clock_event_device clockevent_gpt = {
 	.name		= "gp_timer",
 	.features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
@@ -135,6 +165,8 @@ static struct clock_event_device clockevent_gpt = {
 	.rating		= 300,
 	.set_next_event	= omap2_gp_timer_set_next_event,
 	.set_mode	= omap2_gp_timer_set_mode,
+	.suspend	= omap_clkevt_suspend,
+	.resume		= omap_clkevt_resume,

AFAIK, this is only applicable for AM335x devices and so should not be added for all.

Agreed. Will address this in the next version.

Quoted 6 lines
Quoted 6 lines
arch/arm/mach-omap2/timer.c +0 -0
 };
 
 static struct property device_disabled = {
@@ -323,6 +355,7 @@ static void __init omap2_gp_clockevent_init(int gptimer_id,
 	int res;
 
Quoted 8 lines

arch/arm/mach-omap2/timer.c static void __init omap2_gp_clockevent_init(int gptimer_id, · line 358

Quoted 2 lines
arch/arm/mach-omap2/timer.c +1 -0
 	clkev.errata = omap_dm_timer_get_errata();
+	clkev.id = gptimer_id;

We should not use gptimer_id anymore. This will go away once the migration to dev-tree is completed. You may be better off storing the oh_name in the clock_event_device name field and passing to the suspend/resume handlers.

Currently the name field in clock_event_device is set to "gp_timer". Should I set the name in omap2_gp_clockevent_init() based on the gptimer_id?

Regards,
Vaibhav

#42 lore raw

RFC v2 13/18 ARM: OMAP2+: AM33XX: timer: Interchance clkevt and clksrc timers

In reply to Vaibhav Bedia

AM33XX has two timers (DTIMER0/1) in the WKUP domain. On GP devices the source of DMTIMER0 is fixed to an inaccurate internal 32k RC oscillator and this makes the DMTIMER0 practically either as a clocksource or as clockevent.

Currently the timer instance in WKUP domain is used as the clockevent and the timer in non-WKUP domain as the clocksource. DMTIMER1 in WKUP domain can keep running in suspend from a 32K clock fed from external OSC and can serve as the persistent clock for the kernel. To enable this, interchange the timers used as clocksource and clockevent for AM33XX.

For now a new DT property has been added to allow the timer code to select the timer with the right property.

It has been pointed out by Santosh Shilimkar and Kevin Hilman that such a change will result in soc-idle never being achieved on AM33XX. There are other reasons why soc-idle does not look feasible on AM33XX so for now we go ahead with the interchange of the the timers. If at a later point of time we do come up with an approach which makes soc-idle possible on AM33XX, this can be revisited.

Cc Tony Lingren <[email protected]>
Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Jon Hunter <[email protected]>

---
v1->v2:
Use DT properties for changing the timers

 .../devicetree/bindings/arm/omap/timer.txt         |    2 ++
 arch/arm/boot/dts/am33xx.dtsi                      |    1 +
 arch/arm/mach-omap2/timer.c                        |    6 +++---
 3 files changed, 6 insertions(+), 3 deletions(-)
Documentation/devicetree/bindings/arm/omap/timer.txt +2 -0
index 8732d4d..62d4f2c 100644
@@ -18,6 +18,8 @@ Optional properties:
 - ti,timer-pwm: 	Indicates the timer can generate a PWM output.
 - ti,timer-secure: 	Indicates the timer is reserved on a secure OMAP device
 			and therefore cannot be used by the kernel.
+- ti,timer-non-wkup	Indicates the timer is in non-wkup power domain and hence
+			will lose register context when the power domain transitions
 
 Example:
 
arch/arm/boot/dts/am33xx.dtsi +1 -0
index 4731748..b4e8bf7 100644
@@ -251,6 +251,7 @@
 			reg = <0x48040000 0x400>;
 			interrupts = <68>;
 			ti,hwmods = "timer2";
+			ti,timer-non-wkup;
 		};
 
 		timer3: timer at 48042000 {
arch/arm/mach-omap2/timer.c +3 -3
index 38f9cbc..cfb3413 100644
@@ -264,7 +264,7 @@ static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer,
 	int r = 0;
 
 	if (of_have_populated_dt()) {
-		np = omap_get_timer_dt(omap_timer_match, NULL);
+		np = omap_get_timer_dt(omap_timer_match, property);
 		if (!np)
 			return -ENODEV;
 
@@ -633,8 +633,8 @@ OMAP_SYS_TIMER(3_gp, gptimer);
 #endif /* CONFIG_ARCH_OMAP3 */
 
 #ifdef CONFIG_SOC_AM33XX
-OMAP_SYS_GP_TIMER_INIT(3_am33xx, 1, OMAP4_MPU_SOURCE, "ti,timer-alwon",
-		       2, OMAP4_MPU_SOURCE);
+OMAP_SYS_GP_TIMER_INIT(3_am33xx, 2, OMAP4_MPU_SOURCE, "ti,timer-non-wkup",
+		       1, OMAP4_MPU_SOURCE);
 OMAP_SYS_TIMER(3_am33xx, gptimer);
 #endif /* CONFIG_SOC_AM33XX */
 
Signature · 1 line
1.7.0.4
#43 lore raw

RFC v2 13/18 ARM: OMAP2+: AM33XX: timer: Interchance clkevt and clksrc timers

Quoted 17 lines · Vaibhav Bedia

AM33XX has two timers (DTIMER0/1) in the WKUP domain. On GP devices the source of DMTIMER0 is fixed to an inaccurate internal 32k RC oscillator and this makes the DMTIMER0 practically either as a clocksource or as clockevent.

Currently the timer instance in WKUP domain is used as the clockevent and the timer in non-WKUP domain as the clocksource. DMTIMER1 in WKUP domain can keep running in suspend from a 32K clock fed from external OSC and can serve as the persistent clock for the kernel. To enable this, interchange the timers used as clocksource and clockevent for AM33XX.

For now a new DT property has been added to allow the timer code to select the timer with the right property.

Quoted 8 lines

It has been pointed out by Santosh Shilimkar and Kevin Hilman that such a change will result in soc-idle never being achieved on AM33XX. There are other reasons why soc-idle does not look feasible on AM33XX so for now we go ahead with the interchange of the the timers. If at a later point of time we do come up with an approach which makes soc-idle possible on AM33XX, this can be revisited.

Can you please explain other reasons as well for clarity ?

Regards
Santosh

#44 lore raw

RFC v2 13/18 ARM: OMAP2+: AM33XX: timer: Interchance clkevt and clksrc timers

Quoted 26 lines · Santosh
Quoted 17 lines · Vaibhav Bedia

AM33XX has two timers (DTIMER0/1) in the WKUP domain. On GP devices the source of DMTIMER0 is fixed to an inaccurate internal 32k RC oscillator and this makes the DMTIMER0 practically either as a clocksource or as clockevent.

Currently the timer instance in WKUP domain is used as the clockevent and the timer in non-WKUP domain as the clocksource. DMTIMER1 in WKUP domain can keep running in suspend from a 32K clock fed from external OSC and can serve as the persistent clock for the kernel. To enable this, interchange the timers used as clocksource and clockevent for AM33XX.

For now a new DT property has been added to allow the timer code to select the timer with the right property.

Quoted 8 lines

It has been pointed out by Santosh Shilimkar and Kevin Hilman that such a change will result in soc-idle never being achieved on AM33XX. There are other reasons why soc-idle does not look feasible on AM33XX so for now we go ahead with the interchange of the the timers. If at a later point of time we do come up with an approach which makes soc-idle possible on AM33XX, this can be revisited.

Quoted 2 lines

Can you please explain other reasons as well for clarity ?

I guess from h/w perspective it boils down to lack of HW-AUTO and IO-Daisy chaining on AM33xx [1].

Since there's no 32ksynctimer, do we also need to register DMTIMER1 as the persistent clock on AM33xx? This can only be done from DMTIMER1 which is currently serving as the clockevent.

Regards,
Vaibhav

[1] http://marc.info/?l=linux-arm-kernel&m=135238055717206&w=4

#45 lore raw

RFC v2 13/18 ARM: OMAP2+: AM33XX: timer: Interchance clkevt and clksrc timers

In reply to Vaibhav Bedia
Quoted 48 lines · Vaibhav Bedia

AM33XX has two timers (DTIMER0/1) in the WKUP domain. On GP devices the source of DMTIMER0 is fixed to an inaccurate internal 32k RC oscillator and this makes the DMTIMER0 practically either as a clocksource or as clockevent.

Currently the timer instance in WKUP domain is used as the clockevent and the timer in non-WKUP domain as the clocksource. DMTIMER1 in WKUP domain can keep running in suspend from a 32K clock fed from external OSC and can serve as the persistent clock for the kernel. To enable this, interchange the timers used as clocksource and clockevent for AM33XX.

For now a new DT property has been added to allow the timer code to select the timer with the right property.

It has been pointed out by Santosh Shilimkar and Kevin Hilman that such a change will result in soc-idle never being achieved on AM33XX. There are other reasons why soc-idle does not look feasible on AM33XX so for now we go ahead with the interchange of the the timers. If at a later point of time we do come up with an approach which makes soc-idle possible on AM33XX, this can be revisited.

Cc Tony Lingren <[email protected]>
Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Jon Hunter <[email protected]>

---
v1->v2:
Use DT properties for changing the timers

 .../devicetree/bindings/arm/omap/timer.txt         |    2 ++
 arch/arm/boot/dts/am33xx.dtsi                      |    1 +
 arch/arm/mach-omap2/timer.c                        |    6 +++---
 3 files changed, 6 insertions(+), 3 deletions(-)
Documentation/devicetree/bindings/arm/omap/timer.txt +0 -0
index 8732d4d..62d4f2c 100644

Documentation/devicetree/bindings/arm/omap/timer.txt Optional properties: · line 22

Quoted 5 lines
Documentation/devicetree/bindings/arm/omap/timer.txt +2 -0
@@ -18,6 +18,8 @@ Optional properties:
 - ti,timer-pwm: 	Indicates the timer can generate a PWM output.
 - ti,timer-secure: 	Indicates the timer is reserved on a secure OMAP device
 			and therefore cannot be used by the kernel.
+- ti,timer-non-wkup	Indicates the timer is in non-wkup power domain and hence
+			will lose register context when the power domain transitions

I was hoping that we could avoid adding another property, especially given that his equivalent to a timer that does not have the "ti,timer-alwon" property.

arch/arm/boot/dts/am33xx.dtsi line 254

Quoted 11 lines
Documentation/devicetree/bindings/arm/omap/timer.txt +0 -0
 Example:
 
arch/arm/boot/dts/am33xx.dtsi +1 -0
index 4731748..b4e8bf7 100644
@@ -251,6 +251,7 @@
 			reg = <0x48040000 0x400>;
 			interrupts = <68>;
 			ti,hwmods = "timer2";
+			ti,timer-non-wkup;

Is this is only one not in the wake-up domain?

Quoted 17 lines
arch/arm/boot/dts/am33xx.dtsi +0 -0
 		};
 
 		timer3: timer at 48042000 {
arch/arm/mach-omap2/timer.c +1 -1
index 38f9cbc..cfb3413 100644
@@ -264,7 +264,7 @@ static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer,
 	int r = 0;
 
 	if (of_have_populated_dt()) {
-		np = omap_get_timer_dt(omap_timer_match, NULL);
+		np = omap_get_timer_dt(omap_timer_match, property);
 		if (!np)
 			return -ENODEV;
 

arch/arm/mach-omap2/timer.c OMAP_SYS_TIMER(3_gp, gptimer); · line 637

Quoted 7 lines
arch/arm/mach-omap2/timer.c +2 -2
@@ -633,8 +633,8 @@ OMAP_SYS_TIMER(3_gp, gptimer);
 #endif /* CONFIG_ARCH_OMAP3 */
 
 #ifdef CONFIG_SOC_AM33XX
-OMAP_SYS_GP_TIMER_INIT(3_am33xx, 1, OMAP4_MPU_SOURCE, "ti,timer-alwon",
-		       2, OMAP4_MPU_SOURCE);
+OMAP_SYS_GP_TIMER_INIT(3_am33xx, 2, OMAP4_MPU_SOURCE, "ti,timer-non-wkup",
+		       1, OMAP4_MPU_SOURCE);

It seems to me here that we should specify the property "ti,timer-alwon" for the clocksource and then no property for the clockevent. Hence, may be the code needs to be adjusted so that if clockevent or clocksource can use any timer (ie. no property specified), we look for a timer that has no "ti-timer-xxxx" properties. This will ensure that if we need a particular timer for clocksource, which we look for after clockevent, it will be available.

Cheers
Jon

#46 lore raw

RFC v2 13/18 ARM: OMAP2+: AM33XX: timer: Interchance clkevt and clksrc timers

In reply to Vaibhav Bedia

Hi Vaibhav,

Quoted 6 lines · Vaibhav Bedia

AM33XX has two timers (DTIMER0/1) in the WKUP domain. On GP devices the source of DMTIMER0 is fixed to an inaccurate internal 32k RC oscillator and this makes the DMTIMER0 practically either as a clocksource or as clockevent.

Quoted 7 lines

Currently the timer instance in WKUP domain is used as the clockevent and the timer in non-WKUP domain as the clocksource. DMTIMER1 in WKUP domain can keep running in suspend from a 32K clock fed from external OSC and can serve as the persistent clock for the kernel. To enable this, interchange the timers used as clocksource and clockevent for AM33XX.

I have been thinking about this some more. In the case where we are using gptimers for both clock-events and clock-source (on both AM33xx and OMAP) and I am wondering if it makes sense to switch the timers so that we use the always-on timer for clock-source and a different one from clock-events.

For OMAP, if we are not using the 32k-sync for clock-source, then we are never going to achieve low power states during idle as we will always have one gptimer running. And in this case, to your point below, it would be better to use the always-on for clock-source so that in suspend we can at least hit low power states and maintain time.

Quoted 10 lines

For now a new DT property has been added to allow the timer code to select the timer with the right property.

It has been pointed out by Santosh Shilimkar and Kevin Hilman that such a change will result in soc-idle never being achieved on AM33XX. There are other reasons why soc-idle does not look feasible on AM33XX so for now we go ahead with the interchange of the the timers. If at a later point of time we do come up with an approach which makes soc-idle possible on AM33XX, this can be revisited.

Right, but this would also be true for OMAP if we don't use the 32k-sync as we only have one gptimer in the wake-up domain.

Cheers
Jon

#47 lore raw

RFC v2 13/18 ARM: OMAP2+: AM33XX: timer: Interchance clkevt and clksrc timers

Quoted 23 lines · Jon Hunter

Hi Vaibhav,

Quoted 6 lines · Vaibhav Bedia

AM33XX has two timers (DTIMER0/1) in the WKUP domain. On GP devices the source of DMTIMER0 is fixed to an inaccurate internal 32k RC oscillator and this makes the DMTIMER0 practically either as a clocksource or as clockevent.

Quoted 7 lines

Currently the timer instance in WKUP domain is used as the clockevent and the timer in non-WKUP domain as the clocksource. DMTIMER1 in WKUP domain can keep running in suspend from a 32K clock fed from external OSC and can serve as the persistent clock for the kernel. To enable this, interchange the timers used as clocksource and clockevent for AM33XX.

I have been thinking about this some more. In the case where we are using gptimers for both clock-events and clock-source (on both AM33xx and OMAP) and I am wondering if it makes sense to switch the timers so that we use the always-on timer for clock-source and a different one from clock-events.

Quoted 5 lines

For OMAP, if we are not using the 32k-sync for clock-source, then we are never going to achieve low power states during idle as we will always have one gptimer running. And in this case, to your point below, it would be better to use the always-on for clock-source so that in suspend we can at least hit low power states and maintain time.

I have posted a patch today [1] that I hope will address this issue for you. Can you give that a try?

Cheers
Jon

[1] http://www.spinics.net/lists/arm-kernel/msg221265.html

#48 lore raw

RFC v2 13/18 ARM: OMAP2+: AM33XX: timer: Interchance clkevt and clksrc timers

Quoted 25 lines · Jon Hunter
Quoted 23 lines · Jon Hunter

Hi Vaibhav,

Quoted 6 lines · Vaibhav Bedia

AM33XX has two timers (DTIMER0/1) in the WKUP domain. On GP devices the source of DMTIMER0 is fixed to an inaccurate internal 32k RC oscillator and this makes the DMTIMER0 practically either as a clocksource or as clockevent.

Quoted 7 lines

Currently the timer instance in WKUP domain is used as the clockevent and the timer in non-WKUP domain as the clocksource. DMTIMER1 in WKUP domain can keep running in suspend from a 32K clock fed from external OSC and can serve as the persistent clock for the kernel. To enable this, interchange the timers used as clocksource and clockevent for AM33XX.

I have been thinking about this some more. In the case where we are using gptimers for both clock-events and clock-source (on both AM33xx and OMAP) and I am wondering if it makes sense to switch the timers so that we use the always-on timer for clock-source and a different one from clock-events.

Quoted 8 lines
Quoted 5 lines

For OMAP, if we are not using the 32k-sync for clock-source, then we are never going to achieve low power states during idle as we will always have one gptimer running. And in this case, to your point below, it would be better to use the always-on for clock-source so that in suspend we can at least hit low power states and maintain time.

I have posted a patch today [1] that I hope will address this issue for you. Can you give that a try?

By the way, this need to be applied on top of the fix I sent yesterday to pass the property.

Cheers
Jon

#49 lore raw

RFC v2 13/18 ARM: OMAP2+: AM33XX: timer: Interchance clkevt and clksrc timers

Quoted 4 lines · Jon

By the way, this need to be applied on top of the fix I sent yesterday to pass the property.

Ok. Thanks for pointing this out.

#50 lore raw

RFC v2 14/18 ARM: OMAP2+: AM33XX: control: Add some control module registers and APIs

In reply to Vaibhav Bedia

Add minimal APIs for writing to the IPC and the M3_TXEV registers in the Control module. These will be used in a subsequent patch which adds suspend-resume support for AM33XX.

Cc Tony Lingren <[email protected]>
Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>

---
v1->v2:
This a new patch in the series to isolate the control module accesses as pointed out by Kevin Hilman.

 arch/arm/mach-omap2/control.c |   20 ++++++++++++++++++++
 arch/arm/mach-omap2/control.h |   41 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/control.c +20 -0
index 2adb268..c5d54ae 100644
@@ -604,3 +604,23 @@ int omap3_ctrl_save_padconf(void)
 }
 
 #endif /* CONFIG_ARCH_OMAP3 && CONFIG_PM */
+
+#if defined(CONFIG_SOC_AM33XX) && defined(CONFIG_PM)
+void am33xx_txev_eoi(void)
+{
+	omap_ctrl_writel(AM33XX_M3_TXEV_ACK, AM33XX_CONTROL_M3_TXEV_EOI);
+}
+
+void am33xx_txev_enable(void)
+{
+	omap_ctrl_writel(AM33XX_M3_TXEV_ENABLE, AM33XX_CONTROL_M3_TXEV_EOI);
+}
+
+void am33xx_wkup_m3_ipc_cmd(struct am33xx_ipc_data *data)
+{
+	omap_ctrl_writel(data->resume_addr, AM33XX_CONTROL_IPC_MSG_REG0);
+	omap_ctrl_writel(data->sleep_mode, AM33XX_CONTROL_IPC_MSG_REG1);
+	omap_ctrl_writel(data->param1, AM33XX_CONTROL_IPC_MSG_REG2);
+	omap_ctrl_writel(data->param2, AM33XX_CONTROL_IPC_MSG_REG3);
+}
+#endif /* CONFIG_SOC_AM33XX && CONFIG_PM */
arch/arm/mach-omap2/control.h +41 -0

53 lines not on this page · raw message

Signature · 1 line
1.7.0.4
#51 lore raw

RFC v2 14/18 ARM: OMAP2+: AM33XX: control: Add some control module registers and APIs

Quoted 4 lines · Vaibhav Bedia

Add minimal APIs for writing to the IPC and the M3_TXEV registers in the Control module. These will be used in a subsequent patch which adds suspend-resume support for AM33XX.

Quoted 8 lines
Cc Tony Lingren <[email protected]>
Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>

---

On Control module, we are trying to move driver/module specific code to respective drivers. Can you not add below code to ipc related driver component.

If not, then patch as such is fine with me.

Regards
Santosh

#52 lore raw

RFC v2 14/18 ARM: OMAP2+: AM33XX: control: Add some control module registers and APIs

Quoted 13 lines · Santosh
Quoted 4 lines · Vaibhav Bedia

Add minimal APIs for writing to the IPC and the M3_TXEV registers in the Control module. These will be used in a subsequent patch which adds suspend-resume support for AM33XX.

Quoted 8 lines
Cc Tony Lingren <[email protected]>
Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>

---

Quoted 6 lines

On Control module, we are trying to move driver/module specific code to respective drivers. Can you not add below code to ipc related driver component.

If not, then patch as such is fine with me.

I had it in the pm33xx.c in the previous version. Kevin had asked me to move it to control.c. Should I revert move it back there?

Regards,
Vaibhav

#53 lore raw

RFC v2 14/18 ARM: OMAP2+: AM33XX: control: Add some control module registers and APIs

Quoted 18 lines · Vaibhav
Quoted 17 lines · Santosh
Quoted 4 lines · Vaibhav Bedia

Add minimal APIs for writing to the IPC and the M3_TXEV registers in the Control module. These will be used in a subsequent patch which adds suspend-resume support for AM33XX.

Quoted 8 lines
Cc Tony Lingren <[email protected]>
Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>

---

On Control module, we are trying to move driver/module specific code to respective drivers. Can you not add below code to ipc related driver component.

Quoted 6 lines
Quoted 2 lines

If not, then patch as such is fine with me.

I had it in the pm33xx.c in the previous version. Kevin had asked me to move it to control.c. Should I revert move it back there?

pm33xx.c is not the right place. I was hoping to move to some driver. If that is not possible then leave it in control module.

Regards
Santosh

#54 lore raw

RFC v2 14/18 ARM: OMAP2+: AM33XX: control: Add some control module registers and APIs

Quoted 19 lines · Santosh
Quoted 18 lines · Vaibhav
Quoted 17 lines · Santosh
Quoted 4 lines · Vaibhav Bedia

Add minimal APIs for writing to the IPC and the M3_TXEV registers in the Control module. These will be used in a subsequent patch which adds suspend-resume support for AM33XX.

Quoted 8 lines
Cc Tony Lingren <[email protected]>
Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>

---

On Control module, we are trying to move driver/module specific code to respective drivers. Can you not add below code to ipc related driver component.

Quoted 9 lines
Quoted 6 lines
Quoted 2 lines

If not, then patch as such is fine with me.

I had it in the pm33xx.c in the previous version. Kevin had asked me to move it to control.c. Should I revert move it back there?

pm33xx.c is not the right place. I was hoping to move to some driver. If that is not possible then leave it in control module.

I think I'll leave it here for now. Could I have you ack in that case?

Thanks,
Vaibhav

#55 lore raw

RFC v2 14/18 ARM: OMAP2+: AM33XX: control: Add some control module registers and APIs

Quoted 23 lines · Vaibhav
Quoted 22 lines · Santosh
Quoted 21 lines · Vaibhav
Quoted 19 lines · Santosh
Quoted 4 lines · Vaibhav Bedia

Add minimal APIs for writing to the IPC and the M3_TXEV registers in the Control module. These will be used in a subsequent patch which adds suspend-resume support for AM33XX.

Quoted 8 lines
Cc Tony Lingren <[email protected]>
Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
Cc Vaibhav Hiremath <[email protected]>

---

On Control module, we are trying to move driver/module specific code to respective drivers. Can you not add below code to ipc related driver component.

If not, then patch as such is fine with me.

Quoted 9 lines
Quoted 6 lines
Quoted 3 lines

I had it in the pm33xx.c in the previous version. Kevin had asked me to move it to control.c. Should I revert move it back there?

pm33xx.c is not the right place. I was hoping to move to some driver. If that is not possible then leave it in control module.

I think I'll leave it here for now. Could I have you ack in that case?

Yep.

#56 lore raw

RFC v2 15/18 ARM: OMAP2+: AM33XX: Add assembly code for PM operations

In reply to Vaibhav Bedia

In preparation for suspend-resume support for AM33XX, add the assembly file with the code which is copied to internal memory (OCMC RAM) during bootup and runs from there.

As part of the low power entry (DeepSleep0 mode in AM33XX TRM), the code running from OCMC RAM does the following
1. Stores the EMIF configuration
2. Puts external memory in self-refresh
3. Disables EMIF clock
4. Puts the PLLs in bypass
5. Executes WFI after writing to MPU_CLKCTRL register.

If no interrupts have come, WFI execution on MPU gets registered as an interrupt with the WKUP-M3. WKUP-M3 takes care of disabling some clocks which MPU should not (L3, L4, OCMC RAM etc) and takes care of clockdomain and powerdomain transitions as part of the DeepSleep0 mode entry.

In case a late interrupt comes in, WFI ends up as a NOP and MPU continues execution from internal memory. The 'abort path' code undoes whatever was done as part of the low power entry and indicates a suspend failure by passing a non-zero value to the cpu_resume routine.

The 'resume path' code is similar to the 'abort path' with the key difference of MMU being enabled in the 'abort path' but being disabled in the 'resume path' due to MPU getting powered off.

In addition to the top level steps outlined above, there are some additional register writes related to external memory controller which help in lowering the overall power consumption in the suspended state. These include changing the state of the IOs to LVCMOS mode and enabling pull downs on the IOs to reduce leakage in low power state.

Cc Santosh Shilimkar <[email protected]>
Cc Kevin Hilman <[email protected]>

---
v1->v2:
This is a new patch in the series to keep the assembly code additionl separate as mentioned by Kevin Hilman

 arch/arm/mach-omap2/sleep33xx.S |  584 +++++++++++++++++++++++++++++++++++++++
 1 files changed, 584 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-omap2/sleep33xx.S
arch/arm/mach-omap2/sleep33xx.S new file +584 -0
new file mode 100644
index 0000000..98fa76c
@@ -0,0 +1,584 @@
+/*
+ * Low level suspend code for AM33XX SoCs
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ * Vaibhav Bedia <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/linkage.h>
+#include <linux/ti_emif.h>
+#include <asm/memory.h>
+#include <asm/assembler.h>
+
+#include "cm33xx.h"
+#include "pm33xx.h"
+#include "prm33xx.h"
+#include "control.h"
+
+	.text
+	.align 3
+
+	.macro	pll_bypass, name, clk_mode_addr, idlest_addr, pll_mode
+pll_bypass_\name:
+	ldr	r0, \clk_mode_addr
+	ldr	r1, [r0]
+	str	r1, clk_mode_\pll_mode
+	bic	r1, r1, #(7 << 0)
+	orr	r1, r1, #0x5
+	str	r1, [r0]
+	ldr	r0, \idlest_addr
+wait_pll_bypass_\name:
+	ldr	r1, [r0]
+	tst	r1, #0x0
+	bne	wait_pll_bypass_\name
+	.endm
+
+	.macro	pll_lock, name, clk_mode_addr, idlest_addr, pll_mode
+pll_lock_\name:
+	ldr	r0, \clk_mode_addr
+	ldr	r1, clk_mode_\pll_mode
+	str	r1, [r0]
+	and	r1, r1, #0x7
+	cmp	r1, #0x7
+	bne	pll_mode_restored_\name
+	ldr	r0, \idlest_addr
+wait_pll_lock_\name:
+	ldr	r1, [r0]
+	ands	r1, #0x1
+	beq	wait_pll_lock_\name
+pll_mode_restored_\name:
+	nop
+	.endm
+
+	.macro	ddr_self_refresh, num
+ddr_self_refresh_\num:
+	add	r1, r0, #EMIF_POWER_MANAGEMENT_CONTROL
+	ldr	r2, [r1]
+	orr	r2, r2, #0xa0		@ a reasonable delay for entering SR
+	str	r2, [r1, #0]
+	str	r2, [r1, #4]		@ write to shadow register also
+
+	ldr	r2, ddr_start		@ do a dummy access to DDR
+	ldr	r3, [r2, #0]
+	ldr	r3, [r1, #0]		@ now set the LP MODE to Self-Refresh
+	orr	r3, r3, #0x200
+	str	r3, [r1, #0]
+
+	mov	r1, #0x1000		@ Give some time for system to enter SR
+wait_sr_\num:
+	subs	r1, r1, #1
+	bne	wait_sr_\num
+	.endm
+
+	.macro	wait_sdram_config, num
+wait_sdram_config_\num:
+	mov	r0, #0x100
+wait_sc_\num:
+	subs	r0, r0 ,#1
+	bne	wait_sc_\num
+	.endm
+
+ENTRY(am33xx_do_wfi)
+	stmfd	sp!, {r4 - r11, lr}	@ save registers on stack
+	/* Get the EMIF virtual address */
+	ldr	r0, emif_addr_func
+	blx	r0
+	/* Save it for later use */
+	str	r0, emif_addr_virt
+
+	/* This ensures isb */
+	ldr	r0, dcache_flush
+	blx	r0
+
+	/* Same as v7_flush_icache_all - saving a branch */
+	mov	r0, #0
+	mcr	p15, 0, r0, c7, c5, 0	@ I+BTB cache invalidate
+
+	ldr	r0, emif_addr_virt
+	/* Save EMIF configuration */
+	ldr	r1, [r0, #EMIF_SDRAM_CONFIG]
+	str	r1, emif_sdcfg_val
+	ldr	r1, [r0, #EMIF_SDRAM_REFRESH_CONTROL]
+	str	r1, emif_ref_ctrl_val
+	ldr	r1, [r0, #EMIF_SDRAM_TIMING_1]
+	str	r1, emif_timing1_val
+	ldr	r1, [r0, #EMIF_SDRAM_TIMING_2]
+	str	r1, emif_timing2_val
+	ldr	r1, [r0, #EMIF_SDRAM_TIMING_3]
+	str	r1, emif_timing3_val
+	ldr	r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
+	str	r1, emif_pmcr_val
+	ldr	r1, [r0, #EMIF_POWER_MANAGEMENT_CTRL_SHDW]
+	str	r1, emif_pmcr_shdw_val
+	ldr	r1, [r0, #EMIF_SDRAM_OUTPUT_IMPEDANCE_CALIBRATION_CONFIG]
+	str	r1, emif_zqcfg_val
+	ldr	r1, [r0, #EMIF_DDR_PHY_CTRL_1]
+	str	r1, emif_rd_lat_val
+
+	/* Ensure that all the writes to DDR leave the A8 */
+	dsb
+	dmb
+	isb
+
+	ddr_self_refresh	1
+
+	/* Disable EMIF at this point */
+	ldr	r1, virt_emif_clkctrl
+	ldr	r2, [r1]
+	bic	r2, r2, #(3 << 0)
+	str	r2, [r1]
+
+	ldr	r1, virt_emif_clkctrl
+wait_emif_disable:
+	ldr	r2, [r1]
+	ldr	r3, module_disabled_val
+	cmp	r2, r3
+	bne	wait_emif_disable
+
+	/*
+	 * For the MPU WFI to be registered as an interrupt
+	 * to WKUP_M3, MPU_CLKCTRL.MODULEMODE needs to be set
+	 * to DISABLED
+	 */
+	ldr	r1, virt_mpu_clkctrl
+	ldr	r2, [r1]
+	bic	r2, r2, #(3 << 0)
+	str	r2, [r1]
+
+	/* DDR3 reset override and mDDR mode selection */
+	ldr	r0, virt_ddr_io_ctrl
+	mov	r1, #(0x9 << 28)
+	str	r1, [r0]
+
+	/* Weak pull down for DQ, DM */
+	ldr	r1, virt_ddr_data0_ioctrl
+	ldr	r2, susp_io_pull_data
+	str	r2, [r1]
+
+	ldr	r1, virt_ddr_data1_ioctrl
+	ldr	r2, susp_io_pull_data
+	str	r2, [r1]
+
+	/* Disable VTP */
+	ldr	r1, virt_ddr_vtp_ctrl
+	ldr	r2, susp_vtp_ctrl_val
+	str	r2, [r1]
+
+	/* Enable SRAM LDO ret mode */
+	ldr	r0, virt_sram_ldo_addr
+	ldr	r1, [r0]
+	orr	r1, #1
+	str	r1, [r0]
+
+put_pll_bypass:
+	/* Put the PLLs in bypass mode */
+	pll_bypass	core, virt_core_clk_mode, virt_core_idlest, core_val
+	pll_bypass	ddr, virt_ddr_clk_mode, virt_ddr_idlest, ddr_val
+	pll_bypass	disp, virt_disp_clk_mode, virt_disp_idlest, disp_val
+	pll_bypass	per, virt_per_clk_mode, virt_per_idlest, per_val
+	pll_bypass	mpu, virt_mpu_clk_mode, virt_mpu_idlest, mpu_val
+
+	dsb
+	dmb
+	isb
+
+	wfi
+
+	/* NOPs to ensure the A8 pipeline is clean */
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+
+	/* We come here in case of an abort due to a late interrupt */
+
+	/* Set MPU_CLKCTRL.MODULEMODE back to ENABLE */
+	ldr	r1, virt_mpu_clkctrl
+	mov	r2, #0x2
+	str	r2, [r1]
+
+	/* Relock the PLLs */
+	pll_lock	mpu_abt, virt_mpu_clk_mode, virt_mpu_idlest, mpu_val
+	pll_lock	per_abt, virt_per_clk_mode, virt_per_idlest, per_val
+	pll_lock	disp_abt, virt_disp_clk_mode, virt_disp_idlest, disp_val
+	pll_lock	ddr_abt, virt_ddr_clk_mode, virt_ddr_idlest, ddr_val
+	pll_lock	core_abt, virt_core_clk_mode, virt_core_idlest, core_val
+
+	/* Disable SRAM LDO ret mode */
+	ldr	r0, virt_sram_ldo_addr
+	ldr	r1, [r0]
+	bic	r1, #1
+	str	r1, [r0]
+
+	/* Restore the pull for DQ, DM */
+	ldr	r1, virt_ddr_data0_ioctrl
+	ldr	r2, resume_io_pull_data
+	str	r2, [r1]
+
+	ldr	r1, virt_ddr_data1_ioctrl
+	ldr	r2, resume_io_pull_data
+	str	r2, [r1]
+
+	/* Enable EMIF */
+	ldr	r1, virt_emif_clkctrl
+	mov	r2, #0x2
+	str	r2, [r1]
+wait_emif_enable:
+	ldr	r3, [r1]
+	cmp	r2, r3
+	bne	wait_emif_enable
+
+	/* Enable VTP */
+config_vtp_abt:
+	ldr	r0, virt_ddr_vtp_ctrl
+	ldr	r1, [r0]
+	mov	r2, #0x0	@ clear the register
+	str	r2, [r0]
+	mov	r2, #0x6	@ write the filter value
+	str	r2, [r0]
+
+	ldr	r1, [r0]
+	ldr	r2, vtp_enable	@ set the enable bit
+	orr	r2, r2, r1
+	str	r2, [r0]
+
+	ldr	r1, [r0]	@ toggle the CLRZ bit
+	bic	r1, #1
+	str	r1, [r0]
+
+	ldr	r1, [r0]
+	orr	r1, #1
+	str	r1, [r0]
+
+poll_vtp_ready_abt:
+	ldr	r1, [r0]	@ poll for VTP ready
+	tst	r1, #(AM33XX_VTP_CTRL_READY)
+	beq	poll_vtp_ready_abt
+
+	/* DDR3 reset override and mDDR mode clear */
+	ldr	r0, virt_ddr_io_ctrl
+	mov	r1, #0
+	str	r1, [r0]
+
+emif_self_refresh_dis:
+	/* Disable EMIF self-refresh */
+	ldr	r0, emif_addr_virt
+	add	r0, r0, #EMIF_POWER_MANAGEMENT_CONTROL
+	ldr	r1, [r0]
+	bic	r1, r1, #LP_MODE_MASK
+	str	r1, [r0]
+	str	r1, [r0, #4]
+
+	/*
+	 * A write to SDRAM CONFIG register triggers
+	 * an init sequence and hence it must be done
+	 * at the end
+	 */
+	ldr r0, emif_addr_virt
+	add r0, r0, #EMIF_SDRAM_CONFIG
+	ldr r4, emif_sdcfg_val
+	str r4, [r0]
+
+	mov r0, #0x1000
+wait_abt:
+	subs   r0, r0, #1
+	bne wait_abt
+
+	/* Let the suspend code know about the abort */
+	mov	r0, #1
+	ldmfd	sp!, {r4 - r11, pc}	@ restore regs and return
+ENDPROC(am33xx_do_wfi)
+
+	.align
+ENTRY(am33xx_resume_offset)
+	.word . - am33xx_do_wfi
+
+ENTRY(am33xx_resume_from_deep_sleep)
+	/* Take the PLLs out of LP_BYPASS */
+	pll_lock	mpu, phys_mpu_clk_mode, phys_mpu_idlest, mpu_val
+	pll_lock	per, phys_per_clk_mode, phys_per_idlest, per_val
+	pll_lock	disp, phys_disp_clk_mode, phys_disp_idlest, disp_val
+	pll_lock	ddr, phys_ddr_clk_mode, phys_ddr_idlest, ddr_val
+	pll_lock	core, phys_core_clk_mode, phys_core_idlest, core_val
+
+	/* Disable SRAM LDO ret mode */
+	ldr	r0, phys_sram_ldo_addr
+	ldr	r1, [r0]
+	bic	r1, #1
+	str	r1, [r0]
+
+	/* Restore the pull for DQ, DM */
+	ldr	r1, phys_ddr_data0_ioctrl
+	ldr	r2, resume_io_pull_data
+	str	r2, [r1]
+
+	ldr	r1, phys_ddr_data1_ioctrl
+	ldr	r2, resume_io_pull_data
+	str	r2, [r1]
+
+config_vtp:
+	ldr	r0, phys_ddr_vtp_ctrl
+	ldr	r1, [r0]
+	mov	r2, #0x0	@ clear the register
+	str	r2, [r0]
+	mov	r2, #0x6	@ write the filter value
+	str	r2, [r0]
+
+	ldr	r1, [r0]
+	ldr	r2, vtp_enable	@ set the enable bit
+	orr	r2, r2, r1
+	str	r2, [r0]
+
+	ldr	r1, [r0]	@ toggle the CLRZ bit
+	bic	r1, #1
+	str	r1, [r0]
+
+	ldr	r1, [r0]
+	orr	r1, #1
+	str	r1, [r0]
+
+poll_vtp_ready:
+	ldr	r1, [r0]	@ poll for VTP ready
+	tst	r1, #AM33XX_VTP_CTRL_READY
+	beq	poll_vtp_ready
+
+	/* DDR3 reset override and mDDR mode clear */
+	ldr	r0, phys_ddr_io_ctrl
+	mov	r1, #0
+	str	r1, [r0]
+
+	/* Enable EMIF */
+	ldr	r1, phys_emif_clkctrl
+	mov	r2, #0x2
+	str	r2, [r1]
+wait_emif_enable1:
+	ldr	r3, [r1]
+	cmp	r2, r3
+	bne	wait_emif_enable1
+
+config_emif_timings:
+	ldr	r3, emif_phys_addr
+	ldr	r4, emif_rd_lat_val
+	str	r4, [r3, #EMIF_DDR_PHY_CTRL_1]
+	str	r4, [r3, #EMIF_DDR_PHY_CTRL_1_SHDW]
+	ldr	r4, emif_timing1_val
+	str	r4, [r3, #EMIF_SDRAM_TIMING_1]
+	str	r4, [r3, #EMIF_SDRAM_TIMING_1_SHDW]
+	ldr	r4, emif_timing2_val
+	str	r4, [r3, #EMIF_SDRAM_TIMING_2]
+	str	r4, [r3, #EMIF_SDRAM_TIMING_2_SHDW]
+	ldr	r4, emif_timing3_val
+	str	r4, [r3, #EMIF_SDRAM_TIMING_3]
+	str	r4, [r3, #EMIF_SDRAM_TIMING_3_SHDW]
+	ldr	r4, emif_ref_ctrl_val
+	str	r4, [r3, #EMIF_SDRAM_REFRESH_CONTROL]
+	str	r4, [r3, #EMIF_SDRAM_REFRESH_CTRL_SHDW]
+	ldr	r4, emif_pmcr_val
+	str	r4, [r3, #EMIF_POWER_MANAGEMENT_CONTROL]
+	ldr	r4, emif_pmcr_shdw_val
+	str	r4, [r3, #EMIF_POWER_MANAGEMENT_CTRL_SHDW]
+
+	/*
+	 * A write to SDRAM CONFIG register triggers
+	 * an init sequence and hence it must be done
+	 *@the end
+	 */
+	ldr	r4, emif_sdcfg_val
+	str	r4, [r3, #EMIF_SDRAM_CONFIG]
+
+	/* Back from la-la-land. Kill some time for sanity to settle in */
+	mov	r0, #0x1000
+wait_resume:
+	subs	r0, r0, #1
+	bne	wait_resume
+
+	/* We are back. Branch to the common CPU resume routine */
+	mov	r0, #0
+	ldr	pc, resume_addr
+ENDPROC(am33xx_resume_from_deep_sleep)
+
+
+/*
+ * Local variables
+ */
+	.align
+resume_addr:
+	.word	cpu_resume - PAGE_OFFSET + 0x80000000
+dcache_flush:
+	.word   v7_flush_dcache_all
+emif_addr_func:
+	.word	am33xx_get_emif_base
+ddr_start:
+	.word	PAGE_OFFSET
+emif_phys_addr:
+	.word	AM33XX_EMIF_BASE
+virt_mpu_idlest:
+	.word	AM33XX_CM_IDLEST_DPLL_MPU
+virt_mpu_clk_mode:
+	.word	AM33XX_CM_CLKMODE_DPLL_MPU
+phys_mpu_clk_mode:
+	.word	(AM33XX_CM_BASE + AM33XX_CM_WKUP_MOD + \
+		AM33XX_CM_CLKMODE_DPLL_MPU_OFFSET)
+phys_mpu_idlest:
+	.word	(AM33XX_CM_BASE + AM33XX_CM_WKUP_MOD + \
+		AM33XX_CM_IDLEST_DPLL_MPU_OFFSET)
+virt_core_idlest:
+	.word	AM33XX_CM_IDLEST_DPLL_CORE
+virt_core_clk_mode:
+	.word	AM33XX_CM_CLKMODE_DPLL_CORE
+phys_core_clk_mode:
+	.word	(AM33XX_CM_BASE + AM33XX_CM_WKUP_MOD + \
+		AM33XX_CM_CLKMODE_DPLL_CORE_OFFSET)
+phys_core_idlest:
+	.word	(AM33XX_CM_BASE + AM33XX_CM_WKUP_MOD + \
+		AM33XX_CM_IDLEST_DPLL_CORE_OFFSET)
+virt_per_idlest:
+	.word	AM33XX_CM_IDLEST_DPLL_PER
+virt_per_clk_mode:
+	.word	AM33XX_CM_CLKMODE_DPLL_PER
+phys_per_clk_mode:
+	.word	(AM33XX_CM_BASE + AM33XX_CM_WKUP_MOD + \
+		AM33XX_CM_CLKMODE_DPLL_PER_OFFSET)
+phys_per_idlest:
+	.word	(AM33XX_CM_BASE + AM33XX_CM_WKUP_MOD + \
+		AM33XX_CM_IDLEST_DPLL_PER_OFFSET)
+virt_disp_idlest:
+	.word	AM33XX_CM_IDLEST_DPLL_DISP
+virt_disp_clk_mode:
+	.word	AM33XX_CM_CLKMODE_DPLL_DISP
+phys_disp_clk_mode:
+	.word	(AM33XX_CM_BASE + AM33XX_CM_WKUP_MOD + \
+		AM33XX_CM_CLKMODE_DPLL_DISP_OFFSET)
+phys_disp_idlest:
+	.word	(AM33XX_CM_BASE + AM33XX_CM_WKUP_MOD + \
+		AM33XX_CM_IDLEST_DPLL_DISP_OFFSET)
+virt_ddr_idlest:
+	.word	AM33XX_CM_IDLEST_DPLL_DDR
+virt_ddr_clk_mode:
+	.word	AM33XX_CM_CLKMODE_DPLL_DDR
+phys_ddr_clk_mode:
+	.word	(AM33XX_CM_BASE + AM33XX_CM_WKUP_MOD + \
+		AM33XX_CM_CLKMODE_DPLL_DDR_OFFSET)
+phys_ddr_idlest:
+	.word	(AM33XX_CM_BASE + AM33XX_CM_WKUP_MOD + \
+		AM33XX_CM_IDLEST_DPLL_DDR_OFFSET)
+virt_sram_ldo_addr:
+	.word	AM33XX_PRM_LDO_SRAM_MPU_CTRL
+phys_sram_ldo_addr:
+	.word	(AM33XX_PRM_BASE + AM33XX_PRM_DEVICE_MOD + \
+		AM33XX_PRM_LDO_SRAM_MPU_CTRL_OFFSET)
+virt_mpu_clkctrl:
+	.word	AM33XX_CM_MPU_MPU_CLKCTRL
+virt_emif_clkctrl:
+	.word	AM33XX_CM_PER_EMIF_CLKCTRL
+phys_emif_clkctrl:
+	.word	(AM33XX_CM_BASE + AM33XX_CM_PER_MOD + \
+		AM33XX_CM_PER_EMIF_CLKCTRL_OFFSET)
+module_disabled_val:
+	.word	0x30000
+
+/* DDR related defines */
+virt_ddr_io_ctrl:
+	.word	AM33XX_CTRL_REGADDR(AM33XX_DDR_IO_CTRL)
+phys_ddr_io_ctrl:
+	.word	AM33XX_CTRL_BASE + AM33XX_DDR_IO_CTRL
+virt_ddr_vtp_ctrl:
+	.word	AM33XX_CTRL_REGADDR(AM33XX_VTP0_CTRL_REG)
+phys_ddr_vtp_ctrl:
+	.word	AM33XX_CTRL_BASE + AM33XX_VTP0_CTRL_REG
+virt_ddr_cmd0_ioctrl:
+	.word	AM33XX_CTRL_REGADDR(AM33XX_DDR_CMD0_IOCTRL)
+phys_ddr_cmd0_ioctrl:
+	.word	AM33XX_CTRL_BASE + AM33XX_DDR_CMD0_IOCTRL
+virt_ddr_cmd1_ioctrl:
+	.word	AM33XX_CTRL_REGADDR(AM33XX_DDR_CMD1_IOCTRL)
+phys_ddr_cmd1_ioctrl:
+	.word	AM33XX_CTRL_BASE + AM33XX_DDR_CMD1_IOCTRL
+virt_ddr_cmd2_ioctrl:
+	.word	AM33XX_CTRL_REGADDR(AM33XX_DDR_CMD2_IOCTRL)
+phys_ddr_cmd2_ioctrl:
+	.word	AM33XX_CTRL_BASE + AM33XX_DDR_CMD2_IOCTRL
+virt_ddr_data0_ioctrl:
+	.word	AM33XX_CTRL_REGADDR(AM33XX_DDR_DATA0_IOCTRL)
+phys_ddr_data0_ioctrl:
+	.word	AM33XX_CTRL_BASE + AM33XX_DDR_DATA0_IOCTRL
+virt_ddr_data1_ioctrl:
+	.word	AM33XX_CTRL_REGADDR(AM33XX_DDR_DATA1_IOCTRL)
+phys_ddr_data1_ioctrl:
+	.word	AM33XX_CTRL_BASE + AM33XX_DDR_DATA1_IOCTRL
+vtp_enable:
+	.word	AM33XX_VTP_CTRL_ENABLE
+
+/*
+ * Values recommended by the HW team. These change the pulls
+ * on certain IOs of DATA and CMD macros
+ */
+susp_io_pull_data:
+	.word	0x3FF00003
+susp_io_pull_cmd1:
+	.word   0xFFE0018B
+susp_io_pull_cmd2:
+	.word   0xFFA0098B
+resume_io_pull_data:
+	.word	0x18B
+resume_io_pull_cmd:
+	.word	0x18B
+susp_vtp_ctrl_val:
+	.word	0x10117
+
+/* Placeholder for storing EMIF configuration */
+emif_addr_virt:
+	.word	0xDEADBEEF
+emif_rd_lat_val:
+	.word	0xDEADBEEF
+emif_timing1_val:
+	.word	0xDEADBEEF
+emif_timing2_val:
+	.word	0xDEADBEEF
+emif_timing3_val:
+	.word	0xDEADBEEF
+emif_sdcfg_val:
+	.word	0xDEADBEEF
+emif_ref_ctrl_val:
+	.word	0xDEADBEEF
+emif_zqcfg_val:
+	.word	0xDEADBEEF
+emif_pmcr_val:
+	.word	0xDEADBEEF
+emif_pmcr_shdw_val:
+	.word	0xDEADBEEF
+
+/* Placeholder for storing PLL mode */
+clk_mode_mpu_val:
+	.word	0xDEADBEEF
+clk_mode_per_val:
+	.word	0xDEADBEEF
+clk_mode_disp_val:
+	.word	0xDEADBEEF
+clk_mode_ddr_val:
+	.word	0xDEADBEEF
+clk_mode_core_val:
+	.word	0xDEADBEEF
+
+	.align 3
+ENTRY(am33xx_do_wfi_sz)
+	.word	. - am33xx_do_wfi
+
Signature · 1 line
1.7.0.4
#57 lore raw

RFC v2 16/18 ARM: OMAP2+: AM33XX: Basic suspend resume support

In reply to Vaibhav Bedia

AM335x supports various low power modes as documented in section 8.1.4.3 of the AM335x TRM which is available @ http://www.ti.com/litv/pdf/spruh73f

DeepSleep0 mode offers the lowest power mode with limited wakeup sources without a system reboot and is mapped as the suspend state in the kernel. In this state, MPU and PER domains are turned off with the internal RAM held in retention to facilitate resume process. As part of the boot process, the assembly code is copied over to OCMCRAM using the OMAP SRAM code.

AM335x has a Cortex-M3 (WKUP_M3) which assists the MPU in DeepSleep0 entry and exit. WKUP_M3 takes care of the clockdomain and powerdomain transitions based on the intended low power state. MPU needs to load the appropriate WKUP_M3 binary onto the WKUP_M3 memory space before it can leverage any of the PM features like DeepSleep.

The IPC mechanism between MPU and WKUP_M3 uses a mailbox sub-module and 8 IPC registers in the Control module. MPU uses the assigned Mailbox for issuing an interrupt to WKUP_M3 which then goes and checks the IPC registers for the payload. WKUP_M3 has the ability to trigger on interrupt to MPU by executing the "sev" instruction.

In the current implementation when the suspend process is initiated MPU interrupts the WKUP_M3 to let it know about the intent of entering DeepSleep0 and waits for an ACK. When the ACK is received MPU continues with its suspend process to suspend all the drivers and then jumps to assembly in OCMC RAM. The assembly code puts the PLLs in bypass, puts the external RAM in self-refresh mode and then finally execute the WFI instruction. Execution of the WFI instruction triggers another interrupt to the WKUP_M3 which then continues wiht the power down sequence wherein the clockdomain and powerdomain transition takes place. As part of the sleep sequence, WKUP_M3 unmasks the interrupt lines for the wakeup sources. WFI execution on WKUP_M3 causes the hardware to disable the main oscillator of the SoC.

When a wakeup event occurs, WKUP_M3 starts the power-up sequence by switching on the power domains and finally enabling the clock to MPU. Since the MPU gets powered down as part of the sleep sequence in the resume path ROM code starts executing. The ROM code detects a wakeup from sleep and then jumps to the resume location in OCMC which was populated in one of the IPC registers as part of the suspend sequence.

The low level code in OCMC relocks the PLLs, enables access to external RAM and then jumps to the cpu_resume code of the kernel to finish the resume process.

Cc Tony Lingren <[email protected]>
Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>

---
v1->v2:
Move assembly code addition, control module access and hookup in OMAP PM framework in separate patches. Address other comments from Kevin Hilman and Santosh Shilimkar on v1. The discussion on v1 is present @
http://lists.infradead.org/pipermail/linux-arm-kernel/2012-November/129979.html
Note: The mailbox change will need slight rework once the driver is finalized.

 arch/arm/mach-omap2/pm33xx.c |  469 ++++++++++++++++++++++++++++++++++++++++++
 arch/arm/mach-omap2/pm33xx.h |   56 +++++
 2 files changed, 525 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-omap2/pm33xx.c
 create mode 100644 arch/arm/mach-omap2/pm33xx.h
arch/arm/mach-omap2/pm33xx.c new file +469 -0
new file mode 100644
index 0000000..aaa4daa
@@ -0,0 +1,469 @@
+/*
+ * AM33XX Power Management Routines
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ * Vaibhav Bedia <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/suspend.h>
+#include <linux/completion.h>
+#include <linux/module.h>
+#include <linux/mailbox.h>
+#include <linux/interrupt.h>
+
+#include <asm/suspend.h>
+#include <asm/proc-fns.h>
+#include <asm/sizes.h>
+#include <asm/fncpy.h>
+#include <asm/system_misc.h>
+
+#include "pm.h"
+#include "cm33xx.h"
+#include "pm33xx.h"
+#include "control.h"
+#include "clockdomain.h"
+#include "powerdomain.h"
+#include "omap_hwmod.h"
+#include "omap_device.h"
+#include "soc.h"
+#include "sram.h"
+
+void (*am33xx_do_wfi_sram)(void);
+
+static void __iomem *am33xx_emif_base;
+static struct powerdomain *cefuse_pwrdm, *gfx_pwrdm, *per_pwrdm;
+static struct clockdomain *gfx_l4ls_clkdm;
+static struct omap_hwmod *usb_oh, *cpsw_oh, *tptc0_oh, *tptc1_oh, *tptc2_oh;
+static struct wkup_m3_context *wkup_m3;
+
+static DECLARE_COMPLETION(wkup_m3_sync);
+
+#ifdef CONFIG_SUSPEND
+static int am33xx_do_sram_idle(long unsigned int unused)
+{
+	am33xx_do_wfi_sram();
+	return 0;
+}
+
+static int am33xx_pm_suspend(void)
+{
+	int status, ret = 0;
+
+	/*
+	 * By default the following IPs do not have MSTANDBY asserted
+	 * which is necessary for PER domain transition. If the drivers
+	 * are not compiled into the kernel HWMOD code will not change the
+	 * state of the IPs if the IP was not never enabled. To ensure
+	 * that there no issues with or without the drivers being compiled
+	 * in the kernel, we forcefully put these IPs to idle.
+	 */
+	omap_hwmod_enable(usb_oh);
+	omap_hwmod_enable(tptc0_oh);
+	omap_hwmod_enable(tptc1_oh);
+	omap_hwmod_enable(tptc2_oh);
+	omap_hwmod_enable(cpsw_oh);
+
+	omap_hwmod_idle(usb_oh);
+	omap_hwmod_idle(tptc0_oh);
+	omap_hwmod_idle(tptc1_oh);
+	omap_hwmod_idle(tptc2_oh);
+	omap_hwmod_idle(cpsw_oh);
+
+	/* Try to put GFX to sleep */
+	pwrdm_set_next_fpwrst(gfx_pwrdm, PWRDM_FUNC_PWRST_OFF);
+
+	ret = cpu_suspend(0, am33xx_do_sram_idle);
+
+	status = pwrdm_read_fpwrst(gfx_pwrdm);
+	if (status != PWRDM_FUNC_PWRST_OFF)
+		pr_err("GFX domain did not transition\n");
+	else
+		pr_info("GFX domain entered low power state\n");
+
+	/*
+	 * GFX_L4LS clock domain needs to be woken up to
+	 * ensure thet L4LS clock domain does not get stuck in transition
+	 * If that happens L3 module does not get disabled, thereby leading
+	 * to PER power domain transition failing
+	 *
+	 * The clock framework should take care of ensuring
+	 * that the clock domain is in the right state when
+	 * GFX driver is active.
+	 */
+	clkdm_wakeup(gfx_l4ls_clkdm);
+	clkdm_sleep(gfx_l4ls_clkdm);
+
+	if (ret) {
+		pr_err("Kernel suspend failure\n");
+	} else {
+		status = omap_ctrl_readl(AM33XX_CONTROL_IPC_MSG_REG1);
+		status &= IPC_RESP_MASK;
+		status >>= __ffs(IPC_RESP_MASK);
+
+		switch (status) {
+		case 0:
+			pr_info("Successfully put all powerdomains to target state\n");
+			/*
+			 * XXX: Leads to loss of logic state in PER power domain
+			 * Use SOC specific ops for this?
+			 */
+			break;
+		case 1:
+			pr_err("Could not transition all powerdomains to target state\n");
+			ret = -1;
+			break;
+		default:
+			pr_err("Something went wrong :(\nStatus = %d\n",
+				status);
+			ret = -1;
+		}
+	}
+
+	return ret;
+}
+
+static int am33xx_pm_enter(suspend_state_t suspend_state)
+{
+	int ret = 0;
+
+	switch (suspend_state) {
+	case PM_SUSPEND_STANDBY:
+	case PM_SUSPEND_MEM:
+		ret = am33xx_pm_suspend();
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int am33xx_pm_begin(suspend_state_t state)
+{
+	int ret = 0;
+	struct mailbox_msg dummy_msg;
+
+	disable_hlt();
+
+	MAILBOX_FILL_HEADER_MSG(dummy_msg, 0xABCDABCD);
+
+	wkup_m3->ipc_data.sleep_mode = IPC_CMD_DS0;
+	wkup_m3->ipc_data.param1  = DS_IPC_DEFAULT;
+	wkup_m3->ipc_data.param2  = DS_IPC_DEFAULT;
+
+	am33xx_wkup_m3_ipc_cmd(&wkup_m3->ipc_data);
+
+	wkup_m3->state = M3_STATE_MSG_FOR_LP;
+
+	mailbox_enable_irq(wkup_m3->mbox, IRQ_RX);
+
+	ret = mailbox_msg_send(wkup_m3->mbox, &dummy_msg);
+	if (ret) {
+		pr_err("A8<->CM3 MSG for LP failed\n");
+		am33xx_m3_state_machine_reset();
+		ret = -1;
+	}
+
+	/* Give some time to M3 to respond. 500msec is a random value here */
+	if (!wait_for_completion_timeout(&wkup_m3_sync,
+					msecs_to_jiffies(500))) {
+		pr_err("A8<->CM3 sync failure\n");
+		am33xx_m3_state_machine_reset();
+		ret = -1;
+	} else {
+		pr_debug("Message sent for entering DeepSleep mode\n");
+		mailbox_disable_irq(wkup_m3->mbox, IRQ_RX);
+	}
+
+	return ret;
+}
+
+static void am33xx_pm_end(void)
+{
+	mailbox_enable_irq(wkup_m3->mbox, IRQ_RX);
+
+	am33xx_m3_state_machine_reset();
+
+	enable_hlt();
+
+	return;
+}
+
+static const struct platform_suspend_ops am33xx_pm_ops = {
+	.begin		= am33xx_pm_begin,
+	.end		= am33xx_pm_end,
+	.enter		= am33xx_pm_enter,
+	.valid		= suspend_valid_only_mem,
+};
+
+static void am33xx_m3_state_machine_reset(void)
+{
+	int ret = 0;
+	struct mailbox_msg dummy_msg;
+
+	MAILBOX_FILL_HEADER_MSG(dummy_msg, 0xABCDABCD);
+
+	wkup_m3->ipc_data.sleep_mode	= IPC_CMD_RESET;
+	wkup_m3->ipc_data.param1	= DS_IPC_DEFAULT;
+	wkup_m3->ipc_data.param2	= DS_IPC_DEFAULT;
+
+	am33xx_wkup_m3_ipc_cmd(&wkup_m3->ipc_data);
+
+	wkup_m3->state = M3_STATE_MSG_FOR_RESET;
+
+	ret = mailbox_msg_send(wkup_m3->mbox, &dummy_msg);
+	if (!ret) {
+		pr_debug("Message sent for resetting M3 state machine\n");
+		/* Give some to M3 to respond. 500msec is a random value here */
+		if (!wait_for_completion_timeout(&wkup_m3_sync,
+						msecs_to_jiffies(500)))
+			pr_err("A8<->CM3 sync failure\n");
+	} else {
+		pr_err("Could not reset M3 state machine!!!\n");
+		wkup_m3->state = M3_STATE_UNKNOWN;
+	}
+}
+#endif /* CONFIG_SUSPEND */
+
+/*
+ * Dummy notifier for the mailbox
+ * XXX: Get rid of this requirement once the MBX driver has been finalized
+ */
+static int wkup_mbox_msg(struct notifier_block *self, unsigned long len,
+		void *msg)
+{
+	return 0;
+}
+
+static struct notifier_block wkup_mbox_notifier = {
+	.notifier_call = wkup_mbox_msg,
+};
+
+static irqreturn_t wkup_m3_txev_handler(int irq, void *unused)
+{
+	am33xx_txev_eoi();
+
+	switch (wkup_m3->state) {
+	case M3_STATE_RESET:
+		wkup_m3->state = M3_STATE_INITED;
+		break;
+	case M3_STATE_MSG_FOR_RESET:
+		wkup_m3->state = M3_STATE_INITED;
+		mailbox_rx_flush(wkup_m3->mbox);
+		complete(&wkup_m3_sync);
+		break;
+	case M3_STATE_MSG_FOR_LP:
+		mailbox_rx_flush(wkup_m3->mbox);
+		complete(&wkup_m3_sync);
+		break;
+	case M3_STATE_UNKNOWN:
+		pr_err("IRQ %d with WKUP_M3 in unknown state\n", irq);
+		mailbox_rx_flush(wkup_m3->mbox);
+		return IRQ_NONE;
+	}
+
+	am33xx_txev_enable();
+	return IRQ_HANDLED;
+}
+
+static void am33xx_pm_firmware_cb(const struct firmware *fw, void *context)
+{
+	struct wkup_m3_context *wkup_m3_context = context;
+	struct platform_device *pdev = to_platform_device(wkup_m3_context->dev);
+	int ret = 0;
+
+	/* no firmware found */
+	if (!fw) {
+		dev_err(wkup_m3_context->dev, "request_firmware failed\n");
+		goto err;
+	}
+
+	memcpy((void *)wkup_m3_context->code, fw->data, fw->size);
+	pr_info("Copied the M3 firmware to UMEM\n");
+
+	wkup_m3->state = M3_STATE_RESET;
+
+	ret = omap_device_deassert_hardreset(pdev, "wkup_m3");
+	if (ret) {
+		pr_err("Could not deassert the reset for WKUP_M3\n");
+		goto err;
+	} else {
+#ifdef CONFIG_SUSPEND
+		suspend_set_ops(&am33xx_pm_ops);
+		/*
+		 * Physical resume address to be used by ROM code
+		 */
+		wkup_m3->ipc_data.resume_addr = (AM33XX_OCMC_END -
+				am33xx_do_wfi_sz + am33xx_resume_offset + 0x4);
+#endif
+		return;
+	}
+
+err:
+	mailbox_put(wkup_m3_context->mbox, &wkup_mbox_notifier);
+}
+
+static int wkup_m3_init(void)
+{
+	int irq, ret = 0;
+	struct resource *mem;
+	struct platform_device *pdev = to_platform_device(wkup_m3->dev);
+
+	omap_device_enable_hwmods(to_omap_device(pdev));
+
+	/* Reserve the MBOX for sending messages to M3 */
+	wkup_m3->mbox = mailbox_get("wkup_m3", &wkup_mbox_notifier);
+	if (IS_ERR(wkup_m3->mbox)) {
+		pr_err("Could not reserve mailbox for A8->M3 IPC\n");
+		ret = -ENODEV;
+		goto exit;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (!irq) {
+		dev_err(wkup_m3->dev, "no irq resource\n");
+		ret = -ENXIO;
+		goto err;
+	}
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem) {
+		dev_err(wkup_m3->dev, "no memory resource\n");
+		ret = -ENXIO;
+		goto err;
+	}
+
+	wkup_m3->code = devm_request_and_ioremap(wkup_m3->dev, mem);
+	if (!wkup_m3->code) {
+		dev_err(wkup_m3->dev, "could not ioremap\n");
+		ret = -EADDRNOTAVAIL;
+		goto err;
+	}
+
+	ret = devm_request_irq(wkup_m3->dev, irq, wkup_m3_txev_handler,
+		  IRQF_DISABLED, "wkup_m3_txev", NULL);
+	if (ret) {
+		dev_err(wkup_m3->dev, "request_irq failed\n");
+		goto err;
+	} else {
+		am33xx_txev_enable();
+	}
+
+	pr_info("Trying to load am335x-pm-firmware.bin");
+
+	/* We don't want to delay boot */
+	request_firmware_nowait(THIS_MODULE, 0, "am335x-pm-firmware.bin",
+				wkup_m3->dev, GFP_KERNEL, wkup_m3,
+				am33xx_pm_firmware_cb);
+	return 0;
+
+err:
+	mailbox_put(wkup_m3->mbox, &wkup_mbox_notifier);
+exit:
+	return ret;
+}
+
+/*
+ * Push the minimal suspend-resume code to SRAM
+ */
+void am33xx_push_sram_idle(void)
+{
+	am33xx_do_wfi_sram = (void *)omap_sram_push
+					(am33xx_do_wfi, am33xx_do_wfi_sz);
+}
+
+static int __init am33xx_map_emif(void)
+{
+	am33xx_emif_base = ioremap(AM33XX_EMIF_BASE, SZ_32K);
+
+	if (!am33xx_emif_base)
+		return -ENOMEM;
+
+	return 0;
+}
+
+void __iomem *am33xx_get_emif_base(void)
+{
+	return am33xx_emif_base;
+}
+
+int __init am33xx_pm_init(void)
+{
+	int ret;
+
+	if (!soc_is_am33xx())
+		return -ENODEV;
+
+	pr_info("Power Management for AM33XX family\n");
+
+	/*
+	 * By default the following IPs do not have MSTANDBY asserted
+	 * which is necessary for PER domain transition. If the drivers
+	 * are not compiled into the kernel HWMOD code will not change the
+	 * state of the IPs if the IP was not never enabled
+	 */
+	usb_oh		= omap_hwmod_lookup("usb_otg_hs");
+	tptc0_oh	= omap_hwmod_lookup("tptc0");
+	tptc1_oh	= omap_hwmod_lookup("tptc1");
+	tptc2_oh	= omap_hwmod_lookup("tptc2");
+	cpsw_oh		= omap_hwmod_lookup("cpgmac0");
+
+	gfx_pwrdm = pwrdm_lookup("gfx_pwrdm");
+	per_pwrdm = pwrdm_lookup("per_pwrdm");
+
+	gfx_l4ls_clkdm = clkdm_lookup("gfx_l4ls_gfx_clkdm");
+
+	if ((!usb_oh) || (!tptc0_oh) || (!tptc1_oh) || (!tptc2_oh) ||
+		(!cpsw_oh) || (!gfx_pwrdm) || (!per_pwrdm) ||
+		(!gfx_l4ls_clkdm)) {
+		ret = -ENODEV;
+		goto err;
+	}
+
+	wkup_m3 = kzalloc(sizeof(struct wkup_m3_context), GFP_KERNEL);
+	if (!wkup_m3) {
+		pr_err("Memory allocation failed\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	ret = am33xx_map_emif();
+	if (ret) {
+		pr_err("Could not ioremap EMIF\n");
+		goto err;
+	}
+
+	(void) clkdm_for_each(omap_pm_clkdms_setup, NULL);
+
+	/* CEFUSE domain can be turned off post bootup */
+	cefuse_pwrdm = pwrdm_lookup("cefuse_pwrdm");
+	if (cefuse_pwrdm)
+		pwrdm_set_next_fpwrst(cefuse_pwrdm, PWRDM_FUNC_PWRST_OFF);
+	else
+		pr_err("Failed to get cefuse_pwrdm\n");
+
+	wkup_m3->dev = omap_device_get_by_hwmod_name("wkup_m3");
+
+	ret = wkup_m3_init();
+	if (ret)
+		pr_err("Could not initialise firmware loading\n");
+
+err:
+	return ret;
+}
arch/arm/mach-omap2/pm33xx.h new file +56 -0

56 lines not on this page · raw message

Signature · 1 line
1.7.0.4
#58 lore raw

RFC v2 16/18 ARM: OMAP2+: AM33XX: Basic suspend resume support

Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

"V" == Vaibhav Bedia <[email protected]> writes:

Hi,

V> +static void am33xx_pm_firmware_cb(const struct firmware *fw, void *context) V> +{
V> + struct wkup_m3_context *wkup_m3_context = context;
V> + struct platform_device *pdev = to_platform_device(wkup_m3_context->dev);
V> + int ret = 0;
V> +
V> + /* no firmware found */
V> + if (!fw) {
V> + dev_err(wkup_m3_context->dev, "request_firmware failed\n");
V> + goto err;
V> + }
V> +
V> + memcpy((void *)wkup_m3_context->code, fw->data, fw->size);

A size check would be good. I don't know much about the finer details about the m3 and how it is connected, but don't you need to ensure data is flushed before resetting the m3?

V> + pr_info("Copied the M3 firmware to UMEM\n");
V> +
V> + wkup_m3->state = M3_STATE_RESET;
V> +
V> + ret = omap_device_deassert_hardreset(pdev, "wkup_m3");
V> + if (ret) {
V> + pr_err("Could not deassert the reset for WKUP_M3\n");
V> + goto err;
V> + } else {
V> +#ifdef CONFIG_SUSPEND
V> + suspend_set_ops(&am33xx_pm_ops);
V> + /*
V> + * Physical resume address to be used by ROM code
V> + */
V> + wkup_m3->ipc_data.resume_addr = (AM33XX_OCMC_END -
V> + am33xx_do_wfi_sz + am33xx_resume_offset + 0x4);
V> +#endif
V> + return;
V> + }
V> +
V> +err:
V> + mailbox_put(wkup_m3_context->mbox, &wkup_mbox_notifier);
V> +}

Signature · 1 line
Bye, Peter Korsgaard
#59 lore raw

RFC v2 16/18 ARM: OMAP2+: AM33XX: Basic suspend resume support

Hi Peter,

Quoted 18 lines · Peter Korsgaard
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

"V" == Vaibhav Bedia <[email protected]> writes:

Hi,

V> +static void am33xx_pm_firmware_cb(const struct firmware *fw, void *context) V> +{
V> + struct wkup_m3_context *wkup_m3_context = context;
V> + struct platform_device *pdev = to_platform_device(wkup_m3_context->dev);
V> + int ret = 0;
V> +
V> + /* no firmware found */
V> + if (!fw) {
V> + dev_err(wkup_m3_context->dev, "request_firmware failed\n");
V> + goto err;
V> + }
V> +
V> + memcpy((void *)wkup_m3_context->code, fw->data, fw->size);

Quoted 4 lines

A size check would be good. I don't know much about the finer details about the m3 and how it is connected, but don't you need to ensure data is flushed before resetting the m3?

Will add the reg property in the next version. The wkup-m3 memory is coming via ioremap() so AFAIK it should be ok. I realized that I do need to replace the memcpy() with memcpy_toio() here.

Thanks,
Vaibhav

#60 lore raw

RFC v2 16/18 ARM: OMAP2+: AM33XX: Basic suspend resume support

Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

"V" == Bedia, Vaibhav <[email protected]> writes:

Hi,

V> + memcpy((void *)wkup_m3_context->code, fw->data, fw->size);

Quoted 5 lines
Quoted 5 lines

A size check would be good. I don't know much about the finer details about the m3 and how it is connected, but don't you need to ensure data is flushed before resetting the m3?

V> Will add the reg property in the next version. The wkup-m3 memory is V> coming via ioremap() so AFAIK it should be ok. I realized that I do V> need to replace the memcpy() with memcpy_toio() here.

Ok, thanks.

Signature · 1 line
Bye, Peter Korsgaard
#61 lore raw

RFC v2 16/18 ARM: OMAP2+: AM33XX: Basic suspend resume support

In reply to Vaibhav Bedia

Hi Viabhav,

Quoted 53 lines · Vaibhav Bedia

AM335x supports various low power modes as documented in section 8.1.4.3 of the AM335x TRM which is available @ http://www.ti.com/litv/pdf/spruh73f

DeepSleep0 mode offers the lowest power mode with limited wakeup sources without a system reboot and is mapped as the suspend state in the kernel. In this state, MPU and PER domains are turned off with the internal RAM held in retention to facilitate resume process. As part of the boot process, the assembly code is copied over to OCMCRAM using the OMAP SRAM code.

AM335x has a Cortex-M3 (WKUP_M3) which assists the MPU in DeepSleep0 entry and exit. WKUP_M3 takes care of the clockdomain and powerdomain transitions based on the intended low power state. MPU needs to load the appropriate WKUP_M3 binary onto the WKUP_M3 memory space before it can leverage any of the PM features like DeepSleep.

The IPC mechanism between MPU and WKUP_M3 uses a mailbox sub-module and 8 IPC registers in the Control module. MPU uses the assigned Mailbox for issuing an interrupt to WKUP_M3 which then goes and checks the IPC registers for the payload. WKUP_M3 has the ability to trigger on interrupt to MPU by executing the "sev" instruction.

In the current implementation when the suspend process is initiated MPU interrupts the WKUP_M3 to let it know about the intent of entering DeepSleep0 and waits for an ACK. When the ACK is received MPU continues with its suspend process to suspend all the drivers and then jumps to assembly in OCMC RAM. The assembly code puts the PLLs in bypass, puts the external RAM in self-refresh mode and then finally execute the WFI instruction. Execution of the WFI instruction triggers another interrupt to the WKUP_M3 which then continues wiht the power down sequence wherein the clockdomain and powerdomain transition takes place. As part of the sleep sequence, WKUP_M3 unmasks the interrupt lines for the wakeup sources. WFI execution on WKUP_M3 causes the hardware to disable the main oscillator of the SoC.

When a wakeup event occurs, WKUP_M3 starts the power-up sequence by switching on the power domains and finally enabling the clock to MPU. Since the MPU gets powered down as part of the sleep sequence in the resume path ROM code starts executing. The ROM code detects a wakeup from sleep and then jumps to the resume location in OCMC which was populated in one of the IPC registers as part of the suspend sequence.

The low level code in OCMC relocks the PLLs, enables access to external RAM and then jumps to the cpu_resume code of the kernel to finish the resume process.

Quoted 6 lines
Cc Tony Lingren <[email protected]>
Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>

Thanks for the updated series here, and my apologies for the delayed review.

I've just had a quick scan of this patch, and have a few general comments, I'll probably have a few more comments after a closer look.

Quoted 59 lines

---
v1->v2:
Move assembly code addition, control module access and hookup in OMAP PM framework in separate patches. Address other comments from Kevin Hilman and Santosh Shilimkar on v1. The discussion on v1 is present @
http://lists.infradead.org/pipermail/linux-arm-kernel/2012-November/129979.html
Note: The mailbox change will need slight rework once the driver is finalized.

 arch/arm/mach-omap2/pm33xx.c |  469 ++++++++++++++++++++++++++++++++++++++++++
 arch/arm/mach-omap2/pm33xx.h |   56 +++++
 2 files changed, 525 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-omap2/pm33xx.c
 create mode 100644 arch/arm/mach-omap2/pm33xx.h
arch/arm/mach-omap2/pm33xx.c new file +37 -0
new file mode 100644
index 0000000..aaa4daa
@@ -0,0 +1,469 @@
+/*
+ * AM33XX Power Management Routines
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ * Vaibhav Bedia <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/suspend.h>
+#include <linux/completion.h>
+#include <linux/module.h>
+#include <linux/mailbox.h>
+#include <linux/interrupt.h>
+
+#include <asm/suspend.h>
+#include <asm/proc-fns.h>
+#include <asm/sizes.h>
+#include <asm/fncpy.h>
+#include <asm/system_misc.h>
+
+#include "pm.h"
+#include "cm33xx.h"

arch/arm/mach-omap2/pm33xx.c line 47

Quoted 10 lines
arch/arm/mach-omap2/pm33xx.c new file +10 -0
+#include "pm33xx.h"
+#include "control.h"
+#include "clockdomain.h"
+#include "powerdomain.h"
+#include "omap_hwmod.h"
+#include "omap_device.h"
+#include "soc.h"
+#include "sram.h"
+
+void (*am33xx_do_wfi_sram)(void);

static?

Quoted 28 lines
arch/arm/mach-omap2/pm33xx.c new file +28 -0
+static void __iomem *am33xx_emif_base;
+static struct powerdomain *cefuse_pwrdm, *gfx_pwrdm, *per_pwrdm;
+static struct clockdomain *gfx_l4ls_clkdm;
+static struct omap_hwmod *usb_oh, *cpsw_oh, *tptc0_oh, *tptc1_oh, *tptc2_oh;
+static struct wkup_m3_context *wkup_m3;
+
+static DECLARE_COMPLETION(wkup_m3_sync);
+
+#ifdef CONFIG_SUSPEND
+static int am33xx_do_sram_idle(long unsigned int unused)
+{
+	am33xx_do_wfi_sram();
+	return 0;
+}
+
+static int am33xx_pm_suspend(void)
+{
+	int status, ret = 0;
+
+	/*
+	 * By default the following IPs do not have MSTANDBY asserted
+	 * which is necessary for PER domain transition. If the drivers
+	 * are not compiled into the kernel HWMOD code will not change the
+	 * state of the IPs if the IP was not never enabled. To ensure
+	 * that there no issues with or without the drivers being compiled
+	 * in the kernel, we forcefully put these IPs to idle.
+	 */
+	omap_hwmod_enable(usb_oh);

arch/arm/mach-omap2/pm33xx.c line 86

Quoted 10 lines
arch/arm/mach-omap2/pm33xx.c new file +10 -0
+	omap_hwmod_enable(tptc0_oh);
+	omap_hwmod_enable(tptc1_oh);
+	omap_hwmod_enable(tptc2_oh);
+	omap_hwmod_enable(cpsw_oh);
+
+	omap_hwmod_idle(usb_oh);
+	omap_hwmod_idle(tptc0_oh);
+	omap_hwmod_idle(tptc1_oh);
+	omap_hwmod_idle(tptc2_oh);
+	omap_hwmod_idle(cpsw_oh);

I think I asked this in my review of v1, but why does this need to happen on every suspend attempt?

This should happen once on init, which will handle the case where there are no drivers, and if there are drivers, then the drivers need to handle this properly.

I don't like this happening here on every suspend attempt, because it will surely hide bugs where drivers are not properly managing their own PM.

arch/arm/mach-omap2/pm33xx.c line 97

Quoted 9 lines
arch/arm/mach-omap2/pm33xx.c new file +9 -0
+	/* Try to put GFX to sleep */
+	pwrdm_set_next_fpwrst(gfx_pwrdm, PWRDM_FUNC_PWRST_OFF);
+
+	ret = cpu_suspend(0, am33xx_do_sram_idle);
+	status = pwrdm_read_fpwrst(gfx_pwrdm);
+	if (status != PWRDM_FUNC_PWRST_OFF)
+		pr_err("GFX domain did not transition\n");
+	else
+		pr_info("GFX domain entered low power state\n");

Do you really want this printed every time?

arch/arm/mach-omap2/pm33xx.c line 107

Quoted 9 lines
arch/arm/mach-omap2/pm33xx.c new file +9 -0
+	/*
+	 * GFX_L4LS clock domain needs to be woken up to
+	 * ensure thet L4LS clock domain does not get stuck in transition
+	 * If that happens L3 module does not get disabled, thereby leading
+	 * to PER power domain transition failing
+	 *
+	 * The clock framework should take care of ensuring
+	 * that the clock domain is in the right state when
+	 * GFX driver is active.

Are you suggesting that the clock framework is not doing this already?

arch/arm/mach-omap2/pm33xx.c line 115

Quoted 8 lines
arch/arm/mach-omap2/pm33xx.c new file +8 -0
+	 */
+	clkdm_wakeup(gfx_l4ls_clkdm);
+	clkdm_sleep(gfx_l4ls_clkdm);
+
+	if (ret) {
+		pr_err("Kernel suspend failure\n");
+	} else {
+		status = omap_ctrl_readl(AM33XX_CONTROL_IPC_MSG_REG1);

We're trying to git rid of direct control module register access, and consolidate them into control.c (for an eventual move to a driver.) I see you've mostly done that in other parts of the series, but here's one that needs to move.

arch/arm/mach-omap2/pm33xx.c line 125

Quoted 10 lines
arch/arm/mach-omap2/pm33xx.c new file +10 -0
+		status &= IPC_RESP_MASK;
+		status >>= __ffs(IPC_RESP_MASK);
+
+		switch (status) {
+		case 0:
+			pr_info("Successfully put all powerdomains to target state\n");
+			/*
+			 * XXX: Leads to loss of logic state in PER power domain
+			 * Use SOC specific ops for this?
+			 */

huh?

Quoted 48 lines
arch/arm/mach-omap2/pm33xx.c new file +48 -0
+			break;
+		case 1:
+			pr_err("Could not transition all powerdomains to target state\n");
+			ret = -1;
+			break;
+		default:
+			pr_err("Something went wrong :(\nStatus = %d\n",
+				status);
+			ret = -1;
+		}
+	}
+
+	return ret;
+}
+
+static int am33xx_pm_enter(suspend_state_t suspend_state)
+{
+	int ret = 0;
+
+	switch (suspend_state) {
+	case PM_SUSPEND_STANDBY:
+	case PM_SUSPEND_MEM:
+		ret = am33xx_pm_suspend();
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int am33xx_pm_begin(suspend_state_t state)
+{
+	int ret = 0;
+	struct mailbox_msg dummy_msg;
+
+	disable_hlt();
+
+	MAILBOX_FILL_HEADER_MSG(dummy_msg, 0xABCDABCD);
+
+	wkup_m3->ipc_data.sleep_mode = IPC_CMD_DS0;
+	wkup_m3->ipc_data.param1  = DS_IPC_DEFAULT;
+	wkup_m3->ipc_data.param2  = DS_IPC_DEFAULT;
+
+	am33xx_wkup_m3_ipc_cmd(&wkup_m3->ipc_data);
+
+	wkup_m3->state = M3_STATE_MSG_FOR_LP;
+

arch/arm/mach-omap2/pm33xx.c line 183

Quoted 10 lines
arch/arm/mach-omap2/pm33xx.c new file +10 -0
+	mailbox_enable_irq(wkup_m3->mbox, IRQ_RX);
+
+	ret = mailbox_msg_send(wkup_m3->mbox, &dummy_msg);
+	if (ret) {
+		pr_err("A8<->CM3 MSG for LP failed\n");
+		am33xx_m3_state_machine_reset();
+		ret = -1;
+	}
+
+	/* Give some time to M3 to respond. 500msec is a random value here */

random? really?

Quoted 53 lines
arch/arm/mach-omap2/pm33xx.c new file +53 -0
+	if (!wait_for_completion_timeout(&wkup_m3_sync,
+					msecs_to_jiffies(500))) {
+		pr_err("A8<->CM3 sync failure\n");
+		am33xx_m3_state_machine_reset();
+		ret = -1;
+	} else {
+		pr_debug("Message sent for entering DeepSleep mode\n");
+		mailbox_disable_irq(wkup_m3->mbox, IRQ_RX);
+	}
+
+	return ret;
+}
+
+static void am33xx_pm_end(void)
+{
+	mailbox_enable_irq(wkup_m3->mbox, IRQ_RX);
+
+	am33xx_m3_state_machine_reset();
+
+	enable_hlt();
+
+	return;
+}
+
+static const struct platform_suspend_ops am33xx_pm_ops = {
+	.begin		= am33xx_pm_begin,
+	.end		= am33xx_pm_end,
+	.enter		= am33xx_pm_enter,
+	.valid		= suspend_valid_only_mem,
+};
+
+static void am33xx_m3_state_machine_reset(void)
+{
+	int ret = 0;
+	struct mailbox_msg dummy_msg;
+
+	MAILBOX_FILL_HEADER_MSG(dummy_msg, 0xABCDABCD);
+
+	wkup_m3->ipc_data.sleep_mode	= IPC_CMD_RESET;
+	wkup_m3->ipc_data.param1	= DS_IPC_DEFAULT;
+	wkup_m3->ipc_data.param2	= DS_IPC_DEFAULT;
+
+	am33xx_wkup_m3_ipc_cmd(&wkup_m3->ipc_data);
+
+	wkup_m3->state = M3_STATE_MSG_FOR_RESET;
+
+	ret = mailbox_msg_send(wkup_m3->mbox, &dummy_msg);
+	if (!ret) {
+		pr_debug("Message sent for resetting M3 state machine\n");
+		/* Give some to M3 to respond. 500msec is a random value here */
+		if (!wait_for_completion_timeout(&wkup_m3_sync,
+						msecs_to_jiffies(500)))
+			pr_err("A8<->CM3 sync failure\n");

arch/arm/mach-omap2/pm33xx.c line 246

Quoted 10 lines
arch/arm/mach-omap2/pm33xx.c new file +10 -0
+	} else {
+		pr_err("Could not reset M3 state machine!!!\n");
+		wkup_m3->state = M3_STATE_UNKNOWN;
+	}
+}
+#endif /* CONFIG_SUSPEND */
+
+/*
+ * Dummy notifier for the mailbox
+ * XXX: Get rid of this requirement once the MBX driver has been finalized

IIRC, I suggested a trivial fix to the mailbox driver that would remove the need for this, which could be done today.

Quoted 72 lines
arch/arm/mach-omap2/pm33xx.c new file +72 -0
+ */
+static int wkup_mbox_msg(struct notifier_block *self, unsigned long len,
+		void *msg)
+{
+	return 0;
+}
+
+static struct notifier_block wkup_mbox_notifier = {
+	.notifier_call = wkup_mbox_msg,
+};
+
+static irqreturn_t wkup_m3_txev_handler(int irq, void *unused)
+{
+	am33xx_txev_eoi();
+
+	switch (wkup_m3->state) {
+	case M3_STATE_RESET:
+		wkup_m3->state = M3_STATE_INITED;
+		break;
+	case M3_STATE_MSG_FOR_RESET:
+		wkup_m3->state = M3_STATE_INITED;
+		mailbox_rx_flush(wkup_m3->mbox);
+		complete(&wkup_m3_sync);
+		break;
+	case M3_STATE_MSG_FOR_LP:
+		mailbox_rx_flush(wkup_m3->mbox);
+		complete(&wkup_m3_sync);
+		break;
+	case M3_STATE_UNKNOWN:
+		pr_err("IRQ %d with WKUP_M3 in unknown state\n", irq);
+		mailbox_rx_flush(wkup_m3->mbox);
+		return IRQ_NONE;
+	}
+
+	am33xx_txev_enable();
+	return IRQ_HANDLED;
+}
+
+static void am33xx_pm_firmware_cb(const struct firmware *fw, void *context)
+{
+	struct wkup_m3_context *wkup_m3_context = context;
+	struct platform_device *pdev = to_platform_device(wkup_m3_context->dev);
+	int ret = 0;
+
+	/* no firmware found */
+	if (!fw) {
+		dev_err(wkup_m3_context->dev, "request_firmware failed\n");
+		goto err;
+	}
+
+	memcpy((void *)wkup_m3_context->code, fw->data, fw->size);
+	pr_info("Copied the M3 firmware to UMEM\n");
+
+	wkup_m3->state = M3_STATE_RESET;
+
+	ret = omap_device_deassert_hardreset(pdev, "wkup_m3");
+	if (ret) {
+		pr_err("Could not deassert the reset for WKUP_M3\n");
+		goto err;
+	} else {
+#ifdef CONFIG_SUSPEND
+		suspend_set_ops(&am33xx_pm_ops);
+		/*
+		 * Physical resume address to be used by ROM code
+		 */
+		wkup_m3->ipc_data.resume_addr = (AM33XX_OCMC_END -
+				am33xx_do_wfi_sz + am33xx_resume_offset + 0x4);
+#endif
+		return;
+	}
+
+err:

arch/arm/mach-omap2/pm33xx.c line 328

Quoted 10 lines
arch/arm/mach-omap2/pm33xx.c new file +10 -0
+	mailbox_put(wkup_m3_context->mbox, &wkup_mbox_notifier);
+}
+
+static int wkup_m3_init(void)
+{
+	int irq, ret = 0;
+	struct resource *mem;
+	struct platform_device *pdev = to_platform_device(wkup_m3->dev);
+
+	omap_device_enable_hwmods(to_omap_device(pdev));

Why not omap_device_enable(pdev) ?

Quoted 42 lines
arch/arm/mach-omap2/pm33xx.c new file +42 -0
+
+	/* Reserve the MBOX for sending messages to M3 */
+	wkup_m3->mbox = mailbox_get("wkup_m3", &wkup_mbox_notifier);
+	if (IS_ERR(wkup_m3->mbox)) {
+		pr_err("Could not reserve mailbox for A8->M3 IPC\n");
+		ret = -ENODEV;
+		goto exit;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (!irq) {
+		dev_err(wkup_m3->dev, "no irq resource\n");
+		ret = -ENXIO;
+		goto err;
+	}
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem) {
+		dev_err(wkup_m3->dev, "no memory resource\n");
+		ret = -ENXIO;
+		goto err;
+	}
+
+	wkup_m3->code = devm_request_and_ioremap(wkup_m3->dev, mem);
+	if (!wkup_m3->code) {
+		dev_err(wkup_m3->dev, "could not ioremap\n");
+		ret = -EADDRNOTAVAIL;
+		goto err;
+	}
+
+	ret = devm_request_irq(wkup_m3->dev, irq, wkup_m3_txev_handler,
+		  IRQF_DISABLED, "wkup_m3_txev", NULL);
+	if (ret) {
+		dev_err(wkup_m3->dev, "request_irq failed\n");
+		goto err;
+	} else {
+		am33xx_txev_enable();
+	}
+
+	pr_info("Trying to load am335x-pm-firmware.bin");
+
+	/* We don't want to delay boot */

arch/arm/mach-omap2/pm33xx.c line 380

Quoted 10 lines
arch/arm/mach-omap2/pm33xx.c new file +10 -0
+	request_firmware_nowait(THIS_MODULE, 0, "am335x-pm-firmware.bin",
+				wkup_m3->dev, GFP_KERNEL, wkup_m3,
+				am33xx_pm_firmware_cb);
+	return 0;
+
+err:
+	mailbox_put(wkup_m3->mbox, &wkup_mbox_notifier);
+exit:
+	return ret;
+}

Kevin

#62 lore raw

RFC v2 16/18 ARM: OMAP2+: AM33XX: Basic suspend resume support

Hi Kevin,

On Tue, Feb 12, 2013 at 06:57:50, Kevin Hilman wrote:
[...]

Quoted 4 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+
+void (*am33xx_do_wfi_sram)(void);

static?

Will fix.

[...]

Quoted 24 lines
Quoted 10 lines
arch/arm/mach-omap2/pm33xx.c new file +10 -0
+
+	/*
+	 * By default the following IPs do not have MSTANDBY asserted
+	 * which is necessary for PER domain transition. If the drivers
+	 * are not compiled into the kernel HWMOD code will not change the
+	 * state of the IPs if the IP was not never enabled. To ensure
+	 * that there no issues with or without the drivers being compiled
+	 * in the kernel, we forcefully put these IPs to idle.
+	 */
+	omap_hwmod_enable(usb_oh);

arch/arm/mach-omap2/pm33xx.c line 86

Quoted 10 lines
arch/arm/mach-omap2/pm33xx.c new file +10 -0
+	omap_hwmod_enable(tptc0_oh);
+	omap_hwmod_enable(tptc1_oh);
+	omap_hwmod_enable(tptc2_oh);
+	omap_hwmod_enable(cpsw_oh);
+
+	omap_hwmod_idle(usb_oh);
+	omap_hwmod_idle(tptc0_oh);
+	omap_hwmod_idle(tptc1_oh);
+	omap_hwmod_idle(tptc2_oh);
+	omap_hwmod_idle(cpsw_oh);

I think I asked this in my review of v1, but why does this need to happen on every suspend attempt?

Quoted 7 lines

This should happen once on init, which will handle the case where there are no drivers, and if there are drivers, then the drivers need to handle this properly.

I don't like this happening here on every suspend attempt, because it will surely hide bugs where drivers are not properly managing their own PM.

By default these IPs don't have MSTANDBY asserted. When a low power transition happens, the peripheral power domain loses context and hence the forced MSTANDBY configuration in the IP is lost. To work around this problem we need to assert MSTANDBY in every suspend-resume iteration.

I agree that this might hide PM bugs in the driver but to solve this problem we need some mechanism for the PM code to know whether or not a driver is bound to the corresponding platform devices. Any suggestions on this?

Quoted 12 lines

arch/arm/mach-omap2/pm33xx.c line 97

Quoted 9 lines
arch/arm/mach-omap2/pm33xx.c new file +9 -0
+	/* Try to put GFX to sleep */
+	pwrdm_set_next_fpwrst(gfx_pwrdm, PWRDM_FUNC_PWRST_OFF);
+
+	ret = cpu_suspend(0, am33xx_do_sram_idle);
+	status = pwrdm_read_fpwrst(gfx_pwrdm);
+	if (status != PWRDM_FUNC_PWRST_OFF)
+		pr_err("GFX domain did not transition\n");
+	else
+		pr_info("GFX domain entered low power state\n");

Do you really want this printed every time?

Hmm... it could perhaps be clubbed with the overall status that's printed. I kept it here since the GFX power domain is completely under MPU control and hence this information would be useful in finding out if there's a problem with the GFX suspend-resume.

Quoted 12 lines

arch/arm/mach-omap2/pm33xx.c line 107

Quoted 9 lines
arch/arm/mach-omap2/pm33xx.c new file +9 -0
+	/*
+	 * GFX_L4LS clock domain needs to be woken up to
+	 * ensure thet L4LS clock domain does not get stuck in transition
+	 * If that happens L3 module does not get disabled, thereby leading
+	 * to PER power domain transition failing
+	 *
+	 * The clock framework should take care of ensuring
+	 * that the clock domain is in the right state when
+	 * GFX driver is active.

Are you suggesting that the clock framework is not doing this already?

No. This clkdm_*() calls are here to work-around an issue that I observed when implementing suspend-resume. The force wakeup and sleep of the gfx_l4ls clock domain across every suspend-resume is something I don't think can be pushed to the clock framework.

Quoted 5 lines
Quoted 5 lines
arch/arm/mach-omap2/pm33xx.c new file +5 -0
+	 */
+	clkdm_wakeup(gfx_l4ls_clkdm);
+	clkdm_sleep(gfx_l4ls_clkdm);
+
+	if (ret) {
Quoted 8 lines

arch/arm/mach-omap2/pm33xx.c line 115

Quoted 3 lines
arch/arm/mach-omap2/pm33xx.c new file +3 -0
+		pr_err("Kernel suspend failure\n");
+	} else {
+		status = omap_ctrl_readl(AM33XX_CONTROL_IPC_MSG_REG1);

We're trying to git rid of direct control module register access, and consolidate them into control.c (for an eventual move to a driver.) I see you've mostly done that in other parts of the series, but here's one that needs to move.

Yes, I somehow missed this one. Will take care of it in the next version.

Quoted 14 lines

arch/arm/mach-omap2/pm33xx.c line 125

Quoted 11 lines
arch/arm/mach-omap2/pm33xx.c new file +10 -0
+		status &= IPC_RESP_MASK;
+		status >>= __ffs(IPC_RESP_MASK);
+
+		switch (status) {
+		case 0:
+			pr_info("Successfully put all powerdomains to target state\n");
+			/*
+			 * XXX: Leads to loss of logic state in PER power domain
+			 * Use SOC specific ops for this?
+			 */

huh?

Ah... this is more of a TODO. There's no previous state entered information in the PRCM registers. So to ensure that the drivers get the right information when they check with the PM layer about the loss of context and hence the need to restore the registers, I need to update the logic and membank state counters for the PER power domain manually. I was thinking of leveraging the SoC specific power domain ops for doing this.

[...]

Quoted 4 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+
+	/* Give some time to M3 to respond. 500msec is a random value here */

random? really?

Sort of. I don't have any numbers from the h/w guys on the worst case delay in getting an interrupt from M3 to MPU. At the same time I want to handle the scenario where something goes wrong on the M3 side and it doesn't respond.

[...]

Quoted 8 lines

arch/arm/mach-omap2/pm33xx.c line 246

Quoted 4 lines
arch/arm/mach-omap2/pm33xx.c new file +4 -0
+
+/*
+ * Dummy notifier for the mailbox
+ * XXX: Get rid of this requirement once the MBX driver has been finalized

IIRC, I suggested a trivial fix to the mailbox driver that would remove the need for this, which could be done today.

Yes. I plan to do that once the mailbox code movement to drivers/ along with other changes to make it generic enough are finalized. It was adding in one more dependency and hence I kept this as a TODO in this version.

[...]

Quoted 10 lines

arch/arm/mach-omap2/pm33xx.c line 328

Quoted 7 lines
arch/arm/mach-omap2/pm33xx.c new file +7 -0
+static int wkup_m3_init(void)
+{
+	int irq, ret = 0;
+	struct resource *mem;
+	struct platform_device *pdev = to_platform_device(wkup_m3->dev);
+
+	omap_device_enable_hwmods(to_omap_device(pdev));

Why not omap_device_enable(pdev) ?

The objective is to leverage the hwmod code to get the WKUP-M3 functional and not have OMAP runtime PM code coming in the way. Using omap_device_enable() triggers the following dev_warn() from omap_device_enable().

[    2.033718] platform 44d00000.wkup_m3: omap_device_late_idle: enabled but no driver.  Idling
[    2.042676] ------------[ cut here ]------------
[    2.047572] WARNING: at arch/arm/mach-omap2/omap_hwmod.c:2187 _idle+0x164/0x1c0()
[    2.055459] omap_hwmod: wkup_m3: idle state can only be entered from enabled state
[    2.063435] Modules linked in:
[    2.066712] [<c001afe4>] (unwind_backtrace+0x0/0xf0) from [<c004380c>] (warn_slowpath_common+0x4c/0x64)
[    2.076626] [<c004380c>] (warn_slowpath_common+0x4c/0x64) from [<c00438b8>] (warn_slowpath_fmt+0x30/0x40)
[    2.086720] [<c00438b8>] (warn_slowpath_fmt+0x30/0x40) from [<c002aad0>] (_idle+0x164/0x1c0)
[    2.095624] [<c002aad0>] (_idle+0x164/0x1c0) from [<c002af98>] (omap_hwmod_idle+0x24/0x40)
[    2.104348] [<c002af98>] (omap_hwmod_idle+0x24/0x40) from [<c002bd60>] (omap_device_idle_hwmods+0x24/0x3c)
[    2.114536] [<c002bd60>] (omap_device_idle_hwmods+0x24/0x3c) from [<c002bf4c>] (_omap_device_deactivate+0x98/0x134)
[    2.125546] [<c002bf4c>] (_omap_device_deactivate+0x98/0x134) from [<c002c87c>] (omap_device_idle+0x28/0x54)
[    2.135921] [<c002c87c>] (omap_device_idle+0x28/0x54) from [<c06ec00c>] (omap_device_late_idle+0x44/0x54)
[    2.146025] [<c06ec00c>] (omap_device_late_idle+0x44/0x54) from [<c031f850>] (bus_for_each_dev+0x50/0x7c)
[    2.156120] [<c031f850>] (bus_for_each_dev+0x50/0x7c) from [<c06ebe10>] (omap_device_late_init+0x18/0x28)
[    2.166216] [<c06ebe10>] (omap_device_late_init+0x18/0x28) from [<c00086e4>] (do_one_initcall+0x34/0x180)
[    2.176314] [<c00086e4>] (do_one_initcall+0x34/0x180) from [<c06df8f8>] (kernel_init_freeable+0xfc/0x1cc)
[    2.186408] [<c06df8f8>] (kernel_init_freeable+0xfc/0x1cc) from [<c04d4b20>] (kernel_init+0x8/0xe4)
[    2.195962] [<c04d4b20>] (kernel_init+0x8/0xe4) from [<c0013410>] (ret_from_fork+0x14/0x24)
[    2.204905] ---[ end trace 8f61b319779f6e57 ]---

Regards,
Vaibhav

#63 lore raw

RFC v2 16/18 ARM: OMAP2+: AM33XX: Basic suspend resume support

Quoted 41 lines · Vaibhav

Hi Kevin,

On Tue, Feb 12, 2013 at 06:57:50, Kevin Hilman wrote:
[...]

Quoted 4 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+
+void (*am33xx_do_wfi_sram)(void);

static?

Will fix.

[...]

Quoted 28 lines
Quoted 10 lines
arch/arm/mach-omap2/pm33xx.c new file +10 -0
+
+	/*
+	 * By default the following IPs do not have MSTANDBY asserted
+	 * which is necessary for PER domain transition. If the drivers
+	 * are not compiled into the kernel HWMOD code will not change the
+	 * state of the IPs if the IP was not never enabled. To ensure
+	 * that there no issues with or without the drivers being compiled
+	 * in the kernel, we forcefully put these IPs to idle.
+	 */
+	omap_hwmod_enable(usb_oh);

arch/arm/mach-omap2/pm33xx.c line 86

Quoted 10 lines
arch/arm/mach-omap2/pm33xx.c new file +10 -0
+	omap_hwmod_enable(tptc0_oh);
+	omap_hwmod_enable(tptc1_oh);
+	omap_hwmod_enable(tptc2_oh);
+	omap_hwmod_enable(cpsw_oh);
+
+	omap_hwmod_idle(usb_oh);
+	omap_hwmod_idle(tptc0_oh);
+	omap_hwmod_idle(tptc1_oh);
+	omap_hwmod_idle(tptc2_oh);
+	omap_hwmod_idle(cpsw_oh);

I think I asked this in my review of v1, but why does this need to happen on every suspend attempt?

This should happen once on init, which will handle the case where there are no drivers, and if there are drivers, then the drivers need to handle this properly.

Quoted 5 lines
Quoted 3 lines

I don't like this happening here on every suspend attempt, because it will surely hide bugs where drivers are not properly managing their own PM.

By default these IPs don't have MSTANDBY asserted.

When you say "by default", I guess you mean after reset (and/or context loss), right?

Quoted 4 lines

When a low power transition happens, the peripheral power domain loses context and hence the forced MSTANDBY configuration in the IP is lost. To work around this problem we need to assert MSTANDBY in every suspend-resume iteration.

Yuck. More clever hardware. ;)

Quoted 3 lines

I agree that this might hide PM bugs in the driver but to solve this problem we need some mechanism for the PM code to know whether or not a driver is bound to the corresponding platform devices. Any suggestions on this?

Driver bound status can be tracked easily using bus notifiers. You can see an example in the omap_device core.

Quoted 13 lines
Quoted 12 lines

arch/arm/mach-omap2/pm33xx.c line 97

Quoted 9 lines
arch/arm/mach-omap2/pm33xx.c new file +9 -0
+	/* Try to put GFX to sleep */
+	pwrdm_set_next_fpwrst(gfx_pwrdm, PWRDM_FUNC_PWRST_OFF);
+
+	ret = cpu_suspend(0, am33xx_do_sram_idle);
+	status = pwrdm_read_fpwrst(gfx_pwrdm);
+	if (status != PWRDM_FUNC_PWRST_OFF)
+		pr_err("GFX domain did not transition\n");
+	else
+		pr_info("GFX domain entered low power state\n");

Do you really want this printed every time?

Quoted 4 lines

Hmm... it could perhaps be clubbed with the overall status that's printed. I kept it here since the GFX power domain is completely under MPU control and hence this information would be useful in finding out if there's a problem with the GFX suspend-resume.

OK.

Quoted 13 lines
Quoted 12 lines

arch/arm/mach-omap2/pm33xx.c line 107

Quoted 9 lines
arch/arm/mach-omap2/pm33xx.c new file +9 -0
+	/*
+	 * GFX_L4LS clock domain needs to be woken up to
+	 * ensure thet L4LS clock domain does not get stuck in transition
+	 * If that happens L3 module does not get disabled, thereby leading
+	 * to PER power domain transition failing
+	 *
+	 * The clock framework should take care of ensuring
+	 * that the clock domain is in the right state when
+	 * GFX driver is active.

Are you suggesting that the clock framework is not doing this already?

Quoted 4 lines

No. This clkdm_*() calls are here to work-around an issue that I observed when implementing suspend-resume. The force wakeup and sleep of the gfx_l4ls clock domain across every suspend-resume is something I don't think can be pushed to the clock framework.

I still don't follow what you're suggesting the clock framework "should" do. Are you describing the case when there is a GFX driver vs. when there isn't a driver? If so, it needs to be more clear.

Also, some more description about why the device gets 'stuck in transition' would be helpful. Is this an erratum workaround?

Quoted 31 lines
Quoted 5 lines
Quoted 5 lines
arch/arm/mach-omap2/pm33xx.c new file +5 -0
+	 */
+	clkdm_wakeup(gfx_l4ls_clkdm);
+	clkdm_sleep(gfx_l4ls_clkdm);
+
+	if (ret) {
Quoted 8 lines

arch/arm/mach-omap2/pm33xx.c line 115

Quoted 3 lines
arch/arm/mach-omap2/pm33xx.c new file +3 -0
+		pr_err("Kernel suspend failure\n");
+	} else {
+		status = omap_ctrl_readl(AM33XX_CONTROL_IPC_MSG_REG1);

We're trying to git rid of direct control module register access, and consolidate them into control.c (for an eventual move to a driver.) I see you've mostly done that in other parts of the series, but here's one that needs to move.

Yes, I somehow missed this one. Will take care of it in the next version.

Quoted 14 lines

arch/arm/mach-omap2/pm33xx.c line 125

Quoted 11 lines
arch/arm/mach-omap2/pm33xx.c new file +10 -0
+		status &= IPC_RESP_MASK;
+		status >>= __ffs(IPC_RESP_MASK);
+
+		switch (status) {
+		case 0:
+			pr_info("Successfully put all powerdomains to target state\n");
+			/*
+			 * XXX: Leads to loss of logic state in PER power domain
+			 * Use SOC specific ops for this?
+			 */

huh?

Quoted 8 lines

Ah... this is more of a TODO. There's no previous state entered information in the PRCM registers. So to ensure that the drivers get the right information when they check with the PM layer about the loss of context and hence the need to restore the registers, I need to update the logic and membank state counters for the PER power domain manually. I was thinking of leveraging the SoC specific power domain ops for doing this.

[...]

I see, then probably a TODO here with more description would be more helpful.

So, IIUC, without implemeting this, the drivers will never be able to detect loss of context, correct? Sounds like something that should be decribed in the changelog as that's a rather important limitation to this implementaion.

Quoted 9 lines
Quoted 4 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+
+	/* Give some time to M3 to respond. 500msec is a random value here */

random? really?

Sort of. I don't have any numbers from the h/w guys on the worst case delay in getting an interrupt from M3 to MPU. At the same time I want to handle the scenario where something goes wrong on the M3 side and it doesn't respond.

OK, then it's not random. You have some reasoning behind the number that should be documented.

That being said, in the absence of numbers from HW folks, can't you measure the typical times so you know roughly what's "normal".

Quoted 10 lines
Quoted 9 lines

arch/arm/mach-omap2/pm33xx.c line 246

Quoted 5 lines
arch/arm/mach-omap2/pm33xx.c new file +4 -0
+
+/*
+ * Dummy notifier for the mailbox
+ * XXX: Get rid of this requirement once the MBX driver has been finalized

IIRC, I suggested a trivial fix to the mailbox driver that would remove the need for this, which could be done today.

Quoted 3 lines

Yes. I plan to do that once the mailbox code movement to drivers/ along with other changes to make it generic enough are finalized. It was adding in one more dependency and hence I kept this as a TODO in this version.

OK, fair enough.

Quoted 11 lines
Quoted 10 lines

arch/arm/mach-omap2/pm33xx.c line 328

Quoted 7 lines
arch/arm/mach-omap2/pm33xx.c new file +7 -0
+static int wkup_m3_init(void)
+{
+	int irq, ret = 0;
+	struct resource *mem;
+	struct platform_device *pdev = to_platform_device(wkup_m3->dev);
+
+	omap_device_enable_hwmods(to_omap_device(pdev));

Why not omap_device_enable(pdev) ?

Quoted 2 lines

The objective is to leverage the hwmod code to get the WKUP-M3 functional and not have OMAP runtime PM code coming in the way.

FWIW, it is not runtime PM getting in the way.

Quoted 2 lines

Using omap_device_enable() triggers the following dev_warn() from omap_device_enable().

Looking closer at the trace, you'll see it's not omap_device_enable() that is triggering this warning. What is happening is that omap_device has a late_initcall which forcibly idles omap_devices that have been enabled, but that don't have a driver. You're hacking around that.

IMO, this would be a much cleaner implementation if you just created a small driver for the wkup_m3. You're already doing a bunch of driver-like stuff for it (requesting base/IRQs, mapping regions, firmware, etc.) I think this should separated out into a real driver. Then it will be bound to the right omap_device, and normal PM operations will work as expected.

Also, doing it this way will be more flexible for those wanting to use their own firmware on the M3, or customize the current firmware.

Quoted 21 lines
[    2.033718] platform 44d00000.wkup_m3: omap_device_late_idle: enabled but no driver.  Idling
[    2.042676] ------------[ cut here ]------------
[    2.047572] WARNING: at arch/arm/mach-omap2/omap_hwmod.c:2187 _idle+0x164/0x1c0()
[    2.055459] omap_hwmod: wkup_m3: idle state can only be entered from enabled state
[    2.063435] Modules linked in:
[    2.066712] [<c001afe4>] (unwind_backtrace+0x0/0xf0) from [<c004380c>] (warn_slowpath_common+0x4c/0x64)
[ 2.076626] [<c004380c>] (warn_slowpath_common+0x4c/0x64) from
[<c00438b8>] (warn_slowpath_fmt+0x30/0x40)
[    2.086720] [<c00438b8>] (warn_slowpath_fmt+0x30/0x40) from [<c002aad0>] (_idle+0x164/0x1c0)
[    2.095624] [<c002aad0>] (_idle+0x164/0x1c0) from [<c002af98>] (omap_hwmod_idle+0x24/0x40)
[ 2.104348] [<c002af98>] (omap_hwmod_idle+0x24/0x40) from [<c002bd60>]
(omap_device_idle_hwmods+0x24/0x3c)
[ 2.114536] [<c002bd60>] (omap_device_idle_hwmods+0x24/0x3c) from
[<c002bf4c>] (_omap_device_deactivate+0x98/0x134)
[ 2.125546] [<c002bf4c>] (_omap_device_deactivate+0x98/0x134) from
[<c002c87c>] (omap_device_idle+0x28/0x54)
[ 2.135921] [<c002c87c>] (omap_device_idle+0x28/0x54) from
[<c06ec00c>] (omap_device_late_idle+0x44/0x54)
[ 2.146025] [<c06ec00c>] (omap_device_late_idle+0x44/0x54) from
[<c031f850>] (bus_for_each_dev+0x50/0x7c)
[ 2.156120] [<c031f850>] (bus_for_each_dev+0x50/0x7c) from
Quoted 8 lines

[<c06ebe10>] (omap_device_late_init+0x18/0x28)

[ 2.166216] [<c06ebe10>] (omap_device_late_init+0x18/0x28) from
[<c00086e4>] (do_one_initcall+0x34/0x180)
[ 2.176314] [<c00086e4>] (do_one_initcall+0x34/0x180) from
[<c06df8f8>] (kernel_init_freeable+0xfc/0x1cc)
[    2.186408] [<c06df8f8>] (kernel_init_freeable+0xfc/0x1cc) from [<c04d4b20>] (kernel_init+0x8/0xe4)
[    2.195962] [<c04d4b20>] (kernel_init+0x8/0xe4) from [<c0013410>] (ret_from_fork+0x14/0x24)
[    2.204905] ---[ end trace 8f61b319779f6e57 ]---

Kevin

#64 lore raw

RFC v2 16/18 ARM: OMAP2+: AM33XX: Basic suspend resume support

On Mon, Feb 18, 2013 at 21:41:49, Kevin Hilman wrote:
[...]

Quoted 5 lines
Quoted 1 line

By default these IPs don't have MSTANDBY asserted.

When you say "by default", I guess you mean after reset (and/or context loss), right?

Yes

Quoted 6 lines
Quoted 4 lines

When a low power transition happens, the peripheral power domain loses context and hence the forced MSTANDBY configuration in the IP is lost. To work around this problem we need to assert MSTANDBY in every suspend-resume iteration.

Yuck. More clever hardware. ;)

We are getting this gradually :)

Quoted 7 lines
Quoted 4 lines

I agree that this might hide PM bugs in the driver but to solve this problem we need some mechanism for the PM code to know whether or not a driver is bound to the corresponding platform devices. Any suggestions on this?

Driver bound status can be tracked easily using bus notifiers. You can see an example in the omap_device core.

Ok. I'll try to use the driver bound status in the next version.

[...]

Quoted 13 lines
Quoted 13 lines
Quoted 12 lines

arch/arm/mach-omap2/pm33xx.c line 107

Quoted 9 lines
arch/arm/mach-omap2/pm33xx.c new file +9 -0
+	/*
+	 * GFX_L4LS clock domain needs to be woken up to
+	 * ensure thet L4LS clock domain does not get stuck in transition
+	 * If that happens L3 module does not get disabled, thereby leading
+	 * to PER power domain transition failing
+	 *
+	 * The clock framework should take care of ensuring
+	 * that the clock domain is in the right state when
+	 * GFX driver is active.

Are you suggesting that the clock framework is not doing this already?

Quoted 9 lines
Quoted 4 lines

No. This clkdm_*() calls are here to work-around an issue that I observed when implementing suspend-resume. The force wakeup and sleep of the gfx_l4ls clock domain across every suspend-resume is something I don't think can be pushed to the clock framework.

I still don't follow what you're suggesting the clock framework "should" do. Are you describing the case when there is a GFX driver vs. when there isn't a driver? If so, it needs to be more clear.

No. The issue with GFX_L4LS happens irrespective of the state of the GFX driver and needs to be handled in the suspend-resume sequence. I guess the second part of comment is what created the confusion. I'll get rid of that.

Quoted 3 lines

Also, some more description about why the device gets 'stuck in transition' would be helpful. Is this an erratum workaround?

I'll follow up with the design folks to find out more. From some past discussions this is not expected so looks like we need to an erratum published for this issue.

Quoted 9 lines

I see, then probably a TODO here with more description would be more helpful.

So, IIUC, without implemeting this, the drivers will never be able to detect loss of context, correct? Sounds like something that should be decribed in the changelog as that's a rather important limitation to this implementaion.

Ok. I'll address this limitation in the next version and improve the changelog.

Quoted 5 lines
Quoted 5 lines
Quoted 4 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+
+	/* Give some time to M3 to respond. 500msec is a random value here */

random? really?

Quoted 7 lines
Quoted 4 lines

Sort of. I don't have any numbers from the h/w guys on the worst case delay in getting an interrupt from M3 to MPU. At the same time I want to handle the scenario where something goes wrong on the M3 side and it doesn't respond.

OK, then it's not random. You have some reasoning behind the number that should be documented.

Will do.

Quoted 3 lines

That being said, in the absence of numbers from HW folks, can't you measure the typical times so you know roughly what's "normal".

I'll do some timer based profiling and get rough numbers for this.

[...]

Quoted 9 lines
Quoted 6 lines
Quoted 3 lines

Why not omap_device_enable(pdev) ?

The objective is to leverage the hwmod code to get the WKUP-M3 functional and not have OMAP runtime PM code coming in the way.

FWIW, it is not runtime PM getting in the way.

Quoted 8 lines
Quoted 2 lines

Using omap_device_enable() triggers the following dev_warn() from omap_device_enable().

Looking closer at the trace, you'll see it's not omap_device_enable() that is triggering this warning. What is happening is that omap_device has a late_initcall which forcibly idles omap_devices that have been enabled, but that don't have a driver. You're hacking around that.

Thanks for the explanation. I should have looked closer :(

Quoted 9 lines

IMO, this would be a much cleaner implementation if you just created a small driver for the wkup_m3. You're already doing a bunch of driver-like stuff for it (requesting base/IRQs, mapping regions, firmware, etc.) I think this should separated out into a real driver. Then it will be bound to the right omap_device, and normal PM operations will work as expected.

Also, doing it this way will be more flexible for those wanting to use their own firmware on the M3, or customize the current firmware.

Hmm... that definitely sounds more flexible. It should also help in the next SoC AM437x which has a similar PM architecture. Where would you suggest placing this minimal driver?

Regards,
Vaibhav

#65 lore raw

RFC v2 16/18 ARM: OMAP2+: AM33XX: Basic suspend resume support

"Bedia, Vaibhav" <[email protected]> writes:

[...]

Quoted 7 lines
Quoted 7 lines

IMO, this would be a much cleaner implementation if you just created a small driver for the wkup_m3. You're already doing a bunch of driver-like stuff for it (requesting base/IRQs, mapping regions, firmware, etc.) I think this should separated out into a real driver. Then it will be bound to the right omap_device, and normal PM operations will work as expected.

Quoted 6 lines
Quoted 2 lines

Also, doing it this way will be more flexible for those wanting to use their own firmware on the M3, or customize the current firmware.

Hmm... that definitely sounds more flexible. It should also help in the next SoC AM437x which has a similar PM architecture. Where would you suggest placing this minimal driver?

For now, just leave it in mach-omap2 and we can figure out the right home for it eventually.

Kevin

#66 lore raw

RFC v2 16/18 ARM: OMAP2+: AM33XX: Basic suspend resume support

In reply to Vaibhav Bedia

Hi Vaibhav,

Quoted 3 lines · Vaibhav Bedia

AM335x supports various low power modes as documented in section 8.1.4.3 of the AM335x TRM which is available @ http://www.ti.com/litv/pdf/spruh73f

May I ask about the plans for this series? Will you be re-spinning them for a current
tree, and what's the planned merge window for it?

Many thanks,
Daniel

Quoted 617 lines

DeepSleep0 mode offers the lowest power mode with limited wakeup sources without a system reboot and is mapped as the suspend state in the kernel. In this state, MPU and PER domains are turned off with the internal RAM held in retention to facilitate resume process. As part of the boot process, the assembly code is copied over to OCMCRAM using the OMAP SRAM code.

AM335x has a Cortex-M3 (WKUP_M3) which assists the MPU in DeepSleep0 entry and exit. WKUP_M3 takes care of the clockdomain and powerdomain transitions based on the intended low power state. MPU needs to load the appropriate WKUP_M3 binary onto the WKUP_M3 memory space before it can leverage any of the PM features like DeepSleep.

The IPC mechanism between MPU and WKUP_M3 uses a mailbox sub-module and 8 IPC registers in the Control module. MPU uses the assigned Mailbox for issuing an interrupt to WKUP_M3 which then goes and checks the IPC registers for the payload. WKUP_M3 has the ability to trigger on interrupt to MPU by executing the "sev" instruction.

In the current implementation when the suspend process is initiated MPU interrupts the WKUP_M3 to let it know about the intent of entering DeepSleep0 and waits for an ACK. When the ACK is received MPU continues with its suspend process to suspend all the drivers and then jumps to assembly in OCMC RAM. The assembly code puts the PLLs in bypass, puts the external RAM in self-refresh mode and then finally execute the WFI instruction. Execution of the WFI instruction triggers another interrupt to the WKUP_M3 which then continues wiht the power down sequence wherein the clockdomain and powerdomain transition takes place. As part of the sleep sequence, WKUP_M3 unmasks the interrupt lines for the wakeup sources. WFI execution on WKUP_M3 causes the hardware to disable the main oscillator of the SoC.

When a wakeup event occurs, WKUP_M3 starts the power-up sequence by switching on the power domains and finally enabling the clock to MPU. Since the MPU gets powered down as part of the sleep sequence in the resume path ROM code starts executing. The ROM code detects a wakeup from sleep and then jumps to the resume location in OCMC which was populated in one of the IPC registers as part of the suspend sequence.

The low level code in OCMC relocks the PLLs, enables access to external RAM and then jumps to the cpu_resume code of the kernel to finish the resume process.

Cc Tony Lingren <[email protected]>
Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>

---
v1->v2:
Move assembly code addition, control module access and hookup in OMAP PM framework in separate patches. Address other comments from Kevin Hilman and Santosh Shilimkar on v1. The discussion on v1 is present @
http://lists.infradead.org/pipermail/linux-arm-kernel/2012-November/129979.html
Note: The mailbox change will need slight rework once the driver is finalized.

 arch/arm/mach-omap2/pm33xx.c |  469 ++++++++++++++++++++++++++++++++++++++++++
 arch/arm/mach-omap2/pm33xx.h |   56 +++++
 2 files changed, 525 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-omap2/pm33xx.c
 create mode 100644 arch/arm/mach-omap2/pm33xx.h
arch/arm/mach-omap2/pm33xx.c new file +469 -0
new file mode 100644
index 0000000..aaa4daa
@@ -0,0 +1,469 @@
+/*
+ * AM33XX Power Management Routines
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ * Vaibhav Bedia <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/suspend.h>
+#include <linux/completion.h>
+#include <linux/module.h>
+#include <linux/mailbox.h>
+#include <linux/interrupt.h>
+
+#include <asm/suspend.h>
+#include <asm/proc-fns.h>
+#include <asm/sizes.h>
+#include <asm/fncpy.h>
+#include <asm/system_misc.h>
+
+#include "pm.h"
+#include "cm33xx.h"
+#include "pm33xx.h"
+#include "control.h"
+#include "clockdomain.h"
+#include "powerdomain.h"
+#include "omap_hwmod.h"
+#include "omap_device.h"
+#include "soc.h"
+#include "sram.h"
+
+void (*am33xx_do_wfi_sram)(void);
+
+static void __iomem *am33xx_emif_base;
+static struct powerdomain *cefuse_pwrdm, *gfx_pwrdm, *per_pwrdm;
+static struct clockdomain *gfx_l4ls_clkdm;
+static struct omap_hwmod *usb_oh, *cpsw_oh, *tptc0_oh, *tptc1_oh, *tptc2_oh;
+static struct wkup_m3_context *wkup_m3;
+
+static DECLARE_COMPLETION(wkup_m3_sync);
+
+#ifdef CONFIG_SUSPEND
+static int am33xx_do_sram_idle(long unsigned int unused)
+{
+       am33xx_do_wfi_sram();
+       return 0;
+}
+
+static int am33xx_pm_suspend(void)
+{
+       int status, ret = 0;
+
+       /*
+        * By default the following IPs do not have MSTANDBY asserted
+        * which is necessary for PER domain transition. If the drivers
+        * are not compiled into the kernel HWMOD code will not change the
+        * state of the IPs if the IP was not never enabled. To ensure
+        * that there no issues with or without the drivers being compiled
+        * in the kernel, we forcefully put these IPs to idle.
+        */
+       omap_hwmod_enable(usb_oh);
+       omap_hwmod_enable(tptc0_oh);
+       omap_hwmod_enable(tptc1_oh);
+       omap_hwmod_enable(tptc2_oh);
+       omap_hwmod_enable(cpsw_oh);
+
+       omap_hwmod_idle(usb_oh);
+       omap_hwmod_idle(tptc0_oh);
+       omap_hwmod_idle(tptc1_oh);
+       omap_hwmod_idle(tptc2_oh);
+       omap_hwmod_idle(cpsw_oh);
+
+       /* Try to put GFX to sleep */
+       pwrdm_set_next_fpwrst(gfx_pwrdm, PWRDM_FUNC_PWRST_OFF);
+
+       ret = cpu_suspend(0, am33xx_do_sram_idle);
+
+       status = pwrdm_read_fpwrst(gfx_pwrdm);
+       if (status != PWRDM_FUNC_PWRST_OFF)
+               pr_err("GFX domain did not transition\n");
+       else
+               pr_info("GFX domain entered low power state\n");
+
+       /*
+        * GFX_L4LS clock domain needs to be woken up to
+        * ensure thet L4LS clock domain does not get stuck in transition
+        * If that happens L3 module does not get disabled, thereby leading
+        * to PER power domain transition failing
+        *
+        * The clock framework should take care of ensuring
+        * that the clock domain is in the right state when
+        * GFX driver is active.
+        */
+       clkdm_wakeup(gfx_l4ls_clkdm);
+       clkdm_sleep(gfx_l4ls_clkdm);
+
+       if (ret) {
+               pr_err("Kernel suspend failure\n");
+       } else {
+               status = omap_ctrl_readl(AM33XX_CONTROL_IPC_MSG_REG1);
+               status &= IPC_RESP_MASK;
+               status >>= __ffs(IPC_RESP_MASK);
+
+               switch (status) {
+               case 0:
+                       pr_info("Successfully put all powerdomains to target state\n");
+                       /*
+                        * XXX: Leads to loss of logic state in PER power domain
+                        * Use SOC specific ops for this?
+                        */
+                       break;
+               case 1:
+                       pr_err("Could not transition all powerdomains to target state\n");
+                       ret = -1;
+                       break;
+               default:
+                       pr_err("Something went wrong :(\nStatus = %d\n",
+                               status);
+                       ret = -1;
+               }
+       }
+
+       return ret;
+}
+
+static int am33xx_pm_enter(suspend_state_t suspend_state)
+{
+       int ret = 0;
+
+       switch (suspend_state) {
+       case PM_SUSPEND_STANDBY:
+       case PM_SUSPEND_MEM:
+               ret = am33xx_pm_suspend();
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static int am33xx_pm_begin(suspend_state_t state)
+{
+       int ret = 0;
+       struct mailbox_msg dummy_msg;
+
+       disable_hlt();
+
+       MAILBOX_FILL_HEADER_MSG(dummy_msg, 0xABCDABCD);
+
+       wkup_m3->ipc_data.sleep_mode = IPC_CMD_DS0;
+       wkup_m3->ipc_data.param1  = DS_IPC_DEFAULT;
+       wkup_m3->ipc_data.param2  = DS_IPC_DEFAULT;
+
+       am33xx_wkup_m3_ipc_cmd(&wkup_m3->ipc_data);
+
+       wkup_m3->state = M3_STATE_MSG_FOR_LP;
+
+       mailbox_enable_irq(wkup_m3->mbox, IRQ_RX);
+
+       ret = mailbox_msg_send(wkup_m3->mbox, &dummy_msg);
+       if (ret) {
+               pr_err("A8<->CM3 MSG for LP failed\n");
+               am33xx_m3_state_machine_reset();
+               ret = -1;
+       }
+
+       /* Give some time to M3 to respond. 500msec is a random value here */
+       if (!wait_for_completion_timeout(&wkup_m3_sync,
+                                       msecs_to_jiffies(500))) {
+               pr_err("A8<->CM3 sync failure\n");
+               am33xx_m3_state_machine_reset();
+               ret = -1;
+       } else {
+               pr_debug("Message sent for entering DeepSleep mode\n");
+               mailbox_disable_irq(wkup_m3->mbox, IRQ_RX);
+       }
+
+       return ret;
+}
+
+static void am33xx_pm_end(void)
+{
+       mailbox_enable_irq(wkup_m3->mbox, IRQ_RX);
+
+       am33xx_m3_state_machine_reset();
+
+       enable_hlt();
+
+       return;
+}
+
+static const struct platform_suspend_ops am33xx_pm_ops = {
+       .begin          = am33xx_pm_begin,
+       .end            = am33xx_pm_end,
+       .enter          = am33xx_pm_enter,
+       .valid          = suspend_valid_only_mem,
+};
+
+static void am33xx_m3_state_machine_reset(void)
+{
+       int ret = 0;
+       struct mailbox_msg dummy_msg;
+
+       MAILBOX_FILL_HEADER_MSG(dummy_msg, 0xABCDABCD);
+
+       wkup_m3->ipc_data.sleep_mode    = IPC_CMD_RESET;
+       wkup_m3->ipc_data.param1        = DS_IPC_DEFAULT;
+       wkup_m3->ipc_data.param2        = DS_IPC_DEFAULT;
+
+       am33xx_wkup_m3_ipc_cmd(&wkup_m3->ipc_data);
+
+       wkup_m3->state = M3_STATE_MSG_FOR_RESET;
+
+       ret = mailbox_msg_send(wkup_m3->mbox, &dummy_msg);
+       if (!ret) {
+               pr_debug("Message sent for resetting M3 state machine\n");
+               /* Give some to M3 to respond. 500msec is a random value here */
+               if (!wait_for_completion_timeout(&wkup_m3_sync,
+                                               msecs_to_jiffies(500)))
+                       pr_err("A8<->CM3 sync failure\n");
+       } else {
+               pr_err("Could not reset M3 state machine!!!\n");
+               wkup_m3->state = M3_STATE_UNKNOWN;
+       }
+}
+#endif /* CONFIG_SUSPEND */
+
+/*
+ * Dummy notifier for the mailbox
+ * XXX: Get rid of this requirement once the MBX driver has been finalized
+ */
+static int wkup_mbox_msg(struct notifier_block *self, unsigned long len,
+               void *msg)
+{
+       return 0;
+}
+
+static struct notifier_block wkup_mbox_notifier = {
+       .notifier_call = wkup_mbox_msg,
+};
+
+static irqreturn_t wkup_m3_txev_handler(int irq, void *unused)
+{
+       am33xx_txev_eoi();
+
+       switch (wkup_m3->state) {
+       case M3_STATE_RESET:
+               wkup_m3->state = M3_STATE_INITED;
+               break;
+       case M3_STATE_MSG_FOR_RESET:
+               wkup_m3->state = M3_STATE_INITED;
+               mailbox_rx_flush(wkup_m3->mbox);
+               complete(&wkup_m3_sync);
+               break;
+       case M3_STATE_MSG_FOR_LP:
+               mailbox_rx_flush(wkup_m3->mbox);
+               complete(&wkup_m3_sync);
+               break;
+       case M3_STATE_UNKNOWN:
+               pr_err("IRQ %d with WKUP_M3 in unknown state\n", irq);
+               mailbox_rx_flush(wkup_m3->mbox);
+               return IRQ_NONE;
+       }
+
+       am33xx_txev_enable();
+       return IRQ_HANDLED;
+}
+
+static void am33xx_pm_firmware_cb(const struct firmware *fw, void *context)
+{
+       struct wkup_m3_context *wkup_m3_context = context;
+       struct platform_device *pdev = to_platform_device(wkup_m3_context->dev);
+       int ret = 0;
+
+       /* no firmware found */
+       if (!fw) {
+               dev_err(wkup_m3_context->dev, "request_firmware failed\n");
+               goto err;
+       }
+
+       memcpy((void *)wkup_m3_context->code, fw->data, fw->size);
+       pr_info("Copied the M3 firmware to UMEM\n");
+
+       wkup_m3->state = M3_STATE_RESET;
+
+       ret = omap_device_deassert_hardreset(pdev, "wkup_m3");
+       if (ret) {
+               pr_err("Could not deassert the reset for WKUP_M3\n");
+               goto err;
+       } else {
+#ifdef CONFIG_SUSPEND
+               suspend_set_ops(&am33xx_pm_ops);
+               /*
+                * Physical resume address to be used by ROM code
+                */
+               wkup_m3->ipc_data.resume_addr = (AM33XX_OCMC_END -
+                               am33xx_do_wfi_sz + am33xx_resume_offset + 0x4);
+#endif
+               return;
+       }
+
+err:
+       mailbox_put(wkup_m3_context->mbox, &wkup_mbox_notifier);
+}
+
+static int wkup_m3_init(void)
+{
+       int irq, ret = 0;
+       struct resource *mem;
+       struct platform_device *pdev = to_platform_device(wkup_m3->dev);
+
+       omap_device_enable_hwmods(to_omap_device(pdev));
+
+       /* Reserve the MBOX for sending messages to M3 */
+       wkup_m3->mbox = mailbox_get("wkup_m3", &wkup_mbox_notifier);
+       if (IS_ERR(wkup_m3->mbox)) {
+               pr_err("Could not reserve mailbox for A8->M3 IPC\n");
+               ret = -ENODEV;
+               goto exit;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (!irq) {
+               dev_err(wkup_m3->dev, "no irq resource\n");
+               ret = -ENXIO;
+               goto err;
+       }
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!mem) {
+               dev_err(wkup_m3->dev, "no memory resource\n");
+               ret = -ENXIO;
+               goto err;
+       }
+
+       wkup_m3->code = devm_request_and_ioremap(wkup_m3->dev, mem);
+       if (!wkup_m3->code) {
+               dev_err(wkup_m3->dev, "could not ioremap\n");
+               ret = -EADDRNOTAVAIL;
+               goto err;
+       }
+
+       ret = devm_request_irq(wkup_m3->dev, irq, wkup_m3_txev_handler,
+                 IRQF_DISABLED, "wkup_m3_txev", NULL);
+       if (ret) {
+               dev_err(wkup_m3->dev, "request_irq failed\n");
+               goto err;
+       } else {
+               am33xx_txev_enable();
+       }
+
+       pr_info("Trying to load am335x-pm-firmware.bin");
+
+       /* We don't want to delay boot */
+       request_firmware_nowait(THIS_MODULE, 0, "am335x-pm-firmware.bin",
+                               wkup_m3->dev, GFP_KERNEL, wkup_m3,
+                               am33xx_pm_firmware_cb);
+       return 0;
+
+err:
+       mailbox_put(wkup_m3->mbox, &wkup_mbox_notifier);
+exit:
+       return ret;
+}
+
+/*
+ * Push the minimal suspend-resume code to SRAM
+ */
+void am33xx_push_sram_idle(void)
+{
+       am33xx_do_wfi_sram = (void *)omap_sram_push
+                                       (am33xx_do_wfi, am33xx_do_wfi_sz);
+}
+
+static int __init am33xx_map_emif(void)
+{
+       am33xx_emif_base = ioremap(AM33XX_EMIF_BASE, SZ_32K);
+
+       if (!am33xx_emif_base)
+               return -ENOMEM;
+
+       return 0;
+}
+
+void __iomem *am33xx_get_emif_base(void)
+{
+       return am33xx_emif_base;
+}
+
+int __init am33xx_pm_init(void)
+{
+       int ret;
+
+       if (!soc_is_am33xx())
+               return -ENODEV;
+
+       pr_info("Power Management for AM33XX family\n");
+
+       /*
+        * By default the following IPs do not have MSTANDBY asserted
+        * which is necessary for PER domain transition. If the drivers
+        * are not compiled into the kernel HWMOD code will not change the
+        * state of the IPs if the IP was not never enabled
+        */
+       usb_oh          = omap_hwmod_lookup("usb_otg_hs");
+       tptc0_oh        = omap_hwmod_lookup("tptc0");
+       tptc1_oh        = omap_hwmod_lookup("tptc1");
+       tptc2_oh        = omap_hwmod_lookup("tptc2");
+       cpsw_oh         = omap_hwmod_lookup("cpgmac0");
+
+       gfx_pwrdm = pwrdm_lookup("gfx_pwrdm");
+       per_pwrdm = pwrdm_lookup("per_pwrdm");
+
+       gfx_l4ls_clkdm = clkdm_lookup("gfx_l4ls_gfx_clkdm");
+
+       if ((!usb_oh) || (!tptc0_oh) || (!tptc1_oh) || (!tptc2_oh) ||
+               (!cpsw_oh) || (!gfx_pwrdm) || (!per_pwrdm) ||
+               (!gfx_l4ls_clkdm)) {
+               ret = -ENODEV;
+               goto err;
+       }
+
+       wkup_m3 = kzalloc(sizeof(struct wkup_m3_context), GFP_KERNEL);
+       if (!wkup_m3) {
+               pr_err("Memory allocation failed\n");
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       ret = am33xx_map_emif();
+       if (ret) {
+               pr_err("Could not ioremap EMIF\n");
+               goto err;
+       }
+
+       (void) clkdm_for_each(omap_pm_clkdms_setup, NULL);
+
+       /* CEFUSE domain can be turned off post bootup */
+       cefuse_pwrdm = pwrdm_lookup("cefuse_pwrdm");
+       if (cefuse_pwrdm)
+               pwrdm_set_next_fpwrst(cefuse_pwrdm, PWRDM_FUNC_PWRST_OFF);
+       else
+               pr_err("Failed to get cefuse_pwrdm\n");
+
+       wkup_m3->dev = omap_device_get_by_hwmod_name("wkup_m3");
+
+       ret = wkup_m3_init();
+       if (ret)
+               pr_err("Could not initialise firmware loading\n");
+
+err:
+       return ret;
+}
arch/arm/mach-omap2/pm33xx.h new file +56 -1
new file mode 100644
index 0000000..13a2c85
@@ -0,0 +1,56 @@
+/*
+ * AM33XX Power Management Routines
+ *
+ * Copyright (C) 2012 Texas Instruments Inc.
+ * Vaibhav Bedia <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __ARCH_ARM_MACH_OMAP2_PM33XX_H
+#define __ARCH_ARM_MACH_OMAP2_PM33XX_H
+
+#include "control.h"
+
+#ifndef __ASSEMBLER__
+struct wkup_m3_context {
+       struct am33xx_ipc_data  ipc_data;
+       struct device           *dev;
+       struct firmware         *firmware;
+       struct mailbox          *mbox;
+       void __iomem            *code;
+       u8                      state;
+};
+
+#ifdef CONFIG_SUSPEND
+static void am33xx_m3_state_machine_reset(void);
+#else
+static inline void am33xx_m3_state_machine_reset(void) {}
+#endif /* CONFIG_SUSPEND */
+
+extern void __iomem *am33xx_get_emif_base(void);
+#endif
+
+#define        IPC_CMD_DS0                     0x3
+#define IPC_CMD_RESET                   0xe
+#define DS_IPC_DEFAULT                 0xffffffff
+
+#define IPC_RESP_SHIFT                 16
+#define IPC_RESP_MASK                  (0xffff << 16)
+
+#define M3_STATE_UNKNOWN               0
+#define M3_STATE_RESET                 1
+#define M3_STATE_INITED                        2
+#define M3_STATE_MSG_FOR_LP            3
+#define M3_STATE_MSG_FOR_RESET         4
+
+#define AM33XX_OCMC_END                        0x40310000
+#define AM33XX_EMIF_BASE               0x4C000000
+
+#endif
--

1.7.0.4

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

#67 lore raw

RFC v2 16/18 ARM: OMAP2+: AM33XX: Basic suspend resume support

Hi Daniel,

Quoted 11 lines · Daniel Mack

Hi Vaibhav,

Quoted 3 lines · Vaibhav Bedia

AM335x supports various low power modes as documented in section 8.1.4.3 of the AM335x TRM which is available @ http://www.ti.com/litv/pdf/spruh73f

May I ask about the plans for this series? Will you be re-spinning them for a current
tree, and what's the planned merge window for it?

I am tied up in some other stuff right now. I plan to address Kevin's comments and repost the patches soon. Since rc5 is already out i think v3.10 is not a realistic target any more but I do hope to have this accepted for v3.11.

Regards,
Vaibhav

#68 lore raw

RFC v2 17/18 ARM: OMAP2+: AM33XX: Select Mailbox when PM is enabled

In reply to Vaibhav Bedia

PM services on AM33XX depend on mailbox for communication with WKUP-M3 core so ensure that the right config options are selected. Thanks to Kevin Hilman <[email protected]> for the suggestion on updating the Kconfig and not just the omap2plus_defconfig which was done in the previous version of the AM33XX suspend-resume support.

Cc Tony Lingren <[email protected]>
Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>

---
v1->v2:
Update the Kconfig instead of the defconfig

 arch/arm/mach-omap2/Kconfig |    4 +++-
 1 files changed, 3 insertions(+), 1 deletions(-)
arch/arm/mach-omap2/Kconfig +3 -1
index 41b581f..c026fbd 100644
@@ -114,9 +114,11 @@ config SOC_AM33XX
 	bool "AM33XX support"
 	default y
 	select ARM_CPU_SUSPEND if PM
+	select COMMON_CLK
 	select CPU_V7
+	select MAILBOX if PM
 	select MULTI_IRQ_HANDLER
-	select COMMON_CLK
+	select OMAP2PLUS_MBOX if PM
 
 config OMAP_PACKAGE_ZAF
        bool
Signature · 1 line
1.7.0.4
#69 lore raw

RFC v2 17/18 ARM: OMAP2+: AM33XX: Select Mailbox when PM is enabled

Quoted 7 lines · Vaibhav Bedia

PM services on AM33XX depend on mailbox for communication with WKUP-M3 core so ensure that the right config options are selected. Thanks to Kevin Hilman <[email protected]> for the suggestion on updating the Kconfig and not just the omap2plus_defconfig which was done in the previous version of the AM33XX suspend-resume support.

Quoted 7 lines
Cc Tony Lingren <[email protected]>
Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>

---

Unrelated to series. You can post this patch separately.

regards
Santosh

#70 lore raw

RFC v2 17/18 ARM: OMAP2+: AM33XX: Select Mailbox when PM is enabled

Quoted 8 lines · Santosh
Quoted 7 lines · Vaibhav Bedia

PM services on AM33XX depend on mailbox for communication with WKUP-M3 core so ensure that the right config options are selected. Thanks to Kevin Hilman <[email protected]> for the suggestion on updating the Kconfig and not just the omap2plus_defconfig which was done in the previous version of the AM33XX suspend-resume support.

Quoted 9 lines
Quoted 7 lines
Cc Tony Lingren <[email protected]>
Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>

---

Unrelated to series. You can post this patch separately.

Ok. I kept it in this series so that it does not look to be some random patch.

Regards,
Vaibhav

#71 lore raw

RFC v2 18/18 ARM: OMAP2+: AM33XX: Hookup AM33XX PM code into OMAP builds

In reply to Vaibhav Bedia

With all the requisite changes in place we can now enable basic PM support on AM33XX. This patch updates the various OMAP files to enable suspend-resume on AM33XX.

Cc Tony Lingren <[email protected]>
Cc Santosh Shilimkar <[email protected]>
Cc Benoit Cousson <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>

---
v1->v2:
This is a new patch in the series

 arch/arm/mach-omap2/Makefile        |    2 ++
 arch/arm/mach-omap2/board-generic.c |    1 +
 arch/arm/mach-omap2/common.h        |   10 ++++++++++
 arch/arm/mach-omap2/io.c            |    7 +++++++
 arch/arm/mach-omap2/pm.h            |    7 +++++++
 arch/arm/mach-omap2/sram.c          |   10 +++++++++-
 arch/arm/mach-omap2/sram.h          |    2 ++
 7 files changed, 38 insertions(+), 1 deletions(-)
arch/arm/mach-omap2/Makefile +2 -0
index e61867b..a5db55b 100644
@@ -81,6 +81,7 @@ obj-$(CONFIG_ARCH_OMAP2)		+= sleep24xx.o
 obj-$(CONFIG_ARCH_OMAP3)		+= pm34xx.o sleep34xx.o
 obj-$(CONFIG_ARCH_OMAP4)		+= pm44xx.o omap-mpuss-lowpower.o
 obj-$(CONFIG_SOC_OMAP5)			+= omap-mpuss-lowpower.o
+obj-$(CONFIG_SOC_AM33XX)		+= pm33xx.o sleep33xx.o
 obj-$(CONFIG_PM_DEBUG)			+= pm-debug.o
 
 obj-$(CONFIG_POWER_AVS_OMAP)		+= sr_device.o
@@ -88,6 +89,7 @@ obj-$(CONFIG_POWER_AVS_OMAP_CLASS3)    += smartreflex-class3.o
 
 AFLAGS_sleep24xx.o			:=-Wa,-march=armv6
 AFLAGS_sleep34xx.o			:=-Wa,-march=armv7-a$(plus_sec)
+AFLAGS_sleep33xx.o			:=-Wa,-march=armv7-a$(plus_sec)
 
 ifeq ($(CONFIG_PM_VERBOSE),y)
 CFLAGS_pm_bus.o				+= -DDEBUG
arch/arm/mach-omap2/board-generic.c +1 -0
index 53cb380..1c9195d 100644
@@ -136,6 +136,7 @@ DT_MACHINE_START(AM33XX_DT, "Generic AM33XX (Flattened Device Tree)")
 	.reserve	= omap_reserve,
 	.map_io		= am33xx_map_io,
 	.init_early	= am33xx_init_early,
+	.init_late	= am33xx_init_late,
 	.init_irq	= omap_intc_of_init,
 	.handle_irq	= omap3_intc_handle_irq,
 	.init_machine	= omap_generic_init,
arch/arm/mach-omap2/common.h +10 -0
index 41d74fe..73cac5c 100644
@@ -68,6 +68,15 @@ static inline int omap4_pm_init(void)
 }
 #endif
 
+#if defined(CONFIG_PM) && defined(CONFIG_SOC_AM33XX)
+int am33xx_pm_init(void);
+#else
+static inline int am33xx_pm_init(void)
+{
+	return 0;
+}
+#endif
+
 #ifdef CONFIG_OMAP_MUX
 int omap_mux_late_init(void);
 #else
@@ -106,6 +115,7 @@ void omap2430_init_late(void);
 void omap3430_init_late(void);
 void omap35xx_init_late(void);
 void omap3630_init_late(void);
+void am33xx_init_late(void);
 void am35xx_init_late(void);
 void ti81xx_init_late(void);
 void omap4430_init_late(void);
arch/arm/mach-omap2/io.c +7 -0
index 2c3fdd6..b5637c6 100644
@@ -570,6 +570,13 @@ void __init am33xx_init_early(void)
 	omap_hwmod_init_postsetup();
 	am33xx_clk_init();
 }
+
+void __init am33xx_init_late(void)
+{
+	omap_mux_late_init();
+	omap2_common_pm_late_init();
+	am33xx_pm_init();
+}
 #endif
 
 #ifdef CONFIG_ARCH_OMAP4
arch/arm/mach-omap2/pm.h +7 -0
index 531a4de..118ebde 100644
@@ -76,6 +76,13 @@ extern unsigned int omap3_do_wfi_sz;
 /* ... and its pointer from SRAM after copy */
 extern void (*omap3_do_wfi_sram)(void);
 
+/* am33xx_do_wfi function pointer and size, for copy to SRAM */
+extern void am33xx_do_wfi(void);
+extern unsigned int am33xx_do_wfi_sz;
+extern unsigned int am33xx_resume_offset;
+/* ... and its pointer from SRAM after copy */
+extern void (*am33xx_do_wfi_sram)(void);
+
 /* save_secure_ram_context function pointer and size, for copy to SRAM */
 extern int save_secure_ram_context(u32 *addr);
 extern unsigned int save_secure_ram_context_sz;
arch/arm/mach-omap2/sram.c +9 -1
index 0ff0f06..7951e0d 100644
@@ -151,7 +151,7 @@ static void __init omap2_map_sram(void)
 		omap_sram_size -= SZ_16K;
 	}
 #endif
-	if (cpu_is_omap34xx()) {
+	if (cpu_is_omap34xx() || soc_is_am33xx()) {
 		/*
 		 * SRAM must be marked as non-cached on OMAP3 since the
 		 * CORE DPLL M2 divider change code (in SRAM) runs with the
@@ -282,10 +282,18 @@ static inline int omap34xx_sram_init(void)
 }
 #endif /* CONFIG_ARCH_OMAP3 */
 
+#ifdef CONFIG_SOC_AM33XX
 static inline int am33xx_sram_init(void)
 {
+	am33xx_push_sram_idle();
 	return 0;
 }
+#else
+static inline int am33xx_sram_init(void)
+{
+	return 0;
+}
+#endif
 
 int __init omap_sram_init(void)
 {
arch/arm/mach-omap2/sram.h +2 -0
index ca7277c..24788b5 100644
@@ -62,8 +62,10 @@ extern unsigned long omap3_sram_configure_core_dpll_sz;
 
 #ifdef CONFIG_PM
 extern void omap_push_sram_idle(void);
+extern void am33xx_push_sram_idle(void);
 #else
 static inline void omap_push_sram_idle(void) {}
+static inline void am33xx_push_sram_idle(void) {}
 #endif /* CONFIG_PM */
 
 #endif /* __ASSEMBLY__ */
Signature · 1 line
1.7.0.4
#72 lore raw

v2 cover ARM: OMAP2+: AM33XX: Add suspend-resume support

In reply to Vaibhav Bedia

Vaibhav,

Quoted 27 lines · Vaibhav Bedia

Hi,

This is the second version of the patch series for adding suspend-resume support for AM33XX. Based on the feedback received on the previous patch series [1] almost all the patches have undergone a bit a rework.

The 1st two patches depend on the changes for mailbox code migration from arch/arm/*-omap*/ to drivers/mailbox/ [2].

The patch series also depends on recent changes to the OMAP PM framework by Paul Walmsley.

I found it easiest to apply the AM33XX suspend-resume patches on top of Paul's TEST_pwrdm_post_fpwrst_devel_a_3.9 branch + the patches @ [2].

With these dependencies met, the PM code uses the firmware interface and expects the userspace to load the WKUP_M3 binary before the suspend-resume functionality is made available. The binary file (and the source-code for WKUP_M3) can be obtained from the 'next' branch at [3]. The WKUP_M3 binary can either be loaded post bootup via the sysfs entry './sys/devices/ocp.2/wkup_m3.4/firmware' or it can be included in the kernel image as part of the build process.

DDR3 specific changes have been skipped for now since mainline U-Boot exhibited stability issues on all the DDR3 based AM335x boards that i could lay my hands on.

Quoted 9 lines

I have done basic testing along with power measurments on the different power rails on the AM335x EVM. PER domain transition on the BeagleBone fails if the CPSW driver is included in the kernel and is yet to be root caused. Along with this issue more extensive testing on other OMAP platforms is also pending right now.

For more details on the AM335x suspend-resume support please refer to the changelog in the different patches.

I still haven't reviewed patch 15, 16 and 18 which adds suspend support. Will do that in coming days since they need a bit a closer look.

But as mentioned in some of the patches, you need to split this series since except 15, 16 and 18 which adds suspend support, rest of the patches are
- data file fixes
- timer suspend/resume update
- mailbox support, control module update.

Would be good to split the series to help the reviews.

Regards
Santosh

#73 lore raw

v2 cover ARM: OMAP2+: AM33XX: Add suspend-resume support

Hi Santosh,

Quoted 42 lines · Santosh

Vaibhav,

Quoted 27 lines · Vaibhav Bedia

Hi,

This is the second version of the patch series for adding suspend-resume support for AM33XX. Based on the feedback received on the previous patch series [1] almost all the patches have undergone a bit a rework.

The 1st two patches depend on the changes for mailbox code migration from arch/arm/*-omap*/ to drivers/mailbox/ [2].

The patch series also depends on recent changes to the OMAP PM framework by Paul Walmsley.

I found it easiest to apply the AM33XX suspend-resume patches on top of Paul's TEST_pwrdm_post_fpwrst_devel_a_3.9 branch + the patches @ [2].

With these dependencies met, the PM code uses the firmware interface and expects the userspace to load the WKUP_M3 binary before the suspend-resume functionality is made available. The binary file (and the source-code for WKUP_M3) can be obtained from the 'next' branch at [3]. The WKUP_M3 binary can either be loaded post bootup via the sysfs entry './sys/devices/ocp.2/wkup_m3.4/firmware' or it can be included in the kernel image as part of the build process.

DDR3 specific changes have been skipped for now since mainline U-Boot exhibited stability issues on all the DDR3 based AM335x boards that i could lay my hands on.

Quoted 9 lines

I have done basic testing along with power measurments on the different power rails on the AM335x EVM. PER domain transition on the BeagleBone fails if the CPSW driver is included in the kernel and is yet to be root caused. Along with this issue more extensive testing on other OMAP platforms is also pending right now.

For more details on the AM335x suspend-resume support please refer to the changelog in the different patches.

I still haven't reviewed patch 15, 16 and 18 which adds suspend support. Will do that in coming days since they need a bit a closer look.

Quoted 9 lines

But as mentioned in some of the patches, you need to split this series since except 15, 16 and 18 which adds suspend support, rest of the patches are
- data file fixes
- timer suspend/resume update
- mailbox support, control module update.

Would be good to split the series to help the reviews.

Sure. I'll split it up like you mentioned. Since all these changes are needed for a working suspend-resume I kept it in a single series to reduce dependencies and also to help set an initial context for the AM33xx PM changes.

Regards,
Vaibhav