All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 00/11] ARM: OMAP2+: AM33XX: Add suspend-resume support
@ 2014-07-11  2:55 ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11  2:55 UTC (permalink / raw)
  To: linux-arm-kernel, linux-omap
  Cc: Paul Walmsley, Kevin Hilman, Tony Lindgren, Tero Kristo,
	Nishanth Menon, Russ Dill, Santosh Shilimkar, Daniel Mack,
	Suman Anna, Benoit Cousson, Dave Gerlach

Hello,

This series adds suspend/resume support for am335x. Version 3 of this
series can be found at [1]. I apologize for the large delay between this
and the previous revision. This code has been heavily refined
since the last version based on the various comments received for v3. The
major change from previous version is moving all wkup_m3 code into a
remoteproc based driver. The new driver handles all IPC and fw loading
and exposes a small API to be used by PM code to achieve low power states.

Firmware that can be used for testing this can be found at [2] on branch
pm-remote-proc-v3, using am335x-pm-firmware.elf found in bin directory.
Please note this has changed from all previous versions and is no longer
the .bin file. Firmware can be built into kernel or placed in /lib/firmware
in rootfs for automatic loading during boot.

This series has several dependencies. The wkup_m3_rproc utilizes a mailbox
to communicate with the cm3 and depends on Suman's series for omap mbox
support [3], which has several dependencies of it's own, listed in the
cover letter. Also, a few changes to remoteproc itself were needed and
have been provided by Suman here [4]. The edma patch included in this
series was previously submitted by Daniel Mack and after discussion with
him we agreed to include an updated version with this series as resume
has a direct dependency on it due to hangs in mmc without it.

Because of the high number of dependencies I have pushed a branch for
testing here [6] if anyone desires to try it out on branch pm-ds0-v3.16.

As is this series will only suspend and resume one time and then
fail to resume afterwards due to the removal of direct PM code control
of hwmods that do not properly assert their MSTANDBY signal after a context
loss, discussed here [7]. In particular it is due to the usb_otg_hs hwmod
that currently has no driver controlling it in the kernel. The main
cause of the issue is that the SYSCONFIG register present within
the IP must be reprogrammed after every suspend cycle and this
only happens at boot if no driver is present. Work is in progress to
allow suspend to function with or without drivers for the troublesome
hwmods (cpgmac, usb_otg_hs, and tptc1-3) and will be provided in a separate
future patch. The previous suggestion of allowing omap_device to handle
it proved to be too invasive into both omap_device and omap_hwmod and
the approach of allowing the firmware to handle it is not possible due
to the inability of the CM3 to access the IPs causing the issue. I'd
be happy to discuss this at length if anybody is interested.

Regards,
Dave

[1] http://www.spinics.net/lists/linux-omap/msg95305.html
[2] https://github.com/dgerlach/am33x-cm3
[3] http://www.spinics.net/lists/linux-omap/msg108595.html
[4] http://www.spinics.net/lists/linux-omap/msg109173.html
[5] http://www.spinics.net/lists/linux-omap/msg100606.html
[6] https://github.com/dgerlach/linux-pm
[7] http://www.spinics.net/lists/linux-omap/msg95353.html

Daniel Mack (1):
  ARM: omap: edma: add suspend suspend/resume hooks

Dave Gerlach (8):
  memory: emif: Move EMIF register defines to include/linux/
  ARM: OMAP2+: Use pdata-quirks for wkup_m3 deassert_hardreset
  Documentation: dt: add ti,am3353_wkup_m3 bindings
  remoteproc: wkup_m3: Add wkup_m3 remote proc driver
  ARM: dts: am33xx: Update wkup_m3 node
  ARM: OMAP2+: AM33XX: Add assembly code for PM operations
  ARM: OMAP2+: AM33XX: Basic suspend resume support
  ARM: OMAP2+: AM33XX: Hookup AM33XX PM code into OMAP builds

Vaibhav Bedia (2):
  ARM: OMAP2+: timer: Add suspend-resume callbacks for clkevent device
  ARM: OMAP2+: AM33XX: Reserve memory to comply with EMIF spec

 .../bindings/remoteproc/wkup_m3_rproc.txt          |  46 ++
 arch/arm/boot/dts/am33xx.dtsi                      |   9 +-
 arch/arm/common/edma.c                             |  86 +++-
 arch/arm/mach-omap2/Kconfig                        |   1 +
 arch/arm/mach-omap2/Makefile                       |   2 +
 arch/arm/mach-omap2/board-generic.c                |   2 +-
 arch/arm/mach-omap2/common.c                       |  32 ++
 arch/arm/mach-omap2/common.h                       |  13 +
 arch/arm/mach-omap2/io.c                           |   2 +
 arch/arm/mach-omap2/pdata-quirks.c                 |  12 +
 arch/arm/mach-omap2/pm.h                           |   5 +
 arch/arm/mach-omap2/pm33xx.c                       | 346 +++++++++++++
 arch/arm/mach-omap2/pm33xx.h                       |  64 +++
 arch/arm/mach-omap2/sleep33xx.S                    | 380 ++++++++++++++
 arch/arm/mach-omap2/sram.c                         |  10 +-
 arch/arm/mach-omap2/sram.h                         |   2 +
 arch/arm/mach-omap2/timer.c                        |  28 ++
 drivers/memory/emif.h                              | 543 +-------------------
 drivers/remoteproc/Kconfig                         |  15 +
 drivers/remoteproc/Makefile                        |   1 +
 drivers/remoteproc/wkup_m3_rproc.c                 | 512 +++++++++++++++++++
 include/linux/platform_data/wkup_m3.h              |  17 +
 include/linux/ti_emif.h                            | 558 +++++++++++++++++++++
 include/linux/wkup_m3.h                            |  71 +++
 24 files changed, 2209 insertions(+), 548 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
 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
 create mode 100644 drivers/remoteproc/wkup_m3_rproc.c
 create mode 100644 include/linux/platform_data/wkup_m3.h
 create mode 100644 include/linux/ti_emif.h
 create mode 100644 include/linux/wkup_m3.h

-- 
1.9.0


^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 00/11] ARM: OMAP2+: AM33XX: Add suspend-resume support
@ 2014-07-11  2:55 ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11  2:55 UTC (permalink / raw)
  To: linux-arm-kernel

Hello,

This series adds suspend/resume support for am335x. Version 3 of this
series can be found at [1]. I apologize for the large delay between this
and the previous revision. This code has been heavily refined
since the last version based on the various comments received for v3. The
major change from previous version is moving all wkup_m3 code into a
remoteproc based driver. The new driver handles all IPC and fw loading
and exposes a small API to be used by PM code to achieve low power states.

Firmware that can be used for testing this can be found at [2] on branch
pm-remote-proc-v3, using am335x-pm-firmware.elf found in bin directory.
Please note this has changed from all previous versions and is no longer
the .bin file. Firmware can be built into kernel or placed in /lib/firmware
in rootfs for automatic loading during boot.

This series has several dependencies. The wkup_m3_rproc utilizes a mailbox
to communicate with the cm3 and depends on Suman's series for omap mbox
support [3], which has several dependencies of it's own, listed in the
cover letter. Also, a few changes to remoteproc itself were needed and
have been provided by Suman here [4]. The edma patch included in this
series was previously submitted by Daniel Mack and after discussion with
him we agreed to include an updated version with this series as resume
has a direct dependency on it due to hangs in mmc without it.

Because of the high number of dependencies I have pushed a branch for
testing here [6] if anyone desires to try it out on branch pm-ds0-v3.16.

As is this series will only suspend and resume one time and then
fail to resume afterwards due to the removal of direct PM code control
of hwmods that do not properly assert their MSTANDBY signal after a context
loss, discussed here [7]. In particular it is due to the usb_otg_hs hwmod
that currently has no driver controlling it in the kernel. The main
cause of the issue is that the SYSCONFIG register present within
the IP must be reprogrammed after every suspend cycle and this
only happens at boot if no driver is present. Work is in progress to
allow suspend to function with or without drivers for the troublesome
hwmods (cpgmac, usb_otg_hs, and tptc1-3) and will be provided in a separate
future patch. The previous suggestion of allowing omap_device to handle
it proved to be too invasive into both omap_device and omap_hwmod and
the approach of allowing the firmware to handle it is not possible due
to the inability of the CM3 to access the IPs causing the issue. I'd
be happy to discuss this at length if anybody is interested.

Regards,
Dave

[1] http://www.spinics.net/lists/linux-omap/msg95305.html
[2] https://github.com/dgerlach/am33x-cm3
[3] http://www.spinics.net/lists/linux-omap/msg108595.html
[4] http://www.spinics.net/lists/linux-omap/msg109173.html
[5] http://www.spinics.net/lists/linux-omap/msg100606.html
[6] https://github.com/dgerlach/linux-pm
[7] http://www.spinics.net/lists/linux-omap/msg95353.html

Daniel Mack (1):
  ARM: omap: edma: add suspend suspend/resume hooks

Dave Gerlach (8):
  memory: emif: Move EMIF register defines to include/linux/
  ARM: OMAP2+: Use pdata-quirks for wkup_m3 deassert_hardreset
  Documentation: dt: add ti,am3353_wkup_m3 bindings
  remoteproc: wkup_m3: Add wkup_m3 remote proc driver
  ARM: dts: am33xx: Update wkup_m3 node
  ARM: OMAP2+: AM33XX: Add assembly code for PM operations
  ARM: OMAP2+: AM33XX: Basic suspend resume support
  ARM: OMAP2+: AM33XX: Hookup AM33XX PM code into OMAP builds

Vaibhav Bedia (2):
  ARM: OMAP2+: timer: Add suspend-resume callbacks for clkevent device
  ARM: OMAP2+: AM33XX: Reserve memory to comply with EMIF spec

 .../bindings/remoteproc/wkup_m3_rproc.txt          |  46 ++
 arch/arm/boot/dts/am33xx.dtsi                      |   9 +-
 arch/arm/common/edma.c                             |  86 +++-
 arch/arm/mach-omap2/Kconfig                        |   1 +
 arch/arm/mach-omap2/Makefile                       |   2 +
 arch/arm/mach-omap2/board-generic.c                |   2 +-
 arch/arm/mach-omap2/common.c                       |  32 ++
 arch/arm/mach-omap2/common.h                       |  13 +
 arch/arm/mach-omap2/io.c                           |   2 +
 arch/arm/mach-omap2/pdata-quirks.c                 |  12 +
 arch/arm/mach-omap2/pm.h                           |   5 +
 arch/arm/mach-omap2/pm33xx.c                       | 346 +++++++++++++
 arch/arm/mach-omap2/pm33xx.h                       |  64 +++
 arch/arm/mach-omap2/sleep33xx.S                    | 380 ++++++++++++++
 arch/arm/mach-omap2/sram.c                         |  10 +-
 arch/arm/mach-omap2/sram.h                         |   2 +
 arch/arm/mach-omap2/timer.c                        |  28 ++
 drivers/memory/emif.h                              | 543 +-------------------
 drivers/remoteproc/Kconfig                         |  15 +
 drivers/remoteproc/Makefile                        |   1 +
 drivers/remoteproc/wkup_m3_rproc.c                 | 512 +++++++++++++++++++
 include/linux/platform_data/wkup_m3.h              |  17 +
 include/linux/ti_emif.h                            | 558 +++++++++++++++++++++
 include/linux/wkup_m3.h                            |  71 +++
 24 files changed, 2209 insertions(+), 548 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
 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
 create mode 100644 drivers/remoteproc/wkup_m3_rproc.c
 create mode 100644 include/linux/platform_data/wkup_m3.h
 create mode 100644 include/linux/ti_emif.h
 create mode 100644 include/linux/wkup_m3.h

-- 
1.9.0

^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 01/11] ARM: omap: edma: add suspend suspend/resume hooks
  2014-07-11  2:55 ` Dave Gerlach
@ 2014-07-11  2:55   ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11  2:55 UTC (permalink / raw)
  To: linux-arm-kernel, linux-omap
  Cc: Paul Walmsley, Kevin Hilman, Tony Lindgren, Tero Kristo,
	Nishanth Menon, Russ Dill, Santosh Shilimkar, Daniel Mack,
	Suman Anna, Benoit Cousson, Daniel Mack, Dave Gerlach

From: Daniel Mack <zonque@gmail.com>

This patch makes the edma driver resume correctly after suspend. Tested
on an AM33xx platform with cyclic audio streams and omap_hsmmc.

All information can be reconstructed by already known runtime
information.

As we now use some functions that were previously only used from __init
context, annotations had to be dropped.

[nm@ti.com: added error handling for runtime + suspend_late/early_resume]
Signed-off-by: Nishanth Menon <nm@ti.com>
Signed-off-by: Daniel Mack <zonque@gmail.com>
Tested-by: Joel Fernandes <joelf@ti.com>
Acked-by: Joel Fernandes <joelf@ti.com>
[d-gerlach@ti.com: updated to remove queue_tc_mapping use]
Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
---
 arch/arm/common/edma.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 84 insertions(+), 2 deletions(-)

diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c
index 485be42..8b1b9aa 100644
--- a/arch/arm/common/edma.c
+++ b/arch/arm/common/edma.c
@@ -244,6 +244,8 @@ struct edma {
 	/* list of channels with no even trigger; terminated by "-1" */
 	const s8	*noevent;
 
+	struct edma_soc_info *info;
+
 	/* The edma_inuse bit for each PaRAM slot is clear unless the
 	 * channel is in use ... by ARM or DSP, for QDMA, or whatever.
 	 */
@@ -295,7 +297,7 @@ static void map_dmach_queue(unsigned ctlr, unsigned ch_no,
 			~(0x7 << bit), queue_no << bit);
 }
 
-static void __init assign_priority_to_queue(unsigned ctlr, int queue_no,
+static void assign_priority_to_queue(unsigned ctlr, int queue_no,
 		int priority)
 {
 	int bit = queue_no * 4;
@@ -314,7 +316,7 @@ static void __init assign_priority_to_queue(unsigned ctlr, int queue_no,
  * included in that particular EDMA variant (Eg : dm646x)
  *
  */
-static void __init map_dmach_param(unsigned ctlr)
+static void map_dmach_param(unsigned ctlr)
 {
 	int i;
 	for (i = 0; i < EDMA_MAX_DMACH; i++)
@@ -1762,15 +1764,95 @@ static int edma_probe(struct platform_device *pdev)
 			edma_write_array2(j, EDMA_DRAE, i, 1, 0x0);
 			edma_write_array(j, EDMA_QRAE, i, 0x0);
 		}
+		edma_cc[j]->info = info[j];
 		arch_num_cc++;
 	}
 
 	return 0;
 }
 
+static int edma_pm_suspend(struct device *dev)
+{
+	int j, r;
+
+	r = pm_runtime_get_sync(dev);
+	if (r < 0) {
+		dev_err(dev, "%s: get_sync returned %d\n", __func__, r);
+		return r;
+	}
+
+	for (j = 0; j < arch_num_cc; j++) {
+		struct edma *ecc = edma_cc[j];
+
+		disable_irq(ecc->irq_res_start);
+		disable_irq(ecc->irq_res_end);
+	}
+
+	pm_runtime_put_sync(dev);
+
+	return 0;
+}
+
+static int edma_pm_resume(struct device *dev)
+{
+	int i, j, r;
+
+	r = pm_runtime_get_sync(dev);
+	if (r < 0) {
+		dev_err(dev, "%s: get_sync returned %d\n", __func__, r);
+		return r;
+	}
+
+	for (j = 0; j < arch_num_cc; j++) {
+		struct edma *cc = edma_cc[j];
+
+		s8 (*queue_priority_mapping)[2];
+
+		queue_priority_mapping = cc->info->queue_priority_mapping;
+
+		/* Event queue priority mapping */
+		for (i = 0; queue_priority_mapping[i][0] != -1; i++)
+			assign_priority_to_queue(j,
+						 queue_priority_mapping[i][0],
+						 queue_priority_mapping[i][1]);
+
+		/*
+		 * Map the channel to param entry if channel mapping logic
+		 * exist
+		 */
+		if (edma_read(j, EDMA_CCCFG) & CHMAP_EXIST)
+			map_dmach_param(j);
+
+		for (i = 0; i < cc->num_channels; i++) {
+			if (test_bit(i, cc->edma_inuse)) {
+				/* ensure access through shadow region 0 */
+				edma_or_array2(j, EDMA_DRAE, 0, i >> 5,
+					       BIT(i & 0x1f));
+
+				setup_dma_interrupt(i,
+						    cc->intr_data[i].callback,
+						    cc->intr_data[i].data);
+			}
+		}
+
+		enable_irq(cc->irq_res_start);
+		enable_irq(cc->irq_res_end);
+	}
+
+	pm_runtime_put_sync(dev);
+
+	return 0;
+}
+
+static const struct dev_pm_ops edma_pm_ops = {
+	.suspend_late	= edma_pm_suspend,
+	.resume_early	= edma_pm_resume,
+};
+
 static struct platform_driver edma_driver = {
 	.driver = {
 		.name	= "edma",
+		.pm	= &edma_pm_ops,
 		.of_match_table = edma_of_ids,
 	},
 	.probe = edma_probe,
-- 
1.9.0


^ permalink raw reply related	[flat|nested] 106+ messages in thread

* [PATCH v4 01/11] ARM: omap: edma: add suspend suspend/resume hooks
@ 2014-07-11  2:55   ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11  2:55 UTC (permalink / raw)
  To: linux-arm-kernel

From: Daniel Mack <zonque@gmail.com>

This patch makes the edma driver resume correctly after suspend. Tested
on an AM33xx platform with cyclic audio streams and omap_hsmmc.

All information can be reconstructed by already known runtime
information.

As we now use some functions that were previously only used from __init
context, annotations had to be dropped.

[nm at ti.com: added error handling for runtime + suspend_late/early_resume]
Signed-off-by: Nishanth Menon <nm@ti.com>
Signed-off-by: Daniel Mack <zonque@gmail.com>
Tested-by: Joel Fernandes <joelf@ti.com>
Acked-by: Joel Fernandes <joelf@ti.com>
[d-gerlach at ti.com: updated to remove queue_tc_mapping use]
Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
---
 arch/arm/common/edma.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 84 insertions(+), 2 deletions(-)

diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c
index 485be42..8b1b9aa 100644
--- a/arch/arm/common/edma.c
+++ b/arch/arm/common/edma.c
@@ -244,6 +244,8 @@ struct edma {
 	/* list of channels with no even trigger; terminated by "-1" */
 	const s8	*noevent;
 
+	struct edma_soc_info *info;
+
 	/* The edma_inuse bit for each PaRAM slot is clear unless the
 	 * channel is in use ... by ARM or DSP, for QDMA, or whatever.
 	 */
@@ -295,7 +297,7 @@ static void map_dmach_queue(unsigned ctlr, unsigned ch_no,
 			~(0x7 << bit), queue_no << bit);
 }
 
-static void __init assign_priority_to_queue(unsigned ctlr, int queue_no,
+static void assign_priority_to_queue(unsigned ctlr, int queue_no,
 		int priority)
 {
 	int bit = queue_no * 4;
@@ -314,7 +316,7 @@ static void __init assign_priority_to_queue(unsigned ctlr, int queue_no,
  * included in that particular EDMA variant (Eg : dm646x)
  *
  */
-static void __init map_dmach_param(unsigned ctlr)
+static void map_dmach_param(unsigned ctlr)
 {
 	int i;
 	for (i = 0; i < EDMA_MAX_DMACH; i++)
@@ -1762,15 +1764,95 @@ static int edma_probe(struct platform_device *pdev)
 			edma_write_array2(j, EDMA_DRAE, i, 1, 0x0);
 			edma_write_array(j, EDMA_QRAE, i, 0x0);
 		}
+		edma_cc[j]->info = info[j];
 		arch_num_cc++;
 	}
 
 	return 0;
 }
 
+static int edma_pm_suspend(struct device *dev)
+{
+	int j, r;
+
+	r = pm_runtime_get_sync(dev);
+	if (r < 0) {
+		dev_err(dev, "%s: get_sync returned %d\n", __func__, r);
+		return r;
+	}
+
+	for (j = 0; j < arch_num_cc; j++) {
+		struct edma *ecc = edma_cc[j];
+
+		disable_irq(ecc->irq_res_start);
+		disable_irq(ecc->irq_res_end);
+	}
+
+	pm_runtime_put_sync(dev);
+
+	return 0;
+}
+
+static int edma_pm_resume(struct device *dev)
+{
+	int i, j, r;
+
+	r = pm_runtime_get_sync(dev);
+	if (r < 0) {
+		dev_err(dev, "%s: get_sync returned %d\n", __func__, r);
+		return r;
+	}
+
+	for (j = 0; j < arch_num_cc; j++) {
+		struct edma *cc = edma_cc[j];
+
+		s8 (*queue_priority_mapping)[2];
+
+		queue_priority_mapping = cc->info->queue_priority_mapping;
+
+		/* Event queue priority mapping */
+		for (i = 0; queue_priority_mapping[i][0] != -1; i++)
+			assign_priority_to_queue(j,
+						 queue_priority_mapping[i][0],
+						 queue_priority_mapping[i][1]);
+
+		/*
+		 * Map the channel to param entry if channel mapping logic
+		 * exist
+		 */
+		if (edma_read(j, EDMA_CCCFG) & CHMAP_EXIST)
+			map_dmach_param(j);
+
+		for (i = 0; i < cc->num_channels; i++) {
+			if (test_bit(i, cc->edma_inuse)) {
+				/* ensure access through shadow region 0 */
+				edma_or_array2(j, EDMA_DRAE, 0, i >> 5,
+					       BIT(i & 0x1f));
+
+				setup_dma_interrupt(i,
+						    cc->intr_data[i].callback,
+						    cc->intr_data[i].data);
+			}
+		}
+
+		enable_irq(cc->irq_res_start);
+		enable_irq(cc->irq_res_end);
+	}
+
+	pm_runtime_put_sync(dev);
+
+	return 0;
+}
+
+static const struct dev_pm_ops edma_pm_ops = {
+	.suspend_late	= edma_pm_suspend,
+	.resume_early	= edma_pm_resume,
+};
+
 static struct platform_driver edma_driver = {
 	.driver = {
 		.name	= "edma",
+		.pm	= &edma_pm_ops,
 		.of_match_table = edma_of_ids,
 	},
 	.probe = edma_probe,
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 106+ messages in thread

* [PATCH v4 02/11] memory: emif: Move EMIF register defines to include/linux/
  2014-07-11  2:55 ` Dave Gerlach
@ 2014-07-11  2:55   ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11  2:55 UTC (permalink / raw)
  To: linux-arm-kernel, linux-omap
  Cc: Paul Walmsley, Kevin Hilman, Tony Lindgren, Tero Kristo,
	Nishanth Menon, Russ Dill, Santosh Shilimkar, Daniel Mack,
	Suman Anna, Benoit Cousson, Dave Gerlach

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 register defines in the emif header file to include/linux so that
both the EMIF driver and the AM33XX PM code can benefit.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
Reviewed-by: Russ Dill <russ.dill@ti.com>
Acked-by: Santosh Shililmar <santosh.shilimkar@ti.com>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
v3->v4:
patch unchanged from original:
	http://www.spinics.net/lists/linux-omap/msg95314.html

 drivers/memory/emif.h   | 543 +---------------------------------------------
 include/linux/ti_emif.h | 558 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 559 insertions(+), 542 deletions(-)
 create mode 100644 include/linux/ti_emif.h

diff --git a/drivers/memory/emif.h b/drivers/memory/emif.h
index bfe08ba..8214f07 100644
--- a/drivers/memory/emif.h
+++ b/drivers/memory/emif.h
@@ -12,548 +12,7 @@
 #ifndef __EMIF_H
 #define __EMIF_H
 
-/*
- * Maximum number of different frequencies supported by EMIF driver
- * Determines the number of entries in the pointer array for register
- * cache
- */
-#define EMIF_MAX_NUM_FREQUENCIES			6
-
-/* State of the core voltage */
-#define DDR_VOLTAGE_STABLE				0
-#define DDR_VOLTAGE_RAMPING				1
-
-/* Defines for timing De-rating */
-#define EMIF_NORMAL_TIMINGS				0
-#define EMIF_DERATED_TIMINGS				1
-
-/* Length of the forced read idle period in terms of cycles */
-#define EMIF_READ_IDLE_LEN_VAL				5
-
-/*
- * forced read idle interval to be used when voltage
- * is changed as part of DVFS/DPS - 1ms
- */
-#define READ_IDLE_INTERVAL_DVFS				(1*1000000)
-
-/*
- * Forced read idle interval to be used when voltage is stable
- * 50us - or maximum value will do
- */
-#define READ_IDLE_INTERVAL_NORMAL			(50*1000000)
-
-/* DLL calibration interval when voltage is NOT stable - 1us */
-#define DLL_CALIB_INTERVAL_DVFS				(1*1000000)
-
-#define DLL_CALIB_ACK_WAIT_VAL				5
-
-/* Interval between ZQCS commands - hw team recommended value */
-#define EMIF_ZQCS_INTERVAL_US				(50*1000)
-/* Enable ZQ Calibration on exiting Self-refresh */
-#define ZQ_SFEXITEN_ENABLE				1
-/*
- * ZQ Calibration simultaneously on both chip-selects:
- * Needs one calibration resistor per CS
- */
-#define	ZQ_DUALCALEN_DISABLE				0
-#define	ZQ_DUALCALEN_ENABLE				1
-
-#define T_ZQCS_DEFAULT_NS				90
-#define T_ZQCL_DEFAULT_NS				360
-#define T_ZQINIT_DEFAULT_NS				1000
-
-/* DPD_EN */
-#define DPD_DISABLE					0
-#define DPD_ENABLE					1
-
-/*
- * Default values for the low-power entry to be used if not provided by user.
- * OMAP4/5 has a hw bug(i735) due to which this value can not be less than 512
- * Timeout values are in DDR clock 'cycles' and frequency threshold in Hz
- */
-#define EMIF_LP_MODE_TIMEOUT_PERFORMANCE		2048
-#define EMIF_LP_MODE_TIMEOUT_POWER			512
-#define EMIF_LP_MODE_FREQ_THRESHOLD			400000000
-
-/* DDR_PHY_CTRL_1 values for EMIF4D - ATTILA PHY combination */
-#define EMIF_DDR_PHY_CTRL_1_BASE_VAL_ATTILAPHY		0x049FF000
-#define EMIF_DLL_SLAVE_DLY_CTRL_400_MHZ_ATTILAPHY	0x41
-#define EMIF_DLL_SLAVE_DLY_CTRL_200_MHZ_ATTILAPHY	0x80
-#define EMIF_DLL_SLAVE_DLY_CTRL_100_MHZ_AND_LESS_ATTILAPHY 0xFF
-
-/* DDR_PHY_CTRL_1 values for EMIF4D5 INTELLIPHY combination */
-#define EMIF_DDR_PHY_CTRL_1_BASE_VAL_INTELLIPHY		0x0E084200
-#define EMIF_PHY_TOTAL_READ_LATENCY_INTELLIPHY_PS	10000
-
-/* TEMP_ALERT_CONFIG - corresponding to temp gradient 5 C/s */
-#define TEMP_ALERT_POLL_INTERVAL_DEFAULT_MS		360
-
-#define EMIF_T_CSTA					3
-#define EMIF_T_PDLL_UL					128
-
-/* External PHY control registers magic values */
-#define EMIF_EXT_PHY_CTRL_1_VAL				0x04020080
-#define EMIF_EXT_PHY_CTRL_5_VAL				0x04010040
-#define EMIF_EXT_PHY_CTRL_6_VAL				0x01004010
-#define EMIF_EXT_PHY_CTRL_7_VAL				0x00001004
-#define EMIF_EXT_PHY_CTRL_8_VAL				0x04010040
-#define EMIF_EXT_PHY_CTRL_9_VAL				0x01004010
-#define EMIF_EXT_PHY_CTRL_10_VAL			0x00001004
-#define EMIF_EXT_PHY_CTRL_11_VAL			0x00000000
-#define EMIF_EXT_PHY_CTRL_12_VAL			0x00000000
-#define EMIF_EXT_PHY_CTRL_13_VAL			0x00000000
-#define EMIF_EXT_PHY_CTRL_14_VAL			0x80080080
-#define EMIF_EXT_PHY_CTRL_15_VAL			0x00800800
-#define EMIF_EXT_PHY_CTRL_16_VAL			0x08102040
-#define EMIF_EXT_PHY_CTRL_17_VAL			0x00000001
-#define EMIF_EXT_PHY_CTRL_18_VAL			0x540A8150
-#define EMIF_EXT_PHY_CTRL_19_VAL			0xA81502A0
-#define EMIF_EXT_PHY_CTRL_20_VAL			0x002A0540
-#define EMIF_EXT_PHY_CTRL_21_VAL			0x00000000
-#define EMIF_EXT_PHY_CTRL_22_VAL			0x00000000
-#define EMIF_EXT_PHY_CTRL_23_VAL			0x00000000
-#define EMIF_EXT_PHY_CTRL_24_VAL			0x00000077
-
-#define EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS	1200
-
-/* Registers offset */
-#define EMIF_MODULE_ID_AND_REVISION			0x0000
-#define EMIF_STATUS					0x0004
-#define EMIF_SDRAM_CONFIG				0x0008
-#define EMIF_SDRAM_CONFIG_2				0x000c
-#define EMIF_SDRAM_REFRESH_CONTROL			0x0010
-#define EMIF_SDRAM_REFRESH_CTRL_SHDW			0x0014
-#define EMIF_SDRAM_TIMING_1				0x0018
-#define EMIF_SDRAM_TIMING_1_SHDW			0x001c
-#define EMIF_SDRAM_TIMING_2				0x0020
-#define EMIF_SDRAM_TIMING_2_SHDW			0x0024
-#define EMIF_SDRAM_TIMING_3				0x0028
-#define EMIF_SDRAM_TIMING_3_SHDW			0x002c
-#define EMIF_LPDDR2_NVM_TIMING				0x0030
-#define EMIF_LPDDR2_NVM_TIMING_SHDW			0x0034
-#define EMIF_POWER_MANAGEMENT_CONTROL			0x0038
-#define EMIF_POWER_MANAGEMENT_CTRL_SHDW			0x003c
-#define EMIF_LPDDR2_MODE_REG_DATA			0x0040
-#define EMIF_LPDDR2_MODE_REG_CONFIG			0x0050
-#define EMIF_OCP_CONFIG					0x0054
-#define EMIF_OCP_CONFIG_VALUE_1				0x0058
-#define EMIF_OCP_CONFIG_VALUE_2				0x005c
-#define EMIF_IODFT_TEST_LOGIC_GLOBAL_CONTROL		0x0060
-#define EMIF_IODFT_TEST_LOGIC_CTRL_MISR_RESULT		0x0064
-#define EMIF_IODFT_TEST_LOGIC_ADDRESS_MISR_RESULT	0x0068
-#define EMIF_IODFT_TEST_LOGIC_DATA_MISR_RESULT_1	0x006c
-#define EMIF_IODFT_TEST_LOGIC_DATA_MISR_RESULT_2	0x0070
-#define EMIF_IODFT_TEST_LOGIC_DATA_MISR_RESULT_3	0x0074
-#define EMIF_PERFORMANCE_COUNTER_1			0x0080
-#define EMIF_PERFORMANCE_COUNTER_2			0x0084
-#define EMIF_PERFORMANCE_COUNTER_CONFIG			0x0088
-#define EMIF_PERFORMANCE_COUNTER_MASTER_REGION_SELECT	0x008c
-#define EMIF_PERFORMANCE_COUNTER_TIME			0x0090
-#define EMIF_MISC_REG					0x0094
-#define EMIF_DLL_CALIB_CTRL				0x0098
-#define EMIF_DLL_CALIB_CTRL_SHDW			0x009c
-#define EMIF_END_OF_INTERRUPT				0x00a0
-#define EMIF_SYSTEM_OCP_INTERRUPT_RAW_STATUS		0x00a4
-#define EMIF_LL_OCP_INTERRUPT_RAW_STATUS		0x00a8
-#define EMIF_SYSTEM_OCP_INTERRUPT_STATUS		0x00ac
-#define EMIF_LL_OCP_INTERRUPT_STATUS			0x00b0
-#define EMIF_SYSTEM_OCP_INTERRUPT_ENABLE_SET		0x00b4
-#define EMIF_LL_OCP_INTERRUPT_ENABLE_SET		0x00b8
-#define EMIF_SYSTEM_OCP_INTERRUPT_ENABLE_CLEAR		0x00bc
-#define EMIF_LL_OCP_INTERRUPT_ENABLE_CLEAR		0x00c0
-#define EMIF_SDRAM_OUTPUT_IMPEDANCE_CALIBRATION_CONFIG	0x00c8
-#define EMIF_TEMPERATURE_ALERT_CONFIG			0x00cc
-#define EMIF_OCP_ERROR_LOG				0x00d0
-#define EMIF_READ_WRITE_LEVELING_RAMP_WINDOW		0x00d4
-#define EMIF_READ_WRITE_LEVELING_RAMP_CONTROL		0x00d8
-#define EMIF_READ_WRITE_LEVELING_CONTROL		0x00dc
-#define EMIF_DDR_PHY_CTRL_1				0x00e4
-#define EMIF_DDR_PHY_CTRL_1_SHDW			0x00e8
-#define EMIF_DDR_PHY_CTRL_2				0x00ec
-#define EMIF_PRIORITY_TO_CLASS_OF_SERVICE_MAPPING	0x0100
-#define EMIF_CONNECTION_ID_TO_CLASS_OF_SERVICE_1_MAPPING 0x0104
-#define EMIF_CONNECTION_ID_TO_CLASS_OF_SERVICE_2_MAPPING 0x0108
-#define EMIF_READ_WRITE_EXECUTION_THRESHOLD		0x0120
-#define EMIF_COS_CONFIG					0x0124
-#define EMIF_PHY_STATUS_1				0x0140
-#define EMIF_PHY_STATUS_2				0x0144
-#define EMIF_PHY_STATUS_3				0x0148
-#define EMIF_PHY_STATUS_4				0x014c
-#define EMIF_PHY_STATUS_5				0x0150
-#define EMIF_PHY_STATUS_6				0x0154
-#define EMIF_PHY_STATUS_7				0x0158
-#define EMIF_PHY_STATUS_8				0x015c
-#define EMIF_PHY_STATUS_9				0x0160
-#define EMIF_PHY_STATUS_10				0x0164
-#define EMIF_PHY_STATUS_11				0x0168
-#define EMIF_PHY_STATUS_12				0x016c
-#define EMIF_PHY_STATUS_13				0x0170
-#define EMIF_PHY_STATUS_14				0x0174
-#define EMIF_PHY_STATUS_15				0x0178
-#define EMIF_PHY_STATUS_16				0x017c
-#define EMIF_PHY_STATUS_17				0x0180
-#define EMIF_PHY_STATUS_18				0x0184
-#define EMIF_PHY_STATUS_19				0x0188
-#define EMIF_PHY_STATUS_20				0x018c
-#define EMIF_PHY_STATUS_21				0x0190
-#define EMIF_EXT_PHY_CTRL_1				0x0200
-#define EMIF_EXT_PHY_CTRL_1_SHDW			0x0204
-#define EMIF_EXT_PHY_CTRL_2				0x0208
-#define EMIF_EXT_PHY_CTRL_2_SHDW			0x020c
-#define EMIF_EXT_PHY_CTRL_3				0x0210
-#define EMIF_EXT_PHY_CTRL_3_SHDW			0x0214
-#define EMIF_EXT_PHY_CTRL_4				0x0218
-#define EMIF_EXT_PHY_CTRL_4_SHDW			0x021c
-#define EMIF_EXT_PHY_CTRL_5				0x0220
-#define EMIF_EXT_PHY_CTRL_5_SHDW			0x0224
-#define EMIF_EXT_PHY_CTRL_6				0x0228
-#define EMIF_EXT_PHY_CTRL_6_SHDW			0x022c
-#define EMIF_EXT_PHY_CTRL_7				0x0230
-#define EMIF_EXT_PHY_CTRL_7_SHDW			0x0234
-#define EMIF_EXT_PHY_CTRL_8				0x0238
-#define EMIF_EXT_PHY_CTRL_8_SHDW			0x023c
-#define EMIF_EXT_PHY_CTRL_9				0x0240
-#define EMIF_EXT_PHY_CTRL_9_SHDW			0x0244
-#define EMIF_EXT_PHY_CTRL_10				0x0248
-#define EMIF_EXT_PHY_CTRL_10_SHDW			0x024c
-#define EMIF_EXT_PHY_CTRL_11				0x0250
-#define EMIF_EXT_PHY_CTRL_11_SHDW			0x0254
-#define EMIF_EXT_PHY_CTRL_12				0x0258
-#define EMIF_EXT_PHY_CTRL_12_SHDW			0x025c
-#define EMIF_EXT_PHY_CTRL_13				0x0260
-#define EMIF_EXT_PHY_CTRL_13_SHDW			0x0264
-#define EMIF_EXT_PHY_CTRL_14				0x0268
-#define EMIF_EXT_PHY_CTRL_14_SHDW			0x026c
-#define EMIF_EXT_PHY_CTRL_15				0x0270
-#define EMIF_EXT_PHY_CTRL_15_SHDW			0x0274
-#define EMIF_EXT_PHY_CTRL_16				0x0278
-#define EMIF_EXT_PHY_CTRL_16_SHDW			0x027c
-#define EMIF_EXT_PHY_CTRL_17				0x0280
-#define EMIF_EXT_PHY_CTRL_17_SHDW			0x0284
-#define EMIF_EXT_PHY_CTRL_18				0x0288
-#define EMIF_EXT_PHY_CTRL_18_SHDW			0x028c
-#define EMIF_EXT_PHY_CTRL_19				0x0290
-#define EMIF_EXT_PHY_CTRL_19_SHDW			0x0294
-#define EMIF_EXT_PHY_CTRL_20				0x0298
-#define EMIF_EXT_PHY_CTRL_20_SHDW			0x029c
-#define EMIF_EXT_PHY_CTRL_21				0x02a0
-#define EMIF_EXT_PHY_CTRL_21_SHDW			0x02a4
-#define EMIF_EXT_PHY_CTRL_22				0x02a8
-#define EMIF_EXT_PHY_CTRL_22_SHDW			0x02ac
-#define EMIF_EXT_PHY_CTRL_23				0x02b0
-#define EMIF_EXT_PHY_CTRL_23_SHDW			0x02b4
-#define EMIF_EXT_PHY_CTRL_24				0x02b8
-#define EMIF_EXT_PHY_CTRL_24_SHDW			0x02bc
-#define EMIF_EXT_PHY_CTRL_25				0x02c0
-#define EMIF_EXT_PHY_CTRL_25_SHDW			0x02c4
-#define EMIF_EXT_PHY_CTRL_26				0x02c8
-#define EMIF_EXT_PHY_CTRL_26_SHDW			0x02cc
-#define EMIF_EXT_PHY_CTRL_27				0x02d0
-#define EMIF_EXT_PHY_CTRL_27_SHDW			0x02d4
-#define EMIF_EXT_PHY_CTRL_28				0x02d8
-#define EMIF_EXT_PHY_CTRL_28_SHDW			0x02dc
-#define EMIF_EXT_PHY_CTRL_29				0x02e0
-#define EMIF_EXT_PHY_CTRL_29_SHDW			0x02e4
-#define EMIF_EXT_PHY_CTRL_30				0x02e8
-#define EMIF_EXT_PHY_CTRL_30_SHDW			0x02ec
-
-/* Registers shifts and masks */
-
-/* EMIF_MODULE_ID_AND_REVISION */
-#define SCHEME_SHIFT					30
-#define SCHEME_MASK					(0x3 << 30)
-#define MODULE_ID_SHIFT					16
-#define MODULE_ID_MASK					(0xfff << 16)
-#define RTL_VERSION_SHIFT				11
-#define RTL_VERSION_MASK				(0x1f << 11)
-#define MAJOR_REVISION_SHIFT				8
-#define MAJOR_REVISION_MASK				(0x7 << 8)
-#define MINOR_REVISION_SHIFT				0
-#define MINOR_REVISION_MASK				(0x3f << 0)
-
-/* STATUS */
-#define BE_SHIFT					31
-#define BE_MASK						(1 << 31)
-#define DUAL_CLK_MODE_SHIFT				30
-#define DUAL_CLK_MODE_MASK				(1 << 30)
-#define FAST_INIT_SHIFT					29
-#define FAST_INIT_MASK					(1 << 29)
-#define RDLVLGATETO_SHIFT				6
-#define RDLVLGATETO_MASK				(1 << 6)
-#define RDLVLTO_SHIFT					5
-#define RDLVLTO_MASK					(1 << 5)
-#define WRLVLTO_SHIFT					4
-#define WRLVLTO_MASK					(1 << 4)
-#define PHY_DLL_READY_SHIFT				2
-#define PHY_DLL_READY_MASK				(1 << 2)
-
-/* SDRAM_CONFIG */
-#define SDRAM_TYPE_SHIFT				29
-#define SDRAM_TYPE_MASK					(0x7 << 29)
-#define IBANK_POS_SHIFT					27
-#define IBANK_POS_MASK					(0x3 << 27)
-#define DDR_TERM_SHIFT					24
-#define DDR_TERM_MASK					(0x7 << 24)
-#define DDR2_DDQS_SHIFT					23
-#define DDR2_DDQS_MASK					(1 << 23)
-#define DYN_ODT_SHIFT					21
-#define DYN_ODT_MASK					(0x3 << 21)
-#define DDR_DISABLE_DLL_SHIFT				20
-#define DDR_DISABLE_DLL_MASK				(1 << 20)
-#define SDRAM_DRIVE_SHIFT				18
-#define SDRAM_DRIVE_MASK				(0x3 << 18)
-#define CWL_SHIFT					16
-#define CWL_MASK					(0x3 << 16)
-#define NARROW_MODE_SHIFT				14
-#define NARROW_MODE_MASK				(0x3 << 14)
-#define CL_SHIFT					10
-#define CL_MASK						(0xf << 10)
-#define ROWSIZE_SHIFT					7
-#define ROWSIZE_MASK					(0x7 << 7)
-#define IBANK_SHIFT					4
-#define IBANK_MASK					(0x7 << 4)
-#define EBANK_SHIFT					3
-#define EBANK_MASK					(1 << 3)
-#define PAGESIZE_SHIFT					0
-#define PAGESIZE_MASK					(0x7 << 0)
-
-/* SDRAM_CONFIG_2 */
-#define CS1NVMEN_SHIFT					30
-#define CS1NVMEN_MASK					(1 << 30)
-#define EBANK_POS_SHIFT					27
-#define EBANK_POS_MASK					(1 << 27)
-#define RDBNUM_SHIFT					4
-#define RDBNUM_MASK					(0x3 << 4)
-#define RDBSIZE_SHIFT					0
-#define RDBSIZE_MASK					(0x7 << 0)
-
-/* SDRAM_REFRESH_CONTROL */
-#define INITREF_DIS_SHIFT				31
-#define INITREF_DIS_MASK				(1 << 31)
-#define SRT_SHIFT					29
-#define SRT_MASK					(1 << 29)
-#define ASR_SHIFT					28
-#define ASR_MASK					(1 << 28)
-#define PASR_SHIFT					24
-#define PASR_MASK					(0x7 << 24)
-#define REFRESH_RATE_SHIFT				0
-#define REFRESH_RATE_MASK				(0xffff << 0)
-
-/* SDRAM_TIMING_1 */
-#define T_RTW_SHIFT					29
-#define T_RTW_MASK					(0x7 << 29)
-#define T_RP_SHIFT					25
-#define T_RP_MASK					(0xf << 25)
-#define T_RCD_SHIFT					21
-#define T_RCD_MASK					(0xf << 21)
-#define T_WR_SHIFT					17
-#define T_WR_MASK					(0xf << 17)
-#define T_RAS_SHIFT					12
-#define T_RAS_MASK					(0x1f << 12)
-#define T_RC_SHIFT					6
-#define T_RC_MASK					(0x3f << 6)
-#define T_RRD_SHIFT					3
-#define T_RRD_MASK					(0x7 << 3)
-#define T_WTR_SHIFT					0
-#define T_WTR_MASK					(0x7 << 0)
-
-/* SDRAM_TIMING_2 */
-#define T_XP_SHIFT					28
-#define T_XP_MASK					(0x7 << 28)
-#define T_ODT_SHIFT					25
-#define T_ODT_MASK					(0x7 << 25)
-#define T_XSNR_SHIFT					16
-#define T_XSNR_MASK					(0x1ff << 16)
-#define T_XSRD_SHIFT					6
-#define T_XSRD_MASK					(0x3ff << 6)
-#define T_RTP_SHIFT					3
-#define T_RTP_MASK					(0x7 << 3)
-#define T_CKE_SHIFT					0
-#define T_CKE_MASK					(0x7 << 0)
-
-/* SDRAM_TIMING_3 */
-#define T_PDLL_UL_SHIFT					28
-#define T_PDLL_UL_MASK					(0xf << 28)
-#define T_CSTA_SHIFT					24
-#define T_CSTA_MASK					(0xf << 24)
-#define T_CKESR_SHIFT					21
-#define T_CKESR_MASK					(0x7 << 21)
-#define ZQ_ZQCS_SHIFT					15
-#define ZQ_ZQCS_MASK					(0x3f << 15)
-#define T_TDQSCKMAX_SHIFT				13
-#define T_TDQSCKMAX_MASK				(0x3 << 13)
-#define T_RFC_SHIFT					4
-#define T_RFC_MASK					(0x1ff << 4)
-#define T_RAS_MAX_SHIFT					0
-#define T_RAS_MAX_MASK					(0xf << 0)
-
-/* POWER_MANAGEMENT_CONTROL */
-#define PD_TIM_SHIFT					12
-#define PD_TIM_MASK					(0xf << 12)
-#define DPD_EN_SHIFT					11
-#define DPD_EN_MASK					(1 << 11)
-#define LP_MODE_SHIFT					8
-#define LP_MODE_MASK					(0x7 << 8)
-#define SR_TIM_SHIFT					4
-#define SR_TIM_MASK					(0xf << 4)
-#define CS_TIM_SHIFT					0
-#define CS_TIM_MASK					(0xf << 0)
-
-/* LPDDR2_MODE_REG_DATA */
-#define VALUE_0_SHIFT					0
-#define VALUE_0_MASK					(0x7f << 0)
-
-/* LPDDR2_MODE_REG_CONFIG */
-#define CS_SHIFT					31
-#define CS_MASK						(1 << 31)
-#define REFRESH_EN_SHIFT				30
-#define REFRESH_EN_MASK					(1 << 30)
-#define ADDRESS_SHIFT					0
-#define ADDRESS_MASK					(0xff << 0)
-
-/* OCP_CONFIG */
-#define SYS_THRESH_MAX_SHIFT				24
-#define SYS_THRESH_MAX_MASK				(0xf << 24)
-#define MPU_THRESH_MAX_SHIFT				20
-#define MPU_THRESH_MAX_MASK				(0xf << 20)
-#define LL_THRESH_MAX_SHIFT				16
-#define LL_THRESH_MAX_MASK				(0xf << 16)
-
-/* PERFORMANCE_COUNTER_1 */
-#define COUNTER1_SHIFT					0
-#define COUNTER1_MASK					(0xffffffff << 0)
-
-/* PERFORMANCE_COUNTER_2 */
-#define COUNTER2_SHIFT					0
-#define COUNTER2_MASK					(0xffffffff << 0)
-
-/* PERFORMANCE_COUNTER_CONFIG */
-#define CNTR2_MCONNID_EN_SHIFT				31
-#define CNTR2_MCONNID_EN_MASK				(1 << 31)
-#define CNTR2_REGION_EN_SHIFT				30
-#define CNTR2_REGION_EN_MASK				(1 << 30)
-#define CNTR2_CFG_SHIFT					16
-#define CNTR2_CFG_MASK					(0xf << 16)
-#define CNTR1_MCONNID_EN_SHIFT				15
-#define CNTR1_MCONNID_EN_MASK				(1 << 15)
-#define CNTR1_REGION_EN_SHIFT				14
-#define CNTR1_REGION_EN_MASK				(1 << 14)
-#define CNTR1_CFG_SHIFT					0
-#define CNTR1_CFG_MASK					(0xf << 0)
-
-/* PERFORMANCE_COUNTER_MASTER_REGION_SELECT */
-#define MCONNID2_SHIFT					24
-#define MCONNID2_MASK					(0xff << 24)
-#define REGION_SEL2_SHIFT				16
-#define REGION_SEL2_MASK				(0x3 << 16)
-#define MCONNID1_SHIFT					8
-#define MCONNID1_MASK					(0xff << 8)
-#define REGION_SEL1_SHIFT				0
-#define REGION_SEL1_MASK				(0x3 << 0)
-
-/* PERFORMANCE_COUNTER_TIME */
-#define TOTAL_TIME_SHIFT				0
-#define TOTAL_TIME_MASK					(0xffffffff << 0)
-
-/* DLL_CALIB_CTRL */
-#define ACK_WAIT_SHIFT					16
-#define ACK_WAIT_MASK					(0xf << 16)
-#define DLL_CALIB_INTERVAL_SHIFT			0
-#define DLL_CALIB_INTERVAL_MASK				(0x1ff << 0)
-
-/* END_OF_INTERRUPT */
-#define EOI_SHIFT					0
-#define EOI_MASK					(1 << 0)
-
-/* SYSTEM_OCP_INTERRUPT_RAW_STATUS */
-#define DNV_SYS_SHIFT					2
-#define DNV_SYS_MASK					(1 << 2)
-#define TA_SYS_SHIFT					1
-#define TA_SYS_MASK					(1 << 1)
-#define ERR_SYS_SHIFT					0
-#define ERR_SYS_MASK					(1 << 0)
-
-/* LOW_LATENCY_OCP_INTERRUPT_RAW_STATUS */
-#define DNV_LL_SHIFT					2
-#define DNV_LL_MASK					(1 << 2)
-#define TA_LL_SHIFT					1
-#define TA_LL_MASK					(1 << 1)
-#define ERR_LL_SHIFT					0
-#define ERR_LL_MASK					(1 << 0)
-
-/* SYSTEM_OCP_INTERRUPT_ENABLE_SET */
-#define EN_DNV_SYS_SHIFT				2
-#define EN_DNV_SYS_MASK					(1 << 2)
-#define EN_TA_SYS_SHIFT					1
-#define EN_TA_SYS_MASK					(1 << 1)
-#define EN_ERR_SYS_SHIFT					0
-#define EN_ERR_SYS_MASK					(1 << 0)
-
-/* LOW_LATENCY_OCP_INTERRUPT_ENABLE_SET */
-#define EN_DNV_LL_SHIFT					2
-#define EN_DNV_LL_MASK					(1 << 2)
-#define EN_TA_LL_SHIFT					1
-#define EN_TA_LL_MASK					(1 << 1)
-#define EN_ERR_LL_SHIFT					0
-#define EN_ERR_LL_MASK					(1 << 0)
-
-/* SDRAM_OUTPUT_IMPEDANCE_CALIBRATION_CONFIG */
-#define ZQ_CS1EN_SHIFT					31
-#define ZQ_CS1EN_MASK					(1 << 31)
-#define ZQ_CS0EN_SHIFT					30
-#define ZQ_CS0EN_MASK					(1 << 30)
-#define ZQ_DUALCALEN_SHIFT				29
-#define ZQ_DUALCALEN_MASK				(1 << 29)
-#define ZQ_SFEXITEN_SHIFT				28
-#define ZQ_SFEXITEN_MASK				(1 << 28)
-#define ZQ_ZQINIT_MULT_SHIFT				18
-#define ZQ_ZQINIT_MULT_MASK				(0x3 << 18)
-#define ZQ_ZQCL_MULT_SHIFT				16
-#define ZQ_ZQCL_MULT_MASK				(0x3 << 16)
-#define ZQ_REFINTERVAL_SHIFT				0
-#define ZQ_REFINTERVAL_MASK				(0xffff << 0)
-
-/* TEMPERATURE_ALERT_CONFIG */
-#define TA_CS1EN_SHIFT					31
-#define TA_CS1EN_MASK					(1 << 31)
-#define TA_CS0EN_SHIFT					30
-#define TA_CS0EN_MASK					(1 << 30)
-#define TA_SFEXITEN_SHIFT				28
-#define TA_SFEXITEN_MASK				(1 << 28)
-#define TA_DEVWDT_SHIFT					26
-#define TA_DEVWDT_MASK					(0x3 << 26)
-#define TA_DEVCNT_SHIFT					24
-#define TA_DEVCNT_MASK					(0x3 << 24)
-#define TA_REFINTERVAL_SHIFT				0
-#define TA_REFINTERVAL_MASK				(0x3fffff << 0)
-
-/* OCP_ERROR_LOG */
-#define MADDRSPACE_SHIFT				14
-#define MADDRSPACE_MASK					(0x3 << 14)
-#define MBURSTSEQ_SHIFT					11
-#define MBURSTSEQ_MASK					(0x7 << 11)
-#define MCMD_SHIFT					8
-#define MCMD_MASK					(0x7 << 8)
-#define MCONNID_SHIFT					0
-#define MCONNID_MASK					(0xff << 0)
-
-/* DDR_PHY_CTRL_1 - EMIF4D */
-#define DLL_SLAVE_DLY_CTRL_SHIFT_4D			4
-#define DLL_SLAVE_DLY_CTRL_MASK_4D			(0xFF << 4)
-#define READ_LATENCY_SHIFT_4D				0
-#define READ_LATENCY_MASK_4D				(0xf << 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)
+#include <linux/ti_emif.h>
 
 #ifndef __ASSEMBLY__
 /*
diff --git a/include/linux/ti_emif.h b/include/linux/ti_emif.h
new file mode 100644
index 0000000..be26e7f
--- /dev/null
+++ b/include/linux/ti_emif.h
@@ -0,0 +1,558 @@
+/*
+ * Register defines for the EMIF driver
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * Benoit Cousson (b-cousson@ti.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __TI_EMIF_H
+#define __TI_EMIF_H
+
+/*
+ * Maximum number of different frequencies supported by EMIF driver
+ * Determines the number of entries in the pointer array for register
+ * cache
+ */
+#define EMIF_MAX_NUM_FREQUENCIES			6
+
+/* State of the core voltage */
+#define DDR_VOLTAGE_STABLE				0
+#define DDR_VOLTAGE_RAMPING				1
+
+/* Defines for timing De-rating */
+#define EMIF_NORMAL_TIMINGS				0
+#define EMIF_DERATED_TIMINGS				1
+
+/* Length of the forced read idle period in terms of cycles */
+#define EMIF_READ_IDLE_LEN_VAL				5
+
+/*
+ * forced read idle interval to be used when voltage
+ * is changed as part of DVFS/DPS - 1ms
+ */
+#define READ_IDLE_INTERVAL_DVFS				(1*1000000)
+
+/*
+ * Forced read idle interval to be used when voltage is stable
+ * 50us - or maximum value will do
+ */
+#define READ_IDLE_INTERVAL_NORMAL			(50*1000000)
+
+/* DLL calibration interval when voltage is NOT stable - 1us */
+#define DLL_CALIB_INTERVAL_DVFS				(1*1000000)
+
+#define DLL_CALIB_ACK_WAIT_VAL				5
+
+/* Interval between ZQCS commands - hw team recommended value */
+#define EMIF_ZQCS_INTERVAL_US				(50*1000)
+/* Enable ZQ Calibration on exiting Self-refresh */
+#define ZQ_SFEXITEN_ENABLE				1
+/*
+ * ZQ Calibration simultaneously on both chip-selects:
+ * Needs one calibration resistor per CS
+ */
+#define	ZQ_DUALCALEN_DISABLE				0
+#define	ZQ_DUALCALEN_ENABLE				1
+
+#define T_ZQCS_DEFAULT_NS				90
+#define T_ZQCL_DEFAULT_NS				360
+#define T_ZQINIT_DEFAULT_NS				1000
+
+/* DPD_EN */
+#define DPD_DISABLE					0
+#define DPD_ENABLE					1
+
+/*
+ * Default values for the low-power entry to be used if not provided by user.
+ * OMAP4/5 has a hw bug(i735) due to which this value can not be less than 512
+ * Timeout values are in DDR clock 'cycles' and frequency threshold in Hz
+ */
+#define EMIF_LP_MODE_TIMEOUT_PERFORMANCE		2048
+#define EMIF_LP_MODE_TIMEOUT_POWER			512
+#define EMIF_LP_MODE_FREQ_THRESHOLD			400000000
+
+/* DDR_PHY_CTRL_1 values for EMIF4D - ATTILA PHY combination */
+#define EMIF_DDR_PHY_CTRL_1_BASE_VAL_ATTILAPHY		0x049FF000
+#define EMIF_DLL_SLAVE_DLY_CTRL_400_MHZ_ATTILAPHY	0x41
+#define EMIF_DLL_SLAVE_DLY_CTRL_200_MHZ_ATTILAPHY	0x80
+#define EMIF_DLL_SLAVE_DLY_CTRL_100_MHZ_AND_LESS_ATTILAPHY 0xFF
+
+/* DDR_PHY_CTRL_1 values for EMIF4D5 INTELLIPHY combination */
+#define EMIF_DDR_PHY_CTRL_1_BASE_VAL_INTELLIPHY		0x0E084200
+#define EMIF_PHY_TOTAL_READ_LATENCY_INTELLIPHY_PS	10000
+
+/* TEMP_ALERT_CONFIG - corresponding to temp gradient 5 C/s */
+#define TEMP_ALERT_POLL_INTERVAL_DEFAULT_MS		360
+
+#define EMIF_T_CSTA					3
+#define EMIF_T_PDLL_UL					128
+
+/* External PHY control registers magic values */
+#define EMIF_EXT_PHY_CTRL_1_VAL				0x04020080
+#define EMIF_EXT_PHY_CTRL_5_VAL				0x04010040
+#define EMIF_EXT_PHY_CTRL_6_VAL				0x01004010
+#define EMIF_EXT_PHY_CTRL_7_VAL				0x00001004
+#define EMIF_EXT_PHY_CTRL_8_VAL				0x04010040
+#define EMIF_EXT_PHY_CTRL_9_VAL				0x01004010
+#define EMIF_EXT_PHY_CTRL_10_VAL			0x00001004
+#define EMIF_EXT_PHY_CTRL_11_VAL			0x00000000
+#define EMIF_EXT_PHY_CTRL_12_VAL			0x00000000
+#define EMIF_EXT_PHY_CTRL_13_VAL			0x00000000
+#define EMIF_EXT_PHY_CTRL_14_VAL			0x80080080
+#define EMIF_EXT_PHY_CTRL_15_VAL			0x00800800
+#define EMIF_EXT_PHY_CTRL_16_VAL			0x08102040
+#define EMIF_EXT_PHY_CTRL_17_VAL			0x00000001
+#define EMIF_EXT_PHY_CTRL_18_VAL			0x540A8150
+#define EMIF_EXT_PHY_CTRL_19_VAL			0xA81502A0
+#define EMIF_EXT_PHY_CTRL_20_VAL			0x002A0540
+#define EMIF_EXT_PHY_CTRL_21_VAL			0x00000000
+#define EMIF_EXT_PHY_CTRL_22_VAL			0x00000000
+#define EMIF_EXT_PHY_CTRL_23_VAL			0x00000000
+#define EMIF_EXT_PHY_CTRL_24_VAL			0x00000077
+
+#define EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS	1200
+
+/* Registers offset */
+#define EMIF_MODULE_ID_AND_REVISION			0x0000
+#define EMIF_STATUS					0x0004
+#define EMIF_SDRAM_CONFIG				0x0008
+#define EMIF_SDRAM_CONFIG_2				0x000c
+#define EMIF_SDRAM_REFRESH_CONTROL			0x0010
+#define EMIF_SDRAM_REFRESH_CTRL_SHDW			0x0014
+#define EMIF_SDRAM_TIMING_1				0x0018
+#define EMIF_SDRAM_TIMING_1_SHDW			0x001c
+#define EMIF_SDRAM_TIMING_2				0x0020
+#define EMIF_SDRAM_TIMING_2_SHDW			0x0024
+#define EMIF_SDRAM_TIMING_3				0x0028
+#define EMIF_SDRAM_TIMING_3_SHDW			0x002c
+#define EMIF_LPDDR2_NVM_TIMING				0x0030
+#define EMIF_LPDDR2_NVM_TIMING_SHDW			0x0034
+#define EMIF_POWER_MANAGEMENT_CONTROL			0x0038
+#define EMIF_POWER_MANAGEMENT_CTRL_SHDW			0x003c
+#define EMIF_LPDDR2_MODE_REG_DATA			0x0040
+#define EMIF_LPDDR2_MODE_REG_CONFIG			0x0050
+#define EMIF_OCP_CONFIG					0x0054
+#define EMIF_OCP_CONFIG_VALUE_1				0x0058
+#define EMIF_OCP_CONFIG_VALUE_2				0x005c
+#define EMIF_IODFT_TEST_LOGIC_GLOBAL_CONTROL		0x0060
+#define EMIF_IODFT_TEST_LOGIC_CTRL_MISR_RESULT		0x0064
+#define EMIF_IODFT_TEST_LOGIC_ADDRESS_MISR_RESULT	0x0068
+#define EMIF_IODFT_TEST_LOGIC_DATA_MISR_RESULT_1	0x006c
+#define EMIF_IODFT_TEST_LOGIC_DATA_MISR_RESULT_2	0x0070
+#define EMIF_IODFT_TEST_LOGIC_DATA_MISR_RESULT_3	0x0074
+#define EMIF_PERFORMANCE_COUNTER_1			0x0080
+#define EMIF_PERFORMANCE_COUNTER_2			0x0084
+#define EMIF_PERFORMANCE_COUNTER_CONFIG			0x0088
+#define EMIF_PERFORMANCE_COUNTER_MASTER_REGION_SELECT	0x008c
+#define EMIF_PERFORMANCE_COUNTER_TIME			0x0090
+#define EMIF_MISC_REG					0x0094
+#define EMIF_DLL_CALIB_CTRL				0x0098
+#define EMIF_DLL_CALIB_CTRL_SHDW			0x009c
+#define EMIF_END_OF_INTERRUPT				0x00a0
+#define EMIF_SYSTEM_OCP_INTERRUPT_RAW_STATUS		0x00a4
+#define EMIF_LL_OCP_INTERRUPT_RAW_STATUS		0x00a8
+#define EMIF_SYSTEM_OCP_INTERRUPT_STATUS		0x00ac
+#define EMIF_LL_OCP_INTERRUPT_STATUS			0x00b0
+#define EMIF_SYSTEM_OCP_INTERRUPT_ENABLE_SET		0x00b4
+#define EMIF_LL_OCP_INTERRUPT_ENABLE_SET		0x00b8
+#define EMIF_SYSTEM_OCP_INTERRUPT_ENABLE_CLEAR		0x00bc
+#define EMIF_LL_OCP_INTERRUPT_ENABLE_CLEAR		0x00c0
+#define EMIF_SDRAM_OUTPUT_IMPEDANCE_CALIBRATION_CONFIG	0x00c8
+#define EMIF_TEMPERATURE_ALERT_CONFIG			0x00cc
+#define EMIF_OCP_ERROR_LOG				0x00d0
+#define EMIF_READ_WRITE_LEVELING_RAMP_WINDOW		0x00d4
+#define EMIF_READ_WRITE_LEVELING_RAMP_CONTROL		0x00d8
+#define EMIF_READ_WRITE_LEVELING_CONTROL		0x00dc
+#define EMIF_DDR_PHY_CTRL_1				0x00e4
+#define EMIF_DDR_PHY_CTRL_1_SHDW			0x00e8
+#define EMIF_DDR_PHY_CTRL_2				0x00ec
+#define EMIF_PRIORITY_TO_CLASS_OF_SERVICE_MAPPING	0x0100
+#define EMIF_CONNECTION_ID_TO_CLASS_OF_SERVICE_1_MAPPING 0x0104
+#define EMIF_CONNECTION_ID_TO_CLASS_OF_SERVICE_2_MAPPING 0x0108
+#define EMIF_READ_WRITE_EXECUTION_THRESHOLD		0x0120
+#define EMIF_COS_CONFIG					0x0124
+#define EMIF_PHY_STATUS_1				0x0140
+#define EMIF_PHY_STATUS_2				0x0144
+#define EMIF_PHY_STATUS_3				0x0148
+#define EMIF_PHY_STATUS_4				0x014c
+#define EMIF_PHY_STATUS_5				0x0150
+#define EMIF_PHY_STATUS_6				0x0154
+#define EMIF_PHY_STATUS_7				0x0158
+#define EMIF_PHY_STATUS_8				0x015c
+#define EMIF_PHY_STATUS_9				0x0160
+#define EMIF_PHY_STATUS_10				0x0164
+#define EMIF_PHY_STATUS_11				0x0168
+#define EMIF_PHY_STATUS_12				0x016c
+#define EMIF_PHY_STATUS_13				0x0170
+#define EMIF_PHY_STATUS_14				0x0174
+#define EMIF_PHY_STATUS_15				0x0178
+#define EMIF_PHY_STATUS_16				0x017c
+#define EMIF_PHY_STATUS_17				0x0180
+#define EMIF_PHY_STATUS_18				0x0184
+#define EMIF_PHY_STATUS_19				0x0188
+#define EMIF_PHY_STATUS_20				0x018c
+#define EMIF_PHY_STATUS_21				0x0190
+#define EMIF_EXT_PHY_CTRL_1				0x0200
+#define EMIF_EXT_PHY_CTRL_1_SHDW			0x0204
+#define EMIF_EXT_PHY_CTRL_2				0x0208
+#define EMIF_EXT_PHY_CTRL_2_SHDW			0x020c
+#define EMIF_EXT_PHY_CTRL_3				0x0210
+#define EMIF_EXT_PHY_CTRL_3_SHDW			0x0214
+#define EMIF_EXT_PHY_CTRL_4				0x0218
+#define EMIF_EXT_PHY_CTRL_4_SHDW			0x021c
+#define EMIF_EXT_PHY_CTRL_5				0x0220
+#define EMIF_EXT_PHY_CTRL_5_SHDW			0x0224
+#define EMIF_EXT_PHY_CTRL_6				0x0228
+#define EMIF_EXT_PHY_CTRL_6_SHDW			0x022c
+#define EMIF_EXT_PHY_CTRL_7				0x0230
+#define EMIF_EXT_PHY_CTRL_7_SHDW			0x0234
+#define EMIF_EXT_PHY_CTRL_8				0x0238
+#define EMIF_EXT_PHY_CTRL_8_SHDW			0x023c
+#define EMIF_EXT_PHY_CTRL_9				0x0240
+#define EMIF_EXT_PHY_CTRL_9_SHDW			0x0244
+#define EMIF_EXT_PHY_CTRL_10				0x0248
+#define EMIF_EXT_PHY_CTRL_10_SHDW			0x024c
+#define EMIF_EXT_PHY_CTRL_11				0x0250
+#define EMIF_EXT_PHY_CTRL_11_SHDW			0x0254
+#define EMIF_EXT_PHY_CTRL_12				0x0258
+#define EMIF_EXT_PHY_CTRL_12_SHDW			0x025c
+#define EMIF_EXT_PHY_CTRL_13				0x0260
+#define EMIF_EXT_PHY_CTRL_13_SHDW			0x0264
+#define EMIF_EXT_PHY_CTRL_14				0x0268
+#define EMIF_EXT_PHY_CTRL_14_SHDW			0x026c
+#define EMIF_EXT_PHY_CTRL_15				0x0270
+#define EMIF_EXT_PHY_CTRL_15_SHDW			0x0274
+#define EMIF_EXT_PHY_CTRL_16				0x0278
+#define EMIF_EXT_PHY_CTRL_16_SHDW			0x027c
+#define EMIF_EXT_PHY_CTRL_17				0x0280
+#define EMIF_EXT_PHY_CTRL_17_SHDW			0x0284
+#define EMIF_EXT_PHY_CTRL_18				0x0288
+#define EMIF_EXT_PHY_CTRL_18_SHDW			0x028c
+#define EMIF_EXT_PHY_CTRL_19				0x0290
+#define EMIF_EXT_PHY_CTRL_19_SHDW			0x0294
+#define EMIF_EXT_PHY_CTRL_20				0x0298
+#define EMIF_EXT_PHY_CTRL_20_SHDW			0x029c
+#define EMIF_EXT_PHY_CTRL_21				0x02a0
+#define EMIF_EXT_PHY_CTRL_21_SHDW			0x02a4
+#define EMIF_EXT_PHY_CTRL_22				0x02a8
+#define EMIF_EXT_PHY_CTRL_22_SHDW			0x02ac
+#define EMIF_EXT_PHY_CTRL_23				0x02b0
+#define EMIF_EXT_PHY_CTRL_23_SHDW			0x02b4
+#define EMIF_EXT_PHY_CTRL_24				0x02b8
+#define EMIF_EXT_PHY_CTRL_24_SHDW			0x02bc
+#define EMIF_EXT_PHY_CTRL_25				0x02c0
+#define EMIF_EXT_PHY_CTRL_25_SHDW			0x02c4
+#define EMIF_EXT_PHY_CTRL_26				0x02c8
+#define EMIF_EXT_PHY_CTRL_26_SHDW			0x02cc
+#define EMIF_EXT_PHY_CTRL_27				0x02d0
+#define EMIF_EXT_PHY_CTRL_27_SHDW			0x02d4
+#define EMIF_EXT_PHY_CTRL_28				0x02d8
+#define EMIF_EXT_PHY_CTRL_28_SHDW			0x02dc
+#define EMIF_EXT_PHY_CTRL_29				0x02e0
+#define EMIF_EXT_PHY_CTRL_29_SHDW			0x02e4
+#define EMIF_EXT_PHY_CTRL_30				0x02e8
+#define EMIF_EXT_PHY_CTRL_30_SHDW			0x02ec
+
+/* Registers shifts and masks */
+
+/* EMIF_MODULE_ID_AND_REVISION */
+#define SCHEME_SHIFT					30
+#define SCHEME_MASK					(0x3 << 30)
+#define MODULE_ID_SHIFT					16
+#define MODULE_ID_MASK					(0xfff << 16)
+#define RTL_VERSION_SHIFT				11
+#define RTL_VERSION_MASK				(0x1f << 11)
+#define MAJOR_REVISION_SHIFT				8
+#define MAJOR_REVISION_MASK				(0x7 << 8)
+#define MINOR_REVISION_SHIFT				0
+#define MINOR_REVISION_MASK				(0x3f << 0)
+
+/* STATUS */
+#define BE_SHIFT					31
+#define BE_MASK						(1 << 31)
+#define DUAL_CLK_MODE_SHIFT				30
+#define DUAL_CLK_MODE_MASK				(1 << 30)
+#define FAST_INIT_SHIFT					29
+#define FAST_INIT_MASK					(1 << 29)
+#define RDLVLGATETO_SHIFT				6
+#define RDLVLGATETO_MASK				(1 << 6)
+#define RDLVLTO_SHIFT					5
+#define RDLVLTO_MASK					(1 << 5)
+#define WRLVLTO_SHIFT					4
+#define WRLVLTO_MASK					(1 << 4)
+#define PHY_DLL_READY_SHIFT				2
+#define PHY_DLL_READY_MASK				(1 << 2)
+
+/* SDRAM_CONFIG */
+#define SDRAM_TYPE_SHIFT				29
+#define SDRAM_TYPE_MASK					(0x7 << 29)
+#define IBANK_POS_SHIFT					27
+#define IBANK_POS_MASK					(0x3 << 27)
+#define DDR_TERM_SHIFT					24
+#define DDR_TERM_MASK					(0x7 << 24)
+#define DDR2_DDQS_SHIFT					23
+#define DDR2_DDQS_MASK					(1 << 23)
+#define DYN_ODT_SHIFT					21
+#define DYN_ODT_MASK					(0x3 << 21)
+#define DDR_DISABLE_DLL_SHIFT				20
+#define DDR_DISABLE_DLL_MASK				(1 << 20)
+#define SDRAM_DRIVE_SHIFT				18
+#define SDRAM_DRIVE_MASK				(0x3 << 18)
+#define CWL_SHIFT					16
+#define CWL_MASK					(0x3 << 16)
+#define NARROW_MODE_SHIFT				14
+#define NARROW_MODE_MASK				(0x3 << 14)
+#define CL_SHIFT					10
+#define CL_MASK						(0xf << 10)
+#define ROWSIZE_SHIFT					7
+#define ROWSIZE_MASK					(0x7 << 7)
+#define IBANK_SHIFT					4
+#define IBANK_MASK					(0x7 << 4)
+#define EBANK_SHIFT					3
+#define EBANK_MASK					(1 << 3)
+#define PAGESIZE_SHIFT					0
+#define PAGESIZE_MASK					(0x7 << 0)
+
+/* SDRAM_CONFIG_2 */
+#define CS1NVMEN_SHIFT					30
+#define CS1NVMEN_MASK					(1 << 30)
+#define EBANK_POS_SHIFT					27
+#define EBANK_POS_MASK					(1 << 27)
+#define RDBNUM_SHIFT					4
+#define RDBNUM_MASK					(0x3 << 4)
+#define RDBSIZE_SHIFT					0
+#define RDBSIZE_MASK					(0x7 << 0)
+
+/* SDRAM_REFRESH_CONTROL */
+#define INITREF_DIS_SHIFT				31
+#define INITREF_DIS_MASK				(1 << 31)
+#define SRT_SHIFT					29
+#define SRT_MASK					(1 << 29)
+#define ASR_SHIFT					28
+#define ASR_MASK					(1 << 28)
+#define PASR_SHIFT					24
+#define PASR_MASK					(0x7 << 24)
+#define REFRESH_RATE_SHIFT				0
+#define REFRESH_RATE_MASK				(0xffff << 0)
+
+/* SDRAM_TIMING_1 */
+#define T_RTW_SHIFT					29
+#define T_RTW_MASK					(0x7 << 29)
+#define T_RP_SHIFT					25
+#define T_RP_MASK					(0xf << 25)
+#define T_RCD_SHIFT					21
+#define T_RCD_MASK					(0xf << 21)
+#define T_WR_SHIFT					17
+#define T_WR_MASK					(0xf << 17)
+#define T_RAS_SHIFT					12
+#define T_RAS_MASK					(0x1f << 12)
+#define T_RC_SHIFT					6
+#define T_RC_MASK					(0x3f << 6)
+#define T_RRD_SHIFT					3
+#define T_RRD_MASK					(0x7 << 3)
+#define T_WTR_SHIFT					0
+#define T_WTR_MASK					(0x7 << 0)
+
+/* SDRAM_TIMING_2 */
+#define T_XP_SHIFT					28
+#define T_XP_MASK					(0x7 << 28)
+#define T_ODT_SHIFT					25
+#define T_ODT_MASK					(0x7 << 25)
+#define T_XSNR_SHIFT					16
+#define T_XSNR_MASK					(0x1ff << 16)
+#define T_XSRD_SHIFT					6
+#define T_XSRD_MASK					(0x3ff << 6)
+#define T_RTP_SHIFT					3
+#define T_RTP_MASK					(0x7 << 3)
+#define T_CKE_SHIFT					0
+#define T_CKE_MASK					(0x7 << 0)
+
+/* SDRAM_TIMING_3 */
+#define T_PDLL_UL_SHIFT					28
+#define T_PDLL_UL_MASK					(0xf << 28)
+#define T_CSTA_SHIFT					24
+#define T_CSTA_MASK					(0xf << 24)
+#define T_CKESR_SHIFT					21
+#define T_CKESR_MASK					(0x7 << 21)
+#define ZQ_ZQCS_SHIFT					15
+#define ZQ_ZQCS_MASK					(0x3f << 15)
+#define T_TDQSCKMAX_SHIFT				13
+#define T_TDQSCKMAX_MASK				(0x3 << 13)
+#define T_RFC_SHIFT					4
+#define T_RFC_MASK					(0x1ff << 4)
+#define T_RAS_MAX_SHIFT					0
+#define T_RAS_MAX_MASK					(0xf << 0)
+
+/* POWER_MANAGEMENT_CONTROL */
+#define PD_TIM_SHIFT					12
+#define PD_TIM_MASK					(0xf << 12)
+#define DPD_EN_SHIFT					11
+#define DPD_EN_MASK					(1 << 11)
+#define LP_MODE_SHIFT					8
+#define LP_MODE_MASK					(0x7 << 8)
+#define SR_TIM_SHIFT					4
+#define SR_TIM_MASK					(0xf << 4)
+#define CS_TIM_SHIFT					0
+#define CS_TIM_MASK					(0xf << 0)
+
+/* LPDDR2_MODE_REG_DATA */
+#define VALUE_0_SHIFT					0
+#define VALUE_0_MASK					(0x7f << 0)
+
+/* LPDDR2_MODE_REG_CONFIG */
+#define CS_SHIFT					31
+#define CS_MASK						(1 << 31)
+#define REFRESH_EN_SHIFT				30
+#define REFRESH_EN_MASK					(1 << 30)
+#define ADDRESS_SHIFT					0
+#define ADDRESS_MASK					(0xff << 0)
+
+/* OCP_CONFIG */
+#define SYS_THRESH_MAX_SHIFT				24
+#define SYS_THRESH_MAX_MASK				(0xf << 24)
+#define MPU_THRESH_MAX_SHIFT				20
+#define MPU_THRESH_MAX_MASK				(0xf << 20)
+#define LL_THRESH_MAX_SHIFT				16
+#define LL_THRESH_MAX_MASK				(0xf << 16)
+
+/* PERFORMANCE_COUNTER_1 */
+#define COUNTER1_SHIFT					0
+#define COUNTER1_MASK					(0xffffffff << 0)
+
+/* PERFORMANCE_COUNTER_2 */
+#define COUNTER2_SHIFT					0
+#define COUNTER2_MASK					(0xffffffff << 0)
+
+/* PERFORMANCE_COUNTER_CONFIG */
+#define CNTR2_MCONNID_EN_SHIFT				31
+#define CNTR2_MCONNID_EN_MASK				(1 << 31)
+#define CNTR2_REGION_EN_SHIFT				30
+#define CNTR2_REGION_EN_MASK				(1 << 30)
+#define CNTR2_CFG_SHIFT					16
+#define CNTR2_CFG_MASK					(0xf << 16)
+#define CNTR1_MCONNID_EN_SHIFT				15
+#define CNTR1_MCONNID_EN_MASK				(1 << 15)
+#define CNTR1_REGION_EN_SHIFT				14
+#define CNTR1_REGION_EN_MASK				(1 << 14)
+#define CNTR1_CFG_SHIFT					0
+#define CNTR1_CFG_MASK					(0xf << 0)
+
+/* PERFORMANCE_COUNTER_MASTER_REGION_SELECT */
+#define MCONNID2_SHIFT					24
+#define MCONNID2_MASK					(0xff << 24)
+#define REGION_SEL2_SHIFT				16
+#define REGION_SEL2_MASK				(0x3 << 16)
+#define MCONNID1_SHIFT					8
+#define MCONNID1_MASK					(0xff << 8)
+#define REGION_SEL1_SHIFT				0
+#define REGION_SEL1_MASK				(0x3 << 0)
+
+/* PERFORMANCE_COUNTER_TIME */
+#define TOTAL_TIME_SHIFT				0
+#define TOTAL_TIME_MASK					(0xffffffff << 0)
+
+/* DLL_CALIB_CTRL */
+#define ACK_WAIT_SHIFT					16
+#define ACK_WAIT_MASK					(0xf << 16)
+#define DLL_CALIB_INTERVAL_SHIFT			0
+#define DLL_CALIB_INTERVAL_MASK				(0x1ff << 0)
+
+/* END_OF_INTERRUPT */
+#define EOI_SHIFT					0
+#define EOI_MASK					(1 << 0)
+
+/* SYSTEM_OCP_INTERRUPT_RAW_STATUS */
+#define DNV_SYS_SHIFT					2
+#define DNV_SYS_MASK					(1 << 2)
+#define TA_SYS_SHIFT					1
+#define TA_SYS_MASK					(1 << 1)
+#define ERR_SYS_SHIFT					0
+#define ERR_SYS_MASK					(1 << 0)
+
+/* LOW_LATENCY_OCP_INTERRUPT_RAW_STATUS */
+#define DNV_LL_SHIFT					2
+#define DNV_LL_MASK					(1 << 2)
+#define TA_LL_SHIFT					1
+#define TA_LL_MASK					(1 << 1)
+#define ERR_LL_SHIFT					0
+#define ERR_LL_MASK					(1 << 0)
+
+/* SYSTEM_OCP_INTERRUPT_ENABLE_SET */
+#define EN_DNV_SYS_SHIFT				2
+#define EN_DNV_SYS_MASK					(1 << 2)
+#define EN_TA_SYS_SHIFT					1
+#define EN_TA_SYS_MASK					(1 << 1)
+#define EN_ERR_SYS_SHIFT					0
+#define EN_ERR_SYS_MASK					(1 << 0)
+
+/* LOW_LATENCY_OCP_INTERRUPT_ENABLE_SET */
+#define EN_DNV_LL_SHIFT					2
+#define EN_DNV_LL_MASK					(1 << 2)
+#define EN_TA_LL_SHIFT					1
+#define EN_TA_LL_MASK					(1 << 1)
+#define EN_ERR_LL_SHIFT					0
+#define EN_ERR_LL_MASK					(1 << 0)
+
+/* SDRAM_OUTPUT_IMPEDANCE_CALIBRATION_CONFIG */
+#define ZQ_CS1EN_SHIFT					31
+#define ZQ_CS1EN_MASK					(1 << 31)
+#define ZQ_CS0EN_SHIFT					30
+#define ZQ_CS0EN_MASK					(1 << 30)
+#define ZQ_DUALCALEN_SHIFT				29
+#define ZQ_DUALCALEN_MASK				(1 << 29)
+#define ZQ_SFEXITEN_SHIFT				28
+#define ZQ_SFEXITEN_MASK				(1 << 28)
+#define ZQ_ZQINIT_MULT_SHIFT				18
+#define ZQ_ZQINIT_MULT_MASK				(0x3 << 18)
+#define ZQ_ZQCL_MULT_SHIFT				16
+#define ZQ_ZQCL_MULT_MASK				(0x3 << 16)
+#define ZQ_REFINTERVAL_SHIFT				0
+#define ZQ_REFINTERVAL_MASK				(0xffff << 0)
+
+/* TEMPERATURE_ALERT_CONFIG */
+#define TA_CS1EN_SHIFT					31
+#define TA_CS1EN_MASK					(1 << 31)
+#define TA_CS0EN_SHIFT					30
+#define TA_CS0EN_MASK					(1 << 30)
+#define TA_SFEXITEN_SHIFT				28
+#define TA_SFEXITEN_MASK				(1 << 28)
+#define TA_DEVWDT_SHIFT					26
+#define TA_DEVWDT_MASK					(0x3 << 26)
+#define TA_DEVCNT_SHIFT					24
+#define TA_DEVCNT_MASK					(0x3 << 24)
+#define TA_REFINTERVAL_SHIFT				0
+#define TA_REFINTERVAL_MASK				(0x3fffff << 0)
+
+/* OCP_ERROR_LOG */
+#define MADDRSPACE_SHIFT				14
+#define MADDRSPACE_MASK					(0x3 << 14)
+#define MBURSTSEQ_SHIFT					11
+#define MBURSTSEQ_MASK					(0x7 << 11)
+#define MCMD_SHIFT					8
+#define MCMD_MASK					(0x7 << 8)
+#define MCONNID_SHIFT					0
+#define MCONNID_MASK					(0xff << 0)
+
+/* DDR_PHY_CTRL_1 - EMIF4D */
+#define DLL_SLAVE_DLY_CTRL_SHIFT_4D			4
+#define DLL_SLAVE_DLY_CTRL_MASK_4D			(0xFF << 4)
+#define READ_LATENCY_SHIFT_4D				0
+#define READ_LATENCY_MASK_4D				(0xf << 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)
+
+#endif /* __TI_EMIF_H */
-- 
1.9.0


^ permalink raw reply related	[flat|nested] 106+ messages in thread

* [PATCH v4 02/11] memory: emif: Move EMIF register defines to include/linux/
@ 2014-07-11  2:55   ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11  2:55 UTC (permalink / raw)
  To: linux-arm-kernel

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 register defines in the emif header file to include/linux so that
both the EMIF driver and the AM33XX PM code can benefit.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
Reviewed-by: Russ Dill <russ.dill@ti.com>
Acked-by: Santosh Shililmar <santosh.shilimkar@ti.com>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
v3->v4:
patch unchanged from original:
	http://www.spinics.net/lists/linux-omap/msg95314.html

 drivers/memory/emif.h   | 543 +---------------------------------------------
 include/linux/ti_emif.h | 558 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 559 insertions(+), 542 deletions(-)
 create mode 100644 include/linux/ti_emif.h

diff --git a/drivers/memory/emif.h b/drivers/memory/emif.h
index bfe08ba..8214f07 100644
--- a/drivers/memory/emif.h
+++ b/drivers/memory/emif.h
@@ -12,548 +12,7 @@
 #ifndef __EMIF_H
 #define __EMIF_H
 
-/*
- * Maximum number of different frequencies supported by EMIF driver
- * Determines the number of entries in the pointer array for register
- * cache
- */
-#define EMIF_MAX_NUM_FREQUENCIES			6
-
-/* State of the core voltage */
-#define DDR_VOLTAGE_STABLE				0
-#define DDR_VOLTAGE_RAMPING				1
-
-/* Defines for timing De-rating */
-#define EMIF_NORMAL_TIMINGS				0
-#define EMIF_DERATED_TIMINGS				1
-
-/* Length of the forced read idle period in terms of cycles */
-#define EMIF_READ_IDLE_LEN_VAL				5
-
-/*
- * forced read idle interval to be used when voltage
- * is changed as part of DVFS/DPS - 1ms
- */
-#define READ_IDLE_INTERVAL_DVFS				(1*1000000)
-
-/*
- * Forced read idle interval to be used when voltage is stable
- * 50us - or maximum value will do
- */
-#define READ_IDLE_INTERVAL_NORMAL			(50*1000000)
-
-/* DLL calibration interval when voltage is NOT stable - 1us */
-#define DLL_CALIB_INTERVAL_DVFS				(1*1000000)
-
-#define DLL_CALIB_ACK_WAIT_VAL				5
-
-/* Interval between ZQCS commands - hw team recommended value */
-#define EMIF_ZQCS_INTERVAL_US				(50*1000)
-/* Enable ZQ Calibration on exiting Self-refresh */
-#define ZQ_SFEXITEN_ENABLE				1
-/*
- * ZQ Calibration simultaneously on both chip-selects:
- * Needs one calibration resistor per CS
- */
-#define	ZQ_DUALCALEN_DISABLE				0
-#define	ZQ_DUALCALEN_ENABLE				1
-
-#define T_ZQCS_DEFAULT_NS				90
-#define T_ZQCL_DEFAULT_NS				360
-#define T_ZQINIT_DEFAULT_NS				1000
-
-/* DPD_EN */
-#define DPD_DISABLE					0
-#define DPD_ENABLE					1
-
-/*
- * Default values for the low-power entry to be used if not provided by user.
- * OMAP4/5 has a hw bug(i735) due to which this value can not be less than 512
- * Timeout values are in DDR clock 'cycles' and frequency threshold in Hz
- */
-#define EMIF_LP_MODE_TIMEOUT_PERFORMANCE		2048
-#define EMIF_LP_MODE_TIMEOUT_POWER			512
-#define EMIF_LP_MODE_FREQ_THRESHOLD			400000000
-
-/* DDR_PHY_CTRL_1 values for EMIF4D - ATTILA PHY combination */
-#define EMIF_DDR_PHY_CTRL_1_BASE_VAL_ATTILAPHY		0x049FF000
-#define EMIF_DLL_SLAVE_DLY_CTRL_400_MHZ_ATTILAPHY	0x41
-#define EMIF_DLL_SLAVE_DLY_CTRL_200_MHZ_ATTILAPHY	0x80
-#define EMIF_DLL_SLAVE_DLY_CTRL_100_MHZ_AND_LESS_ATTILAPHY 0xFF
-
-/* DDR_PHY_CTRL_1 values for EMIF4D5 INTELLIPHY combination */
-#define EMIF_DDR_PHY_CTRL_1_BASE_VAL_INTELLIPHY		0x0E084200
-#define EMIF_PHY_TOTAL_READ_LATENCY_INTELLIPHY_PS	10000
-
-/* TEMP_ALERT_CONFIG - corresponding to temp gradient 5 C/s */
-#define TEMP_ALERT_POLL_INTERVAL_DEFAULT_MS		360
-
-#define EMIF_T_CSTA					3
-#define EMIF_T_PDLL_UL					128
-
-/* External PHY control registers magic values */
-#define EMIF_EXT_PHY_CTRL_1_VAL				0x04020080
-#define EMIF_EXT_PHY_CTRL_5_VAL				0x04010040
-#define EMIF_EXT_PHY_CTRL_6_VAL				0x01004010
-#define EMIF_EXT_PHY_CTRL_7_VAL				0x00001004
-#define EMIF_EXT_PHY_CTRL_8_VAL				0x04010040
-#define EMIF_EXT_PHY_CTRL_9_VAL				0x01004010
-#define EMIF_EXT_PHY_CTRL_10_VAL			0x00001004
-#define EMIF_EXT_PHY_CTRL_11_VAL			0x00000000
-#define EMIF_EXT_PHY_CTRL_12_VAL			0x00000000
-#define EMIF_EXT_PHY_CTRL_13_VAL			0x00000000
-#define EMIF_EXT_PHY_CTRL_14_VAL			0x80080080
-#define EMIF_EXT_PHY_CTRL_15_VAL			0x00800800
-#define EMIF_EXT_PHY_CTRL_16_VAL			0x08102040
-#define EMIF_EXT_PHY_CTRL_17_VAL			0x00000001
-#define EMIF_EXT_PHY_CTRL_18_VAL			0x540A8150
-#define EMIF_EXT_PHY_CTRL_19_VAL			0xA81502A0
-#define EMIF_EXT_PHY_CTRL_20_VAL			0x002A0540
-#define EMIF_EXT_PHY_CTRL_21_VAL			0x00000000
-#define EMIF_EXT_PHY_CTRL_22_VAL			0x00000000
-#define EMIF_EXT_PHY_CTRL_23_VAL			0x00000000
-#define EMIF_EXT_PHY_CTRL_24_VAL			0x00000077
-
-#define EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS	1200
-
-/* Registers offset */
-#define EMIF_MODULE_ID_AND_REVISION			0x0000
-#define EMIF_STATUS					0x0004
-#define EMIF_SDRAM_CONFIG				0x0008
-#define EMIF_SDRAM_CONFIG_2				0x000c
-#define EMIF_SDRAM_REFRESH_CONTROL			0x0010
-#define EMIF_SDRAM_REFRESH_CTRL_SHDW			0x0014
-#define EMIF_SDRAM_TIMING_1				0x0018
-#define EMIF_SDRAM_TIMING_1_SHDW			0x001c
-#define EMIF_SDRAM_TIMING_2				0x0020
-#define EMIF_SDRAM_TIMING_2_SHDW			0x0024
-#define EMIF_SDRAM_TIMING_3				0x0028
-#define EMIF_SDRAM_TIMING_3_SHDW			0x002c
-#define EMIF_LPDDR2_NVM_TIMING				0x0030
-#define EMIF_LPDDR2_NVM_TIMING_SHDW			0x0034
-#define EMIF_POWER_MANAGEMENT_CONTROL			0x0038
-#define EMIF_POWER_MANAGEMENT_CTRL_SHDW			0x003c
-#define EMIF_LPDDR2_MODE_REG_DATA			0x0040
-#define EMIF_LPDDR2_MODE_REG_CONFIG			0x0050
-#define EMIF_OCP_CONFIG					0x0054
-#define EMIF_OCP_CONFIG_VALUE_1				0x0058
-#define EMIF_OCP_CONFIG_VALUE_2				0x005c
-#define EMIF_IODFT_TEST_LOGIC_GLOBAL_CONTROL		0x0060
-#define EMIF_IODFT_TEST_LOGIC_CTRL_MISR_RESULT		0x0064
-#define EMIF_IODFT_TEST_LOGIC_ADDRESS_MISR_RESULT	0x0068
-#define EMIF_IODFT_TEST_LOGIC_DATA_MISR_RESULT_1	0x006c
-#define EMIF_IODFT_TEST_LOGIC_DATA_MISR_RESULT_2	0x0070
-#define EMIF_IODFT_TEST_LOGIC_DATA_MISR_RESULT_3	0x0074
-#define EMIF_PERFORMANCE_COUNTER_1			0x0080
-#define EMIF_PERFORMANCE_COUNTER_2			0x0084
-#define EMIF_PERFORMANCE_COUNTER_CONFIG			0x0088
-#define EMIF_PERFORMANCE_COUNTER_MASTER_REGION_SELECT	0x008c
-#define EMIF_PERFORMANCE_COUNTER_TIME			0x0090
-#define EMIF_MISC_REG					0x0094
-#define EMIF_DLL_CALIB_CTRL				0x0098
-#define EMIF_DLL_CALIB_CTRL_SHDW			0x009c
-#define EMIF_END_OF_INTERRUPT				0x00a0
-#define EMIF_SYSTEM_OCP_INTERRUPT_RAW_STATUS		0x00a4
-#define EMIF_LL_OCP_INTERRUPT_RAW_STATUS		0x00a8
-#define EMIF_SYSTEM_OCP_INTERRUPT_STATUS		0x00ac
-#define EMIF_LL_OCP_INTERRUPT_STATUS			0x00b0
-#define EMIF_SYSTEM_OCP_INTERRUPT_ENABLE_SET		0x00b4
-#define EMIF_LL_OCP_INTERRUPT_ENABLE_SET		0x00b8
-#define EMIF_SYSTEM_OCP_INTERRUPT_ENABLE_CLEAR		0x00bc
-#define EMIF_LL_OCP_INTERRUPT_ENABLE_CLEAR		0x00c0
-#define EMIF_SDRAM_OUTPUT_IMPEDANCE_CALIBRATION_CONFIG	0x00c8
-#define EMIF_TEMPERATURE_ALERT_CONFIG			0x00cc
-#define EMIF_OCP_ERROR_LOG				0x00d0
-#define EMIF_READ_WRITE_LEVELING_RAMP_WINDOW		0x00d4
-#define EMIF_READ_WRITE_LEVELING_RAMP_CONTROL		0x00d8
-#define EMIF_READ_WRITE_LEVELING_CONTROL		0x00dc
-#define EMIF_DDR_PHY_CTRL_1				0x00e4
-#define EMIF_DDR_PHY_CTRL_1_SHDW			0x00e8
-#define EMIF_DDR_PHY_CTRL_2				0x00ec
-#define EMIF_PRIORITY_TO_CLASS_OF_SERVICE_MAPPING	0x0100
-#define EMIF_CONNECTION_ID_TO_CLASS_OF_SERVICE_1_MAPPING 0x0104
-#define EMIF_CONNECTION_ID_TO_CLASS_OF_SERVICE_2_MAPPING 0x0108
-#define EMIF_READ_WRITE_EXECUTION_THRESHOLD		0x0120
-#define EMIF_COS_CONFIG					0x0124
-#define EMIF_PHY_STATUS_1				0x0140
-#define EMIF_PHY_STATUS_2				0x0144
-#define EMIF_PHY_STATUS_3				0x0148
-#define EMIF_PHY_STATUS_4				0x014c
-#define EMIF_PHY_STATUS_5				0x0150
-#define EMIF_PHY_STATUS_6				0x0154
-#define EMIF_PHY_STATUS_7				0x0158
-#define EMIF_PHY_STATUS_8				0x015c
-#define EMIF_PHY_STATUS_9				0x0160
-#define EMIF_PHY_STATUS_10				0x0164
-#define EMIF_PHY_STATUS_11				0x0168
-#define EMIF_PHY_STATUS_12				0x016c
-#define EMIF_PHY_STATUS_13				0x0170
-#define EMIF_PHY_STATUS_14				0x0174
-#define EMIF_PHY_STATUS_15				0x0178
-#define EMIF_PHY_STATUS_16				0x017c
-#define EMIF_PHY_STATUS_17				0x0180
-#define EMIF_PHY_STATUS_18				0x0184
-#define EMIF_PHY_STATUS_19				0x0188
-#define EMIF_PHY_STATUS_20				0x018c
-#define EMIF_PHY_STATUS_21				0x0190
-#define EMIF_EXT_PHY_CTRL_1				0x0200
-#define EMIF_EXT_PHY_CTRL_1_SHDW			0x0204
-#define EMIF_EXT_PHY_CTRL_2				0x0208
-#define EMIF_EXT_PHY_CTRL_2_SHDW			0x020c
-#define EMIF_EXT_PHY_CTRL_3				0x0210
-#define EMIF_EXT_PHY_CTRL_3_SHDW			0x0214
-#define EMIF_EXT_PHY_CTRL_4				0x0218
-#define EMIF_EXT_PHY_CTRL_4_SHDW			0x021c
-#define EMIF_EXT_PHY_CTRL_5				0x0220
-#define EMIF_EXT_PHY_CTRL_5_SHDW			0x0224
-#define EMIF_EXT_PHY_CTRL_6				0x0228
-#define EMIF_EXT_PHY_CTRL_6_SHDW			0x022c
-#define EMIF_EXT_PHY_CTRL_7				0x0230
-#define EMIF_EXT_PHY_CTRL_7_SHDW			0x0234
-#define EMIF_EXT_PHY_CTRL_8				0x0238
-#define EMIF_EXT_PHY_CTRL_8_SHDW			0x023c
-#define EMIF_EXT_PHY_CTRL_9				0x0240
-#define EMIF_EXT_PHY_CTRL_9_SHDW			0x0244
-#define EMIF_EXT_PHY_CTRL_10				0x0248
-#define EMIF_EXT_PHY_CTRL_10_SHDW			0x024c
-#define EMIF_EXT_PHY_CTRL_11				0x0250
-#define EMIF_EXT_PHY_CTRL_11_SHDW			0x0254
-#define EMIF_EXT_PHY_CTRL_12				0x0258
-#define EMIF_EXT_PHY_CTRL_12_SHDW			0x025c
-#define EMIF_EXT_PHY_CTRL_13				0x0260
-#define EMIF_EXT_PHY_CTRL_13_SHDW			0x0264
-#define EMIF_EXT_PHY_CTRL_14				0x0268
-#define EMIF_EXT_PHY_CTRL_14_SHDW			0x026c
-#define EMIF_EXT_PHY_CTRL_15				0x0270
-#define EMIF_EXT_PHY_CTRL_15_SHDW			0x0274
-#define EMIF_EXT_PHY_CTRL_16				0x0278
-#define EMIF_EXT_PHY_CTRL_16_SHDW			0x027c
-#define EMIF_EXT_PHY_CTRL_17				0x0280
-#define EMIF_EXT_PHY_CTRL_17_SHDW			0x0284
-#define EMIF_EXT_PHY_CTRL_18				0x0288
-#define EMIF_EXT_PHY_CTRL_18_SHDW			0x028c
-#define EMIF_EXT_PHY_CTRL_19				0x0290
-#define EMIF_EXT_PHY_CTRL_19_SHDW			0x0294
-#define EMIF_EXT_PHY_CTRL_20				0x0298
-#define EMIF_EXT_PHY_CTRL_20_SHDW			0x029c
-#define EMIF_EXT_PHY_CTRL_21				0x02a0
-#define EMIF_EXT_PHY_CTRL_21_SHDW			0x02a4
-#define EMIF_EXT_PHY_CTRL_22				0x02a8
-#define EMIF_EXT_PHY_CTRL_22_SHDW			0x02ac
-#define EMIF_EXT_PHY_CTRL_23				0x02b0
-#define EMIF_EXT_PHY_CTRL_23_SHDW			0x02b4
-#define EMIF_EXT_PHY_CTRL_24				0x02b8
-#define EMIF_EXT_PHY_CTRL_24_SHDW			0x02bc
-#define EMIF_EXT_PHY_CTRL_25				0x02c0
-#define EMIF_EXT_PHY_CTRL_25_SHDW			0x02c4
-#define EMIF_EXT_PHY_CTRL_26				0x02c8
-#define EMIF_EXT_PHY_CTRL_26_SHDW			0x02cc
-#define EMIF_EXT_PHY_CTRL_27				0x02d0
-#define EMIF_EXT_PHY_CTRL_27_SHDW			0x02d4
-#define EMIF_EXT_PHY_CTRL_28				0x02d8
-#define EMIF_EXT_PHY_CTRL_28_SHDW			0x02dc
-#define EMIF_EXT_PHY_CTRL_29				0x02e0
-#define EMIF_EXT_PHY_CTRL_29_SHDW			0x02e4
-#define EMIF_EXT_PHY_CTRL_30				0x02e8
-#define EMIF_EXT_PHY_CTRL_30_SHDW			0x02ec
-
-/* Registers shifts and masks */
-
-/* EMIF_MODULE_ID_AND_REVISION */
-#define SCHEME_SHIFT					30
-#define SCHEME_MASK					(0x3 << 30)
-#define MODULE_ID_SHIFT					16
-#define MODULE_ID_MASK					(0xfff << 16)
-#define RTL_VERSION_SHIFT				11
-#define RTL_VERSION_MASK				(0x1f << 11)
-#define MAJOR_REVISION_SHIFT				8
-#define MAJOR_REVISION_MASK				(0x7 << 8)
-#define MINOR_REVISION_SHIFT				0
-#define MINOR_REVISION_MASK				(0x3f << 0)
-
-/* STATUS */
-#define BE_SHIFT					31
-#define BE_MASK						(1 << 31)
-#define DUAL_CLK_MODE_SHIFT				30
-#define DUAL_CLK_MODE_MASK				(1 << 30)
-#define FAST_INIT_SHIFT					29
-#define FAST_INIT_MASK					(1 << 29)
-#define RDLVLGATETO_SHIFT				6
-#define RDLVLGATETO_MASK				(1 << 6)
-#define RDLVLTO_SHIFT					5
-#define RDLVLTO_MASK					(1 << 5)
-#define WRLVLTO_SHIFT					4
-#define WRLVLTO_MASK					(1 << 4)
-#define PHY_DLL_READY_SHIFT				2
-#define PHY_DLL_READY_MASK				(1 << 2)
-
-/* SDRAM_CONFIG */
-#define SDRAM_TYPE_SHIFT				29
-#define SDRAM_TYPE_MASK					(0x7 << 29)
-#define IBANK_POS_SHIFT					27
-#define IBANK_POS_MASK					(0x3 << 27)
-#define DDR_TERM_SHIFT					24
-#define DDR_TERM_MASK					(0x7 << 24)
-#define DDR2_DDQS_SHIFT					23
-#define DDR2_DDQS_MASK					(1 << 23)
-#define DYN_ODT_SHIFT					21
-#define DYN_ODT_MASK					(0x3 << 21)
-#define DDR_DISABLE_DLL_SHIFT				20
-#define DDR_DISABLE_DLL_MASK				(1 << 20)
-#define SDRAM_DRIVE_SHIFT				18
-#define SDRAM_DRIVE_MASK				(0x3 << 18)
-#define CWL_SHIFT					16
-#define CWL_MASK					(0x3 << 16)
-#define NARROW_MODE_SHIFT				14
-#define NARROW_MODE_MASK				(0x3 << 14)
-#define CL_SHIFT					10
-#define CL_MASK						(0xf << 10)
-#define ROWSIZE_SHIFT					7
-#define ROWSIZE_MASK					(0x7 << 7)
-#define IBANK_SHIFT					4
-#define IBANK_MASK					(0x7 << 4)
-#define EBANK_SHIFT					3
-#define EBANK_MASK					(1 << 3)
-#define PAGESIZE_SHIFT					0
-#define PAGESIZE_MASK					(0x7 << 0)
-
-/* SDRAM_CONFIG_2 */
-#define CS1NVMEN_SHIFT					30
-#define CS1NVMEN_MASK					(1 << 30)
-#define EBANK_POS_SHIFT					27
-#define EBANK_POS_MASK					(1 << 27)
-#define RDBNUM_SHIFT					4
-#define RDBNUM_MASK					(0x3 << 4)
-#define RDBSIZE_SHIFT					0
-#define RDBSIZE_MASK					(0x7 << 0)
-
-/* SDRAM_REFRESH_CONTROL */
-#define INITREF_DIS_SHIFT				31
-#define INITREF_DIS_MASK				(1 << 31)
-#define SRT_SHIFT					29
-#define SRT_MASK					(1 << 29)
-#define ASR_SHIFT					28
-#define ASR_MASK					(1 << 28)
-#define PASR_SHIFT					24
-#define PASR_MASK					(0x7 << 24)
-#define REFRESH_RATE_SHIFT				0
-#define REFRESH_RATE_MASK				(0xffff << 0)
-
-/* SDRAM_TIMING_1 */
-#define T_RTW_SHIFT					29
-#define T_RTW_MASK					(0x7 << 29)
-#define T_RP_SHIFT					25
-#define T_RP_MASK					(0xf << 25)
-#define T_RCD_SHIFT					21
-#define T_RCD_MASK					(0xf << 21)
-#define T_WR_SHIFT					17
-#define T_WR_MASK					(0xf << 17)
-#define T_RAS_SHIFT					12
-#define T_RAS_MASK					(0x1f << 12)
-#define T_RC_SHIFT					6
-#define T_RC_MASK					(0x3f << 6)
-#define T_RRD_SHIFT					3
-#define T_RRD_MASK					(0x7 << 3)
-#define T_WTR_SHIFT					0
-#define T_WTR_MASK					(0x7 << 0)
-
-/* SDRAM_TIMING_2 */
-#define T_XP_SHIFT					28
-#define T_XP_MASK					(0x7 << 28)
-#define T_ODT_SHIFT					25
-#define T_ODT_MASK					(0x7 << 25)
-#define T_XSNR_SHIFT					16
-#define T_XSNR_MASK					(0x1ff << 16)
-#define T_XSRD_SHIFT					6
-#define T_XSRD_MASK					(0x3ff << 6)
-#define T_RTP_SHIFT					3
-#define T_RTP_MASK					(0x7 << 3)
-#define T_CKE_SHIFT					0
-#define T_CKE_MASK					(0x7 << 0)
-
-/* SDRAM_TIMING_3 */
-#define T_PDLL_UL_SHIFT					28
-#define T_PDLL_UL_MASK					(0xf << 28)
-#define T_CSTA_SHIFT					24
-#define T_CSTA_MASK					(0xf << 24)
-#define T_CKESR_SHIFT					21
-#define T_CKESR_MASK					(0x7 << 21)
-#define ZQ_ZQCS_SHIFT					15
-#define ZQ_ZQCS_MASK					(0x3f << 15)
-#define T_TDQSCKMAX_SHIFT				13
-#define T_TDQSCKMAX_MASK				(0x3 << 13)
-#define T_RFC_SHIFT					4
-#define T_RFC_MASK					(0x1ff << 4)
-#define T_RAS_MAX_SHIFT					0
-#define T_RAS_MAX_MASK					(0xf << 0)
-
-/* POWER_MANAGEMENT_CONTROL */
-#define PD_TIM_SHIFT					12
-#define PD_TIM_MASK					(0xf << 12)
-#define DPD_EN_SHIFT					11
-#define DPD_EN_MASK					(1 << 11)
-#define LP_MODE_SHIFT					8
-#define LP_MODE_MASK					(0x7 << 8)
-#define SR_TIM_SHIFT					4
-#define SR_TIM_MASK					(0xf << 4)
-#define CS_TIM_SHIFT					0
-#define CS_TIM_MASK					(0xf << 0)
-
-/* LPDDR2_MODE_REG_DATA */
-#define VALUE_0_SHIFT					0
-#define VALUE_0_MASK					(0x7f << 0)
-
-/* LPDDR2_MODE_REG_CONFIG */
-#define CS_SHIFT					31
-#define CS_MASK						(1 << 31)
-#define REFRESH_EN_SHIFT				30
-#define REFRESH_EN_MASK					(1 << 30)
-#define ADDRESS_SHIFT					0
-#define ADDRESS_MASK					(0xff << 0)
-
-/* OCP_CONFIG */
-#define SYS_THRESH_MAX_SHIFT				24
-#define SYS_THRESH_MAX_MASK				(0xf << 24)
-#define MPU_THRESH_MAX_SHIFT				20
-#define MPU_THRESH_MAX_MASK				(0xf << 20)
-#define LL_THRESH_MAX_SHIFT				16
-#define LL_THRESH_MAX_MASK				(0xf << 16)
-
-/* PERFORMANCE_COUNTER_1 */
-#define COUNTER1_SHIFT					0
-#define COUNTER1_MASK					(0xffffffff << 0)
-
-/* PERFORMANCE_COUNTER_2 */
-#define COUNTER2_SHIFT					0
-#define COUNTER2_MASK					(0xffffffff << 0)
-
-/* PERFORMANCE_COUNTER_CONFIG */
-#define CNTR2_MCONNID_EN_SHIFT				31
-#define CNTR2_MCONNID_EN_MASK				(1 << 31)
-#define CNTR2_REGION_EN_SHIFT				30
-#define CNTR2_REGION_EN_MASK				(1 << 30)
-#define CNTR2_CFG_SHIFT					16
-#define CNTR2_CFG_MASK					(0xf << 16)
-#define CNTR1_MCONNID_EN_SHIFT				15
-#define CNTR1_MCONNID_EN_MASK				(1 << 15)
-#define CNTR1_REGION_EN_SHIFT				14
-#define CNTR1_REGION_EN_MASK				(1 << 14)
-#define CNTR1_CFG_SHIFT					0
-#define CNTR1_CFG_MASK					(0xf << 0)
-
-/* PERFORMANCE_COUNTER_MASTER_REGION_SELECT */
-#define MCONNID2_SHIFT					24
-#define MCONNID2_MASK					(0xff << 24)
-#define REGION_SEL2_SHIFT				16
-#define REGION_SEL2_MASK				(0x3 << 16)
-#define MCONNID1_SHIFT					8
-#define MCONNID1_MASK					(0xff << 8)
-#define REGION_SEL1_SHIFT				0
-#define REGION_SEL1_MASK				(0x3 << 0)
-
-/* PERFORMANCE_COUNTER_TIME */
-#define TOTAL_TIME_SHIFT				0
-#define TOTAL_TIME_MASK					(0xffffffff << 0)
-
-/* DLL_CALIB_CTRL */
-#define ACK_WAIT_SHIFT					16
-#define ACK_WAIT_MASK					(0xf << 16)
-#define DLL_CALIB_INTERVAL_SHIFT			0
-#define DLL_CALIB_INTERVAL_MASK				(0x1ff << 0)
-
-/* END_OF_INTERRUPT */
-#define EOI_SHIFT					0
-#define EOI_MASK					(1 << 0)
-
-/* SYSTEM_OCP_INTERRUPT_RAW_STATUS */
-#define DNV_SYS_SHIFT					2
-#define DNV_SYS_MASK					(1 << 2)
-#define TA_SYS_SHIFT					1
-#define TA_SYS_MASK					(1 << 1)
-#define ERR_SYS_SHIFT					0
-#define ERR_SYS_MASK					(1 << 0)
-
-/* LOW_LATENCY_OCP_INTERRUPT_RAW_STATUS */
-#define DNV_LL_SHIFT					2
-#define DNV_LL_MASK					(1 << 2)
-#define TA_LL_SHIFT					1
-#define TA_LL_MASK					(1 << 1)
-#define ERR_LL_SHIFT					0
-#define ERR_LL_MASK					(1 << 0)
-
-/* SYSTEM_OCP_INTERRUPT_ENABLE_SET */
-#define EN_DNV_SYS_SHIFT				2
-#define EN_DNV_SYS_MASK					(1 << 2)
-#define EN_TA_SYS_SHIFT					1
-#define EN_TA_SYS_MASK					(1 << 1)
-#define EN_ERR_SYS_SHIFT					0
-#define EN_ERR_SYS_MASK					(1 << 0)
-
-/* LOW_LATENCY_OCP_INTERRUPT_ENABLE_SET */
-#define EN_DNV_LL_SHIFT					2
-#define EN_DNV_LL_MASK					(1 << 2)
-#define EN_TA_LL_SHIFT					1
-#define EN_TA_LL_MASK					(1 << 1)
-#define EN_ERR_LL_SHIFT					0
-#define EN_ERR_LL_MASK					(1 << 0)
-
-/* SDRAM_OUTPUT_IMPEDANCE_CALIBRATION_CONFIG */
-#define ZQ_CS1EN_SHIFT					31
-#define ZQ_CS1EN_MASK					(1 << 31)
-#define ZQ_CS0EN_SHIFT					30
-#define ZQ_CS0EN_MASK					(1 << 30)
-#define ZQ_DUALCALEN_SHIFT				29
-#define ZQ_DUALCALEN_MASK				(1 << 29)
-#define ZQ_SFEXITEN_SHIFT				28
-#define ZQ_SFEXITEN_MASK				(1 << 28)
-#define ZQ_ZQINIT_MULT_SHIFT				18
-#define ZQ_ZQINIT_MULT_MASK				(0x3 << 18)
-#define ZQ_ZQCL_MULT_SHIFT				16
-#define ZQ_ZQCL_MULT_MASK				(0x3 << 16)
-#define ZQ_REFINTERVAL_SHIFT				0
-#define ZQ_REFINTERVAL_MASK				(0xffff << 0)
-
-/* TEMPERATURE_ALERT_CONFIG */
-#define TA_CS1EN_SHIFT					31
-#define TA_CS1EN_MASK					(1 << 31)
-#define TA_CS0EN_SHIFT					30
-#define TA_CS0EN_MASK					(1 << 30)
-#define TA_SFEXITEN_SHIFT				28
-#define TA_SFEXITEN_MASK				(1 << 28)
-#define TA_DEVWDT_SHIFT					26
-#define TA_DEVWDT_MASK					(0x3 << 26)
-#define TA_DEVCNT_SHIFT					24
-#define TA_DEVCNT_MASK					(0x3 << 24)
-#define TA_REFINTERVAL_SHIFT				0
-#define TA_REFINTERVAL_MASK				(0x3fffff << 0)
-
-/* OCP_ERROR_LOG */
-#define MADDRSPACE_SHIFT				14
-#define MADDRSPACE_MASK					(0x3 << 14)
-#define MBURSTSEQ_SHIFT					11
-#define MBURSTSEQ_MASK					(0x7 << 11)
-#define MCMD_SHIFT					8
-#define MCMD_MASK					(0x7 << 8)
-#define MCONNID_SHIFT					0
-#define MCONNID_MASK					(0xff << 0)
-
-/* DDR_PHY_CTRL_1 - EMIF4D */
-#define DLL_SLAVE_DLY_CTRL_SHIFT_4D			4
-#define DLL_SLAVE_DLY_CTRL_MASK_4D			(0xFF << 4)
-#define READ_LATENCY_SHIFT_4D				0
-#define READ_LATENCY_MASK_4D				(0xf << 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)
+#include <linux/ti_emif.h>
 
 #ifndef __ASSEMBLY__
 /*
diff --git a/include/linux/ti_emif.h b/include/linux/ti_emif.h
new file mode 100644
index 0000000..be26e7f
--- /dev/null
+++ b/include/linux/ti_emif.h
@@ -0,0 +1,558 @@
+/*
+ * Register defines for the EMIF driver
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * Benoit Cousson (b-cousson at ti.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __TI_EMIF_H
+#define __TI_EMIF_H
+
+/*
+ * Maximum number of different frequencies supported by EMIF driver
+ * Determines the number of entries in the pointer array for register
+ * cache
+ */
+#define EMIF_MAX_NUM_FREQUENCIES			6
+
+/* State of the core voltage */
+#define DDR_VOLTAGE_STABLE				0
+#define DDR_VOLTAGE_RAMPING				1
+
+/* Defines for timing De-rating */
+#define EMIF_NORMAL_TIMINGS				0
+#define EMIF_DERATED_TIMINGS				1
+
+/* Length of the forced read idle period in terms of cycles */
+#define EMIF_READ_IDLE_LEN_VAL				5
+
+/*
+ * forced read idle interval to be used when voltage
+ * is changed as part of DVFS/DPS - 1ms
+ */
+#define READ_IDLE_INTERVAL_DVFS				(1*1000000)
+
+/*
+ * Forced read idle interval to be used when voltage is stable
+ * 50us - or maximum value will do
+ */
+#define READ_IDLE_INTERVAL_NORMAL			(50*1000000)
+
+/* DLL calibration interval when voltage is NOT stable - 1us */
+#define DLL_CALIB_INTERVAL_DVFS				(1*1000000)
+
+#define DLL_CALIB_ACK_WAIT_VAL				5
+
+/* Interval between ZQCS commands - hw team recommended value */
+#define EMIF_ZQCS_INTERVAL_US				(50*1000)
+/* Enable ZQ Calibration on exiting Self-refresh */
+#define ZQ_SFEXITEN_ENABLE				1
+/*
+ * ZQ Calibration simultaneously on both chip-selects:
+ * Needs one calibration resistor per CS
+ */
+#define	ZQ_DUALCALEN_DISABLE				0
+#define	ZQ_DUALCALEN_ENABLE				1
+
+#define T_ZQCS_DEFAULT_NS				90
+#define T_ZQCL_DEFAULT_NS				360
+#define T_ZQINIT_DEFAULT_NS				1000
+
+/* DPD_EN */
+#define DPD_DISABLE					0
+#define DPD_ENABLE					1
+
+/*
+ * Default values for the low-power entry to be used if not provided by user.
+ * OMAP4/5 has a hw bug(i735) due to which this value can not be less than 512
+ * Timeout values are in DDR clock 'cycles' and frequency threshold in Hz
+ */
+#define EMIF_LP_MODE_TIMEOUT_PERFORMANCE		2048
+#define EMIF_LP_MODE_TIMEOUT_POWER			512
+#define EMIF_LP_MODE_FREQ_THRESHOLD			400000000
+
+/* DDR_PHY_CTRL_1 values for EMIF4D - ATTILA PHY combination */
+#define EMIF_DDR_PHY_CTRL_1_BASE_VAL_ATTILAPHY		0x049FF000
+#define EMIF_DLL_SLAVE_DLY_CTRL_400_MHZ_ATTILAPHY	0x41
+#define EMIF_DLL_SLAVE_DLY_CTRL_200_MHZ_ATTILAPHY	0x80
+#define EMIF_DLL_SLAVE_DLY_CTRL_100_MHZ_AND_LESS_ATTILAPHY 0xFF
+
+/* DDR_PHY_CTRL_1 values for EMIF4D5 INTELLIPHY combination */
+#define EMIF_DDR_PHY_CTRL_1_BASE_VAL_INTELLIPHY		0x0E084200
+#define EMIF_PHY_TOTAL_READ_LATENCY_INTELLIPHY_PS	10000
+
+/* TEMP_ALERT_CONFIG - corresponding to temp gradient 5 C/s */
+#define TEMP_ALERT_POLL_INTERVAL_DEFAULT_MS		360
+
+#define EMIF_T_CSTA					3
+#define EMIF_T_PDLL_UL					128
+
+/* External PHY control registers magic values */
+#define EMIF_EXT_PHY_CTRL_1_VAL				0x04020080
+#define EMIF_EXT_PHY_CTRL_5_VAL				0x04010040
+#define EMIF_EXT_PHY_CTRL_6_VAL				0x01004010
+#define EMIF_EXT_PHY_CTRL_7_VAL				0x00001004
+#define EMIF_EXT_PHY_CTRL_8_VAL				0x04010040
+#define EMIF_EXT_PHY_CTRL_9_VAL				0x01004010
+#define EMIF_EXT_PHY_CTRL_10_VAL			0x00001004
+#define EMIF_EXT_PHY_CTRL_11_VAL			0x00000000
+#define EMIF_EXT_PHY_CTRL_12_VAL			0x00000000
+#define EMIF_EXT_PHY_CTRL_13_VAL			0x00000000
+#define EMIF_EXT_PHY_CTRL_14_VAL			0x80080080
+#define EMIF_EXT_PHY_CTRL_15_VAL			0x00800800
+#define EMIF_EXT_PHY_CTRL_16_VAL			0x08102040
+#define EMIF_EXT_PHY_CTRL_17_VAL			0x00000001
+#define EMIF_EXT_PHY_CTRL_18_VAL			0x540A8150
+#define EMIF_EXT_PHY_CTRL_19_VAL			0xA81502A0
+#define EMIF_EXT_PHY_CTRL_20_VAL			0x002A0540
+#define EMIF_EXT_PHY_CTRL_21_VAL			0x00000000
+#define EMIF_EXT_PHY_CTRL_22_VAL			0x00000000
+#define EMIF_EXT_PHY_CTRL_23_VAL			0x00000000
+#define EMIF_EXT_PHY_CTRL_24_VAL			0x00000077
+
+#define EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS	1200
+
+/* Registers offset */
+#define EMIF_MODULE_ID_AND_REVISION			0x0000
+#define EMIF_STATUS					0x0004
+#define EMIF_SDRAM_CONFIG				0x0008
+#define EMIF_SDRAM_CONFIG_2				0x000c
+#define EMIF_SDRAM_REFRESH_CONTROL			0x0010
+#define EMIF_SDRAM_REFRESH_CTRL_SHDW			0x0014
+#define EMIF_SDRAM_TIMING_1				0x0018
+#define EMIF_SDRAM_TIMING_1_SHDW			0x001c
+#define EMIF_SDRAM_TIMING_2				0x0020
+#define EMIF_SDRAM_TIMING_2_SHDW			0x0024
+#define EMIF_SDRAM_TIMING_3				0x0028
+#define EMIF_SDRAM_TIMING_3_SHDW			0x002c
+#define EMIF_LPDDR2_NVM_TIMING				0x0030
+#define EMIF_LPDDR2_NVM_TIMING_SHDW			0x0034
+#define EMIF_POWER_MANAGEMENT_CONTROL			0x0038
+#define EMIF_POWER_MANAGEMENT_CTRL_SHDW			0x003c
+#define EMIF_LPDDR2_MODE_REG_DATA			0x0040
+#define EMIF_LPDDR2_MODE_REG_CONFIG			0x0050
+#define EMIF_OCP_CONFIG					0x0054
+#define EMIF_OCP_CONFIG_VALUE_1				0x0058
+#define EMIF_OCP_CONFIG_VALUE_2				0x005c
+#define EMIF_IODFT_TEST_LOGIC_GLOBAL_CONTROL		0x0060
+#define EMIF_IODFT_TEST_LOGIC_CTRL_MISR_RESULT		0x0064
+#define EMIF_IODFT_TEST_LOGIC_ADDRESS_MISR_RESULT	0x0068
+#define EMIF_IODFT_TEST_LOGIC_DATA_MISR_RESULT_1	0x006c
+#define EMIF_IODFT_TEST_LOGIC_DATA_MISR_RESULT_2	0x0070
+#define EMIF_IODFT_TEST_LOGIC_DATA_MISR_RESULT_3	0x0074
+#define EMIF_PERFORMANCE_COUNTER_1			0x0080
+#define EMIF_PERFORMANCE_COUNTER_2			0x0084
+#define EMIF_PERFORMANCE_COUNTER_CONFIG			0x0088
+#define EMIF_PERFORMANCE_COUNTER_MASTER_REGION_SELECT	0x008c
+#define EMIF_PERFORMANCE_COUNTER_TIME			0x0090
+#define EMIF_MISC_REG					0x0094
+#define EMIF_DLL_CALIB_CTRL				0x0098
+#define EMIF_DLL_CALIB_CTRL_SHDW			0x009c
+#define EMIF_END_OF_INTERRUPT				0x00a0
+#define EMIF_SYSTEM_OCP_INTERRUPT_RAW_STATUS		0x00a4
+#define EMIF_LL_OCP_INTERRUPT_RAW_STATUS		0x00a8
+#define EMIF_SYSTEM_OCP_INTERRUPT_STATUS		0x00ac
+#define EMIF_LL_OCP_INTERRUPT_STATUS			0x00b0
+#define EMIF_SYSTEM_OCP_INTERRUPT_ENABLE_SET		0x00b4
+#define EMIF_LL_OCP_INTERRUPT_ENABLE_SET		0x00b8
+#define EMIF_SYSTEM_OCP_INTERRUPT_ENABLE_CLEAR		0x00bc
+#define EMIF_LL_OCP_INTERRUPT_ENABLE_CLEAR		0x00c0
+#define EMIF_SDRAM_OUTPUT_IMPEDANCE_CALIBRATION_CONFIG	0x00c8
+#define EMIF_TEMPERATURE_ALERT_CONFIG			0x00cc
+#define EMIF_OCP_ERROR_LOG				0x00d0
+#define EMIF_READ_WRITE_LEVELING_RAMP_WINDOW		0x00d4
+#define EMIF_READ_WRITE_LEVELING_RAMP_CONTROL		0x00d8
+#define EMIF_READ_WRITE_LEVELING_CONTROL		0x00dc
+#define EMIF_DDR_PHY_CTRL_1				0x00e4
+#define EMIF_DDR_PHY_CTRL_1_SHDW			0x00e8
+#define EMIF_DDR_PHY_CTRL_2				0x00ec
+#define EMIF_PRIORITY_TO_CLASS_OF_SERVICE_MAPPING	0x0100
+#define EMIF_CONNECTION_ID_TO_CLASS_OF_SERVICE_1_MAPPING 0x0104
+#define EMIF_CONNECTION_ID_TO_CLASS_OF_SERVICE_2_MAPPING 0x0108
+#define EMIF_READ_WRITE_EXECUTION_THRESHOLD		0x0120
+#define EMIF_COS_CONFIG					0x0124
+#define EMIF_PHY_STATUS_1				0x0140
+#define EMIF_PHY_STATUS_2				0x0144
+#define EMIF_PHY_STATUS_3				0x0148
+#define EMIF_PHY_STATUS_4				0x014c
+#define EMIF_PHY_STATUS_5				0x0150
+#define EMIF_PHY_STATUS_6				0x0154
+#define EMIF_PHY_STATUS_7				0x0158
+#define EMIF_PHY_STATUS_8				0x015c
+#define EMIF_PHY_STATUS_9				0x0160
+#define EMIF_PHY_STATUS_10				0x0164
+#define EMIF_PHY_STATUS_11				0x0168
+#define EMIF_PHY_STATUS_12				0x016c
+#define EMIF_PHY_STATUS_13				0x0170
+#define EMIF_PHY_STATUS_14				0x0174
+#define EMIF_PHY_STATUS_15				0x0178
+#define EMIF_PHY_STATUS_16				0x017c
+#define EMIF_PHY_STATUS_17				0x0180
+#define EMIF_PHY_STATUS_18				0x0184
+#define EMIF_PHY_STATUS_19				0x0188
+#define EMIF_PHY_STATUS_20				0x018c
+#define EMIF_PHY_STATUS_21				0x0190
+#define EMIF_EXT_PHY_CTRL_1				0x0200
+#define EMIF_EXT_PHY_CTRL_1_SHDW			0x0204
+#define EMIF_EXT_PHY_CTRL_2				0x0208
+#define EMIF_EXT_PHY_CTRL_2_SHDW			0x020c
+#define EMIF_EXT_PHY_CTRL_3				0x0210
+#define EMIF_EXT_PHY_CTRL_3_SHDW			0x0214
+#define EMIF_EXT_PHY_CTRL_4				0x0218
+#define EMIF_EXT_PHY_CTRL_4_SHDW			0x021c
+#define EMIF_EXT_PHY_CTRL_5				0x0220
+#define EMIF_EXT_PHY_CTRL_5_SHDW			0x0224
+#define EMIF_EXT_PHY_CTRL_6				0x0228
+#define EMIF_EXT_PHY_CTRL_6_SHDW			0x022c
+#define EMIF_EXT_PHY_CTRL_7				0x0230
+#define EMIF_EXT_PHY_CTRL_7_SHDW			0x0234
+#define EMIF_EXT_PHY_CTRL_8				0x0238
+#define EMIF_EXT_PHY_CTRL_8_SHDW			0x023c
+#define EMIF_EXT_PHY_CTRL_9				0x0240
+#define EMIF_EXT_PHY_CTRL_9_SHDW			0x0244
+#define EMIF_EXT_PHY_CTRL_10				0x0248
+#define EMIF_EXT_PHY_CTRL_10_SHDW			0x024c
+#define EMIF_EXT_PHY_CTRL_11				0x0250
+#define EMIF_EXT_PHY_CTRL_11_SHDW			0x0254
+#define EMIF_EXT_PHY_CTRL_12				0x0258
+#define EMIF_EXT_PHY_CTRL_12_SHDW			0x025c
+#define EMIF_EXT_PHY_CTRL_13				0x0260
+#define EMIF_EXT_PHY_CTRL_13_SHDW			0x0264
+#define EMIF_EXT_PHY_CTRL_14				0x0268
+#define EMIF_EXT_PHY_CTRL_14_SHDW			0x026c
+#define EMIF_EXT_PHY_CTRL_15				0x0270
+#define EMIF_EXT_PHY_CTRL_15_SHDW			0x0274
+#define EMIF_EXT_PHY_CTRL_16				0x0278
+#define EMIF_EXT_PHY_CTRL_16_SHDW			0x027c
+#define EMIF_EXT_PHY_CTRL_17				0x0280
+#define EMIF_EXT_PHY_CTRL_17_SHDW			0x0284
+#define EMIF_EXT_PHY_CTRL_18				0x0288
+#define EMIF_EXT_PHY_CTRL_18_SHDW			0x028c
+#define EMIF_EXT_PHY_CTRL_19				0x0290
+#define EMIF_EXT_PHY_CTRL_19_SHDW			0x0294
+#define EMIF_EXT_PHY_CTRL_20				0x0298
+#define EMIF_EXT_PHY_CTRL_20_SHDW			0x029c
+#define EMIF_EXT_PHY_CTRL_21				0x02a0
+#define EMIF_EXT_PHY_CTRL_21_SHDW			0x02a4
+#define EMIF_EXT_PHY_CTRL_22				0x02a8
+#define EMIF_EXT_PHY_CTRL_22_SHDW			0x02ac
+#define EMIF_EXT_PHY_CTRL_23				0x02b0
+#define EMIF_EXT_PHY_CTRL_23_SHDW			0x02b4
+#define EMIF_EXT_PHY_CTRL_24				0x02b8
+#define EMIF_EXT_PHY_CTRL_24_SHDW			0x02bc
+#define EMIF_EXT_PHY_CTRL_25				0x02c0
+#define EMIF_EXT_PHY_CTRL_25_SHDW			0x02c4
+#define EMIF_EXT_PHY_CTRL_26				0x02c8
+#define EMIF_EXT_PHY_CTRL_26_SHDW			0x02cc
+#define EMIF_EXT_PHY_CTRL_27				0x02d0
+#define EMIF_EXT_PHY_CTRL_27_SHDW			0x02d4
+#define EMIF_EXT_PHY_CTRL_28				0x02d8
+#define EMIF_EXT_PHY_CTRL_28_SHDW			0x02dc
+#define EMIF_EXT_PHY_CTRL_29				0x02e0
+#define EMIF_EXT_PHY_CTRL_29_SHDW			0x02e4
+#define EMIF_EXT_PHY_CTRL_30				0x02e8
+#define EMIF_EXT_PHY_CTRL_30_SHDW			0x02ec
+
+/* Registers shifts and masks */
+
+/* EMIF_MODULE_ID_AND_REVISION */
+#define SCHEME_SHIFT					30
+#define SCHEME_MASK					(0x3 << 30)
+#define MODULE_ID_SHIFT					16
+#define MODULE_ID_MASK					(0xfff << 16)
+#define RTL_VERSION_SHIFT				11
+#define RTL_VERSION_MASK				(0x1f << 11)
+#define MAJOR_REVISION_SHIFT				8
+#define MAJOR_REVISION_MASK				(0x7 << 8)
+#define MINOR_REVISION_SHIFT				0
+#define MINOR_REVISION_MASK				(0x3f << 0)
+
+/* STATUS */
+#define BE_SHIFT					31
+#define BE_MASK						(1 << 31)
+#define DUAL_CLK_MODE_SHIFT				30
+#define DUAL_CLK_MODE_MASK				(1 << 30)
+#define FAST_INIT_SHIFT					29
+#define FAST_INIT_MASK					(1 << 29)
+#define RDLVLGATETO_SHIFT				6
+#define RDLVLGATETO_MASK				(1 << 6)
+#define RDLVLTO_SHIFT					5
+#define RDLVLTO_MASK					(1 << 5)
+#define WRLVLTO_SHIFT					4
+#define WRLVLTO_MASK					(1 << 4)
+#define PHY_DLL_READY_SHIFT				2
+#define PHY_DLL_READY_MASK				(1 << 2)
+
+/* SDRAM_CONFIG */
+#define SDRAM_TYPE_SHIFT				29
+#define SDRAM_TYPE_MASK					(0x7 << 29)
+#define IBANK_POS_SHIFT					27
+#define IBANK_POS_MASK					(0x3 << 27)
+#define DDR_TERM_SHIFT					24
+#define DDR_TERM_MASK					(0x7 << 24)
+#define DDR2_DDQS_SHIFT					23
+#define DDR2_DDQS_MASK					(1 << 23)
+#define DYN_ODT_SHIFT					21
+#define DYN_ODT_MASK					(0x3 << 21)
+#define DDR_DISABLE_DLL_SHIFT				20
+#define DDR_DISABLE_DLL_MASK				(1 << 20)
+#define SDRAM_DRIVE_SHIFT				18
+#define SDRAM_DRIVE_MASK				(0x3 << 18)
+#define CWL_SHIFT					16
+#define CWL_MASK					(0x3 << 16)
+#define NARROW_MODE_SHIFT				14
+#define NARROW_MODE_MASK				(0x3 << 14)
+#define CL_SHIFT					10
+#define CL_MASK						(0xf << 10)
+#define ROWSIZE_SHIFT					7
+#define ROWSIZE_MASK					(0x7 << 7)
+#define IBANK_SHIFT					4
+#define IBANK_MASK					(0x7 << 4)
+#define EBANK_SHIFT					3
+#define EBANK_MASK					(1 << 3)
+#define PAGESIZE_SHIFT					0
+#define PAGESIZE_MASK					(0x7 << 0)
+
+/* SDRAM_CONFIG_2 */
+#define CS1NVMEN_SHIFT					30
+#define CS1NVMEN_MASK					(1 << 30)
+#define EBANK_POS_SHIFT					27
+#define EBANK_POS_MASK					(1 << 27)
+#define RDBNUM_SHIFT					4
+#define RDBNUM_MASK					(0x3 << 4)
+#define RDBSIZE_SHIFT					0
+#define RDBSIZE_MASK					(0x7 << 0)
+
+/* SDRAM_REFRESH_CONTROL */
+#define INITREF_DIS_SHIFT				31
+#define INITREF_DIS_MASK				(1 << 31)
+#define SRT_SHIFT					29
+#define SRT_MASK					(1 << 29)
+#define ASR_SHIFT					28
+#define ASR_MASK					(1 << 28)
+#define PASR_SHIFT					24
+#define PASR_MASK					(0x7 << 24)
+#define REFRESH_RATE_SHIFT				0
+#define REFRESH_RATE_MASK				(0xffff << 0)
+
+/* SDRAM_TIMING_1 */
+#define T_RTW_SHIFT					29
+#define T_RTW_MASK					(0x7 << 29)
+#define T_RP_SHIFT					25
+#define T_RP_MASK					(0xf << 25)
+#define T_RCD_SHIFT					21
+#define T_RCD_MASK					(0xf << 21)
+#define T_WR_SHIFT					17
+#define T_WR_MASK					(0xf << 17)
+#define T_RAS_SHIFT					12
+#define T_RAS_MASK					(0x1f << 12)
+#define T_RC_SHIFT					6
+#define T_RC_MASK					(0x3f << 6)
+#define T_RRD_SHIFT					3
+#define T_RRD_MASK					(0x7 << 3)
+#define T_WTR_SHIFT					0
+#define T_WTR_MASK					(0x7 << 0)
+
+/* SDRAM_TIMING_2 */
+#define T_XP_SHIFT					28
+#define T_XP_MASK					(0x7 << 28)
+#define T_ODT_SHIFT					25
+#define T_ODT_MASK					(0x7 << 25)
+#define T_XSNR_SHIFT					16
+#define T_XSNR_MASK					(0x1ff << 16)
+#define T_XSRD_SHIFT					6
+#define T_XSRD_MASK					(0x3ff << 6)
+#define T_RTP_SHIFT					3
+#define T_RTP_MASK					(0x7 << 3)
+#define T_CKE_SHIFT					0
+#define T_CKE_MASK					(0x7 << 0)
+
+/* SDRAM_TIMING_3 */
+#define T_PDLL_UL_SHIFT					28
+#define T_PDLL_UL_MASK					(0xf << 28)
+#define T_CSTA_SHIFT					24
+#define T_CSTA_MASK					(0xf << 24)
+#define T_CKESR_SHIFT					21
+#define T_CKESR_MASK					(0x7 << 21)
+#define ZQ_ZQCS_SHIFT					15
+#define ZQ_ZQCS_MASK					(0x3f << 15)
+#define T_TDQSCKMAX_SHIFT				13
+#define T_TDQSCKMAX_MASK				(0x3 << 13)
+#define T_RFC_SHIFT					4
+#define T_RFC_MASK					(0x1ff << 4)
+#define T_RAS_MAX_SHIFT					0
+#define T_RAS_MAX_MASK					(0xf << 0)
+
+/* POWER_MANAGEMENT_CONTROL */
+#define PD_TIM_SHIFT					12
+#define PD_TIM_MASK					(0xf << 12)
+#define DPD_EN_SHIFT					11
+#define DPD_EN_MASK					(1 << 11)
+#define LP_MODE_SHIFT					8
+#define LP_MODE_MASK					(0x7 << 8)
+#define SR_TIM_SHIFT					4
+#define SR_TIM_MASK					(0xf << 4)
+#define CS_TIM_SHIFT					0
+#define CS_TIM_MASK					(0xf << 0)
+
+/* LPDDR2_MODE_REG_DATA */
+#define VALUE_0_SHIFT					0
+#define VALUE_0_MASK					(0x7f << 0)
+
+/* LPDDR2_MODE_REG_CONFIG */
+#define CS_SHIFT					31
+#define CS_MASK						(1 << 31)
+#define REFRESH_EN_SHIFT				30
+#define REFRESH_EN_MASK					(1 << 30)
+#define ADDRESS_SHIFT					0
+#define ADDRESS_MASK					(0xff << 0)
+
+/* OCP_CONFIG */
+#define SYS_THRESH_MAX_SHIFT				24
+#define SYS_THRESH_MAX_MASK				(0xf << 24)
+#define MPU_THRESH_MAX_SHIFT				20
+#define MPU_THRESH_MAX_MASK				(0xf << 20)
+#define LL_THRESH_MAX_SHIFT				16
+#define LL_THRESH_MAX_MASK				(0xf << 16)
+
+/* PERFORMANCE_COUNTER_1 */
+#define COUNTER1_SHIFT					0
+#define COUNTER1_MASK					(0xffffffff << 0)
+
+/* PERFORMANCE_COUNTER_2 */
+#define COUNTER2_SHIFT					0
+#define COUNTER2_MASK					(0xffffffff << 0)
+
+/* PERFORMANCE_COUNTER_CONFIG */
+#define CNTR2_MCONNID_EN_SHIFT				31
+#define CNTR2_MCONNID_EN_MASK				(1 << 31)
+#define CNTR2_REGION_EN_SHIFT				30
+#define CNTR2_REGION_EN_MASK				(1 << 30)
+#define CNTR2_CFG_SHIFT					16
+#define CNTR2_CFG_MASK					(0xf << 16)
+#define CNTR1_MCONNID_EN_SHIFT				15
+#define CNTR1_MCONNID_EN_MASK				(1 << 15)
+#define CNTR1_REGION_EN_SHIFT				14
+#define CNTR1_REGION_EN_MASK				(1 << 14)
+#define CNTR1_CFG_SHIFT					0
+#define CNTR1_CFG_MASK					(0xf << 0)
+
+/* PERFORMANCE_COUNTER_MASTER_REGION_SELECT */
+#define MCONNID2_SHIFT					24
+#define MCONNID2_MASK					(0xff << 24)
+#define REGION_SEL2_SHIFT				16
+#define REGION_SEL2_MASK				(0x3 << 16)
+#define MCONNID1_SHIFT					8
+#define MCONNID1_MASK					(0xff << 8)
+#define REGION_SEL1_SHIFT				0
+#define REGION_SEL1_MASK				(0x3 << 0)
+
+/* PERFORMANCE_COUNTER_TIME */
+#define TOTAL_TIME_SHIFT				0
+#define TOTAL_TIME_MASK					(0xffffffff << 0)
+
+/* DLL_CALIB_CTRL */
+#define ACK_WAIT_SHIFT					16
+#define ACK_WAIT_MASK					(0xf << 16)
+#define DLL_CALIB_INTERVAL_SHIFT			0
+#define DLL_CALIB_INTERVAL_MASK				(0x1ff << 0)
+
+/* END_OF_INTERRUPT */
+#define EOI_SHIFT					0
+#define EOI_MASK					(1 << 0)
+
+/* SYSTEM_OCP_INTERRUPT_RAW_STATUS */
+#define DNV_SYS_SHIFT					2
+#define DNV_SYS_MASK					(1 << 2)
+#define TA_SYS_SHIFT					1
+#define TA_SYS_MASK					(1 << 1)
+#define ERR_SYS_SHIFT					0
+#define ERR_SYS_MASK					(1 << 0)
+
+/* LOW_LATENCY_OCP_INTERRUPT_RAW_STATUS */
+#define DNV_LL_SHIFT					2
+#define DNV_LL_MASK					(1 << 2)
+#define TA_LL_SHIFT					1
+#define TA_LL_MASK					(1 << 1)
+#define ERR_LL_SHIFT					0
+#define ERR_LL_MASK					(1 << 0)
+
+/* SYSTEM_OCP_INTERRUPT_ENABLE_SET */
+#define EN_DNV_SYS_SHIFT				2
+#define EN_DNV_SYS_MASK					(1 << 2)
+#define EN_TA_SYS_SHIFT					1
+#define EN_TA_SYS_MASK					(1 << 1)
+#define EN_ERR_SYS_SHIFT					0
+#define EN_ERR_SYS_MASK					(1 << 0)
+
+/* LOW_LATENCY_OCP_INTERRUPT_ENABLE_SET */
+#define EN_DNV_LL_SHIFT					2
+#define EN_DNV_LL_MASK					(1 << 2)
+#define EN_TA_LL_SHIFT					1
+#define EN_TA_LL_MASK					(1 << 1)
+#define EN_ERR_LL_SHIFT					0
+#define EN_ERR_LL_MASK					(1 << 0)
+
+/* SDRAM_OUTPUT_IMPEDANCE_CALIBRATION_CONFIG */
+#define ZQ_CS1EN_SHIFT					31
+#define ZQ_CS1EN_MASK					(1 << 31)
+#define ZQ_CS0EN_SHIFT					30
+#define ZQ_CS0EN_MASK					(1 << 30)
+#define ZQ_DUALCALEN_SHIFT				29
+#define ZQ_DUALCALEN_MASK				(1 << 29)
+#define ZQ_SFEXITEN_SHIFT				28
+#define ZQ_SFEXITEN_MASK				(1 << 28)
+#define ZQ_ZQINIT_MULT_SHIFT				18
+#define ZQ_ZQINIT_MULT_MASK				(0x3 << 18)
+#define ZQ_ZQCL_MULT_SHIFT				16
+#define ZQ_ZQCL_MULT_MASK				(0x3 << 16)
+#define ZQ_REFINTERVAL_SHIFT				0
+#define ZQ_REFINTERVAL_MASK				(0xffff << 0)
+
+/* TEMPERATURE_ALERT_CONFIG */
+#define TA_CS1EN_SHIFT					31
+#define TA_CS1EN_MASK					(1 << 31)
+#define TA_CS0EN_SHIFT					30
+#define TA_CS0EN_MASK					(1 << 30)
+#define TA_SFEXITEN_SHIFT				28
+#define TA_SFEXITEN_MASK				(1 << 28)
+#define TA_DEVWDT_SHIFT					26
+#define TA_DEVWDT_MASK					(0x3 << 26)
+#define TA_DEVCNT_SHIFT					24
+#define TA_DEVCNT_MASK					(0x3 << 24)
+#define TA_REFINTERVAL_SHIFT				0
+#define TA_REFINTERVAL_MASK				(0x3fffff << 0)
+
+/* OCP_ERROR_LOG */
+#define MADDRSPACE_SHIFT				14
+#define MADDRSPACE_MASK					(0x3 << 14)
+#define MBURSTSEQ_SHIFT					11
+#define MBURSTSEQ_MASK					(0x7 << 11)
+#define MCMD_SHIFT					8
+#define MCMD_MASK					(0x7 << 8)
+#define MCONNID_SHIFT					0
+#define MCONNID_MASK					(0xff << 0)
+
+/* DDR_PHY_CTRL_1 - EMIF4D */
+#define DLL_SLAVE_DLY_CTRL_SHIFT_4D			4
+#define DLL_SLAVE_DLY_CTRL_MASK_4D			(0xFF << 4)
+#define READ_LATENCY_SHIFT_4D				0
+#define READ_LATENCY_MASK_4D				(0xf << 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)
+
+#endif /* __TI_EMIF_H */
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 106+ messages in thread

* [PATCH v4 03/11] ARM: OMAP2+: timer: Add suspend-resume callbacks for clkevent device
  2014-07-11  2:55 ` Dave Gerlach
@ 2014-07-11  2:55   ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11  2:55 UTC (permalink / raw)
  To: linux-arm-kernel, linux-omap
  Cc: Paul Walmsley, Kevin Hilman, Tony Lindgren, Tero Kristo,
	Nishanth Menon, Russ Dill, Santosh Shilimkar, Daniel Mack,
	Suman Anna, Benoit Cousson, Vaibhav Bedia, Dave Gerlach

From: Vaibhav Bedia <vaibhav.bedia@ti.com>

OMAP timer code registers two timers - one as clocksource
and one as clockevent. Since AM33XX has only one usable timer
in the WKUP domain 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.

Signed-off-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
---
v3->v4:
	Only use for am33xx soc now.

 arch/arm/mach-omap2/timer.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
index 43d03fb..6fc1748 100644
--- a/arch/arm/mach-omap2/timer.c
+++ b/arch/arm/mach-omap2/timer.c
@@ -128,6 +128,29 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
 	}
 }
 
+static void omap_clkevt_suspend(struct clock_event_device *unused)
+{
+	struct omap_hwmod *oh;
+
+	oh = omap_hwmod_lookup(clockevent_gpt.name);
+	if (!oh)
+		return;
+
+	omap_hwmod_idle(oh);
+}
+
+static void omap_clkevt_resume(struct clock_event_device *unused)
+{
+	struct omap_hwmod *oh;
+
+	oh = omap_hwmod_lookup(clockevent_gpt.name);
+	if (!oh)
+		return;
+
+	omap_hwmod_enable(oh);
+	__omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);
+}
+
 static struct clock_event_device clockevent_gpt = {
 	.features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
 	.rating		= 300,
@@ -333,6 +356,11 @@ static void __init omap2_gp_clockevent_init(int gptimer_id,
 	clkev.id = gptimer_id;
 	clkev.errata = omap_dm_timer_get_errata();
 
+	if (soc_is_am33xx()) {
+		clockevent_gpt.suspend = omap_clkevt_suspend;
+		clockevent_gpt.resume = omap_clkevt_resume;
+	}
+
 	/*
 	 * For clock-event timers we never read the timer counter and
 	 * so we are not impacted by errata i103 and i767. Therefore,
-- 
1.9.0


^ permalink raw reply related	[flat|nested] 106+ messages in thread

* [PATCH v4 03/11] ARM: OMAP2+: timer: Add suspend-resume callbacks for clkevent device
@ 2014-07-11  2:55   ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11  2:55 UTC (permalink / raw)
  To: linux-arm-kernel

From: Vaibhav Bedia <vaibhav.bedia@ti.com>

OMAP timer code registers two timers - one as clocksource
and one as clockevent. Since AM33XX has only one usable timer
in the WKUP domain 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.

Signed-off-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
---
v3->v4:
	Only use for am33xx soc now.

 arch/arm/mach-omap2/timer.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
index 43d03fb..6fc1748 100644
--- a/arch/arm/mach-omap2/timer.c
+++ b/arch/arm/mach-omap2/timer.c
@@ -128,6 +128,29 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
 	}
 }
 
+static void omap_clkevt_suspend(struct clock_event_device *unused)
+{
+	struct omap_hwmod *oh;
+
+	oh = omap_hwmod_lookup(clockevent_gpt.name);
+	if (!oh)
+		return;
+
+	omap_hwmod_idle(oh);
+}
+
+static void omap_clkevt_resume(struct clock_event_device *unused)
+{
+	struct omap_hwmod *oh;
+
+	oh = omap_hwmod_lookup(clockevent_gpt.name);
+	if (!oh)
+		return;
+
+	omap_hwmod_enable(oh);
+	__omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);
+}
+
 static struct clock_event_device clockevent_gpt = {
 	.features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
 	.rating		= 300,
@@ -333,6 +356,11 @@ static void __init omap2_gp_clockevent_init(int gptimer_id,
 	clkev.id = gptimer_id;
 	clkev.errata = omap_dm_timer_get_errata();
 
+	if (soc_is_am33xx()) {
+		clockevent_gpt.suspend = omap_clkevt_suspend;
+		clockevent_gpt.resume = omap_clkevt_resume;
+	}
+
 	/*
 	 * For clock-event timers we never read the timer counter and
 	 * so we are not impacted by errata i103 and i767. Therefore,
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 106+ messages in thread

* [PATCH v4 04/11] ARM: OMAP2+: Use pdata-quirks for wkup_m3 deassert_hardreset
  2014-07-11  2:55 ` Dave Gerlach
@ 2014-07-11  2:55   ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11  2:55 UTC (permalink / raw)
  To: linux-arm-kernel, linux-omap
  Cc: Paul Walmsley, Kevin Hilman, Tony Lindgren, Tero Kristo,
	Nishanth Menon, Russ Dill, Santosh Shilimkar, Daniel Mack,
	Suman Anna, Benoit Cousson, Dave Gerlach

Use pdata-quirks to reset the wkup_m3 during boot.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
---
 arch/arm/mach-omap2/pdata-quirks.c    | 12 ++++++++++++
 include/linux/platform_data/wkup_m3.h | 17 +++++++++++++++++
 2 files changed, 29 insertions(+)
 create mode 100644 include/linux/platform_data/wkup_m3.h

diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c
index 90c88d4..8e4a541 100644
--- a/arch/arm/mach-omap2/pdata-quirks.c
+++ b/arch/arm/mach-omap2/pdata-quirks.c
@@ -17,6 +17,7 @@
 
 #include <linux/platform_data/pinctrl-single.h>
 #include <linux/platform_data/iommu-omap.h>
+#include <linux/platform_data/wkup_m3.h>
 
 #include "am35xx.h"
 #include "common.h"
@@ -255,6 +256,13 @@ static void __init nokia_n900_legacy_init(void)
 }
 #endif /* CONFIG_ARCH_OMAP3 */
 
+#ifdef CONFIG_SOC_AM33XX
+static struct wkup_m3_platform_data wkup_m3_data = {
+	.reset_name = "wkup_m3",
+	.deassert_reset = omap_device_deassert_hardreset,
+};
+#endif
+
 #ifdef CONFIG_ARCH_OMAP4
 static void __init omap4_sdp_legacy_init(void)
 {
@@ -348,6 +356,10 @@ struct of_dev_auxdata omap_auxdata_lookup[] __initdata = {
 	OF_DEV_AUXDATA("ti,am3517-emac", 0x5c000000, "davinci_emac.0",
 		       &am35xx_emac_pdata),
 #endif
+#ifdef CONFIG_SOC_AM33XX
+	OF_DEV_AUXDATA("ti,am3353-wkup-m3", 0x44d00000, "44d00000.wkup_m3",
+		       &wkup_m3_data),
+#endif
 #ifdef CONFIG_ARCH_OMAP4
 	OF_DEV_AUXDATA("ti,omap4-padconf", 0x4a100040, "4a100040.pinmux", &pcs_pdata),
 	OF_DEV_AUXDATA("ti,omap4-padconf", 0x4a31e040, "4a31e040.pinmux", &pcs_pdata),
diff --git a/include/linux/platform_data/wkup_m3.h b/include/linux/platform_data/wkup_m3.h
new file mode 100644
index 0000000..07282bd
--- /dev/null
+++ b/include/linux/platform_data/wkup_m3.h
@@ -0,0 +1,17 @@
+/*
+ * omap wkup_m3: platform data
+ *
+ * Copyright (C) 2014 Texas Instruments, Inc.
+ *
+ * Dave Gerlach <d-gerlach@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+struct wkup_m3_platform_data {
+	const char *reset_name;
+
+	int (*deassert_reset)(struct platform_device *pdev, const char *name);
+};
-- 
1.9.0


^ permalink raw reply related	[flat|nested] 106+ messages in thread

* [PATCH v4 04/11] ARM: OMAP2+: Use pdata-quirks for wkup_m3 deassert_hardreset
@ 2014-07-11  2:55   ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11  2:55 UTC (permalink / raw)
  To: linux-arm-kernel

Use pdata-quirks to reset the wkup_m3 during boot.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
---
 arch/arm/mach-omap2/pdata-quirks.c    | 12 ++++++++++++
 include/linux/platform_data/wkup_m3.h | 17 +++++++++++++++++
 2 files changed, 29 insertions(+)
 create mode 100644 include/linux/platform_data/wkup_m3.h

diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c
index 90c88d4..8e4a541 100644
--- a/arch/arm/mach-omap2/pdata-quirks.c
+++ b/arch/arm/mach-omap2/pdata-quirks.c
@@ -17,6 +17,7 @@
 
 #include <linux/platform_data/pinctrl-single.h>
 #include <linux/platform_data/iommu-omap.h>
+#include <linux/platform_data/wkup_m3.h>
 
 #include "am35xx.h"
 #include "common.h"
@@ -255,6 +256,13 @@ static void __init nokia_n900_legacy_init(void)
 }
 #endif /* CONFIG_ARCH_OMAP3 */
 
+#ifdef CONFIG_SOC_AM33XX
+static struct wkup_m3_platform_data wkup_m3_data = {
+	.reset_name = "wkup_m3",
+	.deassert_reset = omap_device_deassert_hardreset,
+};
+#endif
+
 #ifdef CONFIG_ARCH_OMAP4
 static void __init omap4_sdp_legacy_init(void)
 {
@@ -348,6 +356,10 @@ struct of_dev_auxdata omap_auxdata_lookup[] __initdata = {
 	OF_DEV_AUXDATA("ti,am3517-emac", 0x5c000000, "davinci_emac.0",
 		       &am35xx_emac_pdata),
 #endif
+#ifdef CONFIG_SOC_AM33XX
+	OF_DEV_AUXDATA("ti,am3353-wkup-m3", 0x44d00000, "44d00000.wkup_m3",
+		       &wkup_m3_data),
+#endif
 #ifdef CONFIG_ARCH_OMAP4
 	OF_DEV_AUXDATA("ti,omap4-padconf", 0x4a100040, "4a100040.pinmux", &pcs_pdata),
 	OF_DEV_AUXDATA("ti,omap4-padconf", 0x4a31e040, "4a31e040.pinmux", &pcs_pdata),
diff --git a/include/linux/platform_data/wkup_m3.h b/include/linux/platform_data/wkup_m3.h
new file mode 100644
index 0000000..07282bd
--- /dev/null
+++ b/include/linux/platform_data/wkup_m3.h
@@ -0,0 +1,17 @@
+/*
+ * omap wkup_m3: platform data
+ *
+ * Copyright (C) 2014 Texas Instruments, Inc.
+ *
+ * Dave Gerlach <d-gerlach@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+struct wkup_m3_platform_data {
+	const char *reset_name;
+
+	int (*deassert_reset)(struct platform_device *pdev, const char *name);
+};
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 106+ messages in thread

* [PATCH v4 05/11] Documentation: dt: add ti,am3353_wkup_m3 bindings
  2014-07-11  2:55 ` Dave Gerlach
@ 2014-07-11  2:55   ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11  2:55 UTC (permalink / raw)
  To: linux-arm-kernel, linux-omap
  Cc: Paul Walmsley, Kevin Hilman, Tony Lindgren, Tero Kristo,
	Nishanth Menon, Russ Dill, Santosh Shilimkar, Daniel Mack,
	Suman Anna, Benoit Cousson, Dave Gerlach, Ohad Ben-Cohen

Add the device tree bindings document for am3353 wkup_m3.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
CC: Ohad Ben-Cohen <ohad@wizery.com>
CC: Benoit Cousson <bcousson@baylibre.com>
---
 .../bindings/remoteproc/wkup_m3_rproc.txt          | 46 ++++++++++++++++++++++
 1 file changed, 46 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt

diff --git a/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt b/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
new file mode 100644
index 0000000..e9dd909
--- /dev/null
+++ b/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
@@ -0,0 +1,46 @@
+Wakeup M3 Remote Proc Driver
+=====================
+
+TI AMx3 Family devices use a Cortex M3 co-processor to help with various
+low power tasks that cannot be controlled from the MPU. The CM3 requires
+a firmware binary to accomplish this and communicates with the MPU through
+IPC registers present in the SoCs control module. The wkup_m3 remoteproc
+driver handles the loading of the firmware and exposes an API to
+communicate with the wkup_m3 through the use of the IPC registers and a
+mailbox.
+
+Wkup M3 Device Node:
+====================
+A wkup_m3 device node is used to represent a wakeup M3 IP instance within
+a SoC. The sub-mailboxes are represented as child node of this parent node.
+
+Required properties:
+--------------------
+- compatible:		Should be "ti,am3353-wkup-m3" for AM33xx SoCs
+- reg:			Contains the wkup_m3 register address ranges for
+			umem, dmem, and ipc-regs.
+- reg-names:		Names for reg addresses given above
+- interrupts:		Contains the interrupt information for the wkup_m3
+			interrupt that signals the MPU.
+- ti,hwmods:		Name of the hwmod associated with the mailbox
+- ti,no-reset-on-init:	Reset is handled after fw has been loaded, not at
+			init of hwmod.
+- mbox-names:		Name of the mbox channel for the IPC framework
+- mbox:			Phandle used by IPC framework to get correct mbox
+			channel for communication.
+
+Example:
+--------
+/* AM33xx */
+wkup_m3: wkup_m3@44d00000 {
+	compatible = "ti,am3353-wkup-m3";
+	reg = <0x44d00000 0x4000
+	       0x44d80000 0x2000
+	       0x44e11324 0x0024>;
+	reg-names = "m3_umem", "m3_dmem", "ipc_regs";
+	interrupts = <78>;
+	ti,hwmods = "wkup_m3";
+	ti,no-reset-on-init;
+	mbox-names = "wkup_m3";
+	mbox = <&mailbox &mbox_wkupm3>;
+};
-- 
1.9.0


^ permalink raw reply related	[flat|nested] 106+ messages in thread

* [PATCH v4 05/11] Documentation: dt: add ti,am3353_wkup_m3 bindings
@ 2014-07-11  2:55   ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11  2:55 UTC (permalink / raw)
  To: linux-arm-kernel

Add the device tree bindings document for am3353 wkup_m3.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
CC: Ohad Ben-Cohen <ohad@wizery.com>
CC: Benoit Cousson <bcousson@baylibre.com>
---
 .../bindings/remoteproc/wkup_m3_rproc.txt          | 46 ++++++++++++++++++++++
 1 file changed, 46 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt

diff --git a/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt b/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
new file mode 100644
index 0000000..e9dd909
--- /dev/null
+++ b/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
@@ -0,0 +1,46 @@
+Wakeup M3 Remote Proc Driver
+=====================
+
+TI AMx3 Family devices use a Cortex M3 co-processor to help with various
+low power tasks that cannot be controlled from the MPU. The CM3 requires
+a firmware binary to accomplish this and communicates with the MPU through
+IPC registers present in the SoCs control module. The wkup_m3 remoteproc
+driver handles the loading of the firmware and exposes an API to
+communicate with the wkup_m3 through the use of the IPC registers and a
+mailbox.
+
+Wkup M3 Device Node:
+====================
+A wkup_m3 device node is used to represent a wakeup M3 IP instance within
+a SoC. The sub-mailboxes are represented as child node of this parent node.
+
+Required properties:
+--------------------
+- compatible:		Should be "ti,am3353-wkup-m3" for AM33xx SoCs
+- reg:			Contains the wkup_m3 register address ranges for
+			umem, dmem, and ipc-regs.
+- reg-names:		Names for reg addresses given above
+- interrupts:		Contains the interrupt information for the wkup_m3
+			interrupt that signals the MPU.
+- ti,hwmods:		Name of the hwmod associated with the mailbox
+- ti,no-reset-on-init:	Reset is handled after fw has been loaded, not at
+			init of hwmod.
+- mbox-names:		Name of the mbox channel for the IPC framework
+- mbox:			Phandle used by IPC framework to get correct mbox
+			channel for communication.
+
+Example:
+--------
+/* AM33xx */
+wkup_m3: wkup_m3 at 44d00000 {
+	compatible = "ti,am3353-wkup-m3";
+	reg = <0x44d00000 0x4000
+	       0x44d80000 0x2000
+	       0x44e11324 0x0024>;
+	reg-names = "m3_umem", "m3_dmem", "ipc_regs";
+	interrupts = <78>;
+	ti,hwmods = "wkup_m3";
+	ti,no-reset-on-init;
+	mbox-names = "wkup_m3";
+	mbox = <&mailbox &mbox_wkupm3>;
+};
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 106+ messages in thread

* [PATCH v4 06/11] remoteproc: wkup_m3: Add wkup_m3 remote proc driver
  2014-07-11  2:55 ` Dave Gerlach
@ 2014-07-11  2:55   ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11  2:55 UTC (permalink / raw)
  To: linux-arm-kernel, linux-omap
  Cc: Paul Walmsley, Kevin Hilman, Tony Lindgren, Tero Kristo,
	Nishanth Menon, Russ Dill, Santosh Shilimkar, Daniel Mack,
	Suman Anna, Benoit Cousson, Dave Gerlach, Ohad Ben-Cohen

Add a remoteproc driver to load the firmware for and boot the wkup_m3
present on am33xx. The wkup_m3 is an integrated Cortex M3 that allows
the SoC to enter the lowest possible power state by taking control from
the MPU after it has gone into its own low power state and shutting off
any additional peripherals.

Communication between the MPU and CM3 is handled by several IPC
registers in the control module and a mailbox. An API is exposed for
programming the aforementioned IPC registers and notifying the wkup_m3
of pending data using the mailbox. The wkup_m3 has the ability to
trigger an interrupt back to the MPU to allow for bidirectional
communication through these registers.

Two callbacks are provided. rproc_ready allows code to hook into the
driver to see when firmware has been loaded and execute other code and
txev_handler allows external code to run when the wkup_m3 triggers an
interrupt back to the m3.

The driver expects a resource table to be present in the wkup_m3 to
define the required memory resources needed by wkup_m3, at least the
data memory so that the firmware can be copied for the proper place for
execution.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
CC: Ohad Ben-Cohen <ohad@wizery.com>
---
 drivers/remoteproc/Kconfig         |  15 ++
 drivers/remoteproc/Makefile        |   1 +
 drivers/remoteproc/wkup_m3_rproc.c | 512 +++++++++++++++++++++++++++++++++++++
 include/linux/wkup_m3.h            |  71 +++++
 4 files changed, 599 insertions(+)
 create mode 100644 drivers/remoteproc/wkup_m3_rproc.c
 create mode 100644 include/linux/wkup_m3.h

diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 5e343ba..4b00c21 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -41,6 +41,21 @@ config STE_MODEM_RPROC
 	  This can be either built-in or a loadable module.
 	  If unsure say N.
 
+config WKUP_M3_RPROC
+	bool "AM33xx wkup-m3 remoteproc support"
+        depends on SOC_AM33XX
+        select REMOTEPROC
+	select MAILBOX
+	select OMAP2PLUS_MBOX
+	default n
+	help
+	  Say y here to support AM33xx wkup-m3.
+
+	  Required for Suspend-to-ram and CPUIdle on AM33xx. Allows for
+	  loading of firmware and communication with CM3 PM coproccesor
+	  that is present on AM33xx family of SoCs
+	  If unsure say N.
+
 config DA8XX_REMOTEPROC
 	tristate "DA8xx/OMAP-L13x remoteproc support"
 	depends on ARCH_DAVINCI_DA8XX
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index ac2ff75..81b04d1 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -9,4 +9,5 @@ remoteproc-y				+= remoteproc_virtio.o
 remoteproc-y				+= remoteproc_elf_loader.o
 obj-$(CONFIG_OMAP_REMOTEPROC)		+= omap_remoteproc.o
 obj-$(CONFIG_STE_MODEM_RPROC)	 	+= ste_modem_rproc.o
+obj-$(CONFIG_WKUP_M3_RPROC)		+= wkup_m3_rproc.o
 obj-$(CONFIG_DA8XX_REMOTEPROC)		+= da8xx_remoteproc.o
diff --git a/drivers/remoteproc/wkup_m3_rproc.c b/drivers/remoteproc/wkup_m3_rproc.c
new file mode 100644
index 0000000..58aeaf2
--- /dev/null
+++ b/drivers/remoteproc/wkup_m3_rproc.c
@@ -0,0 +1,512 @@
+/*
+ * AMx3 Wkup M3 Remote Processor driver
+ *
+ * Copyright (C) 2014 Texas Instruments, Inc.
+ *
+ * Dave Gerlach <d-gerlach@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/elf.h>
+#include <linux/pm_runtime.h>
+#include <linux/firmware.h>
+#include <linux/remoteproc.h>
+#include <linux/omap-mailbox.h>
+#include <linux/mailbox_client.h>
+#include <linux/wkup_m3.h>
+#include <linux/kthread.h>
+#include "remoteproc_internal.h"
+
+#include <linux/platform_data/wkup_m3.h>
+
+#define WKUP_M3_WAKE_SRC_MASK		0xFF
+
+#define WKUP_M3_STATUS_RESP_SHIFT	16
+#define WKUP_M3_STATUS_RESP_MASK	(0xffff << 16)
+
+#define WKUP_M3_FW_VERSION_SHIFT	0
+#define WKUP_M3_FW_VERSION_MASK		0xffff
+
+/* AM33XX M3_TXEV_EOI register */
+#define AM33XX_CONTROL_M3_TXEV_EOI	0x00
+
+#define AM33XX_M3_TXEV_ACK		(0x1 << 0)
+#define AM33XX_M3_TXEV_ENABLE		(0x0 << 0)
+
+/* AM33XX IPC message registers */
+#define AM33XX_CONTROL_IPC_MSG_REG0	0x04
+#define AM33XX_CONTROL_IPC_MSG_REG1	0x08
+#define AM33XX_CONTROL_IPC_MSG_REG2	0x0c
+#define AM33XX_CONTROL_IPC_MSG_REG3	0x10
+#define AM33XX_CONTROL_IPC_MSG_REG4	0x14
+#define AM33XX_CONTROL_IPC_MSG_REG5	0x18
+#define AM33XX_CONTROL_IPC_MSG_REG6	0x1c
+#define AM33XX_CONTROL_IPC_MSG_REG7	0x20
+
+struct wkup_m3_rproc {
+	struct rproc *rproc;
+
+	void * __iomem dev_table_va;
+	void * __iomem ipc_mem_base;
+	struct platform_device *pdev;
+
+	struct mbox_client mbox_client;
+	struct mbox_chan *mbox;
+	struct wkup_m3_ops *ops;
+
+	bool is_active;
+};
+
+static struct wkup_m3_rproc *m3_rproc_static;
+
+static struct wkup_m3_wakeup_src wakeups[] = {
+	{.irq_nr = 35,	.src = "USB0_PHY"},
+	{.irq_nr = 36,	.src = "USB1_PHY"},
+	{.irq_nr = 40,	.src = "I2C0"},
+	{.irq_nr = 41,	.src = "RTC Timer"},
+	{.irq_nr = 42,	.src = "RTC Alarm"},
+	{.irq_nr = 43,	.src = "Timer0"},
+	{.irq_nr = 44,	.src = "Timer1"},
+	{.irq_nr = 45,	.src = "UART"},
+	{.irq_nr = 46,	.src = "GPIO0"},
+	{.irq_nr = 48,	.src = "MPU_WAKE"},
+	{.irq_nr = 49,	.src = "WDT0"},
+	{.irq_nr = 50,	.src = "WDT1"},
+	{.irq_nr = 51,	.src = "ADC_TSC"},
+	{.irq_nr = 0,	.src = "Unknown"},
+};
+
+static void am33xx_txev_eoi(struct wkup_m3_rproc *m3_rproc)
+{
+	writel(AM33XX_M3_TXEV_ACK,
+	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
+}
+
+static void am33xx_txev_enable(struct wkup_m3_rproc *m3_rproc)
+{
+	writel(AM33XX_M3_TXEV_ENABLE,
+	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
+}
+
+static void wkup_m3_ctrl_ipc_write(struct wkup_m3_rproc *m3_rproc,
+				   struct wkup_m3_ipc_regs *ipc_regs)
+{
+	writel(ipc_regs->reg0,
+	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG0);
+	writel(ipc_regs->reg1,
+	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG1);
+	writel(ipc_regs->reg2,
+	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG2);
+	writel(ipc_regs->reg3,
+	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG3);
+	writel(ipc_regs->reg4,
+	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG4);
+	writel(ipc_regs->reg5,
+	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG5);
+	writel(ipc_regs->reg6,
+	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG6);
+	writel(ipc_regs->reg7,
+	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG7);
+}
+
+static void wkup_m3_ctrl_ipc_read(struct wkup_m3_rproc *m3_rproc,
+				  struct wkup_m3_ipc_regs *ipc_regs)
+{
+	ipc_regs->reg0 = readl(m3_rproc->ipc_mem_base
+			       + AM33XX_CONTROL_IPC_MSG_REG0);
+	ipc_regs->reg1 = readl(m3_rproc->ipc_mem_base
+			       + AM33XX_CONTROL_IPC_MSG_REG1);
+	ipc_regs->reg2 = readl(m3_rproc->ipc_mem_base
+			       + AM33XX_CONTROL_IPC_MSG_REG2);
+	ipc_regs->reg3 = readl(m3_rproc->ipc_mem_base
+			       + AM33XX_CONTROL_IPC_MSG_REG3);
+	ipc_regs->reg4 = readl(m3_rproc->ipc_mem_base
+			       + AM33XX_CONTROL_IPC_MSG_REG4);
+	ipc_regs->reg5 = readl(m3_rproc->ipc_mem_base
+			       + AM33XX_CONTROL_IPC_MSG_REG5);
+	ipc_regs->reg6 = readl(m3_rproc->ipc_mem_base
+			       + AM33XX_CONTROL_IPC_MSG_REG6);
+	ipc_regs->reg7 = readl(m3_rproc->ipc_mem_base
+			       + AM33XX_CONTROL_IPC_MSG_REG7);
+}
+
+static irqreturn_t wkup_m3_txev_handler(int irq, void *unused)
+{
+	am33xx_txev_eoi(m3_rproc_static);
+
+	if (m3_rproc_static->ops && m3_rproc_static->ops->txev_handler)
+		m3_rproc_static->ops->txev_handler();
+
+	am33xx_txev_enable(m3_rproc_static);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * wkup_m3_fw_version_clear - Clear FW version from ipc regs
+ *
+ * Invalidate M3 firmware version before hardreset.
+ * Write invalid version in lower 4 nibbles of parameter
+ * register (ipc_regs + 0x8).
+ */
+
+static void wkup_m3_fw_version_clear(void)
+{
+	struct wkup_m3_ipc_regs ipc_regs;
+
+	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
+	ipc_regs.reg2 &= 0xFFFF0000;
+	wkup_m3_ctrl_ipc_write(m3_rproc_static, &ipc_regs);
+}
+
+static void wkup_m3_mbox_callback(struct mbox_client *client, void *data)
+{
+	omap_mbox_disable_irq(m3_rproc_static->mbox, IRQ_RX);
+}
+
+static int wkup_m3_rproc_start(struct rproc *rproc)
+{
+	struct wkup_m3_rproc *m3_rproc = rproc->priv;
+	struct platform_device *pdev = m3_rproc->pdev;
+	struct device *dev = &pdev->dev;
+	struct wkup_m3_platform_data *pdata = dev->platform_data;
+	int ret;
+
+	wkup_m3_fw_version_clear();
+
+	if (pdata && pdata->deassert_reset) {
+		ret = pdata->deassert_reset(pdev, pdata->reset_name);
+		if (ret) {
+			dev_err(dev, "Unable to reset wkup_m3!\n");
+			return -ENODEV;
+		}
+	} else {
+		dev_err(dev, "Platform data missing deassert_reset!\n");
+		return -ENODEV;
+	}
+
+	m3_rproc->mbox_client.dev = dev;
+	m3_rproc->mbox_client.tx_done = NULL;
+	m3_rproc->mbox_client.rx_callback = wkup_m3_mbox_callback;
+	m3_rproc->mbox_client.tx_block = false;
+	m3_rproc->mbox_client.knows_txdone = false;
+
+	m3_rproc->mbox = mbox_request_channel(&m3_rproc->mbox_client);
+
+	if (IS_ERR(m3_rproc->mbox)) {
+		dev_err(dev, "IPC Request for A8->M3 Channel failed!\n");
+		ret = PTR_ERR(m3_rproc->mbox);
+		m3_rproc->mbox = NULL;
+		return ret;
+	}
+
+	if (m3_rproc_static->ops && m3_rproc_static->ops->rproc_ready)
+		m3_rproc_static->ops->rproc_ready();
+
+	m3_rproc_static->is_active = 1;
+
+	return 0;
+}
+
+
+static int wkup_m3_rproc_stop(struct rproc *rproc)
+{
+	return 0;
+}
+
+static void wkup_m3_rproc_kick(struct rproc *rproc, int vqid)
+{
+}
+
+static struct rproc_ops wkup_m3_rproc_ops = {
+	.start		= wkup_m3_rproc_start,
+	.stop		= wkup_m3_rproc_stop,
+	.kick		= wkup_m3_rproc_kick,
+};
+
+/* Public Functions */
+
+/**
+ * wkup_m3_set_ops - Set callbacks for user of rproc
+ * @ops - struct wkup_m3_ops *
+ *
+ * Registers callbacks to wkup_m3 to be invoked after rproc is ready to use
+ * and after an interrupt is handled.
+ */
+void wkup_m3_set_ops(struct wkup_m3_ops *ops)
+{
+	m3_rproc_static->ops = ops;
+
+	if (m3_rproc_static->is_active && m3_rproc_static->ops &&
+	    m3_rproc_static->ops->rproc_ready)
+		m3_rproc_static->ops->rproc_ready();
+}
+
+/**
+ * wkup_m3_ping - Send a dummy msg to wkup_m3 to tell to to check IPC regs
+ *
+ * Returns the result of sending mbox msg or -EIO if no mbox handle is present
+ */
+int wkup_m3_ping(void)
+{
+	int ret;
+	mbox_msg_t dummy_msg = 0;
+
+	if (!m3_rproc_static->mbox) {
+		dev_err(&m3_rproc_static->pdev->dev,
+			"No IPC channel to communicate with wkup_m3!\n");
+		return -EIO;
+	}
+
+	/*
+	 * Write a dummy message to the mailbox in order to trigger the RX
+	 * interrupt to alert the M3 that data is available in the IPC
+	 * registers. We must enable the IRQ here and disable it after in
+	 * the RX callback to avoid multiple interrupts being received
+	 * by the CM3.
+	 */
+	omap_mbox_enable_irq(m3_rproc_static->mbox, IRQ_RX);
+	ret = mbox_send_message(m3_rproc_static->mbox, (void *)dummy_msg);
+
+	if (ret < 0) {
+		pr_err("%s: mbox_send_message() failed: %d\n", __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * wkup_m3_wake_src - Get the wakeup source info passed from wkup_m3
+ * @wkup_m3_wakeup: struct wkup_m3_wakeup_src * gets assigned the
+ *		    wakeup src value
+ */
+void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wkup_m3_wakeup)
+{
+	struct wkup_m3_ipc_regs ipc_regs;
+	unsigned int wakeup_src_idx;
+	int j;
+
+	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
+
+	wakeup_src_idx = ipc_regs.reg6 & WKUP_M3_WAKE_SRC_MASK;
+
+	for (j = 0; j < ARRAY_SIZE(wakeups)-1; j++) {
+		if (wakeups[j].irq_nr == wakeup_src_idx) {
+			*wkup_m3_wakeup = wakeups[j];
+			return;
+		}
+	}
+	*wkup_m3_wakeup = wakeups[j];
+}
+
+/**
+ * wkup_m3_pm_status - Return the status code from wkup_m3 after sleep event
+ *
+ * Returns an error code that indicates whether or not the dsired sleep
+ * action was a success or not.
+ */
+int wkup_m3_pm_status(void)
+{
+	unsigned int i;
+	struct wkup_m3_ipc_regs ipc_regs;
+
+	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
+
+	i = WKUP_M3_STATUS_RESP_MASK & ipc_regs.reg1;
+	i >>= __ffs(WKUP_M3_STATUS_RESP_MASK);
+
+	return i;
+}
+
+/**
+ * wkup_m3_fw_version_read - Return the fw version given by the wkup_m3
+ *
+ * After boot the fw version should be read to ensure it is compatible.
+ */
+int wkup_m3_fw_version_read(void)
+{
+	struct wkup_m3_ipc_regs ipc_regs;
+
+	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
+
+	return ipc_regs.reg2 & WKUP_M3_FW_VERSION_MASK;
+}
+
+/**
+ * wkup_m3_set_cmd - write contents of struct to ipc regs
+ * @ipc_regs: struct wkup_m3_ipc_regs *
+ */
+void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs)
+{
+	wkup_m3_ctrl_ipc_write(m3_rproc_static, ipc_regs);
+}
+
+static void wkup_m3_rproc_loader_thread(struct rproc *rproc)
+{
+	struct wkup_m3_rproc *m3_rproc = rproc->priv;
+	struct device *dev = &m3_rproc->pdev->dev;
+	int ret;
+
+	wait_for_completion(&rproc->firmware_loading_complete);
+
+	ret = rproc_boot(rproc);
+	if (ret)
+		dev_err(dev, "rproc_boot failed\n");
+
+	do_exit(0);
+}
+
+static int wkup_m3_rproc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = pdev->dev.of_node;
+	struct wkup_m3_rproc *m3_rproc;
+	struct rproc *rproc;
+	int irq, ret;
+	struct resource *res;
+	struct task_struct *task;
+	const char *mbox_name;
+
+	pm_runtime_enable(&pdev->dev);
+
+	ret = pm_runtime_get_sync(&pdev->dev);
+	if (IS_ERR_VALUE(ret)) {
+		dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
+		return ret;
+	}
+
+	rproc = rproc_alloc(dev, "wkup_m3", &wkup_m3_rproc_ops,
+			    "am335x-pm-firmware.elf", sizeof(*m3_rproc));
+	if (!rproc)
+		return -ENOMEM;
+
+	m3_rproc = rproc->priv;
+	m3_rproc->rproc = rproc;
+	m3_rproc->pdev = pdev;
+
+	m3_rproc_static = m3_rproc;
+
+	irq = platform_get_irq(pdev, 0);
+	if (!irq) {
+		dev_err(&pdev->dev, "no irq resource\n");
+		ret = -ENXIO;
+		goto err;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ipc_regs");
+	if (!res) {
+		dev_err(&pdev->dev, "no memory resource for ipc\n");
+		ret = -ENXIO;
+		goto err;
+	}
+
+	m3_rproc->ipc_mem_base = devm_request_and_ioremap(dev, res);
+	if (!m3_rproc->ipc_mem_base) {
+		dev_err(dev, "could not ioremap ipc_mem\n");
+		ret = -EADDRNOTAVAIL;
+		goto err;
+	}
+
+	ret = devm_request_irq(dev, irq, wkup_m3_txev_handler,
+			       IRQF_DISABLED, "wkup_m3_txev", m3_rproc);
+	if (ret) {
+		dev_err(dev, "request_irq failed\n");
+		goto err;
+	}
+
+	/* Get mbox name from device tree node */
+	ret = of_property_read_string(np, "mbox-names", &mbox_name);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to get mbox name from dt node: %d\n",
+			ret);
+			goto err;
+	};
+
+	m3_rproc->mbox_client.chan_name = mbox_name;
+
+	/* Register as a remoteproc device */
+	ret = rproc_add(rproc);
+	if (ret) {
+		dev_err(dev, "rproc_add failed\n");
+		goto err;
+	}
+
+	/*
+	 * Wait for firmware loading completion in a thread so we
+	 * can boot the wkup_m3 as soon as it's ready without holding
+	 * up kernel boot
+	 */
+	task = kthread_run((void *)wkup_m3_rproc_loader_thread, rproc,
+			   "wkup_m3_rproc_loader");
+
+	if (IS_ERR(task)) {
+		dev_err(dev, "can't create rproc_loader thread\n");
+		goto err;
+	}
+
+	return 0;
+
+err:
+	rproc_put(rproc);
+	return ret;
+}
+
+static int wkup_m3_rproc_remove(struct platform_device *pdev)
+{
+	struct rproc *rproc = platform_get_drvdata(pdev);
+
+	rproc_del(rproc);
+	rproc_put(rproc);
+
+	return 0;
+}
+
+static int wkup_m3_rpm_suspend(struct device *dev)
+{
+	return -EBUSY;
+}
+
+static int wkup_m3_rpm_resume(struct device *dev)
+{
+	return 0;
+}
+
+static const struct dev_pm_ops wkup_m3_rproc_pm_ops = {
+	SET_RUNTIME_PM_OPS(wkup_m3_rpm_suspend, wkup_m3_rpm_resume, NULL)
+};
+
+static const struct of_device_id wkup_m3_rproc_of_match[] = {
+	{ .compatible = "ti,am3353-wkup-m3", .data = NULL, },
+	{},
+};
+
+static struct platform_driver wkup_m3_rproc_driver = {
+	.probe = wkup_m3_rproc_probe,
+	.remove = wkup_m3_rproc_remove,
+	.driver = {
+		.name = "wkup_m3",
+		.owner = THIS_MODULE,
+		.of_match_table = wkup_m3_rproc_of_match,
+		.pm = &wkup_m3_rproc_pm_ops,
+	},
+};
+
+module_platform_driver(wkup_m3_rproc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("wkup m3 remote processor control driver");
diff --git a/include/linux/wkup_m3.h b/include/linux/wkup_m3.h
new file mode 100644
index 0000000..1a2237f
--- /dev/null
+++ b/include/linux/wkup_m3.h
@@ -0,0 +1,71 @@
+/*
+ * TI Wakeup M3 Power Management Routines
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
+ * Dave Gerlach <d-gerlach@ti.com>
+ *
+ * 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 _LINUX_WKUP_M3_H
+#define _LINUX_WKUP_M3_H
+
+/**
+ * struct wkup_m3_ops - Callbacks for allowing pm code to interact with wkup_m3.
+ *
+ * @txev_handler: Callback to allow pm code to react to response from wkup_m3
+ *		  after pinging it using wkup_m3_ping.
+ *
+ * @firmware_loaded: Callback invoked when the firmware has been loaded to the
+ *		     m3 to allow the pm code to enable suspend/resume ops.
+ */
+
+struct wkup_m3_ops {
+	void (*txev_handler)(void);
+	void (*rproc_ready)(void);
+};
+
+struct wkup_m3_wakeup_src {
+	int irq_nr;
+	char src[10];
+};
+
+struct wkup_m3_ipc_regs {
+	u32 reg0;
+	u32 reg1;
+	u32 reg2;
+	u32 reg3;
+	u32 reg4;
+	u32 reg5;
+	u32 reg6;
+	u32 reg7;
+};
+
+#ifdef CONFIG_WKUP_M3_RPROC
+int wkup_m3_prepare(void);
+void wkup_m3_set_ops(struct wkup_m3_ops *ops);
+int wkup_m3_ping(void);
+void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wakeup_src);
+int wkup_m3_pm_status(void);
+int wkup_m3_is_valid(void);
+int wkup_m3_fw_version_read(void);
+void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs);
+
+#else
+
+int wkup_m3_prepare(void) { return -EINVAL; }
+void wkup_m3_set_ops(struct wkup_m3_ops *ops) { }
+int wkup_m3_ping(void) { return -EINVAL }
+void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wakeup_src) { }
+int wkup_m3_pm_status(void) { return -1; }
+int wkup_m3_fw_version_read(void) { return -1; }
+void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs) { }
+#endif /* CONFIG_WKUP_M3_RPROC */
+#endif /* _LINUX_WKUP_M3_H */
-- 
1.9.0


^ permalink raw reply related	[flat|nested] 106+ messages in thread

* [PATCH v4 06/11] remoteproc: wkup_m3: Add wkup_m3 remote proc driver
@ 2014-07-11  2:55   ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11  2:55 UTC (permalink / raw)
  To: linux-arm-kernel

Add a remoteproc driver to load the firmware for and boot the wkup_m3
present on am33xx. The wkup_m3 is an integrated Cortex M3 that allows
the SoC to enter the lowest possible power state by taking control from
the MPU after it has gone into its own low power state and shutting off
any additional peripherals.

Communication between the MPU and CM3 is handled by several IPC
registers in the control module and a mailbox. An API is exposed for
programming the aforementioned IPC registers and notifying the wkup_m3
of pending data using the mailbox. The wkup_m3 has the ability to
trigger an interrupt back to the MPU to allow for bidirectional
communication through these registers.

Two callbacks are provided. rproc_ready allows code to hook into the
driver to see when firmware has been loaded and execute other code and
txev_handler allows external code to run when the wkup_m3 triggers an
interrupt back to the m3.

The driver expects a resource table to be present in the wkup_m3 to
define the required memory resources needed by wkup_m3, at least the
data memory so that the firmware can be copied for the proper place for
execution.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
CC: Ohad Ben-Cohen <ohad@wizery.com>
---
 drivers/remoteproc/Kconfig         |  15 ++
 drivers/remoteproc/Makefile        |   1 +
 drivers/remoteproc/wkup_m3_rproc.c | 512 +++++++++++++++++++++++++++++++++++++
 include/linux/wkup_m3.h            |  71 +++++
 4 files changed, 599 insertions(+)
 create mode 100644 drivers/remoteproc/wkup_m3_rproc.c
 create mode 100644 include/linux/wkup_m3.h

diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 5e343ba..4b00c21 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -41,6 +41,21 @@ config STE_MODEM_RPROC
 	  This can be either built-in or a loadable module.
 	  If unsure say N.
 
+config WKUP_M3_RPROC
+	bool "AM33xx wkup-m3 remoteproc support"
+        depends on SOC_AM33XX
+        select REMOTEPROC
+	select MAILBOX
+	select OMAP2PLUS_MBOX
+	default n
+	help
+	  Say y here to support AM33xx wkup-m3.
+
+	  Required for Suspend-to-ram and CPUIdle on AM33xx. Allows for
+	  loading of firmware and communication with CM3 PM coproccesor
+	  that is present on AM33xx family of SoCs
+	  If unsure say N.
+
 config DA8XX_REMOTEPROC
 	tristate "DA8xx/OMAP-L13x remoteproc support"
 	depends on ARCH_DAVINCI_DA8XX
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index ac2ff75..81b04d1 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -9,4 +9,5 @@ remoteproc-y				+= remoteproc_virtio.o
 remoteproc-y				+= remoteproc_elf_loader.o
 obj-$(CONFIG_OMAP_REMOTEPROC)		+= omap_remoteproc.o
 obj-$(CONFIG_STE_MODEM_RPROC)	 	+= ste_modem_rproc.o
+obj-$(CONFIG_WKUP_M3_RPROC)		+= wkup_m3_rproc.o
 obj-$(CONFIG_DA8XX_REMOTEPROC)		+= da8xx_remoteproc.o
diff --git a/drivers/remoteproc/wkup_m3_rproc.c b/drivers/remoteproc/wkup_m3_rproc.c
new file mode 100644
index 0000000..58aeaf2
--- /dev/null
+++ b/drivers/remoteproc/wkup_m3_rproc.c
@@ -0,0 +1,512 @@
+/*
+ * AMx3 Wkup M3 Remote Processor driver
+ *
+ * Copyright (C) 2014 Texas Instruments, Inc.
+ *
+ * Dave Gerlach <d-gerlach@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/elf.h>
+#include <linux/pm_runtime.h>
+#include <linux/firmware.h>
+#include <linux/remoteproc.h>
+#include <linux/omap-mailbox.h>
+#include <linux/mailbox_client.h>
+#include <linux/wkup_m3.h>
+#include <linux/kthread.h>
+#include "remoteproc_internal.h"
+
+#include <linux/platform_data/wkup_m3.h>
+
+#define WKUP_M3_WAKE_SRC_MASK		0xFF
+
+#define WKUP_M3_STATUS_RESP_SHIFT	16
+#define WKUP_M3_STATUS_RESP_MASK	(0xffff << 16)
+
+#define WKUP_M3_FW_VERSION_SHIFT	0
+#define WKUP_M3_FW_VERSION_MASK		0xffff
+
+/* AM33XX M3_TXEV_EOI register */
+#define AM33XX_CONTROL_M3_TXEV_EOI	0x00
+
+#define AM33XX_M3_TXEV_ACK		(0x1 << 0)
+#define AM33XX_M3_TXEV_ENABLE		(0x0 << 0)
+
+/* AM33XX IPC message registers */
+#define AM33XX_CONTROL_IPC_MSG_REG0	0x04
+#define AM33XX_CONTROL_IPC_MSG_REG1	0x08
+#define AM33XX_CONTROL_IPC_MSG_REG2	0x0c
+#define AM33XX_CONTROL_IPC_MSG_REG3	0x10
+#define AM33XX_CONTROL_IPC_MSG_REG4	0x14
+#define AM33XX_CONTROL_IPC_MSG_REG5	0x18
+#define AM33XX_CONTROL_IPC_MSG_REG6	0x1c
+#define AM33XX_CONTROL_IPC_MSG_REG7	0x20
+
+struct wkup_m3_rproc {
+	struct rproc *rproc;
+
+	void * __iomem dev_table_va;
+	void * __iomem ipc_mem_base;
+	struct platform_device *pdev;
+
+	struct mbox_client mbox_client;
+	struct mbox_chan *mbox;
+	struct wkup_m3_ops *ops;
+
+	bool is_active;
+};
+
+static struct wkup_m3_rproc *m3_rproc_static;
+
+static struct wkup_m3_wakeup_src wakeups[] = {
+	{.irq_nr = 35,	.src = "USB0_PHY"},
+	{.irq_nr = 36,	.src = "USB1_PHY"},
+	{.irq_nr = 40,	.src = "I2C0"},
+	{.irq_nr = 41,	.src = "RTC Timer"},
+	{.irq_nr = 42,	.src = "RTC Alarm"},
+	{.irq_nr = 43,	.src = "Timer0"},
+	{.irq_nr = 44,	.src = "Timer1"},
+	{.irq_nr = 45,	.src = "UART"},
+	{.irq_nr = 46,	.src = "GPIO0"},
+	{.irq_nr = 48,	.src = "MPU_WAKE"},
+	{.irq_nr = 49,	.src = "WDT0"},
+	{.irq_nr = 50,	.src = "WDT1"},
+	{.irq_nr = 51,	.src = "ADC_TSC"},
+	{.irq_nr = 0,	.src = "Unknown"},
+};
+
+static void am33xx_txev_eoi(struct wkup_m3_rproc *m3_rproc)
+{
+	writel(AM33XX_M3_TXEV_ACK,
+	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
+}
+
+static void am33xx_txev_enable(struct wkup_m3_rproc *m3_rproc)
+{
+	writel(AM33XX_M3_TXEV_ENABLE,
+	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
+}
+
+static void wkup_m3_ctrl_ipc_write(struct wkup_m3_rproc *m3_rproc,
+				   struct wkup_m3_ipc_regs *ipc_regs)
+{
+	writel(ipc_regs->reg0,
+	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG0);
+	writel(ipc_regs->reg1,
+	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG1);
+	writel(ipc_regs->reg2,
+	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG2);
+	writel(ipc_regs->reg3,
+	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG3);
+	writel(ipc_regs->reg4,
+	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG4);
+	writel(ipc_regs->reg5,
+	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG5);
+	writel(ipc_regs->reg6,
+	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG6);
+	writel(ipc_regs->reg7,
+	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG7);
+}
+
+static void wkup_m3_ctrl_ipc_read(struct wkup_m3_rproc *m3_rproc,
+				  struct wkup_m3_ipc_regs *ipc_regs)
+{
+	ipc_regs->reg0 = readl(m3_rproc->ipc_mem_base
+			       + AM33XX_CONTROL_IPC_MSG_REG0);
+	ipc_regs->reg1 = readl(m3_rproc->ipc_mem_base
+			       + AM33XX_CONTROL_IPC_MSG_REG1);
+	ipc_regs->reg2 = readl(m3_rproc->ipc_mem_base
+			       + AM33XX_CONTROL_IPC_MSG_REG2);
+	ipc_regs->reg3 = readl(m3_rproc->ipc_mem_base
+			       + AM33XX_CONTROL_IPC_MSG_REG3);
+	ipc_regs->reg4 = readl(m3_rproc->ipc_mem_base
+			       + AM33XX_CONTROL_IPC_MSG_REG4);
+	ipc_regs->reg5 = readl(m3_rproc->ipc_mem_base
+			       + AM33XX_CONTROL_IPC_MSG_REG5);
+	ipc_regs->reg6 = readl(m3_rproc->ipc_mem_base
+			       + AM33XX_CONTROL_IPC_MSG_REG6);
+	ipc_regs->reg7 = readl(m3_rproc->ipc_mem_base
+			       + AM33XX_CONTROL_IPC_MSG_REG7);
+}
+
+static irqreturn_t wkup_m3_txev_handler(int irq, void *unused)
+{
+	am33xx_txev_eoi(m3_rproc_static);
+
+	if (m3_rproc_static->ops && m3_rproc_static->ops->txev_handler)
+		m3_rproc_static->ops->txev_handler();
+
+	am33xx_txev_enable(m3_rproc_static);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * wkup_m3_fw_version_clear - Clear FW version from ipc regs
+ *
+ * Invalidate M3 firmware version before hardreset.
+ * Write invalid version in lower 4 nibbles of parameter
+ * register (ipc_regs + 0x8).
+ */
+
+static void wkup_m3_fw_version_clear(void)
+{
+	struct wkup_m3_ipc_regs ipc_regs;
+
+	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
+	ipc_regs.reg2 &= 0xFFFF0000;
+	wkup_m3_ctrl_ipc_write(m3_rproc_static, &ipc_regs);
+}
+
+static void wkup_m3_mbox_callback(struct mbox_client *client, void *data)
+{
+	omap_mbox_disable_irq(m3_rproc_static->mbox, IRQ_RX);
+}
+
+static int wkup_m3_rproc_start(struct rproc *rproc)
+{
+	struct wkup_m3_rproc *m3_rproc = rproc->priv;
+	struct platform_device *pdev = m3_rproc->pdev;
+	struct device *dev = &pdev->dev;
+	struct wkup_m3_platform_data *pdata = dev->platform_data;
+	int ret;
+
+	wkup_m3_fw_version_clear();
+
+	if (pdata && pdata->deassert_reset) {
+		ret = pdata->deassert_reset(pdev, pdata->reset_name);
+		if (ret) {
+			dev_err(dev, "Unable to reset wkup_m3!\n");
+			return -ENODEV;
+		}
+	} else {
+		dev_err(dev, "Platform data missing deassert_reset!\n");
+		return -ENODEV;
+	}
+
+	m3_rproc->mbox_client.dev = dev;
+	m3_rproc->mbox_client.tx_done = NULL;
+	m3_rproc->mbox_client.rx_callback = wkup_m3_mbox_callback;
+	m3_rproc->mbox_client.tx_block = false;
+	m3_rproc->mbox_client.knows_txdone = false;
+
+	m3_rproc->mbox = mbox_request_channel(&m3_rproc->mbox_client);
+
+	if (IS_ERR(m3_rproc->mbox)) {
+		dev_err(dev, "IPC Request for A8->M3 Channel failed!\n");
+		ret = PTR_ERR(m3_rproc->mbox);
+		m3_rproc->mbox = NULL;
+		return ret;
+	}
+
+	if (m3_rproc_static->ops && m3_rproc_static->ops->rproc_ready)
+		m3_rproc_static->ops->rproc_ready();
+
+	m3_rproc_static->is_active = 1;
+
+	return 0;
+}
+
+
+static int wkup_m3_rproc_stop(struct rproc *rproc)
+{
+	return 0;
+}
+
+static void wkup_m3_rproc_kick(struct rproc *rproc, int vqid)
+{
+}
+
+static struct rproc_ops wkup_m3_rproc_ops = {
+	.start		= wkup_m3_rproc_start,
+	.stop		= wkup_m3_rproc_stop,
+	.kick		= wkup_m3_rproc_kick,
+};
+
+/* Public Functions */
+
+/**
+ * wkup_m3_set_ops - Set callbacks for user of rproc
+ * @ops - struct wkup_m3_ops *
+ *
+ * Registers callbacks to wkup_m3 to be invoked after rproc is ready to use
+ * and after an interrupt is handled.
+ */
+void wkup_m3_set_ops(struct wkup_m3_ops *ops)
+{
+	m3_rproc_static->ops = ops;
+
+	if (m3_rproc_static->is_active && m3_rproc_static->ops &&
+	    m3_rproc_static->ops->rproc_ready)
+		m3_rproc_static->ops->rproc_ready();
+}
+
+/**
+ * wkup_m3_ping - Send a dummy msg to wkup_m3 to tell to to check IPC regs
+ *
+ * Returns the result of sending mbox msg or -EIO if no mbox handle is present
+ */
+int wkup_m3_ping(void)
+{
+	int ret;
+	mbox_msg_t dummy_msg = 0;
+
+	if (!m3_rproc_static->mbox) {
+		dev_err(&m3_rproc_static->pdev->dev,
+			"No IPC channel to communicate with wkup_m3!\n");
+		return -EIO;
+	}
+
+	/*
+	 * Write a dummy message to the mailbox in order to trigger the RX
+	 * interrupt to alert the M3 that data is available in the IPC
+	 * registers. We must enable the IRQ here and disable it after in
+	 * the RX callback to avoid multiple interrupts being received
+	 * by the CM3.
+	 */
+	omap_mbox_enable_irq(m3_rproc_static->mbox, IRQ_RX);
+	ret = mbox_send_message(m3_rproc_static->mbox, (void *)dummy_msg);
+
+	if (ret < 0) {
+		pr_err("%s: mbox_send_message() failed: %d\n", __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * wkup_m3_wake_src - Get the wakeup source info passed from wkup_m3
+ * @wkup_m3_wakeup: struct wkup_m3_wakeup_src * gets assigned the
+ *		    wakeup src value
+ */
+void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wkup_m3_wakeup)
+{
+	struct wkup_m3_ipc_regs ipc_regs;
+	unsigned int wakeup_src_idx;
+	int j;
+
+	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
+
+	wakeup_src_idx = ipc_regs.reg6 & WKUP_M3_WAKE_SRC_MASK;
+
+	for (j = 0; j < ARRAY_SIZE(wakeups)-1; j++) {
+		if (wakeups[j].irq_nr == wakeup_src_idx) {
+			*wkup_m3_wakeup = wakeups[j];
+			return;
+		}
+	}
+	*wkup_m3_wakeup = wakeups[j];
+}
+
+/**
+ * wkup_m3_pm_status - Return the status code from wkup_m3 after sleep event
+ *
+ * Returns an error code that indicates whether or not the dsired sleep
+ * action was a success or not.
+ */
+int wkup_m3_pm_status(void)
+{
+	unsigned int i;
+	struct wkup_m3_ipc_regs ipc_regs;
+
+	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
+
+	i = WKUP_M3_STATUS_RESP_MASK & ipc_regs.reg1;
+	i >>= __ffs(WKUP_M3_STATUS_RESP_MASK);
+
+	return i;
+}
+
+/**
+ * wkup_m3_fw_version_read - Return the fw version given by the wkup_m3
+ *
+ * After boot the fw version should be read to ensure it is compatible.
+ */
+int wkup_m3_fw_version_read(void)
+{
+	struct wkup_m3_ipc_regs ipc_regs;
+
+	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
+
+	return ipc_regs.reg2 & WKUP_M3_FW_VERSION_MASK;
+}
+
+/**
+ * wkup_m3_set_cmd - write contents of struct to ipc regs
+ * @ipc_regs: struct wkup_m3_ipc_regs *
+ */
+void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs)
+{
+	wkup_m3_ctrl_ipc_write(m3_rproc_static, ipc_regs);
+}
+
+static void wkup_m3_rproc_loader_thread(struct rproc *rproc)
+{
+	struct wkup_m3_rproc *m3_rproc = rproc->priv;
+	struct device *dev = &m3_rproc->pdev->dev;
+	int ret;
+
+	wait_for_completion(&rproc->firmware_loading_complete);
+
+	ret = rproc_boot(rproc);
+	if (ret)
+		dev_err(dev, "rproc_boot failed\n");
+
+	do_exit(0);
+}
+
+static int wkup_m3_rproc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = pdev->dev.of_node;
+	struct wkup_m3_rproc *m3_rproc;
+	struct rproc *rproc;
+	int irq, ret;
+	struct resource *res;
+	struct task_struct *task;
+	const char *mbox_name;
+
+	pm_runtime_enable(&pdev->dev);
+
+	ret = pm_runtime_get_sync(&pdev->dev);
+	if (IS_ERR_VALUE(ret)) {
+		dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
+		return ret;
+	}
+
+	rproc = rproc_alloc(dev, "wkup_m3", &wkup_m3_rproc_ops,
+			    "am335x-pm-firmware.elf", sizeof(*m3_rproc));
+	if (!rproc)
+		return -ENOMEM;
+
+	m3_rproc = rproc->priv;
+	m3_rproc->rproc = rproc;
+	m3_rproc->pdev = pdev;
+
+	m3_rproc_static = m3_rproc;
+
+	irq = platform_get_irq(pdev, 0);
+	if (!irq) {
+		dev_err(&pdev->dev, "no irq resource\n");
+		ret = -ENXIO;
+		goto err;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ipc_regs");
+	if (!res) {
+		dev_err(&pdev->dev, "no memory resource for ipc\n");
+		ret = -ENXIO;
+		goto err;
+	}
+
+	m3_rproc->ipc_mem_base = devm_request_and_ioremap(dev, res);
+	if (!m3_rproc->ipc_mem_base) {
+		dev_err(dev, "could not ioremap ipc_mem\n");
+		ret = -EADDRNOTAVAIL;
+		goto err;
+	}
+
+	ret = devm_request_irq(dev, irq, wkup_m3_txev_handler,
+			       IRQF_DISABLED, "wkup_m3_txev", m3_rproc);
+	if (ret) {
+		dev_err(dev, "request_irq failed\n");
+		goto err;
+	}
+
+	/* Get mbox name from device tree node */
+	ret = of_property_read_string(np, "mbox-names", &mbox_name);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to get mbox name from dt node: %d\n",
+			ret);
+			goto err;
+	};
+
+	m3_rproc->mbox_client.chan_name = mbox_name;
+
+	/* Register as a remoteproc device */
+	ret = rproc_add(rproc);
+	if (ret) {
+		dev_err(dev, "rproc_add failed\n");
+		goto err;
+	}
+
+	/*
+	 * Wait for firmware loading completion in a thread so we
+	 * can boot the wkup_m3 as soon as it's ready without holding
+	 * up kernel boot
+	 */
+	task = kthread_run((void *)wkup_m3_rproc_loader_thread, rproc,
+			   "wkup_m3_rproc_loader");
+
+	if (IS_ERR(task)) {
+		dev_err(dev, "can't create rproc_loader thread\n");
+		goto err;
+	}
+
+	return 0;
+
+err:
+	rproc_put(rproc);
+	return ret;
+}
+
+static int wkup_m3_rproc_remove(struct platform_device *pdev)
+{
+	struct rproc *rproc = platform_get_drvdata(pdev);
+
+	rproc_del(rproc);
+	rproc_put(rproc);
+
+	return 0;
+}
+
+static int wkup_m3_rpm_suspend(struct device *dev)
+{
+	return -EBUSY;
+}
+
+static int wkup_m3_rpm_resume(struct device *dev)
+{
+	return 0;
+}
+
+static const struct dev_pm_ops wkup_m3_rproc_pm_ops = {
+	SET_RUNTIME_PM_OPS(wkup_m3_rpm_suspend, wkup_m3_rpm_resume, NULL)
+};
+
+static const struct of_device_id wkup_m3_rproc_of_match[] = {
+	{ .compatible = "ti,am3353-wkup-m3", .data = NULL, },
+	{},
+};
+
+static struct platform_driver wkup_m3_rproc_driver = {
+	.probe = wkup_m3_rproc_probe,
+	.remove = wkup_m3_rproc_remove,
+	.driver = {
+		.name = "wkup_m3",
+		.owner = THIS_MODULE,
+		.of_match_table = wkup_m3_rproc_of_match,
+		.pm = &wkup_m3_rproc_pm_ops,
+	},
+};
+
+module_platform_driver(wkup_m3_rproc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("wkup m3 remote processor control driver");
diff --git a/include/linux/wkup_m3.h b/include/linux/wkup_m3.h
new file mode 100644
index 0000000..1a2237f
--- /dev/null
+++ b/include/linux/wkup_m3.h
@@ -0,0 +1,71 @@
+/*
+ * TI Wakeup M3 Power Management Routines
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
+ * Dave Gerlach <d-gerlach@ti.com>
+ *
+ * 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 _LINUX_WKUP_M3_H
+#define _LINUX_WKUP_M3_H
+
+/**
+ * struct wkup_m3_ops - Callbacks for allowing pm code to interact with wkup_m3.
+ *
+ * @txev_handler: Callback to allow pm code to react to response from wkup_m3
+ *		  after pinging it using wkup_m3_ping.
+ *
+ * @firmware_loaded: Callback invoked when the firmware has been loaded to the
+ *		     m3 to allow the pm code to enable suspend/resume ops.
+ */
+
+struct wkup_m3_ops {
+	void (*txev_handler)(void);
+	void (*rproc_ready)(void);
+};
+
+struct wkup_m3_wakeup_src {
+	int irq_nr;
+	char src[10];
+};
+
+struct wkup_m3_ipc_regs {
+	u32 reg0;
+	u32 reg1;
+	u32 reg2;
+	u32 reg3;
+	u32 reg4;
+	u32 reg5;
+	u32 reg6;
+	u32 reg7;
+};
+
+#ifdef CONFIG_WKUP_M3_RPROC
+int wkup_m3_prepare(void);
+void wkup_m3_set_ops(struct wkup_m3_ops *ops);
+int wkup_m3_ping(void);
+void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wakeup_src);
+int wkup_m3_pm_status(void);
+int wkup_m3_is_valid(void);
+int wkup_m3_fw_version_read(void);
+void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs);
+
+#else
+
+int wkup_m3_prepare(void) { return -EINVAL; }
+void wkup_m3_set_ops(struct wkup_m3_ops *ops) { }
+int wkup_m3_ping(void) { return -EINVAL }
+void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wakeup_src) { }
+int wkup_m3_pm_status(void) { return -1; }
+int wkup_m3_fw_version_read(void) { return -1; }
+void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs) { }
+#endif /* CONFIG_WKUP_M3_RPROC */
+#endif /* _LINUX_WKUP_M3_H */
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 106+ messages in thread

* [PATCH v4 07/11] ARM: dts: am33xx: Update wkup_m3 node
  2014-07-11  2:55 ` Dave Gerlach
@ 2014-07-11  2:55   ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11  2:55 UTC (permalink / raw)
  To: linux-arm-kernel, linux-omap
  Cc: Paul Walmsley, Kevin Hilman, Tony Lindgren, Tero Kristo,
	Nishanth Menon, Russ Dill, Santosh Shilimkar, Daniel Mack,
	Suman Anna, Benoit Cousson, Dave Gerlach, Ohad Ben-Cohen

Allow interrupt for wkup_m3 to be set from DT, add regs for
IPC regs to allow pm code to communicate with wkup_m3, and add data
needed for mailbox channel.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
CC: Ohad Ben-Cohen <ohad@wizery.com>
CC: Benoit Cousson <bcousson@baylibre.com>
---
 arch/arm/boot/dts/am33xx.dtsi | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
index 6f8f7cf..fe0cff5 100644
--- a/arch/arm/boot/dts/am33xx.dtsi
+++ b/arch/arm/boot/dts/am33xx.dtsi
@@ -736,10 +736,15 @@
 
 		wkup_m3: wkup_m3@44d00000 {
 			compatible = "ti,am3353-wkup-m3";
-			reg = <0x44d00000 0x4000	/* M3 UMEM */
-			       0x44d80000 0x2000>;	/* M3 DMEM */
+			reg = <0x44d00000 0x4000
+			       0x44d80000 0x2000
+			       0x44e11324 0x0024>;
+			reg-names = "m3_umem", "m3_dmem", "ipc_regs";
+			interrupts = <78>;
 			ti,hwmods = "wkup_m3";
 			ti,no-reset-on-init;
+			mbox-names = "wkup_m3";
+			mbox = <&mailbox &mbox_wkupm3>;
 		};
 
 		elm: elm@48080000 {
-- 
1.9.0


^ permalink raw reply related	[flat|nested] 106+ messages in thread

* [PATCH v4 07/11] ARM: dts: am33xx: Update wkup_m3 node
@ 2014-07-11  2:55   ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11  2:55 UTC (permalink / raw)
  To: linux-arm-kernel

Allow interrupt for wkup_m3 to be set from DT, add regs for
IPC regs to allow pm code to communicate with wkup_m3, and add data
needed for mailbox channel.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
CC: Ohad Ben-Cohen <ohad@wizery.com>
CC: Benoit Cousson <bcousson@baylibre.com>
---
 arch/arm/boot/dts/am33xx.dtsi | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
index 6f8f7cf..fe0cff5 100644
--- a/arch/arm/boot/dts/am33xx.dtsi
+++ b/arch/arm/boot/dts/am33xx.dtsi
@@ -736,10 +736,15 @@
 
 		wkup_m3: wkup_m3 at 44d00000 {
 			compatible = "ti,am3353-wkup-m3";
-			reg = <0x44d00000 0x4000	/* M3 UMEM */
-			       0x44d80000 0x2000>;	/* M3 DMEM */
+			reg = <0x44d00000 0x4000
+			       0x44d80000 0x2000
+			       0x44e11324 0x0024>;
+			reg-names = "m3_umem", "m3_dmem", "ipc_regs";
+			interrupts = <78>;
 			ti,hwmods = "wkup_m3";
 			ti,no-reset-on-init;
+			mbox-names = "wkup_m3";
+			mbox = <&mailbox &mbox_wkupm3>;
 		};
 
 		elm: elm at 48080000 {
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 106+ messages in thread

* [PATCH v4 08/11] ARM: OMAP2+: AM33XX: Reserve memory to comply with EMIF spec
  2014-07-11  2:55 ` Dave Gerlach
@ 2014-07-11  2:55   ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11  2:55 UTC (permalink / raw)
  To: linux-arm-kernel, linux-omap
  Cc: Paul Walmsley, Kevin Hilman, Tony Lindgren, Tero Kristo,
	Nishanth Menon, Russ Dill, Santosh Shilimkar, Daniel Mack,
	Suman Anna, Benoit Cousson, Vaibhav Bedia, Dave Gerlach

From: Vaibhav Bedia <vaibhav.bedia@ti.com>

SDRAM controller on AM33XX requires that a modification of certain
bit-fields in PWR_MGMT_CTRL register (ref. section 7.3.5.13 in
AM335x-Rev H) is followed by a dummy read access to SDRAM. This
scenario arises when entering a low power state like DeepSleep.
To ensure that the read is not from a cached region we reserve
some memory during bootup using the arm_memblock_steal() API. The
original call to omap_reserve is removed as it is not used in any
way on am335x.

A subsequent patch will pass along the location of the reserved
memory location to the AM335x suspend handler which modifies the
PWR_MGMT_CTRL register in the EMIF.

Signed-off-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
---
v3->v4:
	Left as is because of use in sram code when EMIF is off to avoid
	copying additional code. Also uses hardcoded virtual address now
	to avoid problems with himem
	
 arch/arm/mach-omap2/board-generic.c |  2 +-
 arch/arm/mach-omap2/common.c        | 32 ++++++++++++++++++++++++++++++++
 arch/arm/mach-omap2/common.h        |  4 ++++
 arch/arm/mach-omap2/io.c            |  1 +
 4 files changed, 38 insertions(+), 1 deletion(-)

diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c
index 9480997..d2c435e 100644
--- a/arch/arm/mach-omap2/board-generic.c
+++ b/arch/arm/mach-omap2/board-generic.c
@@ -163,7 +163,7 @@ static const char *am33xx_boards_compat[] __initconst = {
 };
 
 DT_MACHINE_START(AM33XX_DT, "Generic AM33XX (Flattened Device Tree)")
-	.reserve	= omap_reserve,
+	.reserve	= am33xx_reserve,
 	.map_io		= am33xx_map_io,
 	.init_early	= am33xx_init_early,
 	.init_irq	= omap_intc_of_init,
diff --git a/arch/arm/mach-omap2/common.c b/arch/arm/mach-omap2/common.c
index 2dabb9e..df6394e 100644
--- a/arch/arm/mach-omap2/common.c
+++ b/arch/arm/mach-omap2/common.c
@@ -15,10 +15,14 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/platform_data/dsp-omap.h>
+#include <asm/memblock.h>
+#include <asm/mach/map.h>
 
 #include "common.h"
 #include "omap-secure.h"
 
+#define AM33XX_DRAM_SYNC_VA 0xfe600000
+
 /*
  * Stub function for OMAP2 so that common files
  * continue to build when custom builds are used
@@ -34,3 +38,31 @@ void __init omap_reserve(void)
 	omap_secure_ram_reserve_memblock();
 	omap_barrier_reserve_memblock();
 }
+
+static phys_addr_t am33xx_paddr;
+static u32 am33xx_size;
+
+/* Steal one page physical memory for uncached read DeepSleep */
+void __init am33xx_reserve(void)
+{
+	am33xx_size = ALIGN(PAGE_SIZE, SZ_1M);
+	am33xx_paddr = arm_memblock_steal(am33xx_size, SZ_1M);
+
+	omap_reserve();
+}
+
+void __iomem *am33xx_dram_sync;
+
+void __init am33xx_dram_sync_init(void)
+{
+	struct map_desc dram_io_desc[1];
+
+	dram_io_desc[0].virtual = AM33XX_DRAM_SYNC_VA;
+	dram_io_desc[0].pfn = __phys_to_pfn(am33xx_paddr);
+	dram_io_desc[0].length = am33xx_size;
+	dram_io_desc[0].type = MT_MEMORY_RW_SO;
+
+	iotable_init(dram_io_desc, ARRAY_SIZE(dram_io_desc));
+
+	am33xx_dram_sync = (void __iomem *) dram_io_desc[0].virtual;
+}
diff --git a/arch/arm/mach-omap2/common.h b/arch/arm/mach-omap2/common.h
index b2d252b..1536338 100644
--- a/arch/arm/mach-omap2/common.h
+++ b/arch/arm/mach-omap2/common.h
@@ -316,6 +316,10 @@ extern void omap_sdrc_init(struct omap_sdrc_params *sdrc_cs0,
 struct omap2_hsmmc_info;
 extern void omap_reserve(void);
 
+extern void am33xx_reserve(void);
+extern void am33xx_dram_sync_init(void);
+extern void __iomem *am33xx_dram_sync;
+
 struct omap_hwmod;
 extern int omap_dss_reset(struct omap_hwmod *);
 
diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c
index 8f55945..4d4d150 100644
--- a/arch/arm/mach-omap2/io.c
+++ b/arch/arm/mach-omap2/io.c
@@ -313,6 +313,7 @@ void __init ti81xx_map_io(void)
 void __init am33xx_map_io(void)
 {
 	iotable_init(omapam33xx_io_desc, ARRAY_SIZE(omapam33xx_io_desc));
+	am33xx_dram_sync_init();
 }
 #endif
 
-- 
1.9.0


^ permalink raw reply related	[flat|nested] 106+ messages in thread

* [PATCH v4 08/11] ARM: OMAP2+: AM33XX: Reserve memory to comply with EMIF spec
@ 2014-07-11  2:55   ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11  2:55 UTC (permalink / raw)
  To: linux-arm-kernel

From: Vaibhav Bedia <vaibhav.bedia@ti.com>

SDRAM controller on AM33XX requires that a modification of certain
bit-fields in PWR_MGMT_CTRL register (ref. section 7.3.5.13 in
AM335x-Rev H) is followed by a dummy read access to SDRAM. This
scenario arises when entering a low power state like DeepSleep.
To ensure that the read is not from a cached region we reserve
some memory during bootup using the arm_memblock_steal() API. The
original call to omap_reserve is removed as it is not used in any
way on am335x.

A subsequent patch will pass along the location of the reserved
memory location to the AM335x suspend handler which modifies the
PWR_MGMT_CTRL register in the EMIF.

Signed-off-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
---
v3->v4:
	Left as is because of use in sram code when EMIF is off to avoid
	copying additional code. Also uses hardcoded virtual address now
	to avoid problems with himem
	
 arch/arm/mach-omap2/board-generic.c |  2 +-
 arch/arm/mach-omap2/common.c        | 32 ++++++++++++++++++++++++++++++++
 arch/arm/mach-omap2/common.h        |  4 ++++
 arch/arm/mach-omap2/io.c            |  1 +
 4 files changed, 38 insertions(+), 1 deletion(-)

diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c
index 9480997..d2c435e 100644
--- a/arch/arm/mach-omap2/board-generic.c
+++ b/arch/arm/mach-omap2/board-generic.c
@@ -163,7 +163,7 @@ static const char *am33xx_boards_compat[] __initconst = {
 };
 
 DT_MACHINE_START(AM33XX_DT, "Generic AM33XX (Flattened Device Tree)")
-	.reserve	= omap_reserve,
+	.reserve	= am33xx_reserve,
 	.map_io		= am33xx_map_io,
 	.init_early	= am33xx_init_early,
 	.init_irq	= omap_intc_of_init,
diff --git a/arch/arm/mach-omap2/common.c b/arch/arm/mach-omap2/common.c
index 2dabb9e..df6394e 100644
--- a/arch/arm/mach-omap2/common.c
+++ b/arch/arm/mach-omap2/common.c
@@ -15,10 +15,14 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/platform_data/dsp-omap.h>
+#include <asm/memblock.h>
+#include <asm/mach/map.h>
 
 #include "common.h"
 #include "omap-secure.h"
 
+#define AM33XX_DRAM_SYNC_VA 0xfe600000
+
 /*
  * Stub function for OMAP2 so that common files
  * continue to build when custom builds are used
@@ -34,3 +38,31 @@ void __init omap_reserve(void)
 	omap_secure_ram_reserve_memblock();
 	omap_barrier_reserve_memblock();
 }
+
+static phys_addr_t am33xx_paddr;
+static u32 am33xx_size;
+
+/* Steal one page physical memory for uncached read DeepSleep */
+void __init am33xx_reserve(void)
+{
+	am33xx_size = ALIGN(PAGE_SIZE, SZ_1M);
+	am33xx_paddr = arm_memblock_steal(am33xx_size, SZ_1M);
+
+	omap_reserve();
+}
+
+void __iomem *am33xx_dram_sync;
+
+void __init am33xx_dram_sync_init(void)
+{
+	struct map_desc dram_io_desc[1];
+
+	dram_io_desc[0].virtual = AM33XX_DRAM_SYNC_VA;
+	dram_io_desc[0].pfn = __phys_to_pfn(am33xx_paddr);
+	dram_io_desc[0].length = am33xx_size;
+	dram_io_desc[0].type = MT_MEMORY_RW_SO;
+
+	iotable_init(dram_io_desc, ARRAY_SIZE(dram_io_desc));
+
+	am33xx_dram_sync = (void __iomem *) dram_io_desc[0].virtual;
+}
diff --git a/arch/arm/mach-omap2/common.h b/arch/arm/mach-omap2/common.h
index b2d252b..1536338 100644
--- a/arch/arm/mach-omap2/common.h
+++ b/arch/arm/mach-omap2/common.h
@@ -316,6 +316,10 @@ extern void omap_sdrc_init(struct omap_sdrc_params *sdrc_cs0,
 struct omap2_hsmmc_info;
 extern void omap_reserve(void);
 
+extern void am33xx_reserve(void);
+extern void am33xx_dram_sync_init(void);
+extern void __iomem *am33xx_dram_sync;
+
 struct omap_hwmod;
 extern int omap_dss_reset(struct omap_hwmod *);
 
diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c
index 8f55945..4d4d150 100644
--- a/arch/arm/mach-omap2/io.c
+++ b/arch/arm/mach-omap2/io.c
@@ -313,6 +313,7 @@ void __init ti81xx_map_io(void)
 void __init am33xx_map_io(void)
 {
 	iotable_init(omapam33xx_io_desc, ARRAY_SIZE(omapam33xx_io_desc));
+	am33xx_dram_sync_init();
 }
 #endif
 
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 106+ messages in thread

* [PATCH v4 09/11] ARM: OMAP2+: AM33XX: Add assembly code for PM operations
  2014-07-11  2:55 ` Dave Gerlach
@ 2014-07-11  2:55   ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11  2:55 UTC (permalink / raw)
  To: linux-arm-kernel, linux-omap
  Cc: Paul Walmsley, Kevin Hilman, Tony Lindgren, Tero Kristo,
	Nishanth Menon, Russ Dill, Santosh Shilimkar, Daniel Mack,
	Suman Anna, Benoit Cousson, Dave Gerlach

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. 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.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
---
v3->v4:
	Added macros for magic numbers, some optimizations to avoid
	hangs spotted when bringing DDR3 out of self refresh.

 arch/arm/mach-omap2/sleep33xx.S | 380 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 380 insertions(+)
 create mode 100644 arch/arm/mach-omap2/sleep33xx.S

diff --git a/arch/arm/mach-omap2/sleep33xx.S b/arch/arm/mach-omap2/sleep33xx.S
new file mode 100644
index 0000000..98772a6
--- /dev/null
+++ b/arch/arm/mach-omap2/sleep33xx.S
@@ -0,0 +1,380 @@
+/*
+ * Low level suspend code for AM33XX SoCs
+ *
+ * Copyright (C) 2012-2014 Texas Instruments Incorporated - http://www.ti.com/
+ *	Vaibhav Bedia, Dave Gerlach
+ *
+ * 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 "iomap.h"
+#include "cm33xx.h"
+#include "pm33xx.h"
+#include "prm33xx.h"
+
+#define EMIF_POWER_MGMT_WAIT_SELF_REFRESH_8192_CYCLES		0x00a0
+#define EMIF_POWER_MGMT_SR_TIMER_MASK				0x00f0
+
+#define EMIF_POWER_MGMT_SELF_REFRESH_MODE			0x0200
+#define EMIF_POWER_MGMT_SELF_REFRESH_MODE_MASK			0x0700
+#define EMIF_POWER_MGMT_DELAY_PERIOD				0x1000
+
+#define AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE			0x0003
+#define AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE			0x0002
+
+	.text
+	.align 3
+
+/*
+ * This routine is executed from internal RAM and expects some
+ * parameters to be passed in r0 _strictly_ in following order:
+ * 1) emif_addr_virt - ioremapped EMIF address
+ * 2) mem_type - 2 -> DDR2, 3-> DDR3
+ * 3) dram_sync_word - uncached word in SDRAM
+ *
+ * The code loads these values taking r0 value as reference to
+ * the array in registers starting from r0, i.e emif_addr_virt
+ * goes to r1, mem_type goes to r2 and and so on. These are
+ * then saved into memory locations before proceeding with the
+ * sleep sequence and hence registers r0, r1 etc can still be
+ * used in the rest of the sleep code.
+ */
+
+ENTRY(am33xx_do_wfi)
+	stmfd	sp!, {r4 - r11, lr}	@ save registers on stack
+
+	ldm	r0, {r1-r3}		@ gather values passed
+
+	/* Save the values passed */
+	str	r1, emif_addr_virt
+	str	r2, mem_type
+	str	r3, dram_sync_word
+
+	/*
+	 * Flush all data from the L1 and L2 data cache before disabling
+	 * SCTLR.C bit.
+	 */
+	ldr	r1, kernel_flush
+	blx	r1
+
+	/*
+	 * Clear the SCTLR.C bit to prevent further data cache
+	 * allocation. Clearing SCTLR.C would make all the data accesses
+	 * strongly ordered and would not hit the cache.
+	 */
+	mrc	p15, 0, r0, c1, c0, 0
+	bic	r0, r0, #(1 << 2)	@ Disable the C bit
+	mcr	p15, 0, r0, c1, c0, 0
+	isb
+
+	/*
+	 * Invalidate L1 and L2 data cache.
+	 */
+	ldr	r1, kernel_flush
+	blx	r1
+
+	/* Save EMIF configuration */
+	ldr	r0, emif_addr_virt
+	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
+
+	/* Put SDRAM in self-refresh */
+	ldr	r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
+	bic	r1, r1, #EMIF_POWER_MGMT_SR_TIMER_MASK
+	orr	r1, r1, #EMIF_POWER_MGMT_WAIT_SELF_REFRESH_8192_CYCLES
+	str	r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
+	str	r1, [r0, #EMIF_POWER_MANAGEMENT_CTRL_SHDW]
+
+	ldr	r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
+	bic	r1, r1, #EMIF_POWER_MGMT_SELF_REFRESH_MODE_MASK
+	orr	r1, r1, #EMIF_POWER_MGMT_SELF_REFRESH_MODE
+	str	r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
+
+	ldr	r1, dram_sync_word	@ a dummy access to DDR as per spec
+	ldr	r2, [r1, #0]
+
+
+	mov	r1, #EMIF_POWER_MGMT_DELAY_PERIOD	@ Wait for system
+wait_self_refresh:					@ to enter SR
+	subs	r1, r1, #1
+	bne	wait_self_refresh
+
+	/* Disable EMIF */
+	ldr	r1, virt_emif_clkctrl
+	ldr	r2, [r1]
+	bic	r2, r2, #AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE
+	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, #AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE
+	str	r2, [r1]
+
+	/*
+	 * Execute an ISB instruction to ensure that all of the
+	 * CP15 register changes have been committed.
+	 */
+	isb
+
+	/*
+	 * Execute a barrier instruction to ensure that all cache,
+	 * TLB and branch predictor maintenance operations issued
+	 * have completed.
+	 */
+	dsb
+	dmb
+
+	/*
+	 * Execute a WFI instruction and wait until the
+	 * STANDBYWFI output is asserted to indicate that the
+	 * CPU is in idle and low power state. CPU can specualatively
+	 * prefetch the instructions so add NOPs after WFI. Thirteen
+	 * NOPs as per Cortex-A8 pipeline.
+	 */
+	wfi
+
+	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, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE
+	str	r2, [r1]
+
+	/* Re-enable EMIF */
+	ldr	r1, virt_emif_clkctrl
+	mov	r2, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE
+	str	r2, [r1]
+wait_emif_enable:
+	ldr	r3, [r1]
+	cmp	r2, r3
+	bne	wait_emif_enable
+
+	/* Disable EMIF self-refresh */
+	ldr	r0, emif_addr_virt
+	ldr	r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
+	bic	r1, r1, #LP_MODE_MASK
+	str	r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
+	str	r1, [r0, #EMIF_POWER_MANAGEMENT_CTRL_SHDW]
+
+	/*
+	 * A write to SDRAM CONFIG register triggers
+	 * an init sequence and hence it must be done
+	 * at the end for DDR2
+	 */
+	ldr r0, emif_addr_virt
+	add r0, r0, #EMIF_SDRAM_CONFIG
+	ldr r4, emif_sdcfg_val
+	str r4, [r0]
+
+	/*
+	 * Set SCTLR.C bit to allow data cache allocation
+	 */
+	mrc	p15, 0, r0, c1, c0, 0
+	orr	r0, r0, #(1 << 2)	@ Enable the C bit
+	mcr	p15, 0, r0, c1, c0, 0
+	isb
+
+	/* EMIF needs some time before read/write possible */
+	mov r0, #EMIF_POWER_MGMT_DELAY_PERIOD
+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)
+	/* Re-enable EMIF */
+	ldr	r0, phys_emif_clkctrl
+	mov	r1, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE
+	str	r1, [r0]
+wait_emif_enable1:
+	ldr	r2, [r0]
+	cmp	r1, r2
+	bne	wait_emif_enable1
+
+	/* Config EMIF Timings */
+	ldr	r0, emif_phys_addr
+	ldr	r1, emif_rd_lat_val
+	str	r1, [r0, #EMIF_DDR_PHY_CTRL_1]
+	str	r1, [r0, #EMIF_DDR_PHY_CTRL_1_SHDW]
+	ldr	r1, emif_timing1_val
+	str	r1, [r0, #EMIF_SDRAM_TIMING_1]
+	str	r1, [r0, #EMIF_SDRAM_TIMING_1_SHDW]
+	ldr	r1, emif_timing2_val
+	str	r1, [r0, #EMIF_SDRAM_TIMING_2]
+	str	r1, [r0, #EMIF_SDRAM_TIMING_2_SHDW]
+	ldr	r1, emif_timing3_val
+	str	r1, [r0, #EMIF_SDRAM_TIMING_3]
+	str	r1, [r0, #EMIF_SDRAM_TIMING_3_SHDW]
+	ldr	r1, emif_ref_ctrl_val
+	str	r1, [r0, #EMIF_SDRAM_REFRESH_CONTROL]
+	str	r1, [r0, #EMIF_SDRAM_REFRESH_CTRL_SHDW]
+
+	ldr	r1, emif_pmcr_shdw_val
+	str	r1, [r0, #EMIF_POWER_MANAGEMENT_CTRL_SHDW]
+	/*
+	 * Toggle EMIF to exit refresh mode:
+	 * if EMIF lost context, PWR_MGT_CTRL is currently 0, writing disable
+	 *   (0x0), wont do diddly squat! so do a toggle from SR(0x2) to disable
+	 *   (0x0) here.
+	 * *If* EMIF did not loose context, nothing broken as we write the same
+	 *   value(0x2) to reg before we write a disable (0x0).
+	 */
+	ldr	r1, emif_pmcr_val
+	bic	r1, r1, #EMIF_POWER_MGMT_SELF_REFRESH_MODE_MASK
+	orr	r1, r1, #EMIF_POWER_MGMT_SELF_REFRESH_MODE
+	str	r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
+	bic	r1, r1, #EMIF_POWER_MGMT_SELF_REFRESH_MODE_MASK
+	str	r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
+
+	/*
+	 * Output impedence calib needed only for DDR3
+	 * but since the initial state of this will be
+	 * disabled for DDR2 no harm in restoring the
+	 * old configuration
+	 */
+	ldr	r1, emif_zqcfg_val
+	str	r1, [r0, #EMIF_SDRAM_OUTPUT_IMPEDANCE_CALIBRATION_CONFIG]
+
+	/* Write to SDRAM_CONFIG only for DDR2 */
+	ldr	r2, mem_type
+	cmp	r2, #MEM_TYPE_DDR2
+	bne	resume_to_ddr
+
+	/*
+	 * A write to SDRAM CONFIG register triggers
+	 * an init sequence and hence it must be done
+	 * at the end for DDR2
+	 */
+	ldr	r1, emif_sdcfg_val
+	str	r1, [r0, #EMIF_SDRAM_CONFIG]
+
+resume_to_ddr:
+	/* EMIF needs some time before read/write possible */
+	mov	r1, #EMIF_POWER_MGMT_DELAY_PERIOD
+wait_resume:
+	subs	r1, r1, #1
+	bne	wait_resume
+
+	/* All done.. so restore back enter into suspend configuration */
+	ldr	r1, emif_pmcr_val
+	str	r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
+
+	/* 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
+kernel_flush:
+	.word   v7_flush_dcache_all
+ddr_start:
+	.word	PAGE_OFFSET
+emif_phys_addr:
+	.word	AM33XX_EMIF_BASE
+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 */
+dram_sync_word:
+	.word	0xDEADBEEF
+mem_type:
+	.word	0xDEADBEEF
+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
+
+	.align 3
+ENTRY(am33xx_do_wfi_sz)
+	.word	. - am33xx_do_wfi
-- 
1.9.0


^ permalink raw reply related	[flat|nested] 106+ messages in thread

* [PATCH v4 09/11] ARM: OMAP2+: AM33XX: Add assembly code for PM operations
@ 2014-07-11  2:55   ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11  2:55 UTC (permalink / raw)
  To: linux-arm-kernel

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. 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.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
---
v3->v4:
	Added macros for magic numbers, some optimizations to avoid
	hangs spotted when bringing DDR3 out of self refresh.

 arch/arm/mach-omap2/sleep33xx.S | 380 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 380 insertions(+)
 create mode 100644 arch/arm/mach-omap2/sleep33xx.S

diff --git a/arch/arm/mach-omap2/sleep33xx.S b/arch/arm/mach-omap2/sleep33xx.S
new file mode 100644
index 0000000..98772a6
--- /dev/null
+++ b/arch/arm/mach-omap2/sleep33xx.S
@@ -0,0 +1,380 @@
+/*
+ * Low level suspend code for AM33XX SoCs
+ *
+ * Copyright (C) 2012-2014 Texas Instruments Incorporated - http://www.ti.com/
+ *	Vaibhav Bedia, Dave Gerlach
+ *
+ * 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 "iomap.h"
+#include "cm33xx.h"
+#include "pm33xx.h"
+#include "prm33xx.h"
+
+#define EMIF_POWER_MGMT_WAIT_SELF_REFRESH_8192_CYCLES		0x00a0
+#define EMIF_POWER_MGMT_SR_TIMER_MASK				0x00f0
+
+#define EMIF_POWER_MGMT_SELF_REFRESH_MODE			0x0200
+#define EMIF_POWER_MGMT_SELF_REFRESH_MODE_MASK			0x0700
+#define EMIF_POWER_MGMT_DELAY_PERIOD				0x1000
+
+#define AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE			0x0003
+#define AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE			0x0002
+
+	.text
+	.align 3
+
+/*
+ * This routine is executed from internal RAM and expects some
+ * parameters to be passed in r0 _strictly_ in following order:
+ * 1) emif_addr_virt - ioremapped EMIF address
+ * 2) mem_type - 2 -> DDR2, 3-> DDR3
+ * 3) dram_sync_word - uncached word in SDRAM
+ *
+ * The code loads these values taking r0 value as reference to
+ * the array in registers starting from r0, i.e emif_addr_virt
+ * goes to r1, mem_type goes to r2 and and so on. These are
+ * then saved into memory locations before proceeding with the
+ * sleep sequence and hence registers r0, r1 etc can still be
+ * used in the rest of the sleep code.
+ */
+
+ENTRY(am33xx_do_wfi)
+	stmfd	sp!, {r4 - r11, lr}	@ save registers on stack
+
+	ldm	r0, {r1-r3}		@ gather values passed
+
+	/* Save the values passed */
+	str	r1, emif_addr_virt
+	str	r2, mem_type
+	str	r3, dram_sync_word
+
+	/*
+	 * Flush all data from the L1 and L2 data cache before disabling
+	 * SCTLR.C bit.
+	 */
+	ldr	r1, kernel_flush
+	blx	r1
+
+	/*
+	 * Clear the SCTLR.C bit to prevent further data cache
+	 * allocation. Clearing SCTLR.C would make all the data accesses
+	 * strongly ordered and would not hit the cache.
+	 */
+	mrc	p15, 0, r0, c1, c0, 0
+	bic	r0, r0, #(1 << 2)	@ Disable the C bit
+	mcr	p15, 0, r0, c1, c0, 0
+	isb
+
+	/*
+	 * Invalidate L1 and L2 data cache.
+	 */
+	ldr	r1, kernel_flush
+	blx	r1
+
+	/* Save EMIF configuration */
+	ldr	r0, emif_addr_virt
+	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
+
+	/* Put SDRAM in self-refresh */
+	ldr	r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
+	bic	r1, r1, #EMIF_POWER_MGMT_SR_TIMER_MASK
+	orr	r1, r1, #EMIF_POWER_MGMT_WAIT_SELF_REFRESH_8192_CYCLES
+	str	r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
+	str	r1, [r0, #EMIF_POWER_MANAGEMENT_CTRL_SHDW]
+
+	ldr	r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
+	bic	r1, r1, #EMIF_POWER_MGMT_SELF_REFRESH_MODE_MASK
+	orr	r1, r1, #EMIF_POWER_MGMT_SELF_REFRESH_MODE
+	str	r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
+
+	ldr	r1, dram_sync_word	@ a dummy access to DDR as per spec
+	ldr	r2, [r1, #0]
+
+
+	mov	r1, #EMIF_POWER_MGMT_DELAY_PERIOD	@ Wait for system
+wait_self_refresh:					@ to enter SR
+	subs	r1, r1, #1
+	bne	wait_self_refresh
+
+	/* Disable EMIF */
+	ldr	r1, virt_emif_clkctrl
+	ldr	r2, [r1]
+	bic	r2, r2, #AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE
+	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, #AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE
+	str	r2, [r1]
+
+	/*
+	 * Execute an ISB instruction to ensure that all of the
+	 * CP15 register changes have been committed.
+	 */
+	isb
+
+	/*
+	 * Execute a barrier instruction to ensure that all cache,
+	 * TLB and branch predictor maintenance operations issued
+	 * have completed.
+	 */
+	dsb
+	dmb
+
+	/*
+	 * Execute a WFI instruction and wait until the
+	 * STANDBYWFI output is asserted to indicate that the
+	 * CPU is in idle and low power state. CPU can specualatively
+	 * prefetch the instructions so add NOPs after WFI. Thirteen
+	 * NOPs as per Cortex-A8 pipeline.
+	 */
+	wfi
+
+	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, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE
+	str	r2, [r1]
+
+	/* Re-enable EMIF */
+	ldr	r1, virt_emif_clkctrl
+	mov	r2, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE
+	str	r2, [r1]
+wait_emif_enable:
+	ldr	r3, [r1]
+	cmp	r2, r3
+	bne	wait_emif_enable
+
+	/* Disable EMIF self-refresh */
+	ldr	r0, emif_addr_virt
+	ldr	r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
+	bic	r1, r1, #LP_MODE_MASK
+	str	r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
+	str	r1, [r0, #EMIF_POWER_MANAGEMENT_CTRL_SHDW]
+
+	/*
+	 * A write to SDRAM CONFIG register triggers
+	 * an init sequence and hence it must be done
+	 * at the end for DDR2
+	 */
+	ldr r0, emif_addr_virt
+	add r0, r0, #EMIF_SDRAM_CONFIG
+	ldr r4, emif_sdcfg_val
+	str r4, [r0]
+
+	/*
+	 * Set SCTLR.C bit to allow data cache allocation
+	 */
+	mrc	p15, 0, r0, c1, c0, 0
+	orr	r0, r0, #(1 << 2)	@ Enable the C bit
+	mcr	p15, 0, r0, c1, c0, 0
+	isb
+
+	/* EMIF needs some time before read/write possible */
+	mov r0, #EMIF_POWER_MGMT_DELAY_PERIOD
+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)
+	/* Re-enable EMIF */
+	ldr	r0, phys_emif_clkctrl
+	mov	r1, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE
+	str	r1, [r0]
+wait_emif_enable1:
+	ldr	r2, [r0]
+	cmp	r1, r2
+	bne	wait_emif_enable1
+
+	/* Config EMIF Timings */
+	ldr	r0, emif_phys_addr
+	ldr	r1, emif_rd_lat_val
+	str	r1, [r0, #EMIF_DDR_PHY_CTRL_1]
+	str	r1, [r0, #EMIF_DDR_PHY_CTRL_1_SHDW]
+	ldr	r1, emif_timing1_val
+	str	r1, [r0, #EMIF_SDRAM_TIMING_1]
+	str	r1, [r0, #EMIF_SDRAM_TIMING_1_SHDW]
+	ldr	r1, emif_timing2_val
+	str	r1, [r0, #EMIF_SDRAM_TIMING_2]
+	str	r1, [r0, #EMIF_SDRAM_TIMING_2_SHDW]
+	ldr	r1, emif_timing3_val
+	str	r1, [r0, #EMIF_SDRAM_TIMING_3]
+	str	r1, [r0, #EMIF_SDRAM_TIMING_3_SHDW]
+	ldr	r1, emif_ref_ctrl_val
+	str	r1, [r0, #EMIF_SDRAM_REFRESH_CONTROL]
+	str	r1, [r0, #EMIF_SDRAM_REFRESH_CTRL_SHDW]
+
+	ldr	r1, emif_pmcr_shdw_val
+	str	r1, [r0, #EMIF_POWER_MANAGEMENT_CTRL_SHDW]
+	/*
+	 * Toggle EMIF to exit refresh mode:
+	 * if EMIF lost context, PWR_MGT_CTRL is currently 0, writing disable
+	 *   (0x0), wont do diddly squat! so do a toggle from SR(0x2) to disable
+	 *   (0x0) here.
+	 * *If* EMIF did not loose context, nothing broken as we write the same
+	 *   value(0x2) to reg before we write a disable (0x0).
+	 */
+	ldr	r1, emif_pmcr_val
+	bic	r1, r1, #EMIF_POWER_MGMT_SELF_REFRESH_MODE_MASK
+	orr	r1, r1, #EMIF_POWER_MGMT_SELF_REFRESH_MODE
+	str	r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
+	bic	r1, r1, #EMIF_POWER_MGMT_SELF_REFRESH_MODE_MASK
+	str	r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
+
+	/*
+	 * Output impedence calib needed only for DDR3
+	 * but since the initial state of this will be
+	 * disabled for DDR2 no harm in restoring the
+	 * old configuration
+	 */
+	ldr	r1, emif_zqcfg_val
+	str	r1, [r0, #EMIF_SDRAM_OUTPUT_IMPEDANCE_CALIBRATION_CONFIG]
+
+	/* Write to SDRAM_CONFIG only for DDR2 */
+	ldr	r2, mem_type
+	cmp	r2, #MEM_TYPE_DDR2
+	bne	resume_to_ddr
+
+	/*
+	 * A write to SDRAM CONFIG register triggers
+	 * an init sequence and hence it must be done
+	 *@the end for DDR2
+	 */
+	ldr	r1, emif_sdcfg_val
+	str	r1, [r0, #EMIF_SDRAM_CONFIG]
+
+resume_to_ddr:
+	/* EMIF needs some time before read/write possible */
+	mov	r1, #EMIF_POWER_MGMT_DELAY_PERIOD
+wait_resume:
+	subs	r1, r1, #1
+	bne	wait_resume
+
+	/* All done.. so restore back enter into suspend configuration */
+	ldr	r1, emif_pmcr_val
+	str	r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
+
+	/* 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
+kernel_flush:
+	.word   v7_flush_dcache_all
+ddr_start:
+	.word	PAGE_OFFSET
+emif_phys_addr:
+	.word	AM33XX_EMIF_BASE
+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 */
+dram_sync_word:
+	.word	0xDEADBEEF
+mem_type:
+	.word	0xDEADBEEF
+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
+
+	.align 3
+ENTRY(am33xx_do_wfi_sz)
+	.word	. - am33xx_do_wfi
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 106+ messages in thread

* [PATCH v4 10/11] ARM: OMAP2+: AM33XX: Basic suspend resume support
  2014-07-11  2:55 ` Dave Gerlach
@ 2014-07-11  2:55   ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11  2:55 UTC (permalink / raw)
  To: linux-arm-kernel, linux-omap
  Cc: Paul Walmsley, Kevin Hilman, Tony Lindgren, Tero Kristo,
	Nishanth Menon, Russ Dill, Santosh Shilimkar, Daniel Mack,
	Suman Anna, Benoit Cousson, Dave Gerlach

AM335x supports various low power modes as documented
in section 8.1.4.3 of the AM335x Technical Reference Manual.

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 the resume process. As part of
the boot process, the assembly code is copied over to OCMCRAM
using the OMAP SRAM code so it can be executed to turn of the
EMIF.

AM335x has a Cortex-M3 (WKUP_M3) which assists the MPU
in DeepSleep0 and Standby 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 WKUP_M3 is managed by a remoteproc driver. The PM code hooks
into the remoteproc driver through an rproc_ready callback to
allow PM features to become available once the firmware for the
wkup_m3 has been loaded. This code maintains its own state machine
for the wkup_m3 so that it can be used in the manner intended for
low power modes.

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 external RAM in self-refresh
mode, gates the MPU clock, and then finally executes the WFI
instruction. Execution of the WFI instruction with MPU clock gated
triggers another interrupt to the WKUP_M3 which then continues
with 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 and from here system remains in sleep state
until a wake source brings the system into resume path.

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.

Code is based on work by Vaibhav Bedia.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
---
v3->v4:
	Remove all direct wkup_m3 usage and moved to rproc driver introduced
	in previous patch.

 arch/arm/mach-omap2/pm33xx.c | 346 +++++++++++++++++++++++++++++++++++++++++++
 arch/arm/mach-omap2/pm33xx.h |  64 ++++++++
 2 files changed, 410 insertions(+)
 create mode 100644 arch/arm/mach-omap2/pm33xx.c
 create mode 100644 arch/arm/mach-omap2/pm33xx.h

diff --git a/arch/arm/mach-omap2/pm33xx.c b/arch/arm/mach-omap2/pm33xx.c
new file mode 100644
index 0000000..30d0f7d
--- /dev/null
+++ b/arch/arm/mach-omap2/pm33xx.c
@@ -0,0 +1,346 @@
+/*
+ * AM33XX Power Management Routines
+ *
+ * Copyright (C) 2012-2014 Texas Instruments Incorporated - http://www.ti.com/
+ *	Vaibhav Bedia, Dave Gerlach
+ *
+ * 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/cpu.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/interrupt.h>
+#include <linux/ti_emif.h>
+#include <linux/omap-mailbox.h>
+#include <linux/sizes.h>
+
+#include <asm/suspend.h>
+#include <asm/proc-fns.h>
+#include <asm/fncpy.h>
+#include <asm/system_misc.h>
+
+#include "pm.h"
+#include "cm33xx.h"
+#include "pm33xx.h"
+#include "common.h"
+#include "clockdomain.h"
+#include "powerdomain.h"
+#include "soc.h"
+#include "sram.h"
+
+static void __iomem *am33xx_emif_base;
+static struct powerdomain *cefuse_pwrdm, *gfx_pwrdm, *per_pwrdm, *mpu_pwrdm;
+static struct clockdomain *gfx_l4ls_clkdm;
+
+static struct am33xx_pm_context *am33xx_pm;
+
+static DECLARE_COMPLETION(am33xx_pm_sync);
+
+static void (*am33xx_do_wfi_sram)(struct am33xx_suspend_params *);
+
+static struct am33xx_suspend_params susp_params;
+
+#ifdef CONFIG_SUSPEND
+
+static int am33xx_do_sram_idle(long unsigned int unused)
+{
+	am33xx_do_wfi_sram(&susp_params);
+	return 0;
+}
+
+static int am33xx_pm_suspend(void)
+{
+	int i, ret = 0;
+	int status = 0;
+	struct wkup_m3_wakeup_src wakeup_src = {.irq_nr = 0,
+						.src = "Unknown",};
+
+	/* Try to put GFX to sleep */
+	omap_set_pwrdm_state(gfx_pwrdm, PWRDM_POWER_OFF);
+
+	ret = cpu_suspend(0, am33xx_do_sram_idle);
+
+	status = pwrdm_read_pwrst(gfx_pwrdm);
+	if (status != PWRDM_POWER_OFF)
+		pr_err("PM: GFX domain did not transition\n");
+
+	/*
+	 * BUG: 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
+	 */
+	clkdm_wakeup(gfx_l4ls_clkdm);
+	clkdm_sleep(gfx_l4ls_clkdm);
+
+	if (ret) {
+		pr_err("PM: Kernel suspend failure\n");
+	} else {
+		i = wkup_m3_pm_status();
+
+		switch (i) {
+		case 0:
+			pr_info("PM: Successfully put all powerdomains to target state\n");
+
+			/*
+			 * The PRCM registers on AM335x do not contain
+			 * previous state information like those present on
+			 * OMAP4 so we must manually indicate transition so
+			 * state counters are properly incremented
+			 */
+			pwrdm_post_transition(mpu_pwrdm);
+			pwrdm_post_transition(per_pwrdm);
+			break;
+		case 1:
+			pr_err("PM: Could not transition all powerdomains to target state\n");
+			ret = -1;
+			break;
+		default:
+			pr_err("PM: CM3 returned unknown result = %d\n", i);
+			ret = -1;
+		}
+		/* print the wakeup reason */
+		wkup_m3_wake_src(&wakeup_src);
+
+		pr_info("PM: Wakeup source %s\n", wakeup_src.src);
+	}
+
+	return ret;
+}
+
+static int am33xx_pm_enter(suspend_state_t suspend_state)
+{
+	int ret = 0;
+
+	switch (suspend_state) {
+	case PM_SUSPEND_MEM:
+		ret = am33xx_pm_suspend();
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+
+static void am33xx_m3_state_machine_reset(void)
+{
+	int i;
+
+	am33xx_pm->ipc.reg1 = IPC_CMD_RESET;
+
+	wkup_m3_set_cmd(&am33xx_pm->ipc);
+
+	am33xx_pm->state = M3_STATE_MSG_FOR_RESET;
+
+	if (!wkup_m3_ping()) {
+		i = wait_for_completion_timeout(&am33xx_pm_sync,
+						msecs_to_jiffies(500));
+		if (!i) {
+			WARN(1, "PM: MPU<->CM3 sync failure\n");
+			am33xx_pm->state = M3_STATE_UNKNOWN;
+		}
+	} else {
+		pr_warn("PM: Unable to ping CM3\n");
+	}
+}
+
+static int am33xx_pm_begin(suspend_state_t state)
+{
+	int i;
+
+
+	switch (state) {
+	case PM_SUSPEND_MEM:
+		am33xx_pm->ipc.reg1	= IPC_CMD_DS0;
+		break;
+	}
+
+	am33xx_pm->ipc.reg2		= DS_IPC_DEFAULT;
+	am33xx_pm->ipc.reg3		= DS_IPC_DEFAULT;
+
+	wkup_m3_set_cmd(&am33xx_pm->ipc);
+
+	am33xx_pm->state = M3_STATE_MSG_FOR_LP;
+
+	if (!wkup_m3_ping()) {
+		i = wait_for_completion_timeout(&am33xx_pm_sync,
+						msecs_to_jiffies(500));
+		if (!i) {
+			WARN(1, "PM: MPU<->CM3 sync failure\n");
+			return -1;
+		}
+	} else {
+		pr_warn("PM: Unable to ping CM3\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static void am33xx_pm_end(void)
+{
+	am33xx_m3_state_machine_reset();
+}
+
+static int am33xx_pm_valid(suspend_state_t state)
+{
+	switch (state) {
+	case PM_SUSPEND_MEM:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+static const struct platform_suspend_ops am33xx_pm_ops = {
+	.begin		= am33xx_pm_begin,
+	.end		= am33xx_pm_end,
+	.enter		= am33xx_pm_enter,
+	.valid		= am33xx_pm_valid,
+};
+#endif /* CONFIG_SUSPEND */
+
+static void am33xx_txev_handler(void)
+{
+	switch (am33xx_pm->state) {
+	case M3_STATE_RESET:
+		am33xx_pm->state = M3_STATE_INITED;
+		complete(&am33xx_pm_sync);
+		break;
+	case M3_STATE_MSG_FOR_RESET:
+		am33xx_pm->state = M3_STATE_INITED;
+		complete(&am33xx_pm_sync);
+		break;
+	case M3_STATE_MSG_FOR_LP:
+		complete(&am33xx_pm_sync);
+		break;
+	case M3_STATE_UNKNOWN:
+		pr_warn("PM: Unknown CM3 State\n");
+	}
+}
+
+static void am33xx_m3_ready_cb(void)
+{
+	am33xx_pm->ver = wkup_m3_fw_version_read();
+
+	if (am33xx_pm->ver == M3_VERSION_UNKNOWN ||
+	    am33xx_pm->ver < M3_BASELINE_VERSION) {
+		pr_warn("PM: CM3 Firmware Version %x not supported\n",
+			am33xx_pm->ver);
+		return;
+	} else {
+		pr_info("PM: CM3 Firmware Version = 0x%x\n",
+			am33xx_pm->ver);
+	}
+
+#ifdef CONFIG_SUSPEND
+	suspend_set_ops(&am33xx_pm_ops);
+#endif /* CONFIG_SUSPEND */
+}
+
+static struct wkup_m3_ops am33xx_wkup_m3_ops = {
+	.txev_handler = am33xx_txev_handler,
+	.rproc_ready = am33xx_m3_ready_cb,
+};
+
+/*
+ * 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;
+}
+
+int __init am33xx_pm_init(void)
+{
+	int ret;
+	u32 temp;
+
+	if (!soc_is_am33xx())
+		return -ENODEV;
+
+	gfx_pwrdm = pwrdm_lookup("gfx_pwrdm");
+	per_pwrdm = pwrdm_lookup("per_pwrdm");
+	mpu_pwrdm = pwrdm_lookup("mpu_pwrdm");
+
+	gfx_l4ls_clkdm = clkdm_lookup("gfx_l4ls_gfx_clkdm");
+
+	if ((!gfx_pwrdm) || (!per_pwrdm) || (!mpu_pwrdm) || (!gfx_l4ls_clkdm)) {
+		ret = -ENODEV;
+		goto err;
+	}
+
+	am33xx_pm = kzalloc(sizeof(*am33xx_pm), GFP_KERNEL);
+	if (!am33xx_pm) {
+		pr_err("Memory allocation failed\n");
+		ret = -ENOMEM;
+		return ret;
+	}
+
+	ret = am33xx_map_emif();
+	if (ret) {
+		pr_err("PM: Could not ioremap EMIF\n");
+		goto err;
+	}
+
+	/* Determine Memory Type */
+	temp = readl(am33xx_emif_base + EMIF_SDRAM_CONFIG);
+	temp = (temp & SDRAM_TYPE_MASK) >> SDRAM_TYPE_SHIFT;
+	/* Parameters to pass to aseembly code */
+	susp_params.emif_addr_virt = am33xx_emif_base;
+	susp_params.dram_sync = am33xx_dram_sync;
+	susp_params.mem_type = temp;
+	am33xx_pm->ipc.reg4 = temp;
+
+	(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)
+		omap_set_pwrdm_state(cefuse_pwrdm, PWRDM_POWER_OFF);
+	else
+		pr_err("PM: Failed to get cefuse_pwrdm\n");
+
+	am33xx_pm->state = M3_STATE_RESET;
+
+	wkup_m3_set_ops(&am33xx_wkup_m3_ops);
+
+	/* Physical resume address to be used by ROM code */
+	am33xx_pm->ipc.reg0 = (AM33XX_OCMC_END -
+		am33xx_do_wfi_sz + am33xx_resume_offset + 0x4);
+
+	return 0;
+
+err:
+	kfree(am33xx_pm);
+	return ret;
+}
diff --git a/arch/arm/mach-omap2/pm33xx.h b/arch/arm/mach-omap2/pm33xx.h
new file mode 100644
index 0000000..309e65a
--- /dev/null
+++ b/arch/arm/mach-omap2/pm33xx.h
@@ -0,0 +1,64 @@
+/*
+ * AM33XX Power Management Routines
+ *
+ * Copyright (C) 2012-2014 Texas Instruments Inc.
+ *	Vaibhav Bedia, Dave Gerlach
+ *
+ * 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
+
+#ifndef __ASSEMBLER__
+
+#include <linux/wkup_m3.h>
+
+struct am33xx_pm_context {
+	struct wkup_m3_ipc_regs	ipc;
+	struct firmware		*firmware;
+	struct omap_mbox	*mbox;
+	u8			state;
+	u32			ver;
+};
+
+/*
+ * Params passed to suspend routine
+ *
+ * These are used to load into registers by suspend code,
+ * entries here must always be in sync with the suspend code
+ * in arm/mach-omap2/sleep33xx.S
+ */
+struct am33xx_suspend_params {
+	void __iomem *emif_addr_virt;
+	u32 mem_type;
+	void __iomem *dram_sync;
+};
+
+#endif /*__ASSEMBLER__ */
+
+#define	IPC_CMD_DS0			0x4
+#define	IPC_CMD_STANDBY			0xc
+#define IPC_CMD_RESET			0xe
+#define DS_IPC_DEFAULT			0xffffffff
+#define M3_VERSION_UNKNOWN		0x0000ffff
+#define M3_BASELINE_VERSION		0x187
+
+#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
+
+#define MEM_TYPE_DDR2		2
+
+#endif /* __ARCH_ARM_MACH_OMAP2_PM33XX_H */
-- 
1.9.0


^ permalink raw reply related	[flat|nested] 106+ messages in thread

* [PATCH v4 10/11] ARM: OMAP2+: AM33XX: Basic suspend resume support
@ 2014-07-11  2:55   ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11  2:55 UTC (permalink / raw)
  To: linux-arm-kernel

AM335x supports various low power modes as documented
in section 8.1.4.3 of the AM335x Technical Reference Manual.

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 the resume process. As part of
the boot process, the assembly code is copied over to OCMCRAM
using the OMAP SRAM code so it can be executed to turn of the
EMIF.

AM335x has a Cortex-M3 (WKUP_M3) which assists the MPU
in DeepSleep0 and Standby 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 WKUP_M3 is managed by a remoteproc driver. The PM code hooks
into the remoteproc driver through an rproc_ready callback to
allow PM features to become available once the firmware for the
wkup_m3 has been loaded. This code maintains its own state machine
for the wkup_m3 so that it can be used in the manner intended for
low power modes.

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 external RAM in self-refresh
mode, gates the MPU clock, and then finally executes the WFI
instruction. Execution of the WFI instruction with MPU clock gated
triggers another interrupt to the WKUP_M3 which then continues
with 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 and from here system remains in sleep state
until a wake source brings the system into resume path.

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.

Code is based on work by Vaibhav Bedia.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
---
v3->v4:
	Remove all direct wkup_m3 usage and moved to rproc driver introduced
	in previous patch.

 arch/arm/mach-omap2/pm33xx.c | 346 +++++++++++++++++++++++++++++++++++++++++++
 arch/arm/mach-omap2/pm33xx.h |  64 ++++++++
 2 files changed, 410 insertions(+)
 create mode 100644 arch/arm/mach-omap2/pm33xx.c
 create mode 100644 arch/arm/mach-omap2/pm33xx.h

diff --git a/arch/arm/mach-omap2/pm33xx.c b/arch/arm/mach-omap2/pm33xx.c
new file mode 100644
index 0000000..30d0f7d
--- /dev/null
+++ b/arch/arm/mach-omap2/pm33xx.c
@@ -0,0 +1,346 @@
+/*
+ * AM33XX Power Management Routines
+ *
+ * Copyright (C) 2012-2014 Texas Instruments Incorporated - http://www.ti.com/
+ *	Vaibhav Bedia, Dave Gerlach
+ *
+ * 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/cpu.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/interrupt.h>
+#include <linux/ti_emif.h>
+#include <linux/omap-mailbox.h>
+#include <linux/sizes.h>
+
+#include <asm/suspend.h>
+#include <asm/proc-fns.h>
+#include <asm/fncpy.h>
+#include <asm/system_misc.h>
+
+#include "pm.h"
+#include "cm33xx.h"
+#include "pm33xx.h"
+#include "common.h"
+#include "clockdomain.h"
+#include "powerdomain.h"
+#include "soc.h"
+#include "sram.h"
+
+static void __iomem *am33xx_emif_base;
+static struct powerdomain *cefuse_pwrdm, *gfx_pwrdm, *per_pwrdm, *mpu_pwrdm;
+static struct clockdomain *gfx_l4ls_clkdm;
+
+static struct am33xx_pm_context *am33xx_pm;
+
+static DECLARE_COMPLETION(am33xx_pm_sync);
+
+static void (*am33xx_do_wfi_sram)(struct am33xx_suspend_params *);
+
+static struct am33xx_suspend_params susp_params;
+
+#ifdef CONFIG_SUSPEND
+
+static int am33xx_do_sram_idle(long unsigned int unused)
+{
+	am33xx_do_wfi_sram(&susp_params);
+	return 0;
+}
+
+static int am33xx_pm_suspend(void)
+{
+	int i, ret = 0;
+	int status = 0;
+	struct wkup_m3_wakeup_src wakeup_src = {.irq_nr = 0,
+						.src = "Unknown",};
+
+	/* Try to put GFX to sleep */
+	omap_set_pwrdm_state(gfx_pwrdm, PWRDM_POWER_OFF);
+
+	ret = cpu_suspend(0, am33xx_do_sram_idle);
+
+	status = pwrdm_read_pwrst(gfx_pwrdm);
+	if (status != PWRDM_POWER_OFF)
+		pr_err("PM: GFX domain did not transition\n");
+
+	/*
+	 * BUG: 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
+	 */
+	clkdm_wakeup(gfx_l4ls_clkdm);
+	clkdm_sleep(gfx_l4ls_clkdm);
+
+	if (ret) {
+		pr_err("PM: Kernel suspend failure\n");
+	} else {
+		i = wkup_m3_pm_status();
+
+		switch (i) {
+		case 0:
+			pr_info("PM: Successfully put all powerdomains to target state\n");
+
+			/*
+			 * The PRCM registers on AM335x do not contain
+			 * previous state information like those present on
+			 * OMAP4 so we must manually indicate transition so
+			 * state counters are properly incremented
+			 */
+			pwrdm_post_transition(mpu_pwrdm);
+			pwrdm_post_transition(per_pwrdm);
+			break;
+		case 1:
+			pr_err("PM: Could not transition all powerdomains to target state\n");
+			ret = -1;
+			break;
+		default:
+			pr_err("PM: CM3 returned unknown result = %d\n", i);
+			ret = -1;
+		}
+		/* print the wakeup reason */
+		wkup_m3_wake_src(&wakeup_src);
+
+		pr_info("PM: Wakeup source %s\n", wakeup_src.src);
+	}
+
+	return ret;
+}
+
+static int am33xx_pm_enter(suspend_state_t suspend_state)
+{
+	int ret = 0;
+
+	switch (suspend_state) {
+	case PM_SUSPEND_MEM:
+		ret = am33xx_pm_suspend();
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+
+static void am33xx_m3_state_machine_reset(void)
+{
+	int i;
+
+	am33xx_pm->ipc.reg1 = IPC_CMD_RESET;
+
+	wkup_m3_set_cmd(&am33xx_pm->ipc);
+
+	am33xx_pm->state = M3_STATE_MSG_FOR_RESET;
+
+	if (!wkup_m3_ping()) {
+		i = wait_for_completion_timeout(&am33xx_pm_sync,
+						msecs_to_jiffies(500));
+		if (!i) {
+			WARN(1, "PM: MPU<->CM3 sync failure\n");
+			am33xx_pm->state = M3_STATE_UNKNOWN;
+		}
+	} else {
+		pr_warn("PM: Unable to ping CM3\n");
+	}
+}
+
+static int am33xx_pm_begin(suspend_state_t state)
+{
+	int i;
+
+
+	switch (state) {
+	case PM_SUSPEND_MEM:
+		am33xx_pm->ipc.reg1	= IPC_CMD_DS0;
+		break;
+	}
+
+	am33xx_pm->ipc.reg2		= DS_IPC_DEFAULT;
+	am33xx_pm->ipc.reg3		= DS_IPC_DEFAULT;
+
+	wkup_m3_set_cmd(&am33xx_pm->ipc);
+
+	am33xx_pm->state = M3_STATE_MSG_FOR_LP;
+
+	if (!wkup_m3_ping()) {
+		i = wait_for_completion_timeout(&am33xx_pm_sync,
+						msecs_to_jiffies(500));
+		if (!i) {
+			WARN(1, "PM: MPU<->CM3 sync failure\n");
+			return -1;
+		}
+	} else {
+		pr_warn("PM: Unable to ping CM3\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static void am33xx_pm_end(void)
+{
+	am33xx_m3_state_machine_reset();
+}
+
+static int am33xx_pm_valid(suspend_state_t state)
+{
+	switch (state) {
+	case PM_SUSPEND_MEM:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+static const struct platform_suspend_ops am33xx_pm_ops = {
+	.begin		= am33xx_pm_begin,
+	.end		= am33xx_pm_end,
+	.enter		= am33xx_pm_enter,
+	.valid		= am33xx_pm_valid,
+};
+#endif /* CONFIG_SUSPEND */
+
+static void am33xx_txev_handler(void)
+{
+	switch (am33xx_pm->state) {
+	case M3_STATE_RESET:
+		am33xx_pm->state = M3_STATE_INITED;
+		complete(&am33xx_pm_sync);
+		break;
+	case M3_STATE_MSG_FOR_RESET:
+		am33xx_pm->state = M3_STATE_INITED;
+		complete(&am33xx_pm_sync);
+		break;
+	case M3_STATE_MSG_FOR_LP:
+		complete(&am33xx_pm_sync);
+		break;
+	case M3_STATE_UNKNOWN:
+		pr_warn("PM: Unknown CM3 State\n");
+	}
+}
+
+static void am33xx_m3_ready_cb(void)
+{
+	am33xx_pm->ver = wkup_m3_fw_version_read();
+
+	if (am33xx_pm->ver == M3_VERSION_UNKNOWN ||
+	    am33xx_pm->ver < M3_BASELINE_VERSION) {
+		pr_warn("PM: CM3 Firmware Version %x not supported\n",
+			am33xx_pm->ver);
+		return;
+	} else {
+		pr_info("PM: CM3 Firmware Version = 0x%x\n",
+			am33xx_pm->ver);
+	}
+
+#ifdef CONFIG_SUSPEND
+	suspend_set_ops(&am33xx_pm_ops);
+#endif /* CONFIG_SUSPEND */
+}
+
+static struct wkup_m3_ops am33xx_wkup_m3_ops = {
+	.txev_handler = am33xx_txev_handler,
+	.rproc_ready = am33xx_m3_ready_cb,
+};
+
+/*
+ * 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;
+}
+
+int __init am33xx_pm_init(void)
+{
+	int ret;
+	u32 temp;
+
+	if (!soc_is_am33xx())
+		return -ENODEV;
+
+	gfx_pwrdm = pwrdm_lookup("gfx_pwrdm");
+	per_pwrdm = pwrdm_lookup("per_pwrdm");
+	mpu_pwrdm = pwrdm_lookup("mpu_pwrdm");
+
+	gfx_l4ls_clkdm = clkdm_lookup("gfx_l4ls_gfx_clkdm");
+
+	if ((!gfx_pwrdm) || (!per_pwrdm) || (!mpu_pwrdm) || (!gfx_l4ls_clkdm)) {
+		ret = -ENODEV;
+		goto err;
+	}
+
+	am33xx_pm = kzalloc(sizeof(*am33xx_pm), GFP_KERNEL);
+	if (!am33xx_pm) {
+		pr_err("Memory allocation failed\n");
+		ret = -ENOMEM;
+		return ret;
+	}
+
+	ret = am33xx_map_emif();
+	if (ret) {
+		pr_err("PM: Could not ioremap EMIF\n");
+		goto err;
+	}
+
+	/* Determine Memory Type */
+	temp = readl(am33xx_emif_base + EMIF_SDRAM_CONFIG);
+	temp = (temp & SDRAM_TYPE_MASK) >> SDRAM_TYPE_SHIFT;
+	/* Parameters to pass to aseembly code */
+	susp_params.emif_addr_virt = am33xx_emif_base;
+	susp_params.dram_sync = am33xx_dram_sync;
+	susp_params.mem_type = temp;
+	am33xx_pm->ipc.reg4 = temp;
+
+	(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)
+		omap_set_pwrdm_state(cefuse_pwrdm, PWRDM_POWER_OFF);
+	else
+		pr_err("PM: Failed to get cefuse_pwrdm\n");
+
+	am33xx_pm->state = M3_STATE_RESET;
+
+	wkup_m3_set_ops(&am33xx_wkup_m3_ops);
+
+	/* Physical resume address to be used by ROM code */
+	am33xx_pm->ipc.reg0 = (AM33XX_OCMC_END -
+		am33xx_do_wfi_sz + am33xx_resume_offset + 0x4);
+
+	return 0;
+
+err:
+	kfree(am33xx_pm);
+	return ret;
+}
diff --git a/arch/arm/mach-omap2/pm33xx.h b/arch/arm/mach-omap2/pm33xx.h
new file mode 100644
index 0000000..309e65a
--- /dev/null
+++ b/arch/arm/mach-omap2/pm33xx.h
@@ -0,0 +1,64 @@
+/*
+ * AM33XX Power Management Routines
+ *
+ * Copyright (C) 2012-2014 Texas Instruments Inc.
+ *	Vaibhav Bedia, Dave Gerlach
+ *
+ * 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
+
+#ifndef __ASSEMBLER__
+
+#include <linux/wkup_m3.h>
+
+struct am33xx_pm_context {
+	struct wkup_m3_ipc_regs	ipc;
+	struct firmware		*firmware;
+	struct omap_mbox	*mbox;
+	u8			state;
+	u32			ver;
+};
+
+/*
+ * Params passed to suspend routine
+ *
+ * These are used to load into registers by suspend code,
+ * entries here must always be in sync with the suspend code
+ * in arm/mach-omap2/sleep33xx.S
+ */
+struct am33xx_suspend_params {
+	void __iomem *emif_addr_virt;
+	u32 mem_type;
+	void __iomem *dram_sync;
+};
+
+#endif /*__ASSEMBLER__ */
+
+#define	IPC_CMD_DS0			0x4
+#define	IPC_CMD_STANDBY			0xc
+#define IPC_CMD_RESET			0xe
+#define DS_IPC_DEFAULT			0xffffffff
+#define M3_VERSION_UNKNOWN		0x0000ffff
+#define M3_BASELINE_VERSION		0x187
+
+#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
+
+#define MEM_TYPE_DDR2		2
+
+#endif /* __ARCH_ARM_MACH_OMAP2_PM33XX_H */
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 106+ messages in thread

* [PATCH v4 11/11] ARM: OMAP2+: AM33XX: Hookup AM33XX PM code into OMAP builds
  2014-07-11  2:55 ` Dave Gerlach
@ 2014-07-11  2:55   ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11  2:55 UTC (permalink / raw)
  To: linux-arm-kernel, linux-omap
  Cc: Paul Walmsley, Kevin Hilman, Tony Lindgren, Tero Kristo,
	Nishanth Menon, Russ Dill, Santosh Shilimkar, Daniel Mack,
	Suman Anna, Benoit Cousson, Dave Gerlach

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

Because the suspend resume functionality is different on AM33xx
than other OMAP platforms due to the need for M3 firmware and an
IPC channel to be in place, separate PM ops are used instead of
omap_pm_ops. These are now set using omap2_common_suspend_init
so the AM33xx can make a decision at runtime to enable suspend based
on the availabilty of aforementioned requirements.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
---
v3->v4:
	Updated for rproc usage now.

 arch/arm/mach-omap2/Kconfig  |  1 +
 arch/arm/mach-omap2/Makefile |  2 ++
 arch/arm/mach-omap2/common.h |  9 +++++++++
 arch/arm/mach-omap2/io.c     |  1 +
 arch/arm/mach-omap2/pm.h     |  5 +++++
 arch/arm/mach-omap2/sram.c   | 10 +++++++++-
 arch/arm/mach-omap2/sram.h   |  2 ++
 7 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index 1c1ed73..f8a56e5 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -59,6 +59,7 @@ config SOC_AM33XX
 	select ARCH_OMAP2PLUS
 	select ARCH_HAS_OPP
 	select ARM_CPU_SUSPEND if PM
+	select WKUP_M3_RPROC if PM
 
 config SOC_AM43XX
 	bool "TI AM43x"
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 8ca99e9..3c8d30c 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -90,6 +90,7 @@ 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_DRA7XX)		+= 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
@@ -97,6 +98,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)
 
 endif
 
diff --git a/arch/arm/mach-omap2/common.h b/arch/arm/mach-omap2/common.h
index 1536338..1bcd475 100644
--- a/arch/arm/mach-omap2/common.h
+++ b/arch/arm/mach-omap2/common.h
@@ -75,6 +75,15 @@ static inline int omap4_pm_init_early(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
diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c
index 4d4d150..fb23f83 100644
--- a/arch/arm/mach-omap2/io.c
+++ b/arch/arm/mach-omap2/io.c
@@ -590,6 +590,7 @@ void __init am33xx_init_early(void)
 void __init am33xx_init_late(void)
 {
 	omap_common_late_init();
+	am33xx_pm_init();
 }
 #endif
 
diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h
index e150102..b6f72a4 100644
--- a/arch/arm/mach-omap2/pm.h
+++ b/arch/arm/mach-omap2/pm.h
@@ -81,6 +81,11 @@ 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;
+
 /* 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;
diff --git a/arch/arm/mach-omap2/sram.c b/arch/arm/mach-omap2/sram.c
index ddf1818..2227f20 100644
--- a/arch/arm/mach-omap2/sram.c
+++ b/arch/arm/mach-omap2/sram.c
@@ -154,7 +154,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
@@ -285,10 +285,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)
 {
diff --git a/arch/arm/mach-omap2/sram.h b/arch/arm/mach-omap2/sram.h
index ca7277c..24788b5 100644
--- a/arch/arm/mach-omap2/sram.h
+++ b/arch/arm/mach-omap2/sram.h
@@ -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__ */
-- 
1.9.0


^ permalink raw reply related	[flat|nested] 106+ messages in thread

* [PATCH v4 11/11] ARM: OMAP2+: AM33XX: Hookup AM33XX PM code into OMAP builds
@ 2014-07-11  2:55   ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11  2:55 UTC (permalink / raw)
  To: linux-arm-kernel

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

Because the suspend resume functionality is different on AM33xx
than other OMAP platforms due to the need for M3 firmware and an
IPC channel to be in place, separate PM ops are used instead of
omap_pm_ops. These are now set using omap2_common_suspend_init
so the AM33xx can make a decision at runtime to enable suspend based
on the availabilty of aforementioned requirements.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
---
v3->v4:
	Updated for rproc usage now.

 arch/arm/mach-omap2/Kconfig  |  1 +
 arch/arm/mach-omap2/Makefile |  2 ++
 arch/arm/mach-omap2/common.h |  9 +++++++++
 arch/arm/mach-omap2/io.c     |  1 +
 arch/arm/mach-omap2/pm.h     |  5 +++++
 arch/arm/mach-omap2/sram.c   | 10 +++++++++-
 arch/arm/mach-omap2/sram.h   |  2 ++
 7 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index 1c1ed73..f8a56e5 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -59,6 +59,7 @@ config SOC_AM33XX
 	select ARCH_OMAP2PLUS
 	select ARCH_HAS_OPP
 	select ARM_CPU_SUSPEND if PM
+	select WKUP_M3_RPROC if PM
 
 config SOC_AM43XX
 	bool "TI AM43x"
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 8ca99e9..3c8d30c 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -90,6 +90,7 @@ 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_DRA7XX)		+= 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
@@ -97,6 +98,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)
 
 endif
 
diff --git a/arch/arm/mach-omap2/common.h b/arch/arm/mach-omap2/common.h
index 1536338..1bcd475 100644
--- a/arch/arm/mach-omap2/common.h
+++ b/arch/arm/mach-omap2/common.h
@@ -75,6 +75,15 @@ static inline int omap4_pm_init_early(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
diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c
index 4d4d150..fb23f83 100644
--- a/arch/arm/mach-omap2/io.c
+++ b/arch/arm/mach-omap2/io.c
@@ -590,6 +590,7 @@ void __init am33xx_init_early(void)
 void __init am33xx_init_late(void)
 {
 	omap_common_late_init();
+	am33xx_pm_init();
 }
 #endif
 
diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h
index e150102..b6f72a4 100644
--- a/arch/arm/mach-omap2/pm.h
+++ b/arch/arm/mach-omap2/pm.h
@@ -81,6 +81,11 @@ 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;
+
 /* 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;
diff --git a/arch/arm/mach-omap2/sram.c b/arch/arm/mach-omap2/sram.c
index ddf1818..2227f20 100644
--- a/arch/arm/mach-omap2/sram.c
+++ b/arch/arm/mach-omap2/sram.c
@@ -154,7 +154,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
@@ -285,10 +285,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)
 {
diff --git a/arch/arm/mach-omap2/sram.h b/arch/arm/mach-omap2/sram.h
index ca7277c..24788b5 100644
--- a/arch/arm/mach-omap2/sram.h
+++ b/arch/arm/mach-omap2/sram.h
@@ -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__ */
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 00/11] ARM: OMAP2+: AM33XX: Add suspend-resume support
  2014-07-11  2:55 ` Dave Gerlach
@ 2014-07-11  7:59   ` Daniel Mack
  -1 siblings, 0 replies; 106+ messages in thread
From: Daniel Mack @ 2014-07-11  7:59 UTC (permalink / raw)
  To: Dave Gerlach, linux-arm-kernel, linux-omap
  Cc: Paul Walmsley, Kevin Hilman, Tony Lindgren, Tero Kristo,
	Nishanth Menon, Russ Dill, Santosh Shilimkar, Suman Anna,
	Benoit Cousson, Pascal Hürst

On 07/11/2014 04:55 AM, Dave Gerlach wrote:
> This series adds suspend/resume support for am335x. Version 3 of this
> series can be found at [1]. I apologize for the large delay between this
> and the previous revision. This code has been heavily refined
> since the last version based on the various comments received for v3. The
> major change from previous version is moving all wkup_m3 code into a
> remoteproc based driver. The new driver handles all IPC and fw loading
> and exposes a small API to be used by PM code to achieve low power states.

Thanks a lot for this new series, Dave!

I've given it a quick test on a custom AM335x board and can confirm
success. Tested with both DDR2 and DDR3 versions, both work fine, and
power consumption drops to a reasonable level.

I'd be very happy if these patches made it in for 3.17, but I haven't
followed the discussion on the patches this set depends on.


Best regards,
Daniel


^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 00/11] ARM: OMAP2+: AM33XX: Add suspend-resume support
@ 2014-07-11  7:59   ` Daniel Mack
  0 siblings, 0 replies; 106+ messages in thread
From: Daniel Mack @ 2014-07-11  7:59 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/11/2014 04:55 AM, Dave Gerlach wrote:
> This series adds suspend/resume support for am335x. Version 3 of this
> series can be found at [1]. I apologize for the large delay between this
> and the previous revision. This code has been heavily refined
> since the last version based on the various comments received for v3. The
> major change from previous version is moving all wkup_m3 code into a
> remoteproc based driver. The new driver handles all IPC and fw loading
> and exposes a small API to be used by PM code to achieve low power states.

Thanks a lot for this new series, Dave!

I've given it a quick test on a custom AM335x board and can confirm
success. Tested with both DDR2 and DDR3 versions, both work fine, and
power consumption drops to a reasonable level.

I'd be very happy if these patches made it in for 3.17, but I haven't
followed the discussion on the patches this set depends on.


Best regards,
Daniel

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 00/11] ARM: OMAP2+: AM33XX: Add suspend-resume support
  2014-07-11  2:55 ` Dave Gerlach
@ 2014-07-11 15:30   ` Andre Heider
  -1 siblings, 0 replies; 106+ messages in thread
From: Andre Heider @ 2014-07-11 15:30 UTC (permalink / raw)
  To: Dave Gerlach
  Cc: linux-arm-kernel, linux-omap, Paul Walmsley, Kevin Hilman,
	Tony Lindgren, Tero Kristo, Nishanth Menon, Russ Dill,
	Santosh Shilimkar, Daniel Mack, Suman Anna, Benoit Cousson

Hi Dave,

On Thu, Jul 10, 2014 at 09:55:38PM -0500, Dave Gerlach wrote:
> Hello,
> 
> This series adds suspend/resume support for am335x. Version 3 of this
> series can be found at [1]. I apologize for the large delay between this
> and the previous revision. This code has been heavily refined
> since the last version based on the various comments received for v3. The
> major change from previous version is moving all wkup_m3 code into a
> remoteproc based driver. The new driver handles all IPC and fw loading
> and exposes a small API to be used by PM code to achieve low power states.
> 
> Firmware that can be used for testing this can be found at [2] on branch
> pm-remote-proc-v3, using am335x-pm-firmware.elf found in bin directory.
> Please note this has changed from all previous versions and is no longer
> the .bin file. Firmware can be built into kernel or placed in /lib/firmware
> in rootfs for automatic loading during boot.
> 
> This series has several dependencies. The wkup_m3_rproc utilizes a mailbox
> to communicate with the cm3 and depends on Suman's series for omap mbox
> support [3], which has several dependencies of it's own, listed in the
> cover letter. Also, a few changes to remoteproc itself were needed and
> have been provided by Suman here [4]. The edma patch included in this
> series was previously submitted by Daniel Mack and after discussion with
> him we agreed to include an updated version with this series as resume
> has a direct dependency on it due to hangs in mmc without it.
> 
> Because of the high number of dependencies I have pushed a branch for
> testing here [6] if anyone desires to try it out on branch pm-ds0-v3.16.
> 
> As is this series will only suspend and resume one time and then
> fail to resume afterwards due to the removal of direct PM code control
> of hwmods that do not properly assert their MSTANDBY signal after a context
> loss, discussed here [7]. In particular it is due to the usb_otg_hs hwmod
> that currently has no driver controlling it in the kernel. The main
> cause of the issue is that the SYSCONFIG register present within
> the IP must be reprogrammed after every suspend cycle and this
> only happens at boot if no driver is present. Work is in progress to
> allow suspend to function with or without drivers for the troublesome
> hwmods (cpgmac, usb_otg_hs, and tptc1-3) and will be provided in a separate
> future patch. The previous suggestion of allowing omap_device to handle
> it proved to be too invasive into both omap_device and omap_hwmod and
> the approach of allowing the firmware to handle it is not possible due
> to the inability of the CM3 to access the IPs causing the issue. I'd
> be happy to discuss this at length if anybody is interested.

I gave this a quick spin on boneblack, and it works for me, thanks!

Tested with "echo mem > /sys/power/state" and uart0 input to resume.

Contradictionary to the limits you mention, multiple suspend/resume cycles
do work for me. Maybe because of the enabled drivers in my .config, or
maybe something was messed up, but at first glance it looked fine.

It fails to compile with CONFIG_THUMB2_KERNEL though:
arch/arm/mach-omap2/sleep33xx.S:61: Error: cannot use register index
with PC-relative addressing -- `str r1,emif_addr_virt'

Then I noticed "freeze" in /sys/power/state, when triggered then crashes
with a null pointer dereference in suspend_devices_and_enter(). That might
be expected or even stupid though ;)

Thanks,
Andre

^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 00/11] ARM: OMAP2+: AM33XX: Add suspend-resume support
@ 2014-07-11 15:30   ` Andre Heider
  0 siblings, 0 replies; 106+ messages in thread
From: Andre Heider @ 2014-07-11 15:30 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Dave,

On Thu, Jul 10, 2014 at 09:55:38PM -0500, Dave Gerlach wrote:
> Hello,
> 
> This series adds suspend/resume support for am335x. Version 3 of this
> series can be found at [1]. I apologize for the large delay between this
> and the previous revision. This code has been heavily refined
> since the last version based on the various comments received for v3. The
> major change from previous version is moving all wkup_m3 code into a
> remoteproc based driver. The new driver handles all IPC and fw loading
> and exposes a small API to be used by PM code to achieve low power states.
> 
> Firmware that can be used for testing this can be found at [2] on branch
> pm-remote-proc-v3, using am335x-pm-firmware.elf found in bin directory.
> Please note this has changed from all previous versions and is no longer
> the .bin file. Firmware can be built into kernel or placed in /lib/firmware
> in rootfs for automatic loading during boot.
> 
> This series has several dependencies. The wkup_m3_rproc utilizes a mailbox
> to communicate with the cm3 and depends on Suman's series for omap mbox
> support [3], which has several dependencies of it's own, listed in the
> cover letter. Also, a few changes to remoteproc itself were needed and
> have been provided by Suman here [4]. The edma patch included in this
> series was previously submitted by Daniel Mack and after discussion with
> him we agreed to include an updated version with this series as resume
> has a direct dependency on it due to hangs in mmc without it.
> 
> Because of the high number of dependencies I have pushed a branch for
> testing here [6] if anyone desires to try it out on branch pm-ds0-v3.16.
> 
> As is this series will only suspend and resume one time and then
> fail to resume afterwards due to the removal of direct PM code control
> of hwmods that do not properly assert their MSTANDBY signal after a context
> loss, discussed here [7]. In particular it is due to the usb_otg_hs hwmod
> that currently has no driver controlling it in the kernel. The main
> cause of the issue is that the SYSCONFIG register present within
> the IP must be reprogrammed after every suspend cycle and this
> only happens at boot if no driver is present. Work is in progress to
> allow suspend to function with or without drivers for the troublesome
> hwmods (cpgmac, usb_otg_hs, and tptc1-3) and will be provided in a separate
> future patch. The previous suggestion of allowing omap_device to handle
> it proved to be too invasive into both omap_device and omap_hwmod and
> the approach of allowing the firmware to handle it is not possible due
> to the inability of the CM3 to access the IPs causing the issue. I'd
> be happy to discuss this at length if anybody is interested.

I gave this a quick spin on boneblack, and it works for me, thanks!

Tested with "echo mem > /sys/power/state" and uart0 input to resume.

Contradictionary to the limits you mention, multiple suspend/resume cycles
do work for me. Maybe because of the enabled drivers in my .config, or
maybe something was messed up, but at first glance it looked fine.

It fails to compile with CONFIG_THUMB2_KERNEL though:
arch/arm/mach-omap2/sleep33xx.S:61: Error: cannot use register index
with PC-relative addressing -- `str r1,emif_addr_virt'

Then I noticed "freeze" in /sys/power/state, when triggered then crashes
with a null pointer dereference in suspend_devices_and_enter(). That might
be expected or even stupid though ;)

Thanks,
Andre

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 00/11] ARM: OMAP2+: AM33XX: Add suspend-resume support
  2014-07-11  7:59   ` Daniel Mack
@ 2014-07-11 17:24     ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11 17:24 UTC (permalink / raw)
  To: Daniel Mack
  Cc: linux-arm-kernel, linux-omap, Paul Walmsley, Kevin Hilman,
	Tony Lindgren, Tero Kristo, Nishanth Menon, Russ Dill,
	Santosh Shilimkar, Suman Anna, Benoit Cousson, Pascal Hürst

On 07/11/2014 02:59 AM, Daniel Mack wrote:
> On 07/11/2014 04:55 AM, Dave Gerlach wrote:
>> This series adds suspend/resume support for am335x. Version 3 of this
>> series can be found at [1]. I apologize for the large delay between this
>> and the previous revision. This code has been heavily refined
>> since the last version based on the various comments received for v3. The
>> major change from previous version is moving all wkup_m3 code into a
>> remoteproc based driver. The new driver handles all IPC and fw loading
>> and exposes a small API to be used by PM code to achieve low power states.
>
> Thanks a lot for this new series, Dave!
>
> I've given it a quick test on a custom AM335x board and can confirm
> success. Tested with both DDR2 and DDR3 versions, both work fine, and
> power consumption drops to a reasonable level.
>

I appreciate you trying it out!

Regards,
Dave

> I'd be very happy if these patches made it in for 3.17, but I haven't
> followed the discussion on the patches this set depends on.
>
>
> Best regards,
> Daniel
>


^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 00/11] ARM: OMAP2+: AM33XX: Add suspend-resume support
@ 2014-07-11 17:24     ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11 17:24 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/11/2014 02:59 AM, Daniel Mack wrote:
> On 07/11/2014 04:55 AM, Dave Gerlach wrote:
>> This series adds suspend/resume support for am335x. Version 3 of this
>> series can be found at [1]. I apologize for the large delay between this
>> and the previous revision. This code has been heavily refined
>> since the last version based on the various comments received for v3. The
>> major change from previous version is moving all wkup_m3 code into a
>> remoteproc based driver. The new driver handles all IPC and fw loading
>> and exposes a small API to be used by PM code to achieve low power states.
>
> Thanks a lot for this new series, Dave!
>
> I've given it a quick test on a custom AM335x board and can confirm
> success. Tested with both DDR2 and DDR3 versions, both work fine, and
> power consumption drops to a reasonable level.
>

I appreciate you trying it out!

Regards,
Dave

> I'd be very happy if these patches made it in for 3.17, but I haven't
> followed the discussion on the patches this set depends on.
>
>
> Best regards,
> Daniel
>

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 00/11] ARM: OMAP2+: AM33XX: Add suspend-resume support
  2014-07-11 15:30   ` Andre Heider
@ 2014-07-11 17:31     ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11 17:31 UTC (permalink / raw)
  To: Andre Heider
  Cc: linux-arm-kernel, linux-omap, Paul Walmsley, Kevin Hilman,
	Tony Lindgren, Tero Kristo, Nishanth Menon, Russ Dill,
	Santosh Shilimkar, Daniel Mack, Suman Anna, Benoit Cousson

Andre,

On 07/11/2014 10:30 AM, Andre Heider wrote:
> Hi Dave,
>
> On Thu, Jul 10, 2014 at 09:55:38PM -0500, Dave Gerlach wrote:
>> Hello,
>>
>> This series adds suspend/resume support for am335x. Version 3 of this
>> series can be found at [1]. I apologize for the large delay between this
>> and the previous revision. This code has been heavily refined
>> since the last version based on the various comments received for v3. The
>> major change from previous version is moving all wkup_m3 code into a
>> remoteproc based driver. The new driver handles all IPC and fw loading
>> and exposes a small API to be used by PM code to achieve low power states.
>>
>> Firmware that can be used for testing this can be found at [2] on branch
>> pm-remote-proc-v3, using am335x-pm-firmware.elf found in bin directory.
>> Please note this has changed from all previous versions and is no longer
>> the .bin file. Firmware can be built into kernel or placed in /lib/firmware
>> in rootfs for automatic loading during boot.
>>
>> This series has several dependencies. The wkup_m3_rproc utilizes a mailbox
>> to communicate with the cm3 and depends on Suman's series for omap mbox
>> support [3], which has several dependencies of it's own, listed in the
>> cover letter. Also, a few changes to remoteproc itself were needed and
>> have been provided by Suman here [4]. The edma patch included in this
>> series was previously submitted by Daniel Mack and after discussion with
>> him we agreed to include an updated version with this series as resume
>> has a direct dependency on it due to hangs in mmc without it.
>>
>> Because of the high number of dependencies I have pushed a branch for
>> testing here [6] if anyone desires to try it out on branch pm-ds0-v3.16.
>>
>> As is this series will only suspend and resume one time and then
>> fail to resume afterwards due to the removal of direct PM code control
>> of hwmods that do not properly assert their MSTANDBY signal after a context
>> loss, discussed here [7]. In particular it is due to the usb_otg_hs hwmod
>> that currently has no driver controlling it in the kernel. The main
>> cause of the issue is that the SYSCONFIG register present within
>> the IP must be reprogrammed after every suspend cycle and this
>> only happens at boot if no driver is present. Work is in progress to
>> allow suspend to function with or without drivers for the troublesome
>> hwmods (cpgmac, usb_otg_hs, and tptc1-3) and will be provided in a separate
>> future patch. The previous suggestion of allowing omap_device to handle
>> it proved to be too invasive into both omap_device and omap_hwmod and
>> the approach of allowing the firmware to handle it is not possible due
>> to the inability of the CM3 to access the IPs causing the issue. I'd
>> be happy to discuss this at length if anybody is interested.
>
> I gave this a quick spin on boneblack, and it works for me, thanks!
>
> Tested with "echo mem > /sys/power/state" and uart0 input to resume.
>

Thanks for trying it out!

> Contradictionary to the limits you mention, multiple suspend/resume cycles
> do work for me. Maybe because of the enabled drivers in my .config, or
> maybe something was messed up, but at first glance it looked fine.

Yes, if you have USB enabled and the driver is present, the usb hwmod is 
properly handled and SYSCONFIG properly restored, it is only when there is no 
driver as is the case with the defconfig that we run in to issues. Looking back 
I was not clear about that, sorry.

>
> It fails to compile with CONFIG_THUMB2_KERNEL though:
> arch/arm/mach-omap2/sleep33xx.S:61: Error: cannot use register index
> with PC-relative addressing -- `str r1,emif_addr_virt'

Hmm, interestingly enough I can not reproduce this build error. Can you share 
your config and compiler version?

>
> Then I noticed "freeze" in /sys/power/state, when triggered then crashes
> with a null pointer dereference in suspend_devices_and_enter(). That might
> be expected or even stupid though ;)

This is present before my patches are applied, this series adds support for 
PM_SUSPEND_MEM, that's something else I probably should have been a bit more 
clear about in my cover letter.

Regards,
Dave

>
> Thanks,
> Andre
>


^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 00/11] ARM: OMAP2+: AM33XX: Add suspend-resume support
@ 2014-07-11 17:31     ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-11 17:31 UTC (permalink / raw)
  To: linux-arm-kernel

Andre,

On 07/11/2014 10:30 AM, Andre Heider wrote:
> Hi Dave,
>
> On Thu, Jul 10, 2014 at 09:55:38PM -0500, Dave Gerlach wrote:
>> Hello,
>>
>> This series adds suspend/resume support for am335x. Version 3 of this
>> series can be found at [1]. I apologize for the large delay between this
>> and the previous revision. This code has been heavily refined
>> since the last version based on the various comments received for v3. The
>> major change from previous version is moving all wkup_m3 code into a
>> remoteproc based driver. The new driver handles all IPC and fw loading
>> and exposes a small API to be used by PM code to achieve low power states.
>>
>> Firmware that can be used for testing this can be found at [2] on branch
>> pm-remote-proc-v3, using am335x-pm-firmware.elf found in bin directory.
>> Please note this has changed from all previous versions and is no longer
>> the .bin file. Firmware can be built into kernel or placed in /lib/firmware
>> in rootfs for automatic loading during boot.
>>
>> This series has several dependencies. The wkup_m3_rproc utilizes a mailbox
>> to communicate with the cm3 and depends on Suman's series for omap mbox
>> support [3], which has several dependencies of it's own, listed in the
>> cover letter. Also, a few changes to remoteproc itself were needed and
>> have been provided by Suman here [4]. The edma patch included in this
>> series was previously submitted by Daniel Mack and after discussion with
>> him we agreed to include an updated version with this series as resume
>> has a direct dependency on it due to hangs in mmc without it.
>>
>> Because of the high number of dependencies I have pushed a branch for
>> testing here [6] if anyone desires to try it out on branch pm-ds0-v3.16.
>>
>> As is this series will only suspend and resume one time and then
>> fail to resume afterwards due to the removal of direct PM code control
>> of hwmods that do not properly assert their MSTANDBY signal after a context
>> loss, discussed here [7]. In particular it is due to the usb_otg_hs hwmod
>> that currently has no driver controlling it in the kernel. The main
>> cause of the issue is that the SYSCONFIG register present within
>> the IP must be reprogrammed after every suspend cycle and this
>> only happens at boot if no driver is present. Work is in progress to
>> allow suspend to function with or without drivers for the troublesome
>> hwmods (cpgmac, usb_otg_hs, and tptc1-3) and will be provided in a separate
>> future patch. The previous suggestion of allowing omap_device to handle
>> it proved to be too invasive into both omap_device and omap_hwmod and
>> the approach of allowing the firmware to handle it is not possible due
>> to the inability of the CM3 to access the IPs causing the issue. I'd
>> be happy to discuss this at length if anybody is interested.
>
> I gave this a quick spin on boneblack, and it works for me, thanks!
>
> Tested with "echo mem > /sys/power/state" and uart0 input to resume.
>

Thanks for trying it out!

> Contradictionary to the limits you mention, multiple suspend/resume cycles
> do work for me. Maybe because of the enabled drivers in my .config, or
> maybe something was messed up, but at first glance it looked fine.

Yes, if you have USB enabled and the driver is present, the usb hwmod is 
properly handled and SYSCONFIG properly restored, it is only when there is no 
driver as is the case with the defconfig that we run in to issues. Looking back 
I was not clear about that, sorry.

>
> It fails to compile with CONFIG_THUMB2_KERNEL though:
> arch/arm/mach-omap2/sleep33xx.S:61: Error: cannot use register index
> with PC-relative addressing -- `str r1,emif_addr_virt'

Hmm, interestingly enough I can not reproduce this build error. Can you share 
your config and compiler version?

>
> Then I noticed "freeze" in /sys/power/state, when triggered then crashes
> with a null pointer dereference in suspend_devices_and_enter(). That might
> be expected or even stupid though ;)

This is present before my patches are applied, this series adds support for 
PM_SUSPEND_MEM, that's something else I probably should have been a bit more 
clear about in my cover letter.

Regards,
Dave

>
> Thanks,
> Andre
>

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 00/11] ARM: OMAP2+: AM33XX: Add suspend-resume support
  2014-07-11 17:31     ` Dave Gerlach
@ 2014-07-14  9:37       ` Andre Heider
  -1 siblings, 0 replies; 106+ messages in thread
From: Andre Heider @ 2014-07-14  9:37 UTC (permalink / raw)
  To: Dave Gerlach
  Cc: linux-arm-kernel, linux-omap, Paul Walmsley, Kevin Hilman,
	Tony Lindgren, Tero Kristo, Nishanth Menon, Russ Dill,
	Santosh Shilimkar, Daniel Mack, Suman Anna, Benoit Cousson

On Fri, Jul 11, 2014 at 12:31:06PM -0500, Dave Gerlach wrote:
> >On Thu, Jul 10, 2014 at 09:55:38PM -0500, Dave Gerlach wrote:
> >>Hello,
> >>
> >>This series adds suspend/resume support for am335x. Version 3 of this
> >>series can be found at [1]. I apologize for the large delay between this
> >>and the previous revision. This code has been heavily refined
> >>since the last version based on the various comments received for v3. The
> >>major change from previous version is moving all wkup_m3 code into a
> >>remoteproc based driver. The new driver handles all IPC and fw loading
> >>and exposes a small API to be used by PM code to achieve low power states.
> >>
> >>Firmware that can be used for testing this can be found at [2] on branch
> >>pm-remote-proc-v3, using am335x-pm-firmware.elf found in bin directory.
> >>Please note this has changed from all previous versions and is no longer
> >>the .bin file. Firmware can be built into kernel or placed in /lib/firmware
> >>in rootfs for automatic loading during boot.
> >>
> >>This series has several dependencies. The wkup_m3_rproc utilizes a mailbox
> >>to communicate with the cm3 and depends on Suman's series for omap mbox
> >>support [3], which has several dependencies of it's own, listed in the
> >>cover letter. Also, a few changes to remoteproc itself were needed and
> >>have been provided by Suman here [4]. The edma patch included in this
> >>series was previously submitted by Daniel Mack and after discussion with
> >>him we agreed to include an updated version with this series as resume
> >>has a direct dependency on it due to hangs in mmc without it.
> >>
> >>Because of the high number of dependencies I have pushed a branch for
> >>testing here [6] if anyone desires to try it out on branch pm-ds0-v3.16.
> >>
> >>As is this series will only suspend and resume one time and then
> >>fail to resume afterwards due to the removal of direct PM code control
> >>of hwmods that do not properly assert their MSTANDBY signal after a context
> >>loss, discussed here [7]. In particular it is due to the usb_otg_hs hwmod
> >>that currently has no driver controlling it in the kernel. The main
> >>cause of the issue is that the SYSCONFIG register present within
> >>the IP must be reprogrammed after every suspend cycle and this
> >>only happens at boot if no driver is present. Work is in progress to
> >>allow suspend to function with or without drivers for the troublesome
> >>hwmods (cpgmac, usb_otg_hs, and tptc1-3) and will be provided in a separate
> >>future patch. The previous suggestion of allowing omap_device to handle
> >>it proved to be too invasive into both omap_device and omap_hwmod and
> >>the approach of allowing the firmware to handle it is not possible due
> >>to the inability of the CM3 to access the IPs causing the issue. I'd
> >>be happy to discuss this at length if anybody is interested.

...

> >It fails to compile with CONFIG_THUMB2_KERNEL though:
> >arch/arm/mach-omap2/sleep33xx.S:61: Error: cannot use register index
> >with PC-relative addressing -- `str r1,emif_addr_virt'
> 
> Hmm, interestingly enough I can not reproduce this build error. Can you
> share your config and compiler version?

While I have a stripped down .config for just boneblack I can reproduce this with:
make multi_v7_defconfig
./scripts/config -e THUMB2_KERNEL

Which may not make much sense (exposes other thumb2 related issues), but with that
.config sleep33xx.S fails to assemble with binutils 2.24 and succeeds with 2.23.2.

Which may be binutils' fault, I can't judge since I don't speak ARM asm ;)

Hope that helps,
Andre

^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 00/11] ARM: OMAP2+: AM33XX: Add suspend-resume support
@ 2014-07-14  9:37       ` Andre Heider
  0 siblings, 0 replies; 106+ messages in thread
From: Andre Heider @ 2014-07-14  9:37 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Jul 11, 2014 at 12:31:06PM -0500, Dave Gerlach wrote:
> >On Thu, Jul 10, 2014 at 09:55:38PM -0500, Dave Gerlach wrote:
> >>Hello,
> >>
> >>This series adds suspend/resume support for am335x. Version 3 of this
> >>series can be found at [1]. I apologize for the large delay between this
> >>and the previous revision. This code has been heavily refined
> >>since the last version based on the various comments received for v3. The
> >>major change from previous version is moving all wkup_m3 code into a
> >>remoteproc based driver. The new driver handles all IPC and fw loading
> >>and exposes a small API to be used by PM code to achieve low power states.
> >>
> >>Firmware that can be used for testing this can be found at [2] on branch
> >>pm-remote-proc-v3, using am335x-pm-firmware.elf found in bin directory.
> >>Please note this has changed from all previous versions and is no longer
> >>the .bin file. Firmware can be built into kernel or placed in /lib/firmware
> >>in rootfs for automatic loading during boot.
> >>
> >>This series has several dependencies. The wkup_m3_rproc utilizes a mailbox
> >>to communicate with the cm3 and depends on Suman's series for omap mbox
> >>support [3], which has several dependencies of it's own, listed in the
> >>cover letter. Also, a few changes to remoteproc itself were needed and
> >>have been provided by Suman here [4]. The edma patch included in this
> >>series was previously submitted by Daniel Mack and after discussion with
> >>him we agreed to include an updated version with this series as resume
> >>has a direct dependency on it due to hangs in mmc without it.
> >>
> >>Because of the high number of dependencies I have pushed a branch for
> >>testing here [6] if anyone desires to try it out on branch pm-ds0-v3.16.
> >>
> >>As is this series will only suspend and resume one time and then
> >>fail to resume afterwards due to the removal of direct PM code control
> >>of hwmods that do not properly assert their MSTANDBY signal after a context
> >>loss, discussed here [7]. In particular it is due to the usb_otg_hs hwmod
> >>that currently has no driver controlling it in the kernel. The main
> >>cause of the issue is that the SYSCONFIG register present within
> >>the IP must be reprogrammed after every suspend cycle and this
> >>only happens at boot if no driver is present. Work is in progress to
> >>allow suspend to function with or without drivers for the troublesome
> >>hwmods (cpgmac, usb_otg_hs, and tptc1-3) and will be provided in a separate
> >>future patch. The previous suggestion of allowing omap_device to handle
> >>it proved to be too invasive into both omap_device and omap_hwmod and
> >>the approach of allowing the firmware to handle it is not possible due
> >>to the inability of the CM3 to access the IPs causing the issue. I'd
> >>be happy to discuss this at length if anybody is interested.

...

> >It fails to compile with CONFIG_THUMB2_KERNEL though:
> >arch/arm/mach-omap2/sleep33xx.S:61: Error: cannot use register index
> >with PC-relative addressing -- `str r1,emif_addr_virt'
> 
> Hmm, interestingly enough I can not reproduce this build error. Can you
> share your config and compiler version?

While I have a stripped down .config for just boneblack I can reproduce this with:
make multi_v7_defconfig
./scripts/config -e THUMB2_KERNEL

Which may not make much sense (exposes other thumb2 related issues), but with that
.config sleep33xx.S fails to assemble with binutils 2.24 and succeeds with 2.23.2.

Which may be binutils' fault, I can't judge since I don't speak ARM asm ;)

Hope that helps,
Andre

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 01/11] ARM: omap: edma: add suspend suspend/resume hooks
  2014-07-11  2:55   ` Dave Gerlach
@ 2014-07-14 11:05     ` Tony Lindgren
  -1 siblings, 0 replies; 106+ messages in thread
From: Tony Lindgren @ 2014-07-14 11:05 UTC (permalink / raw)
  To: Dave Gerlach
  Cc: linux-arm-kernel, linux-omap, Paul Walmsley, Kevin Hilman,
	Tero Kristo, Nishanth Menon, Russ Dill, Santosh Shilimkar,
	Daniel Mack, Suman Anna, Benoit Cousson, Daniel Mack

* Dave Gerlach <d-gerlach@ti.com> [140710 19:58]:
> From: Daniel Mack <zonque@gmail.com>
> 
> This patch makes the edma driver resume correctly after suspend. Tested
> on an AM33xx platform with cyclic audio streams and omap_hsmmc.
> 
> All information can be reconstructed by already known runtime
> information.
> 
> As we now use some functions that were previously only used from __init
> context, annotations had to be dropped.

This is something Sekhar needs to look at, did you maybe forget
to Cc him?

Regards,

Tony

^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 01/11] ARM: omap: edma: add suspend suspend/resume hooks
@ 2014-07-14 11:05     ` Tony Lindgren
  0 siblings, 0 replies; 106+ messages in thread
From: Tony Lindgren @ 2014-07-14 11:05 UTC (permalink / raw)
  To: linux-arm-kernel

* Dave Gerlach <d-gerlach@ti.com> [140710 19:58]:
> From: Daniel Mack <zonque@gmail.com>
> 
> This patch makes the edma driver resume correctly after suspend. Tested
> on an AM33xx platform with cyclic audio streams and omap_hsmmc.
> 
> All information can be reconstructed by already known runtime
> information.
> 
> As we now use some functions that were previously only used from __init
> context, annotations had to be dropped.

This is something Sekhar needs to look at, did you maybe forget
to Cc him?

Regards,

Tony

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 02/11] memory: emif: Move EMIF register defines to include/linux/
  2014-07-11  2:55   ` Dave Gerlach
@ 2014-07-14 11:12     ` Tony Lindgren
  -1 siblings, 0 replies; 106+ messages in thread
From: Tony Lindgren @ 2014-07-14 11:12 UTC (permalink / raw)
  To: Dave Gerlach
  Cc: linux-arm-kernel, linux-omap, Paul Walmsley, Kevin Hilman,
	Tero Kristo, Nishanth Menon, Russ Dill, Santosh Shilimkar,
	Daniel Mack, Suman Anna, Benoit Cousson

* Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
> 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 register defines in the emif header file to include/linux so that
> both the EMIF driver and the AM33XX PM code can benefit.
> 
> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
> Reviewed-by: Russ Dill <russ.dill@ti.com>
> Acked-by: Santosh Shililmar <santosh.shilimkar@ti.com>
> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> ---
> v3->v4:
> patch unchanged from original:
> 	http://www.spinics.net/lists/linux-omap/msg95314.html
> 
>  drivers/memory/emif.h   | 543 +---------------------------------------------
>  include/linux/ti_emif.h | 558 ++++++++++++++++++++++++++++++++++++++++++++++++

So far we've seen that exposing hardware registers like this
will lead into various drivers misusing them. I think a better
solution is to implement few targeted functions that allow
sharing code between the platform idle code and memory driver.

Maybe you can have the shared functions in in something like
drivers/memory/ti-emif.c that's always built in? The idle
code won't need any of that early on.

Regards,

Tony

^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 02/11] memory: emif: Move EMIF register defines to include/linux/
@ 2014-07-14 11:12     ` Tony Lindgren
  0 siblings, 0 replies; 106+ messages in thread
From: Tony Lindgren @ 2014-07-14 11:12 UTC (permalink / raw)
  To: linux-arm-kernel

* Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
> 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 register defines in the emif header file to include/linux so that
> both the EMIF driver and the AM33XX PM code can benefit.
> 
> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
> Reviewed-by: Russ Dill <russ.dill@ti.com>
> Acked-by: Santosh Shililmar <santosh.shilimkar@ti.com>
> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> ---
> v3->v4:
> patch unchanged from original:
> 	http://www.spinics.net/lists/linux-omap/msg95314.html
> 
>  drivers/memory/emif.h   | 543 +---------------------------------------------
>  include/linux/ti_emif.h | 558 ++++++++++++++++++++++++++++++++++++++++++++++++

So far we've seen that exposing hardware registers like this
will lead into various drivers misusing them. I think a better
solution is to implement few targeted functions that allow
sharing code between the platform idle code and memory driver.

Maybe you can have the shared functions in in something like
drivers/memory/ti-emif.c that's always built in? The idle
code won't need any of that early on.

Regards,

Tony

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 03/11] ARM: OMAP2+: timer: Add suspend-resume callbacks for clkevent device
  2014-07-11  2:55   ` Dave Gerlach
@ 2014-07-14 11:15     ` Tony Lindgren
  -1 siblings, 0 replies; 106+ messages in thread
From: Tony Lindgren @ 2014-07-14 11:15 UTC (permalink / raw)
  To: Dave Gerlach
  Cc: linux-arm-kernel, linux-omap, Paul Walmsley, Kevin Hilman,
	Tero Kristo, Nishanth Menon, Russ Dill, Santosh Shilimkar,
	Daniel Mack, Suman Anna, Benoit Cousson, Vaibhav Bedia

* Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
> From: Vaibhav Bedia <vaibhav.bedia@ti.com>
> 
> OMAP timer code registers two timers - one as clocksource
> and one as clockevent. Since AM33XX has only one usable timer
> in the WKUP domain 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.
> 
> Signed-off-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
> ---
> v3->v4:
> 	Only use for am33xx soc now.
> 
>  arch/arm/mach-omap2/timer.c | 28 ++++++++++++++++++++++++++++
>  1 file changed, 28 insertions(+)
> 
> diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
> index 43d03fb..6fc1748 100644
> --- a/arch/arm/mach-omap2/timer.c
> +++ b/arch/arm/mach-omap2/timer.c
> @@ -128,6 +128,29 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
>  	}
>  }
>  
> +static void omap_clkevt_suspend(struct clock_event_device *unused)
> +{
> +	struct omap_hwmod *oh;
> +
> +	oh = omap_hwmod_lookup(clockevent_gpt.name);
> +	if (!oh)
> +		return;
> +
> +	omap_hwmod_idle(oh);
> +}
> +
> +static void omap_clkevt_resume(struct clock_event_device *unused)
> +{
> +	struct omap_hwmod *oh;
> +
> +	oh = omap_hwmod_lookup(clockevent_gpt.name);
> +	if (!oh)
> +		return;
> +
> +	omap_hwmod_enable(oh);
> +	__omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);
> +}
> +

This is going to make moving the timer code into drivers one step
tougher to do. And you don't need to look up the hwmod entry every
time, just initialize it during the init.

> +	if (soc_is_am33xx()) {
> +		clockevent_gpt.suspend = omap_clkevt_suspend;
> +		clockevent_gpt.resume = omap_clkevt_resume;
> +	}
> +

Maybe try to set up things so we initialize the SoC specific
timer suspend and resume functions in mach-omap2/timer.c
in a way where eventually the device driver can easily use
them?

Regards,

Tony

^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 03/11] ARM: OMAP2+: timer: Add suspend-resume callbacks for clkevent device
@ 2014-07-14 11:15     ` Tony Lindgren
  0 siblings, 0 replies; 106+ messages in thread
From: Tony Lindgren @ 2014-07-14 11:15 UTC (permalink / raw)
  To: linux-arm-kernel

* Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
> From: Vaibhav Bedia <vaibhav.bedia@ti.com>
> 
> OMAP timer code registers two timers - one as clocksource
> and one as clockevent. Since AM33XX has only one usable timer
> in the WKUP domain 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.
> 
> Signed-off-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
> ---
> v3->v4:
> 	Only use for am33xx soc now.
> 
>  arch/arm/mach-omap2/timer.c | 28 ++++++++++++++++++++++++++++
>  1 file changed, 28 insertions(+)
> 
> diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
> index 43d03fb..6fc1748 100644
> --- a/arch/arm/mach-omap2/timer.c
> +++ b/arch/arm/mach-omap2/timer.c
> @@ -128,6 +128,29 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
>  	}
>  }
>  
> +static void omap_clkevt_suspend(struct clock_event_device *unused)
> +{
> +	struct omap_hwmod *oh;
> +
> +	oh = omap_hwmod_lookup(clockevent_gpt.name);
> +	if (!oh)
> +		return;
> +
> +	omap_hwmod_idle(oh);
> +}
> +
> +static void omap_clkevt_resume(struct clock_event_device *unused)
> +{
> +	struct omap_hwmod *oh;
> +
> +	oh = omap_hwmod_lookup(clockevent_gpt.name);
> +	if (!oh)
> +		return;
> +
> +	omap_hwmod_enable(oh);
> +	__omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);
> +}
> +

This is going to make moving the timer code into drivers one step
tougher to do. And you don't need to look up the hwmod entry every
time, just initialize it during the init.

> +	if (soc_is_am33xx()) {
> +		clockevent_gpt.suspend = omap_clkevt_suspend;
> +		clockevent_gpt.resume = omap_clkevt_resume;
> +	}
> +

Maybe try to set up things so we initialize the SoC specific
timer suspend and resume functions in mach-omap2/timer.c
in a way where eventually the device driver can easily use
them?

Regards,

Tony

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 11/11] ARM: OMAP2+: AM33XX: Hookup AM33XX PM code into OMAP builds
  2014-07-11  2:55   ` Dave Gerlach
@ 2014-07-14 11:21     ` Tony Lindgren
  -1 siblings, 0 replies; 106+ messages in thread
From: Tony Lindgren @ 2014-07-14 11:21 UTC (permalink / raw)
  To: Dave Gerlach
  Cc: linux-arm-kernel, linux-omap, Paul Walmsley, Kevin Hilman,
	Tero Kristo, Nishanth Menon, Russ Dill, Santosh Shilimkar,
	Daniel Mack, Suman Anna, Benoit Cousson

* Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
> With all the requisite changes in place we can now enable the basic
> PM support for AM33xx. This patch updates the various OMAP files
> to enable suspend-resume on AM33xx.
> 
> Because the suspend resume functionality is different on AM33xx
> than other OMAP platforms due to the need for M3 firmware and an
> IPC channel to be in place, separate PM ops are used instead of
> omap_pm_ops. These are now set using omap2_common_suspend_init
> so the AM33xx can make a decision at runtime to enable suspend based
> on the availabilty of aforementioned requirements.
> 
> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
> ---
> v3->v4:
> 	Updated for rproc usage now.
> 
>  arch/arm/mach-omap2/Kconfig  |  1 +
>  arch/arm/mach-omap2/Makefile |  2 ++
>  arch/arm/mach-omap2/common.h |  9 +++++++++
>  arch/arm/mach-omap2/io.c     |  1 +
>  arch/arm/mach-omap2/pm.h     |  5 +++++
>  arch/arm/mach-omap2/sram.c   | 10 +++++++++-
>  arch/arm/mach-omap2/sram.h   |  2 ++
>  7 files changed, 29 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
> index 1c1ed73..f8a56e5 100644
> --- a/arch/arm/mach-omap2/Kconfig
> +++ b/arch/arm/mach-omap2/Kconfig
> @@ -59,6 +59,7 @@ config SOC_AM33XX
>  	select ARCH_OMAP2PLUS
>  	select ARCH_HAS_OPP
>  	select ARM_CPU_SUSPEND if PM
> +	select WKUP_M3_RPROC if PM

You should not do a select in Kconfig for a driver,
that will lead into various randconfig build errors
at some point. It's probably best to make the PM code
depend on the WKUP_M3_RPROC driver instead.

Regards,

Tony 

^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 11/11] ARM: OMAP2+: AM33XX: Hookup AM33XX PM code into OMAP builds
@ 2014-07-14 11:21     ` Tony Lindgren
  0 siblings, 0 replies; 106+ messages in thread
From: Tony Lindgren @ 2014-07-14 11:21 UTC (permalink / raw)
  To: linux-arm-kernel

* Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
> With all the requisite changes in place we can now enable the basic
> PM support for AM33xx. This patch updates the various OMAP files
> to enable suspend-resume on AM33xx.
> 
> Because the suspend resume functionality is different on AM33xx
> than other OMAP platforms due to the need for M3 firmware and an
> IPC channel to be in place, separate PM ops are used instead of
> omap_pm_ops. These are now set using omap2_common_suspend_init
> so the AM33xx can make a decision at runtime to enable suspend based
> on the availabilty of aforementioned requirements.
> 
> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
> ---
> v3->v4:
> 	Updated for rproc usage now.
> 
>  arch/arm/mach-omap2/Kconfig  |  1 +
>  arch/arm/mach-omap2/Makefile |  2 ++
>  arch/arm/mach-omap2/common.h |  9 +++++++++
>  arch/arm/mach-omap2/io.c     |  1 +
>  arch/arm/mach-omap2/pm.h     |  5 +++++
>  arch/arm/mach-omap2/sram.c   | 10 +++++++++-
>  arch/arm/mach-omap2/sram.h   |  2 ++
>  7 files changed, 29 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
> index 1c1ed73..f8a56e5 100644
> --- a/arch/arm/mach-omap2/Kconfig
> +++ b/arch/arm/mach-omap2/Kconfig
> @@ -59,6 +59,7 @@ config SOC_AM33XX
>  	select ARCH_OMAP2PLUS
>  	select ARCH_HAS_OPP
>  	select ARM_CPU_SUSPEND if PM
> +	select WKUP_M3_RPROC if PM

You should not do a select in Kconfig for a driver,
that will lead into various randconfig build errors
at some point. It's probably best to make the PM code
depend on the WKUP_M3_RPROC driver instead.

Regards,

Tony 

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 00/11] ARM: OMAP2+: AM33XX: Add suspend-resume support
  2014-07-11  2:55 ` Dave Gerlach
@ 2014-07-14 11:24   ` Tony Lindgren
  -1 siblings, 0 replies; 106+ messages in thread
From: Tony Lindgren @ 2014-07-14 11:24 UTC (permalink / raw)
  To: Dave Gerlach
  Cc: linux-arm-kernel, linux-omap, Paul Walmsley, Kevin Hilman,
	Tero Kristo, Nishanth Menon, Russ Dill, Santosh Shilimkar,
	Daniel Mack, Suman Anna, Benoit Cousson

* Dave Gerlach <d-gerlach@ti.com> [140710 19:58]:
> Hello,
> 
> This series adds suspend/resume support for am335x. Version 3 of this
> series can be found at [1]. I apologize for the large delay between this
> and the previous revision. This code has been heavily refined
> since the last version based on the various comments received for v3. The
> major change from previous version is moving all wkup_m3 code into a
> remoteproc based driver. The new driver handles all IPC and fw loading
> and exposes a small API to be used by PM code to achieve low power states.

Great, this is nice for all the am335x users. I made few comments
on the series that should be quite simple to fix up.

Then please split up your next version of this series into the
following independet patches:

1. Changes to the EDMA driver for suspend and resume support

2. Patches to add the rproc driver

3. SoC related changes to enable PM support

Regards,

Tony

^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 00/11] ARM: OMAP2+: AM33XX: Add suspend-resume support
@ 2014-07-14 11:24   ` Tony Lindgren
  0 siblings, 0 replies; 106+ messages in thread
From: Tony Lindgren @ 2014-07-14 11:24 UTC (permalink / raw)
  To: linux-arm-kernel

* Dave Gerlach <d-gerlach@ti.com> [140710 19:58]:
> Hello,
> 
> This series adds suspend/resume support for am335x. Version 3 of this
> series can be found at [1]. I apologize for the large delay between this
> and the previous revision. This code has been heavily refined
> since the last version based on the various comments received for v3. The
> major change from previous version is moving all wkup_m3 code into a
> remoteproc based driver. The new driver handles all IPC and fw loading
> and exposes a small API to be used by PM code to achieve low power states.

Great, this is nice for all the am335x users. I made few comments
on the series that should be quite simple to fix up.

Then please split up your next version of this series into the
following independet patches:

1. Changes to the EDMA driver for suspend and resume support

2. Patches to add the rproc driver

3. SoC related changes to enable PM support

Regards,

Tony

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 03/11] ARM: OMAP2+: timer: Add suspend-resume callbacks for clkevent device
  2014-07-14 11:15     ` Tony Lindgren
@ 2014-07-14 14:37       ` Santosh Shilimkar
  -1 siblings, 0 replies; 106+ messages in thread
From: Santosh Shilimkar @ 2014-07-14 14:37 UTC (permalink / raw)
  To: Tony Lindgren, Dave Gerlach
  Cc: Nishanth Menon, Paul Walmsley, Kevin Hilman, Vaibhav Bedia,
	Tero Kristo, Russ Dill, Daniel Mack, Benoit Cousson, linux-omap,
	linux-arm-kernel

On Monday 14 July 2014 07:15 AM, Tony Lindgren wrote:
> * Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
>> From: Vaibhav Bedia <vaibhav.bedia@ti.com>
>>
>> OMAP timer code registers two timers - one as clocksource
>> and one as clockevent. Since AM33XX has only one usable timer
>> in the WKUP domain 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.
>>
>> Signed-off-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>> ---
>> v3->v4:
>> 	Only use for am33xx soc now.
>>
>>  arch/arm/mach-omap2/timer.c | 28 ++++++++++++++++++++++++++++
>>  1 file changed, 28 insertions(+)
>>
>> diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
>> index 43d03fb..6fc1748 100644
>> --- a/arch/arm/mach-omap2/timer.c
>> +++ b/arch/arm/mach-omap2/timer.c
>> @@ -128,6 +128,29 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
>>  	}
>>  }
>>  
>> +static void omap_clkevt_suspend(struct clock_event_device *unused)
>> +{
>> +	struct omap_hwmod *oh;
>> +
>> +	oh = omap_hwmod_lookup(clockevent_gpt.name);
>> +	if (!oh)
>> +		return;
>> +
>> +	omap_hwmod_idle(oh);
>> +}
>> +
>> +static void omap_clkevt_resume(struct clock_event_device *unused)
>> +{
>> +	struct omap_hwmod *oh;
>> +
>> +	oh = omap_hwmod_lookup(clockevent_gpt.name);
>> +	if (!oh)
>> +		return;
>> +
>> +	omap_hwmod_enable(oh);
>> +	__omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);
>> +}
>> +
> 
> This is going to make moving the timer code into drivers one step
> tougher to do. And you don't need to look up the hwmod entry every
> time, just initialize it during the init.
> 
>> +	if (soc_is_am33xx()) {
>> +		clockevent_gpt.suspend = omap_clkevt_suspend;
>> +		clockevent_gpt.resume = omap_clkevt_resume;
>> +	}
>> +
> 
> Maybe try to set up things so we initialize the SoC specific
> timer suspend and resume functions in mach-omap2/timer.c
> in a way where eventually the device driver can easily use
> them?
> 
+1. I had similar comments on the previous version too.

Regards,
Santosh

^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 03/11] ARM: OMAP2+: timer: Add suspend-resume callbacks for clkevent device
@ 2014-07-14 14:37       ` Santosh Shilimkar
  0 siblings, 0 replies; 106+ messages in thread
From: Santosh Shilimkar @ 2014-07-14 14:37 UTC (permalink / raw)
  To: linux-arm-kernel

On Monday 14 July 2014 07:15 AM, Tony Lindgren wrote:
> * Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
>> From: Vaibhav Bedia <vaibhav.bedia@ti.com>
>>
>> OMAP timer code registers two timers - one as clocksource
>> and one as clockevent. Since AM33XX has only one usable timer
>> in the WKUP domain 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.
>>
>> Signed-off-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>> ---
>> v3->v4:
>> 	Only use for am33xx soc now.
>>
>>  arch/arm/mach-omap2/timer.c | 28 ++++++++++++++++++++++++++++
>>  1 file changed, 28 insertions(+)
>>
>> diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
>> index 43d03fb..6fc1748 100644
>> --- a/arch/arm/mach-omap2/timer.c
>> +++ b/arch/arm/mach-omap2/timer.c
>> @@ -128,6 +128,29 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
>>  	}
>>  }
>>  
>> +static void omap_clkevt_suspend(struct clock_event_device *unused)
>> +{
>> +	struct omap_hwmod *oh;
>> +
>> +	oh = omap_hwmod_lookup(clockevent_gpt.name);
>> +	if (!oh)
>> +		return;
>> +
>> +	omap_hwmod_idle(oh);
>> +}
>> +
>> +static void omap_clkevt_resume(struct clock_event_device *unused)
>> +{
>> +	struct omap_hwmod *oh;
>> +
>> +	oh = omap_hwmod_lookup(clockevent_gpt.name);
>> +	if (!oh)
>> +		return;
>> +
>> +	omap_hwmod_enable(oh);
>> +	__omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);
>> +}
>> +
> 
> This is going to make moving the timer code into drivers one step
> tougher to do. And you don't need to look up the hwmod entry every
> time, just initialize it during the init.
> 
>> +	if (soc_is_am33xx()) {
>> +		clockevent_gpt.suspend = omap_clkevt_suspend;
>> +		clockevent_gpt.resume = omap_clkevt_resume;
>> +	}
>> +
> 
> Maybe try to set up things so we initialize the SoC specific
> timer suspend and resume functions in mach-omap2/timer.c
> in a way where eventually the device driver can easily use
> them?
> 
+1. I had similar comments on the previous version too.

Regards,
Santosh

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 05/11] Documentation: dt: add ti,am3353_wkup_m3 bindings
  2014-07-11  2:55   ` Dave Gerlach
@ 2014-07-14 14:41     ` Santosh Shilimkar
  -1 siblings, 0 replies; 106+ messages in thread
From: Santosh Shilimkar @ 2014-07-14 14:41 UTC (permalink / raw)
  To: Dave Gerlach, linux-arm-kernel, linux-omap
  Cc: Paul Walmsley, Kevin Hilman, Tony Lindgren, Tero Kristo,
	Nishanth Menon, Russ Dill, Daniel Mack, Suman Anna,
	Benoit Cousson, Ohad Ben-Cohen

On Thursday 10 July 2014 10:55 PM, Dave Gerlach wrote:
> Add the device tree bindings document for am3353 wkup_m3.
> 
> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
> CC: Ohad Ben-Cohen <ohad@wizery.com>
> CC: Benoit Cousson <bcousson@baylibre.com>
> ---
Looks like you missed to copy device tree list and maintainers.
As Tony suggested, split up the series and send the wkup_m3 related
patches separately along with bindings and mark the DT folks on email.

>  .../bindings/remoteproc/wkup_m3_rproc.txt          | 46 ++++++++++++++++++++++
>  1 file changed, 46 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
> 
> diff --git a/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt b/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
> new file mode 100644
> index 0000000..e9dd909
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
> @@ -0,0 +1,46 @@
> +Wakeup M3 Remote Proc Driver
> +=====================
> +
> +TI AMx3 Family devices use a Cortex M3 co-processor to help with various
> +low power tasks that cannot be controlled from the MPU. The CM3 requires
> +a firmware binary to accomplish this and communicates with the MPU through
> +IPC registers present in the SoCs control module. The wkup_m3 remoteproc
> +driver handles the loading of the firmware and exposes an API to
> +communicate with the wkup_m3 through the use of the IPC registers and a
> +mailbox.
> +
> +Wkup M3 Device Node:
> +====================
> +A wkup_m3 device node is used to represent a wakeup M3 IP instance within
> +a SoC. The sub-mailboxes are represented as child node of this parent node.
> +
> +Required properties:
> +--------------------
> +- compatible:		Should be "ti,am3353-wkup-m3" for AM33xx SoCs
> +- reg:			Contains the wkup_m3 register address ranges for
> +			umem, dmem, and ipc-regs.
> +- reg-names:		Names for reg addresses given above
> +- interrupts:		Contains the interrupt information for the wkup_m3
> +			interrupt that signals the MPU.
> +- ti,hwmods:		Name of the hwmod associated with the mailbox
> +- ti,no-reset-on-init:	Reset is handled after fw has been loaded, not at
> +			init of hwmod.
> +- mbox-names:		Name of the mbox channel for the IPC framework
> +- mbox:			Phandle used by IPC framework to get correct mbox
> +			channel for communication.
> +
> +Example:
> +--------
> +/* AM33xx */
> +wkup_m3: wkup_m3@44d00000 {
> +	compatible = "ti,am3353-wkup-m3";
> +	reg = <0x44d00000 0x4000
> +	       0x44d80000 0x2000
> +	       0x44e11324 0x0024>;
> +	reg-names = "m3_umem", "m3_dmem", "ipc_regs";
> +	interrupts = <78>;
> +	ti,hwmods = "wkup_m3";
> +	ti,no-reset-on-init;
> +	mbox-names = "wkup_m3";
> +	mbox = <&mailbox &mbox_wkupm3>;
> +};
> 


^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 05/11] Documentation: dt: add ti, am3353_wkup_m3 bindings
@ 2014-07-14 14:41     ` Santosh Shilimkar
  0 siblings, 0 replies; 106+ messages in thread
From: Santosh Shilimkar @ 2014-07-14 14:41 UTC (permalink / raw)
  To: linux-arm-kernel

On Thursday 10 July 2014 10:55 PM, Dave Gerlach wrote:
> Add the device tree bindings document for am3353 wkup_m3.
> 
> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
> CC: Ohad Ben-Cohen <ohad@wizery.com>
> CC: Benoit Cousson <bcousson@baylibre.com>
> ---
Looks like you missed to copy device tree list and maintainers.
As Tony suggested, split up the series and send the wkup_m3 related
patches separately along with bindings and mark the DT folks on email.

>  .../bindings/remoteproc/wkup_m3_rproc.txt          | 46 ++++++++++++++++++++++
>  1 file changed, 46 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
> 
> diff --git a/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt b/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
> new file mode 100644
> index 0000000..e9dd909
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
> @@ -0,0 +1,46 @@
> +Wakeup M3 Remote Proc Driver
> +=====================
> +
> +TI AMx3 Family devices use a Cortex M3 co-processor to help with various
> +low power tasks that cannot be controlled from the MPU. The CM3 requires
> +a firmware binary to accomplish this and communicates with the MPU through
> +IPC registers present in the SoCs control module. The wkup_m3 remoteproc
> +driver handles the loading of the firmware and exposes an API to
> +communicate with the wkup_m3 through the use of the IPC registers and a
> +mailbox.
> +
> +Wkup M3 Device Node:
> +====================
> +A wkup_m3 device node is used to represent a wakeup M3 IP instance within
> +a SoC. The sub-mailboxes are represented as child node of this parent node.
> +
> +Required properties:
> +--------------------
> +- compatible:		Should be "ti,am3353-wkup-m3" for AM33xx SoCs
> +- reg:			Contains the wkup_m3 register address ranges for
> +			umem, dmem, and ipc-regs.
> +- reg-names:		Names for reg addresses given above
> +- interrupts:		Contains the interrupt information for the wkup_m3
> +			interrupt that signals the MPU.
> +- ti,hwmods:		Name of the hwmod associated with the mailbox
> +- ti,no-reset-on-init:	Reset is handled after fw has been loaded, not at
> +			init of hwmod.
> +- mbox-names:		Name of the mbox channel for the IPC framework
> +- mbox:			Phandle used by IPC framework to get correct mbox
> +			channel for communication.
> +
> +Example:
> +--------
> +/* AM33xx */
> +wkup_m3: wkup_m3 at 44d00000 {
> +	compatible = "ti,am3353-wkup-m3";
> +	reg = <0x44d00000 0x4000
> +	       0x44d80000 0x2000
> +	       0x44e11324 0x0024>;
> +	reg-names = "m3_umem", "m3_dmem", "ipc_regs";
> +	interrupts = <78>;
> +	ti,hwmods = "wkup_m3";
> +	ti,no-reset-on-init;
> +	mbox-names = "wkup_m3";
> +	mbox = <&mailbox &mbox_wkupm3>;
> +};
> 

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 06/11] remoteproc: wkup_m3: Add wkup_m3 remote proc driver
  2014-07-11  2:55   ` Dave Gerlach
@ 2014-07-14 14:54     ` Santosh Shilimkar
  -1 siblings, 0 replies; 106+ messages in thread
From: Santosh Shilimkar @ 2014-07-14 14:54 UTC (permalink / raw)
  To: Dave Gerlach, linux-arm-kernel, linux-omap
  Cc: Paul Walmsley, Kevin Hilman, Tony Lindgren, Tero Kristo,
	Nishanth Menon, Russ Dill, Daniel Mack, Suman Anna,
	Benoit Cousson, Ohad Ben-Cohen

On Thursday 10 July 2014 10:55 PM, Dave Gerlach wrote:
> Add a remoteproc driver to load the firmware for and boot the wkup_m3
> present on am33xx. The wkup_m3 is an integrated Cortex M3 that allows
> the SoC to enter the lowest possible power state by taking control from
> the MPU after it has gone into its own low power state and shutting off
> any additional peripherals.
> 
> Communication between the MPU and CM3 is handled by several IPC
> registers in the control module and a mailbox. An API is exposed for
> programming the aforementioned IPC registers and notifying the wkup_m3
> of pending data using the mailbox. The wkup_m3 has the ability to
> trigger an interrupt back to the MPU to allow for bidirectional
> communication through these registers.
> 
> Two callbacks are provided. rproc_ready allows code to hook into the
> driver to see when firmware has been loaded and execute other code and
> txev_handler allows external code to run when the wkup_m3 triggers an
> interrupt back to the m3.
> 
> The driver expects a resource table to be present in the wkup_m3 to
> define the required memory resources needed by wkup_m3, at least the
> data memory so that the firmware can be copied for the proper place for
> execution.
> 
> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
> CC: Ohad Ben-Cohen <ohad@wizery.com>
> ---
>  drivers/remoteproc/Kconfig         |  15 ++
>  drivers/remoteproc/Makefile        |   1 +
>  drivers/remoteproc/wkup_m3_rproc.c | 512 +++++++++++++++++++++++++++++++++++++
>  include/linux/wkup_m3.h            |  71 +++++
>  4 files changed, 599 insertions(+)
>  create mode 100644 drivers/remoteproc/wkup_m3_rproc.c
>  create mode 100644 include/linux/wkup_m3.h
> 
> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
> index 5e343ba..4b00c21 100644
> --- a/drivers/remoteproc/Kconfig
> +++ b/drivers/remoteproc/Kconfig
> @@ -41,6 +41,21 @@ config STE_MODEM_RPROC
>  	  This can be either built-in or a loadable module.
>  	  If unsure say N.
>  
> +config WKUP_M3_RPROC
> +	bool "AM33xx wkup-m3 remoteproc support"
> +        depends on SOC_AM33XX
> +        select REMOTEPROC
> +	select MAILBOX
> +	select OMAP2PLUS_MBOX
Please fix the indentation.

> +	default n
Default is always 'n' so drop above.
> +	help
> +	  Say y here to support AM33xx wkup-m3.
> +
> +	  Required for Suspend-to-ram and CPUIdle on AM33xx. Allows for
> +	  loading of firmware and communication with CM3 PM coproccesor
> +	  that is present on AM33xx family of SoCs
> +	  If unsure say N.
> +
>  config DA8XX_REMOTEPROC
>  	tristate "DA8xx/OMAP-L13x remoteproc support"
>  	depends on ARCH_DAVINCI_DA8XX
> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
> index ac2ff75..81b04d1 100644
> --- a/drivers/remoteproc/Makefile
> +++ b/drivers/remoteproc/Makefile
> @@ -9,4 +9,5 @@ remoteproc-y				+= remoteproc_virtio.o
>  remoteproc-y				+= remoteproc_elf_loader.o
>  obj-$(CONFIG_OMAP_REMOTEPROC)		+= omap_remoteproc.o
>  obj-$(CONFIG_STE_MODEM_RPROC)	 	+= ste_modem_rproc.o
> +obj-$(CONFIG_WKUP_M3_RPROC)		+= wkup_m3_rproc.o
>  obj-$(CONFIG_DA8XX_REMOTEPROC)		+= da8xx_remoteproc.o
> diff --git a/drivers/remoteproc/wkup_m3_rproc.c b/drivers/remoteproc/wkup_m3_rproc.c
> new file mode 100644
> index 0000000..58aeaf2
> --- /dev/null
> +++ b/drivers/remoteproc/wkup_m3_rproc.c
> @@ -0,0 +1,512 @@
> +/*
> + * AMx3 Wkup M3 Remote Processor driver
> + *
> + * Copyright (C) 2014 Texas Instruments, Inc.
> + *
> + * Dave Gerlach <d-gerlach@ti.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/err.h>
> +#include <linux/platform_device.h>
> +#include <linux/irq.h>
> +#include <linux/interrupt.h>
> +#include <linux/elf.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/firmware.h>
> +#include <linux/remoteproc.h>
> +#include <linux/omap-mailbox.h>
> +#include <linux/mailbox_client.h>
> +#include <linux/wkup_m3.h>
> +#include <linux/kthread.h>
> +#include "remoteproc_internal.h"
> +
> +#include <linux/platform_data/wkup_m3.h>
> +
> +#define WKUP_M3_WAKE_SRC_MASK		0xFF
> +
> +#define WKUP_M3_STATUS_RESP_SHIFT	16
> +#define WKUP_M3_STATUS_RESP_MASK	(0xffff << 16)
> +
> +#define WKUP_M3_FW_VERSION_SHIFT	0
> +#define WKUP_M3_FW_VERSION_MASK		0xffff
> +
> +/* AM33XX M3_TXEV_EOI register */
> +#define AM33XX_CONTROL_M3_TXEV_EOI	0x00
> +
> +#define AM33XX_M3_TXEV_ACK		(0x1 << 0)
> +#define AM33XX_M3_TXEV_ENABLE		(0x0 << 0)
> +
> +/* AM33XX IPC message registers */
> +#define AM33XX_CONTROL_IPC_MSG_REG0	0x04
> +#define AM33XX_CONTROL_IPC_MSG_REG1	0x08
> +#define AM33XX_CONTROL_IPC_MSG_REG2	0x0c
> +#define AM33XX_CONTROL_IPC_MSG_REG3	0x10
> +#define AM33XX_CONTROL_IPC_MSG_REG4	0x14
> +#define AM33XX_CONTROL_IPC_MSG_REG5	0x18
> +#define AM33XX_CONTROL_IPC_MSG_REG6	0x1c
> +#define AM33XX_CONTROL_IPC_MSG_REG7	0x20
> +
Is this driver going to be AM33xx specific ?

> +struct wkup_m3_rproc {
> +	struct rproc *rproc;
> +
> +	void * __iomem dev_table_va;
> +	void * __iomem ipc_mem_base;
> +	struct platform_device *pdev;
> +
> +	struct mbox_client mbox_client;
> +	struct mbox_chan *mbox;
> +	struct wkup_m3_ops *ops;
> +
> +	bool is_active;
> +};
> +
> +static struct wkup_m3_rproc *m3_rproc_static;
> +
> +static struct wkup_m3_wakeup_src wakeups[] = {
> +	{.irq_nr = 35,	.src = "USB0_PHY"},
> +	{.irq_nr = 36,	.src = "USB1_PHY"},
> +	{.irq_nr = 40,	.src = "I2C0"},
> +	{.irq_nr = 41,	.src = "RTC Timer"},
> +	{.irq_nr = 42,	.src = "RTC Alarm"},
> +	{.irq_nr = 43,	.src = "Timer0"},
> +	{.irq_nr = 44,	.src = "Timer1"},
> +	{.irq_nr = 45,	.src = "UART"},
> +	{.irq_nr = 46,	.src = "GPIO0"},
> +	{.irq_nr = 48,	.src = "MPU_WAKE"},
> +	{.irq_nr = 49,	.src = "WDT0"},
> +	{.irq_nr = 50,	.src = "WDT1"},
> +	{.irq_nr = 51,	.src = "ADC_TSC"},
> +	{.irq_nr = 0,	.src = "Unknown"},
> +};
> +
const ?

> +static void am33xx_txev_eoi(struct wkup_m3_rproc *m3_rproc)
> +{
> +	writel(AM33XX_M3_TXEV_ACK,
> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
> +}
> +
> +static void am33xx_txev_enable(struct wkup_m3_rproc *m3_rproc)
> +{
> +	writel(AM33XX_M3_TXEV_ENABLE,
> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
> +}
> +
> +static void wkup_m3_ctrl_ipc_write(struct wkup_m3_rproc *m3_rproc,
> +				   struct wkup_m3_ipc_regs *ipc_regs)
> +{
> +	writel(ipc_regs->reg0,
> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG0);
> +	writel(ipc_regs->reg1,
> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG1);
> +	writel(ipc_regs->reg2,
> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG2);
> +	writel(ipc_regs->reg3,
> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG3);
> +	writel(ipc_regs->reg4,
> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG4);
> +	writel(ipc_regs->reg5,
> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG5);
> +	writel(ipc_regs->reg6,
> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG6);
> +	writel(ipc_regs->reg7,
> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG7);
> +}
> +
> +static void wkup_m3_ctrl_ipc_read(struct wkup_m3_rproc *m3_rproc,
> +				  struct wkup_m3_ipc_regs *ipc_regs)
> +{
> +	ipc_regs->reg0 = readl(m3_rproc->ipc_mem_base
> +			       + AM33XX_CONTROL_IPC_MSG_REG0);
> +	ipc_regs->reg1 = readl(m3_rproc->ipc_mem_base
> +			       + AM33XX_CONTROL_IPC_MSG_REG1);
> +	ipc_regs->reg2 = readl(m3_rproc->ipc_mem_base
> +			       + AM33XX_CONTROL_IPC_MSG_REG2);
> +	ipc_regs->reg3 = readl(m3_rproc->ipc_mem_base
> +			       + AM33XX_CONTROL_IPC_MSG_REG3);
> +	ipc_regs->reg4 = readl(m3_rproc->ipc_mem_base
> +			       + AM33XX_CONTROL_IPC_MSG_REG4);
> +	ipc_regs->reg5 = readl(m3_rproc->ipc_mem_base
> +			       + AM33XX_CONTROL_IPC_MSG_REG5);
> +	ipc_regs->reg6 = readl(m3_rproc->ipc_mem_base
> +			       + AM33XX_CONTROL_IPC_MSG_REG6);
> +	ipc_regs->reg7 = readl(m3_rproc->ipc_mem_base
> +			       + AM33XX_CONTROL_IPC_MSG_REG7);
> +}
> +
> +static irqreturn_t wkup_m3_txev_handler(int irq, void *unused)
> +{
> +	am33xx_txev_eoi(m3_rproc_static);
> +
> +	if (m3_rproc_static->ops && m3_rproc_static->ops->txev_handler)
> +		m3_rproc_static->ops->txev_handler();
> +
> +	am33xx_txev_enable(m3_rproc_static);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/**
> + * wkup_m3_fw_version_clear - Clear FW version from ipc regs
> + *
> + * Invalidate M3 firmware version before hardreset.
> + * Write invalid version in lower 4 nibbles of parameter
> + * register (ipc_regs + 0x8).
> + */
> +
> +static void wkup_m3_fw_version_clear(void)
> +{
> +	struct wkup_m3_ipc_regs ipc_regs;
> +
> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
> +	ipc_regs.reg2 &= 0xFFFF0000;
Probably define a macro and use it.
> +	wkup_m3_ctrl_ipc_write(m3_rproc_static, &ipc_regs);
> +}
> +
> +static void wkup_m3_mbox_callback(struct mbox_client *client, void *data)
> +{
> +	omap_mbox_disable_irq(m3_rproc_static->mbox, IRQ_RX);
> +}
> +
> +static int wkup_m3_rproc_start(struct rproc *rproc)
> +{
> +	struct wkup_m3_rproc *m3_rproc = rproc->priv;
> +	struct platform_device *pdev = m3_rproc->pdev;
> +	struct device *dev = &pdev->dev;
> +	struct wkup_m3_platform_data *pdata = dev->platform_data;
> +	int ret;
> +
> +	wkup_m3_fw_version_clear();
> +
> +	if (pdata && pdata->deassert_reset) {
> +		ret = pdata->deassert_reset(pdev, pdata->reset_name);
> +		if (ret) {
> +			dev_err(dev, "Unable to reset wkup_m3!\n");
> +			return -ENODEV;
> +		}
> +	} else {
> +		dev_err(dev, "Platform data missing deassert_reset!\n");
> +		return -ENODEV;
> +	}
> +
> +	m3_rproc->mbox_client.dev = dev;
> +	m3_rproc->mbox_client.tx_done = NULL;
> +	m3_rproc->mbox_client.rx_callback = wkup_m3_mbox_callback;
> +	m3_rproc->mbox_client.tx_block = false;
> +	m3_rproc->mbox_client.knows_txdone = false;
> +
> +	m3_rproc->mbox = mbox_request_channel(&m3_rproc->mbox_client);
> +
> +	if (IS_ERR(m3_rproc->mbox)) {
> +		dev_err(dev, "IPC Request for A8->M3 Channel failed!\n");
> +		ret = PTR_ERR(m3_rproc->mbox);
> +		m3_rproc->mbox = NULL;
> +		return ret;
> +	}
> +
> +	if (m3_rproc_static->ops && m3_rproc_static->ops->rproc_ready)
> +		m3_rproc_static->ops->rproc_ready();
> +
> +	m3_rproc_static->is_active = 1;
> +
> +	return 0;
> +}
> +
> +
> +static int wkup_m3_rproc_stop(struct rproc *rproc)
> +{
> +	return 0;
> +}
> +
> +static void wkup_m3_rproc_kick(struct rproc *rproc, int vqid)
> +{
> +}
> +
> +static struct rproc_ops wkup_m3_rproc_ops = {
> +	.start		= wkup_m3_rproc_start,
> +	.stop		= wkup_m3_rproc_stop,
> +	.kick		= wkup_m3_rproc_kick,
> +};
> +
> +/* Public Functions */
> +
> +/**
> + * wkup_m3_set_ops - Set callbacks for user of rproc
> + * @ops - struct wkup_m3_ops *
> + *
> + * Registers callbacks to wkup_m3 to be invoked after rproc is ready to use
> + * and after an interrupt is handled.
> + */
> +void wkup_m3_set_ops(struct wkup_m3_ops *ops)
> +{
> +	m3_rproc_static->ops = ops;
> +
> +	if (m3_rproc_static->is_active && m3_rproc_static->ops &&
> +	    m3_rproc_static->ops->rproc_ready)
> +		m3_rproc_static->ops->rproc_ready();
> +}
> +
> +/**
> + * wkup_m3_ping - Send a dummy msg to wkup_m3 to tell to to check IPC regs
> + *
> + * Returns the result of sending mbox msg or -EIO if no mbox handle is present
> + */
> +int wkup_m3_ping(void)
> +{
> +	int ret;
> +	mbox_msg_t dummy_msg = 0;
> +
> +	if (!m3_rproc_static->mbox) {
> +		dev_err(&m3_rproc_static->pdev->dev,
> +			"No IPC channel to communicate with wkup_m3!\n");
> +		return -EIO;
> +	}
> +
> +	/*
> +	 * Write a dummy message to the mailbox in order to trigger the RX
> +	 * interrupt to alert the M3 that data is available in the IPC
> +	 * registers. We must enable the IRQ here and disable it after in
> +	 * the RX callback to avoid multiple interrupts being received
> +	 * by the CM3.
> +	 */
> +	omap_mbox_enable_irq(m3_rproc_static->mbox, IRQ_RX);
> +	ret = mbox_send_message(m3_rproc_static->mbox, (void *)dummy_msg);
> +
> +	if (ret < 0) {
> +		pr_err("%s: mbox_send_message() failed: %d\n", __func__, ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * wkup_m3_wake_src - Get the wakeup source info passed from wkup_m3
> + * @wkup_m3_wakeup: struct wkup_m3_wakeup_src * gets assigned the
> + *		    wakeup src value
> + */
> +void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wkup_m3_wakeup)
> +{
> +	struct wkup_m3_ipc_regs ipc_regs;
> +	unsigned int wakeup_src_idx;
> +	int j;
> +
> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
> +
> +	wakeup_src_idx = ipc_regs.reg6 & WKUP_M3_WAKE_SRC_MASK;
> +
> +	for (j = 0; j < ARRAY_SIZE(wakeups)-1; j++) {
> +		if (wakeups[j].irq_nr == wakeup_src_idx) {
> +			*wkup_m3_wakeup = wakeups[j];
> +			return;
> +		}
> +	}
> +	*wkup_m3_wakeup = wakeups[j];
> +}
> +
> +/**
> + * wkup_m3_pm_status - Return the status code from wkup_m3 after sleep event
> + *
> + * Returns an error code that indicates whether or not the dsired sleep
> + * action was a success or not.
> + */
> +int wkup_m3_pm_status(void)
> +{
> +	unsigned int i;
> +	struct wkup_m3_ipc_regs ipc_regs;
> +
> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
> +
> +	i = WKUP_M3_STATUS_RESP_MASK & ipc_regs.reg1;
> +	i >>= __ffs(WKUP_M3_STATUS_RESP_MASK);
> +
> +	return i;
> +}
> +
> +/**
> + * wkup_m3_fw_version_read - Return the fw version given by the wkup_m3
> + *
> + * After boot the fw version should be read to ensure it is compatible.
> + */
> +int wkup_m3_fw_version_read(void)
> +{
> +	struct wkup_m3_ipc_regs ipc_regs;
> +
> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
> +
> +	return ipc_regs.reg2 & WKUP_M3_FW_VERSION_MASK;
> +}
> +
> +/**
> + * wkup_m3_set_cmd - write contents of struct to ipc regs
> + * @ipc_regs: struct wkup_m3_ipc_regs *
> + */
> +void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs)
> +{
> +	wkup_m3_ctrl_ipc_write(m3_rproc_static, ipc_regs);
> +}
> +
> +static void wkup_m3_rproc_loader_thread(struct rproc *rproc)
> +{
> +	struct wkup_m3_rproc *m3_rproc = rproc->priv;
> +	struct device *dev = &m3_rproc->pdev->dev;
> +	int ret;
> +
> +	wait_for_completion(&rproc->firmware_loading_complete);
> +
> +	ret = rproc_boot(rproc);
> +	if (ret)
> +		dev_err(dev, "rproc_boot failed\n");
> +
> +	do_exit(0);
> +}
> +
> +static int wkup_m3_rproc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = pdev->dev.of_node;
> +	struct wkup_m3_rproc *m3_rproc;
> +	struct rproc *rproc;
> +	int irq, ret;
> +	struct resource *res;
> +	struct task_struct *task;
> +	const char *mbox_name;
> +
> +	pm_runtime_enable(&pdev->dev);
> +
> +	ret = pm_runtime_get_sync(&pdev->dev);
> +	if (IS_ERR_VALUE(ret)) {
> +		dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
> +		return ret;
> +	}
> +
> +	rproc = rproc_alloc(dev, "wkup_m3", &wkup_m3_rproc_ops,
> +			    "am335x-pm-firmware.elf", sizeof(*m3_rproc));
> +	if (!rproc)
> +		return -ENOMEM;
> +
> +	m3_rproc = rproc->priv;
> +	m3_rproc->rproc = rproc;
> +	m3_rproc->pdev = pdev;
> +
> +	m3_rproc_static = m3_rproc;
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (!irq) {
> +		dev_err(&pdev->dev, "no irq resource\n");
> +		ret = -ENXIO;
> +		goto err;
> +	}
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ipc_regs");
> +	if (!res) {
> +		dev_err(&pdev->dev, "no memory resource for ipc\n");
> +		ret = -ENXIO;
> +		goto err;
> +	}
> +
> +	m3_rproc->ipc_mem_base = devm_request_and_ioremap(dev, res);
> +	if (!m3_rproc->ipc_mem_base) {
> +		dev_err(dev, "could not ioremap ipc_mem\n");
> +		ret = -EADDRNOTAVAIL;
> +		goto err;
> +	}
> +
> +	ret = devm_request_irq(dev, irq, wkup_m3_txev_handler,
> +			       IRQF_DISABLED, "wkup_m3_txev", m3_rproc);
> +	if (ret) {
> +		dev_err(dev, "request_irq failed\n");
> +		goto err;
> +	}
> +
> +	/* Get mbox name from device tree node */
> +	ret = of_property_read_string(np, "mbox-names", &mbox_name);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to get mbox name from dt node: %d\n",
> +			ret);
> +			goto err;
> +	};
> +
> +	m3_rproc->mbox_client.chan_name = mbox_name;
> +
> +	/* Register as a remoteproc device */
> +	ret = rproc_add(rproc);
> +	if (ret) {
> +		dev_err(dev, "rproc_add failed\n");
> +		goto err;
> +	}
> +
> +	/*
> +	 * Wait for firmware loading completion in a thread so we
> +	 * can boot the wkup_m3 as soon as it's ready without holding
> +	 * up kernel boot
> +	 */
> +	task = kthread_run((void *)wkup_m3_rproc_loader_thread, rproc,
> +			   "wkup_m3_rproc_loader");
> +
> +	if (IS_ERR(task)) {
> +		dev_err(dev, "can't create rproc_loader thread\n");
> +		goto err;
> +	}
> +
> +	return 0;
> +
> +err:
> +	rproc_put(rproc);
pm_runtime_put_sync() ?
> +	return ret;
> +}
> +
> +static int wkup_m3_rproc_remove(struct platform_device *pdev)
> +{
> +	struct rproc *rproc = platform_get_drvdata(pdev);
> +
> +	rproc_del(rproc);
> +	rproc_put(rproc);
> +
Here too ?
> +	return 0;
> +}
> +
> +static int wkup_m3_rpm_suspend(struct device *dev)
> +{
> +	return -EBUSY;
> +}
> +
> +static int wkup_m3_rpm_resume(struct device *dev)
> +{
> +	return 0;
> +}
> +
If you are not doing any meaningfull in suspend/resume hooks,
why are you even registering them ?

> +static const struct dev_pm_ops wkup_m3_rproc_pm_ops = {
> +	SET_RUNTIME_PM_OPS(wkup_m3_rpm_suspend, wkup_m3_rpm_resume, NULL)
> +};
> +
> +static const struct of_device_id wkup_m3_rproc_of_match[] = {
> +	{ .compatible = "ti,am3353-wkup-m3", .data = NULL, },
> +	{},
> +};
> +
> +static struct platform_driver wkup_m3_rproc_driver = {
> +	.probe = wkup_m3_rproc_probe,
> +	.remove = wkup_m3_rproc_remove,
> +	.driver = {
> +		.name = "wkup_m3",
> +		.owner = THIS_MODULE,
> +		.of_match_table = wkup_m3_rproc_of_match,
> +		.pm = &wkup_m3_rproc_pm_ops,
> +	},
> +};
> +
> +module_platform_driver(wkup_m3_rproc_driver);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("wkup m3 remote processor control driver");
> diff --git a/include/linux/wkup_m3.h b/include/linux/wkup_m3.h
> new file mode 100644
> index 0000000..1a2237f
> --- /dev/null
> +++ b/include/linux/wkup_m3.h
> @@ -0,0 +1,71 @@
> +/*
> + * TI Wakeup M3 Power Management Routines
> + *
> + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
> + * Dave Gerlach <d-gerlach@ti.com>
> + *
> + * 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 _LINUX_WKUP_M3_H
> +#define _LINUX_WKUP_M3_H
> +
> +/**
> + * struct wkup_m3_ops - Callbacks for allowing pm code to interact with wkup_m3.
> + *
> + * @txev_handler: Callback to allow pm code to react to response from wkup_m3
> + *		  after pinging it using wkup_m3_ping.
> + *
> + * @firmware_loaded: Callback invoked when the firmware has been loaded to the
> + *		     m3 to allow the pm code to enable suspend/resume ops.
> + */
> +
> +struct wkup_m3_ops {
> +	void (*txev_handler)(void);
> +	void (*rproc_ready)(void);
> +};
> +
> +struct wkup_m3_wakeup_src {
> +	int irq_nr;
> +	char src[10];
> +};
> +
> +struct wkup_m3_ipc_regs {
> +	u32 reg0;
> +	u32 reg1;
> +	u32 reg2;
> +	u32 reg3;
> +	u32 reg4;
> +	u32 reg5;
> +	u32 reg6;
> +	u32 reg7;
> +};
> +
> +#ifdef CONFIG_WKUP_M3_RPROC
> +int wkup_m3_prepare(void);
> +void wkup_m3_set_ops(struct wkup_m3_ops *ops);
> +int wkup_m3_ping(void);
> +void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wakeup_src);
> +int wkup_m3_pm_status(void);
> +int wkup_m3_is_valid(void);
> +int wkup_m3_fw_version_read(void);
> +void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs);
> +
> +#else
> +
> +int wkup_m3_prepare(void) { return -EINVAL; }
> +void wkup_m3_set_ops(struct wkup_m3_ops *ops) { }
> +int wkup_m3_ping(void) { return -EINVAL }
> +void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wakeup_src) { }
> +int wkup_m3_pm_status(void) { return -1; }
> +int wkup_m3_fw_version_read(void) { return -1; }
> +void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs) { }

Mark all of above in else as static inline.

> +#endif /* CONFIG_WKUP_M3_RPROC */
> +#endif /* _LINUX_WKUP_M3_H */
> 


^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 06/11] remoteproc: wkup_m3: Add wkup_m3 remote proc driver
@ 2014-07-14 14:54     ` Santosh Shilimkar
  0 siblings, 0 replies; 106+ messages in thread
From: Santosh Shilimkar @ 2014-07-14 14:54 UTC (permalink / raw)
  To: linux-arm-kernel

On Thursday 10 July 2014 10:55 PM, Dave Gerlach wrote:
> Add a remoteproc driver to load the firmware for and boot the wkup_m3
> present on am33xx. The wkup_m3 is an integrated Cortex M3 that allows
> the SoC to enter the lowest possible power state by taking control from
> the MPU after it has gone into its own low power state and shutting off
> any additional peripherals.
> 
> Communication between the MPU and CM3 is handled by several IPC
> registers in the control module and a mailbox. An API is exposed for
> programming the aforementioned IPC registers and notifying the wkup_m3
> of pending data using the mailbox. The wkup_m3 has the ability to
> trigger an interrupt back to the MPU to allow for bidirectional
> communication through these registers.
> 
> Two callbacks are provided. rproc_ready allows code to hook into the
> driver to see when firmware has been loaded and execute other code and
> txev_handler allows external code to run when the wkup_m3 triggers an
> interrupt back to the m3.
> 
> The driver expects a resource table to be present in the wkup_m3 to
> define the required memory resources needed by wkup_m3, at least the
> data memory so that the firmware can be copied for the proper place for
> execution.
> 
> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
> CC: Ohad Ben-Cohen <ohad@wizery.com>
> ---
>  drivers/remoteproc/Kconfig         |  15 ++
>  drivers/remoteproc/Makefile        |   1 +
>  drivers/remoteproc/wkup_m3_rproc.c | 512 +++++++++++++++++++++++++++++++++++++
>  include/linux/wkup_m3.h            |  71 +++++
>  4 files changed, 599 insertions(+)
>  create mode 100644 drivers/remoteproc/wkup_m3_rproc.c
>  create mode 100644 include/linux/wkup_m3.h
> 
> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
> index 5e343ba..4b00c21 100644
> --- a/drivers/remoteproc/Kconfig
> +++ b/drivers/remoteproc/Kconfig
> @@ -41,6 +41,21 @@ config STE_MODEM_RPROC
>  	  This can be either built-in or a loadable module.
>  	  If unsure say N.
>  
> +config WKUP_M3_RPROC
> +	bool "AM33xx wkup-m3 remoteproc support"
> +        depends on SOC_AM33XX
> +        select REMOTEPROC
> +	select MAILBOX
> +	select OMAP2PLUS_MBOX
Please fix the indentation.

> +	default n
Default is always 'n' so drop above.
> +	help
> +	  Say y here to support AM33xx wkup-m3.
> +
> +	  Required for Suspend-to-ram and CPUIdle on AM33xx. Allows for
> +	  loading of firmware and communication with CM3 PM coproccesor
> +	  that is present on AM33xx family of SoCs
> +	  If unsure say N.
> +
>  config DA8XX_REMOTEPROC
>  	tristate "DA8xx/OMAP-L13x remoteproc support"
>  	depends on ARCH_DAVINCI_DA8XX
> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
> index ac2ff75..81b04d1 100644
> --- a/drivers/remoteproc/Makefile
> +++ b/drivers/remoteproc/Makefile
> @@ -9,4 +9,5 @@ remoteproc-y				+= remoteproc_virtio.o
>  remoteproc-y				+= remoteproc_elf_loader.o
>  obj-$(CONFIG_OMAP_REMOTEPROC)		+= omap_remoteproc.o
>  obj-$(CONFIG_STE_MODEM_RPROC)	 	+= ste_modem_rproc.o
> +obj-$(CONFIG_WKUP_M3_RPROC)		+= wkup_m3_rproc.o
>  obj-$(CONFIG_DA8XX_REMOTEPROC)		+= da8xx_remoteproc.o
> diff --git a/drivers/remoteproc/wkup_m3_rproc.c b/drivers/remoteproc/wkup_m3_rproc.c
> new file mode 100644
> index 0000000..58aeaf2
> --- /dev/null
> +++ b/drivers/remoteproc/wkup_m3_rproc.c
> @@ -0,0 +1,512 @@
> +/*
> + * AMx3 Wkup M3 Remote Processor driver
> + *
> + * Copyright (C) 2014 Texas Instruments, Inc.
> + *
> + * Dave Gerlach <d-gerlach@ti.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/err.h>
> +#include <linux/platform_device.h>
> +#include <linux/irq.h>
> +#include <linux/interrupt.h>
> +#include <linux/elf.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/firmware.h>
> +#include <linux/remoteproc.h>
> +#include <linux/omap-mailbox.h>
> +#include <linux/mailbox_client.h>
> +#include <linux/wkup_m3.h>
> +#include <linux/kthread.h>
> +#include "remoteproc_internal.h"
> +
> +#include <linux/platform_data/wkup_m3.h>
> +
> +#define WKUP_M3_WAKE_SRC_MASK		0xFF
> +
> +#define WKUP_M3_STATUS_RESP_SHIFT	16
> +#define WKUP_M3_STATUS_RESP_MASK	(0xffff << 16)
> +
> +#define WKUP_M3_FW_VERSION_SHIFT	0
> +#define WKUP_M3_FW_VERSION_MASK		0xffff
> +
> +/* AM33XX M3_TXEV_EOI register */
> +#define AM33XX_CONTROL_M3_TXEV_EOI	0x00
> +
> +#define AM33XX_M3_TXEV_ACK		(0x1 << 0)
> +#define AM33XX_M3_TXEV_ENABLE		(0x0 << 0)
> +
> +/* AM33XX IPC message registers */
> +#define AM33XX_CONTROL_IPC_MSG_REG0	0x04
> +#define AM33XX_CONTROL_IPC_MSG_REG1	0x08
> +#define AM33XX_CONTROL_IPC_MSG_REG2	0x0c
> +#define AM33XX_CONTROL_IPC_MSG_REG3	0x10
> +#define AM33XX_CONTROL_IPC_MSG_REG4	0x14
> +#define AM33XX_CONTROL_IPC_MSG_REG5	0x18
> +#define AM33XX_CONTROL_IPC_MSG_REG6	0x1c
> +#define AM33XX_CONTROL_IPC_MSG_REG7	0x20
> +
Is this driver going to be AM33xx specific ?

> +struct wkup_m3_rproc {
> +	struct rproc *rproc;
> +
> +	void * __iomem dev_table_va;
> +	void * __iomem ipc_mem_base;
> +	struct platform_device *pdev;
> +
> +	struct mbox_client mbox_client;
> +	struct mbox_chan *mbox;
> +	struct wkup_m3_ops *ops;
> +
> +	bool is_active;
> +};
> +
> +static struct wkup_m3_rproc *m3_rproc_static;
> +
> +static struct wkup_m3_wakeup_src wakeups[] = {
> +	{.irq_nr = 35,	.src = "USB0_PHY"},
> +	{.irq_nr = 36,	.src = "USB1_PHY"},
> +	{.irq_nr = 40,	.src = "I2C0"},
> +	{.irq_nr = 41,	.src = "RTC Timer"},
> +	{.irq_nr = 42,	.src = "RTC Alarm"},
> +	{.irq_nr = 43,	.src = "Timer0"},
> +	{.irq_nr = 44,	.src = "Timer1"},
> +	{.irq_nr = 45,	.src = "UART"},
> +	{.irq_nr = 46,	.src = "GPIO0"},
> +	{.irq_nr = 48,	.src = "MPU_WAKE"},
> +	{.irq_nr = 49,	.src = "WDT0"},
> +	{.irq_nr = 50,	.src = "WDT1"},
> +	{.irq_nr = 51,	.src = "ADC_TSC"},
> +	{.irq_nr = 0,	.src = "Unknown"},
> +};
> +
const ?

> +static void am33xx_txev_eoi(struct wkup_m3_rproc *m3_rproc)
> +{
> +	writel(AM33XX_M3_TXEV_ACK,
> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
> +}
> +
> +static void am33xx_txev_enable(struct wkup_m3_rproc *m3_rproc)
> +{
> +	writel(AM33XX_M3_TXEV_ENABLE,
> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
> +}
> +
> +static void wkup_m3_ctrl_ipc_write(struct wkup_m3_rproc *m3_rproc,
> +				   struct wkup_m3_ipc_regs *ipc_regs)
> +{
> +	writel(ipc_regs->reg0,
> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG0);
> +	writel(ipc_regs->reg1,
> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG1);
> +	writel(ipc_regs->reg2,
> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG2);
> +	writel(ipc_regs->reg3,
> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG3);
> +	writel(ipc_regs->reg4,
> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG4);
> +	writel(ipc_regs->reg5,
> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG5);
> +	writel(ipc_regs->reg6,
> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG6);
> +	writel(ipc_regs->reg7,
> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG7);
> +}
> +
> +static void wkup_m3_ctrl_ipc_read(struct wkup_m3_rproc *m3_rproc,
> +				  struct wkup_m3_ipc_regs *ipc_regs)
> +{
> +	ipc_regs->reg0 = readl(m3_rproc->ipc_mem_base
> +			       + AM33XX_CONTROL_IPC_MSG_REG0);
> +	ipc_regs->reg1 = readl(m3_rproc->ipc_mem_base
> +			       + AM33XX_CONTROL_IPC_MSG_REG1);
> +	ipc_regs->reg2 = readl(m3_rproc->ipc_mem_base
> +			       + AM33XX_CONTROL_IPC_MSG_REG2);
> +	ipc_regs->reg3 = readl(m3_rproc->ipc_mem_base
> +			       + AM33XX_CONTROL_IPC_MSG_REG3);
> +	ipc_regs->reg4 = readl(m3_rproc->ipc_mem_base
> +			       + AM33XX_CONTROL_IPC_MSG_REG4);
> +	ipc_regs->reg5 = readl(m3_rproc->ipc_mem_base
> +			       + AM33XX_CONTROL_IPC_MSG_REG5);
> +	ipc_regs->reg6 = readl(m3_rproc->ipc_mem_base
> +			       + AM33XX_CONTROL_IPC_MSG_REG6);
> +	ipc_regs->reg7 = readl(m3_rproc->ipc_mem_base
> +			       + AM33XX_CONTROL_IPC_MSG_REG7);
> +}
> +
> +static irqreturn_t wkup_m3_txev_handler(int irq, void *unused)
> +{
> +	am33xx_txev_eoi(m3_rproc_static);
> +
> +	if (m3_rproc_static->ops && m3_rproc_static->ops->txev_handler)
> +		m3_rproc_static->ops->txev_handler();
> +
> +	am33xx_txev_enable(m3_rproc_static);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/**
> + * wkup_m3_fw_version_clear - Clear FW version from ipc regs
> + *
> + * Invalidate M3 firmware version before hardreset.
> + * Write invalid version in lower 4 nibbles of parameter
> + * register (ipc_regs + 0x8).
> + */
> +
> +static void wkup_m3_fw_version_clear(void)
> +{
> +	struct wkup_m3_ipc_regs ipc_regs;
> +
> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
> +	ipc_regs.reg2 &= 0xFFFF0000;
Probably define a macro and use it.
> +	wkup_m3_ctrl_ipc_write(m3_rproc_static, &ipc_regs);
> +}
> +
> +static void wkup_m3_mbox_callback(struct mbox_client *client, void *data)
> +{
> +	omap_mbox_disable_irq(m3_rproc_static->mbox, IRQ_RX);
> +}
> +
> +static int wkup_m3_rproc_start(struct rproc *rproc)
> +{
> +	struct wkup_m3_rproc *m3_rproc = rproc->priv;
> +	struct platform_device *pdev = m3_rproc->pdev;
> +	struct device *dev = &pdev->dev;
> +	struct wkup_m3_platform_data *pdata = dev->platform_data;
> +	int ret;
> +
> +	wkup_m3_fw_version_clear();
> +
> +	if (pdata && pdata->deassert_reset) {
> +		ret = pdata->deassert_reset(pdev, pdata->reset_name);
> +		if (ret) {
> +			dev_err(dev, "Unable to reset wkup_m3!\n");
> +			return -ENODEV;
> +		}
> +	} else {
> +		dev_err(dev, "Platform data missing deassert_reset!\n");
> +		return -ENODEV;
> +	}
> +
> +	m3_rproc->mbox_client.dev = dev;
> +	m3_rproc->mbox_client.tx_done = NULL;
> +	m3_rproc->mbox_client.rx_callback = wkup_m3_mbox_callback;
> +	m3_rproc->mbox_client.tx_block = false;
> +	m3_rproc->mbox_client.knows_txdone = false;
> +
> +	m3_rproc->mbox = mbox_request_channel(&m3_rproc->mbox_client);
> +
> +	if (IS_ERR(m3_rproc->mbox)) {
> +		dev_err(dev, "IPC Request for A8->M3 Channel failed!\n");
> +		ret = PTR_ERR(m3_rproc->mbox);
> +		m3_rproc->mbox = NULL;
> +		return ret;
> +	}
> +
> +	if (m3_rproc_static->ops && m3_rproc_static->ops->rproc_ready)
> +		m3_rproc_static->ops->rproc_ready();
> +
> +	m3_rproc_static->is_active = 1;
> +
> +	return 0;
> +}
> +
> +
> +static int wkup_m3_rproc_stop(struct rproc *rproc)
> +{
> +	return 0;
> +}
> +
> +static void wkup_m3_rproc_kick(struct rproc *rproc, int vqid)
> +{
> +}
> +
> +static struct rproc_ops wkup_m3_rproc_ops = {
> +	.start		= wkup_m3_rproc_start,
> +	.stop		= wkup_m3_rproc_stop,
> +	.kick		= wkup_m3_rproc_kick,
> +};
> +
> +/* Public Functions */
> +
> +/**
> + * wkup_m3_set_ops - Set callbacks for user of rproc
> + * @ops - struct wkup_m3_ops *
> + *
> + * Registers callbacks to wkup_m3 to be invoked after rproc is ready to use
> + * and after an interrupt is handled.
> + */
> +void wkup_m3_set_ops(struct wkup_m3_ops *ops)
> +{
> +	m3_rproc_static->ops = ops;
> +
> +	if (m3_rproc_static->is_active && m3_rproc_static->ops &&
> +	    m3_rproc_static->ops->rproc_ready)
> +		m3_rproc_static->ops->rproc_ready();
> +}
> +
> +/**
> + * wkup_m3_ping - Send a dummy msg to wkup_m3 to tell to to check IPC regs
> + *
> + * Returns the result of sending mbox msg or -EIO if no mbox handle is present
> + */
> +int wkup_m3_ping(void)
> +{
> +	int ret;
> +	mbox_msg_t dummy_msg = 0;
> +
> +	if (!m3_rproc_static->mbox) {
> +		dev_err(&m3_rproc_static->pdev->dev,
> +			"No IPC channel to communicate with wkup_m3!\n");
> +		return -EIO;
> +	}
> +
> +	/*
> +	 * Write a dummy message to the mailbox in order to trigger the RX
> +	 * interrupt to alert the M3 that data is available in the IPC
> +	 * registers. We must enable the IRQ here and disable it after in
> +	 * the RX callback to avoid multiple interrupts being received
> +	 * by the CM3.
> +	 */
> +	omap_mbox_enable_irq(m3_rproc_static->mbox, IRQ_RX);
> +	ret = mbox_send_message(m3_rproc_static->mbox, (void *)dummy_msg);
> +
> +	if (ret < 0) {
> +		pr_err("%s: mbox_send_message() failed: %d\n", __func__, ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * wkup_m3_wake_src - Get the wakeup source info passed from wkup_m3
> + * @wkup_m3_wakeup: struct wkup_m3_wakeup_src * gets assigned the
> + *		    wakeup src value
> + */
> +void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wkup_m3_wakeup)
> +{
> +	struct wkup_m3_ipc_regs ipc_regs;
> +	unsigned int wakeup_src_idx;
> +	int j;
> +
> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
> +
> +	wakeup_src_idx = ipc_regs.reg6 & WKUP_M3_WAKE_SRC_MASK;
> +
> +	for (j = 0; j < ARRAY_SIZE(wakeups)-1; j++) {
> +		if (wakeups[j].irq_nr == wakeup_src_idx) {
> +			*wkup_m3_wakeup = wakeups[j];
> +			return;
> +		}
> +	}
> +	*wkup_m3_wakeup = wakeups[j];
> +}
> +
> +/**
> + * wkup_m3_pm_status - Return the status code from wkup_m3 after sleep event
> + *
> + * Returns an error code that indicates whether or not the dsired sleep
> + * action was a success or not.
> + */
> +int wkup_m3_pm_status(void)
> +{
> +	unsigned int i;
> +	struct wkup_m3_ipc_regs ipc_regs;
> +
> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
> +
> +	i = WKUP_M3_STATUS_RESP_MASK & ipc_regs.reg1;
> +	i >>= __ffs(WKUP_M3_STATUS_RESP_MASK);
> +
> +	return i;
> +}
> +
> +/**
> + * wkup_m3_fw_version_read - Return the fw version given by the wkup_m3
> + *
> + * After boot the fw version should be read to ensure it is compatible.
> + */
> +int wkup_m3_fw_version_read(void)
> +{
> +	struct wkup_m3_ipc_regs ipc_regs;
> +
> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
> +
> +	return ipc_regs.reg2 & WKUP_M3_FW_VERSION_MASK;
> +}
> +
> +/**
> + * wkup_m3_set_cmd - write contents of struct to ipc regs
> + * @ipc_regs: struct wkup_m3_ipc_regs *
> + */
> +void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs)
> +{
> +	wkup_m3_ctrl_ipc_write(m3_rproc_static, ipc_regs);
> +}
> +
> +static void wkup_m3_rproc_loader_thread(struct rproc *rproc)
> +{
> +	struct wkup_m3_rproc *m3_rproc = rproc->priv;
> +	struct device *dev = &m3_rproc->pdev->dev;
> +	int ret;
> +
> +	wait_for_completion(&rproc->firmware_loading_complete);
> +
> +	ret = rproc_boot(rproc);
> +	if (ret)
> +		dev_err(dev, "rproc_boot failed\n");
> +
> +	do_exit(0);
> +}
> +
> +static int wkup_m3_rproc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = pdev->dev.of_node;
> +	struct wkup_m3_rproc *m3_rproc;
> +	struct rproc *rproc;
> +	int irq, ret;
> +	struct resource *res;
> +	struct task_struct *task;
> +	const char *mbox_name;
> +
> +	pm_runtime_enable(&pdev->dev);
> +
> +	ret = pm_runtime_get_sync(&pdev->dev);
> +	if (IS_ERR_VALUE(ret)) {
> +		dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
> +		return ret;
> +	}
> +
> +	rproc = rproc_alloc(dev, "wkup_m3", &wkup_m3_rproc_ops,
> +			    "am335x-pm-firmware.elf", sizeof(*m3_rproc));
> +	if (!rproc)
> +		return -ENOMEM;
> +
> +	m3_rproc = rproc->priv;
> +	m3_rproc->rproc = rproc;
> +	m3_rproc->pdev = pdev;
> +
> +	m3_rproc_static = m3_rproc;
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (!irq) {
> +		dev_err(&pdev->dev, "no irq resource\n");
> +		ret = -ENXIO;
> +		goto err;
> +	}
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ipc_regs");
> +	if (!res) {
> +		dev_err(&pdev->dev, "no memory resource for ipc\n");
> +		ret = -ENXIO;
> +		goto err;
> +	}
> +
> +	m3_rproc->ipc_mem_base = devm_request_and_ioremap(dev, res);
> +	if (!m3_rproc->ipc_mem_base) {
> +		dev_err(dev, "could not ioremap ipc_mem\n");
> +		ret = -EADDRNOTAVAIL;
> +		goto err;
> +	}
> +
> +	ret = devm_request_irq(dev, irq, wkup_m3_txev_handler,
> +			       IRQF_DISABLED, "wkup_m3_txev", m3_rproc);
> +	if (ret) {
> +		dev_err(dev, "request_irq failed\n");
> +		goto err;
> +	}
> +
> +	/* Get mbox name from device tree node */
> +	ret = of_property_read_string(np, "mbox-names", &mbox_name);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to get mbox name from dt node: %d\n",
> +			ret);
> +			goto err;
> +	};
> +
> +	m3_rproc->mbox_client.chan_name = mbox_name;
> +
> +	/* Register as a remoteproc device */
> +	ret = rproc_add(rproc);
> +	if (ret) {
> +		dev_err(dev, "rproc_add failed\n");
> +		goto err;
> +	}
> +
> +	/*
> +	 * Wait for firmware loading completion in a thread so we
> +	 * can boot the wkup_m3 as soon as it's ready without holding
> +	 * up kernel boot
> +	 */
> +	task = kthread_run((void *)wkup_m3_rproc_loader_thread, rproc,
> +			   "wkup_m3_rproc_loader");
> +
> +	if (IS_ERR(task)) {
> +		dev_err(dev, "can't create rproc_loader thread\n");
> +		goto err;
> +	}
> +
> +	return 0;
> +
> +err:
> +	rproc_put(rproc);
pm_runtime_put_sync() ?
> +	return ret;
> +}
> +
> +static int wkup_m3_rproc_remove(struct platform_device *pdev)
> +{
> +	struct rproc *rproc = platform_get_drvdata(pdev);
> +
> +	rproc_del(rproc);
> +	rproc_put(rproc);
> +
Here too ?
> +	return 0;
> +}
> +
> +static int wkup_m3_rpm_suspend(struct device *dev)
> +{
> +	return -EBUSY;
> +}
> +
> +static int wkup_m3_rpm_resume(struct device *dev)
> +{
> +	return 0;
> +}
> +
If you are not doing any meaningfull in suspend/resume hooks,
why are you even registering them ?

> +static const struct dev_pm_ops wkup_m3_rproc_pm_ops = {
> +	SET_RUNTIME_PM_OPS(wkup_m3_rpm_suspend, wkup_m3_rpm_resume, NULL)
> +};
> +
> +static const struct of_device_id wkup_m3_rproc_of_match[] = {
> +	{ .compatible = "ti,am3353-wkup-m3", .data = NULL, },
> +	{},
> +};
> +
> +static struct platform_driver wkup_m3_rproc_driver = {
> +	.probe = wkup_m3_rproc_probe,
> +	.remove = wkup_m3_rproc_remove,
> +	.driver = {
> +		.name = "wkup_m3",
> +		.owner = THIS_MODULE,
> +		.of_match_table = wkup_m3_rproc_of_match,
> +		.pm = &wkup_m3_rproc_pm_ops,
> +	},
> +};
> +
> +module_platform_driver(wkup_m3_rproc_driver);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("wkup m3 remote processor control driver");
> diff --git a/include/linux/wkup_m3.h b/include/linux/wkup_m3.h
> new file mode 100644
> index 0000000..1a2237f
> --- /dev/null
> +++ b/include/linux/wkup_m3.h
> @@ -0,0 +1,71 @@
> +/*
> + * TI Wakeup M3 Power Management Routines
> + *
> + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
> + * Dave Gerlach <d-gerlach@ti.com>
> + *
> + * 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 _LINUX_WKUP_M3_H
> +#define _LINUX_WKUP_M3_H
> +
> +/**
> + * struct wkup_m3_ops - Callbacks for allowing pm code to interact with wkup_m3.
> + *
> + * @txev_handler: Callback to allow pm code to react to response from wkup_m3
> + *		  after pinging it using wkup_m3_ping.
> + *
> + * @firmware_loaded: Callback invoked when the firmware has been loaded to the
> + *		     m3 to allow the pm code to enable suspend/resume ops.
> + */
> +
> +struct wkup_m3_ops {
> +	void (*txev_handler)(void);
> +	void (*rproc_ready)(void);
> +};
> +
> +struct wkup_m3_wakeup_src {
> +	int irq_nr;
> +	char src[10];
> +};
> +
> +struct wkup_m3_ipc_regs {
> +	u32 reg0;
> +	u32 reg1;
> +	u32 reg2;
> +	u32 reg3;
> +	u32 reg4;
> +	u32 reg5;
> +	u32 reg6;
> +	u32 reg7;
> +};
> +
> +#ifdef CONFIG_WKUP_M3_RPROC
> +int wkup_m3_prepare(void);
> +void wkup_m3_set_ops(struct wkup_m3_ops *ops);
> +int wkup_m3_ping(void);
> +void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wakeup_src);
> +int wkup_m3_pm_status(void);
> +int wkup_m3_is_valid(void);
> +int wkup_m3_fw_version_read(void);
> +void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs);
> +
> +#else
> +
> +int wkup_m3_prepare(void) { return -EINVAL; }
> +void wkup_m3_set_ops(struct wkup_m3_ops *ops) { }
> +int wkup_m3_ping(void) { return -EINVAL }
> +void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wakeup_src) { }
> +int wkup_m3_pm_status(void) { return -1; }
> +int wkup_m3_fw_version_read(void) { return -1; }
> +void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs) { }

Mark all of above in else as static inline.

> +#endif /* CONFIG_WKUP_M3_RPROC */
> +#endif /* _LINUX_WKUP_M3_H */
> 

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 05/11] Documentation: dt: add ti,am3353_wkup_m3 bindings
  2014-07-14 14:41     ` [PATCH v4 05/11] Documentation: dt: add ti, am3353_wkup_m3 bindings Santosh Shilimkar
@ 2014-07-14 16:33       ` Suman Anna
  -1 siblings, 0 replies; 106+ messages in thread
From: Suman Anna @ 2014-07-14 16:33 UTC (permalink / raw)
  To: Santosh Shilimkar, Dave Gerlach, linux-arm-kernel, linux-omap
  Cc: Paul Walmsley, Kevin Hilman, Tony Lindgren, Tero Kristo,
	Nishanth Menon, Russ Dill, Daniel Mack, Benoit Cousson,
	Ohad Ben-Cohen

Hi Dave,

On 07/14/2014 09:41 AM, Santosh Shilimkar wrote:
> On Thursday 10 July 2014 10:55 PM, Dave Gerlach wrote:
>> Add the device tree bindings document for am3353 wkup_m3.
>>
>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>> CC: Ohad Ben-Cohen <ohad@wizery.com>
>> CC: Benoit Cousson <bcousson@baylibre.com>
>> ---
> Looks like you missed to copy device tree list and maintainers.
> As Tony suggested, split up the series and send the wkup_m3 related
> patches separately along with bindings and mark the DT folks on email.
> 
>>  .../bindings/remoteproc/wkup_m3_rproc.txt          | 46 ++++++++++++++++++++++
>>  1 file changed, 46 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
>>
>> diff --git a/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt b/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
>> new file mode 100644
>> index 0000000..e9dd909
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
>> @@ -0,0 +1,46 @@
>> +Wakeup M3 Remote Proc Driver
>> +=====================
>> +
>> +TI AMx3 Family devices use a Cortex M3 co-processor to help with various
>> +low power tasks that cannot be controlled from the MPU. The CM3 requires
>> +a firmware binary to accomplish this and communicates with the MPU through
>> +IPC registers present in the SoCs control module. The wkup_m3 remoteproc
>> +driver handles the loading of the firmware and exposes an API to
>> +communicate with the wkup_m3 through the use of the IPC registers and a
>> +mailbox.
>> +
>> +Wkup M3 Device Node:
>> +====================
>> +A wkup_m3 device node is used to represent a wakeup M3 IP instance within
>> +a SoC. The sub-mailboxes are represented as child node of this parent node.
>> +
>> +Required properties:
>> +--------------------
>> +- compatible:		Should be "ti,am3353-wkup-m3" for AM33xx SoCs
>> +- reg:			Contains the wkup_m3 register address ranges for
>> +			umem, dmem, and ipc-regs.
>> +- reg-names:		Names for reg addresses given above

You should explicitly state the names here. Giving any different names
will cause the driver to fail.

>> +- interrupts:		Contains the interrupt information for the wkup_m3
>> +			interrupt that signals the MPU.
>> +- ti,hwmods:		Name of the hwmod associated with the mailbox

hwmod is not associated with mailbox here, please fix the cut-copy-paste
error.

regards
Suman


>> +- ti,no-reset-on-init:	Reset is handled after fw has been loaded, not at
>> +			init of hwmod.
>> +- mbox-names:		Name of the mbox channel for the IPC framework
>> +- mbox:			Phandle used by IPC framework to get correct mbox
>> +			channel for communication.
>> +
>> +Example:
>> +--------
>> +/* AM33xx */
>> +wkup_m3: wkup_m3@44d00000 {
>> +	compatible = "ti,am3353-wkup-m3";
>> +	reg = <0x44d00000 0x4000
>> +	       0x44d80000 0x2000
>> +	       0x44e11324 0x0024>;
>> +	reg-names = "m3_umem", "m3_dmem", "ipc_regs";
>> +	interrupts = <78>;
>> +	ti,hwmods = "wkup_m3";
>> +	ti,no-reset-on-init;
>> +	mbox-names = "wkup_m3";
>> +	mbox = <&mailbox &mbox_wkupm3>;
>> +};
>>
> 


^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 05/11] Documentation: dt: add ti, am3353_wkup_m3 bindings
@ 2014-07-14 16:33       ` Suman Anna
  0 siblings, 0 replies; 106+ messages in thread
From: Suman Anna @ 2014-07-14 16:33 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Dave,

On 07/14/2014 09:41 AM, Santosh Shilimkar wrote:
> On Thursday 10 July 2014 10:55 PM, Dave Gerlach wrote:
>> Add the device tree bindings document for am3353 wkup_m3.
>>
>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>> CC: Ohad Ben-Cohen <ohad@wizery.com>
>> CC: Benoit Cousson <bcousson@baylibre.com>
>> ---
> Looks like you missed to copy device tree list and maintainers.
> As Tony suggested, split up the series and send the wkup_m3 related
> patches separately along with bindings and mark the DT folks on email.
> 
>>  .../bindings/remoteproc/wkup_m3_rproc.txt          | 46 ++++++++++++++++++++++
>>  1 file changed, 46 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
>>
>> diff --git a/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt b/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
>> new file mode 100644
>> index 0000000..e9dd909
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
>> @@ -0,0 +1,46 @@
>> +Wakeup M3 Remote Proc Driver
>> +=====================
>> +
>> +TI AMx3 Family devices use a Cortex M3 co-processor to help with various
>> +low power tasks that cannot be controlled from the MPU. The CM3 requires
>> +a firmware binary to accomplish this and communicates with the MPU through
>> +IPC registers present in the SoCs control module. The wkup_m3 remoteproc
>> +driver handles the loading of the firmware and exposes an API to
>> +communicate with the wkup_m3 through the use of the IPC registers and a
>> +mailbox.
>> +
>> +Wkup M3 Device Node:
>> +====================
>> +A wkup_m3 device node is used to represent a wakeup M3 IP instance within
>> +a SoC. The sub-mailboxes are represented as child node of this parent node.
>> +
>> +Required properties:
>> +--------------------
>> +- compatible:		Should be "ti,am3353-wkup-m3" for AM33xx SoCs
>> +- reg:			Contains the wkup_m3 register address ranges for
>> +			umem, dmem, and ipc-regs.
>> +- reg-names:		Names for reg addresses given above

You should explicitly state the names here. Giving any different names
will cause the driver to fail.

>> +- interrupts:		Contains the interrupt information for the wkup_m3
>> +			interrupt that signals the MPU.
>> +- ti,hwmods:		Name of the hwmod associated with the mailbox

hwmod is not associated with mailbox here, please fix the cut-copy-paste
error.

regards
Suman


>> +- ti,no-reset-on-init:	Reset is handled after fw has been loaded, not at
>> +			init of hwmod.
>> +- mbox-names:		Name of the mbox channel for the IPC framework
>> +- mbox:			Phandle used by IPC framework to get correct mbox
>> +			channel for communication.
>> +
>> +Example:
>> +--------
>> +/* AM33xx */
>> +wkup_m3: wkup_m3 at 44d00000 {
>> +	compatible = "ti,am3353-wkup-m3";
>> +	reg = <0x44d00000 0x4000
>> +	       0x44d80000 0x2000
>> +	       0x44e11324 0x0024>;
>> +	reg-names = "m3_umem", "m3_dmem", "ipc_regs";
>> +	interrupts = <78>;
>> +	ti,hwmods = "wkup_m3";
>> +	ti,no-reset-on-init;
>> +	mbox-names = "wkup_m3";
>> +	mbox = <&mailbox &mbox_wkupm3>;
>> +};
>>
> 

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 03/11] ARM: OMAP2+: timer: Add suspend-resume callbacks for clkevent device
  2014-07-14 14:37       ` Santosh Shilimkar
@ 2014-07-14 17:42         ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-14 17:42 UTC (permalink / raw)
  To: Santosh Shilimkar
  Cc: Tony Lindgren, linux-arm-kernel, linux-omap, Paul Walmsley,
	Kevin Hilman, Tero Kristo, Nishanth Menon, Russ Dill,
	Daniel Mack, Suman Anna, Benoit Cousson, Vaibhav Bedia

Santosh, Tony,

On 07/14/2014 09:37 AM, Santosh Shilimkar wrote:
> On Monday 14 July 2014 07:15 AM, Tony Lindgren wrote:
>> * Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
>>> From: Vaibhav Bedia <vaibhav.bedia@ti.com>
>>>
>>> OMAP timer code registers two timers - one as clocksource
>>> and one as clockevent. Since AM33XX has only one usable timer
>>> in the WKUP domain 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.
>>>
>>> Signed-off-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
>>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>>> ---
>>> v3->v4:
>>> 	Only use for am33xx soc now.
>>>
>>>   arch/arm/mach-omap2/timer.c | 28 ++++++++++++++++++++++++++++
>>>   1 file changed, 28 insertions(+)
>>>
>>> diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
>>> index 43d03fb..6fc1748 100644
>>> --- a/arch/arm/mach-omap2/timer.c
>>> +++ b/arch/arm/mach-omap2/timer.c
>>> @@ -128,6 +128,29 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
>>>   	}
>>>   }
>>>
>>> +static void omap_clkevt_suspend(struct clock_event_device *unused)
>>> +{
>>> +	struct omap_hwmod *oh;
>>> +
>>> +	oh = omap_hwmod_lookup(clockevent_gpt.name);
>>> +	if (!oh)
>>> +		return;
>>> +
>>> +	omap_hwmod_idle(oh);
>>> +}
>>> +
>>> +static void omap_clkevt_resume(struct clock_event_device *unused)
>>> +{
>>> +	struct omap_hwmod *oh;
>>> +
>>> +	oh = omap_hwmod_lookup(clockevent_gpt.name);
>>> +	if (!oh)
>>> +		return;
>>> +
>>> +	omap_hwmod_enable(oh);
>>> +	__omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);
>>> +}
>>> +
>>
>> This is going to make moving the timer code into drivers one step
>> tougher to do. And you don't need to look up the hwmod entry every
>> time, just initialize it during the init.

Yes you are right about looking up only at init I need to change that. I agree 
that this makes moving the timers harder but I'm not sure there's any way around 
this. I attempted to use the omap_device layer here but there is no device at 
all created here, it does not hook into the normal PM layer through omap_device. 
This clock must be shut off as it sits in the peripheral power domain and any 
active clock within the domain will prevent suspend from happening, so we end up 
with a platform specific issue here. It seems that the only way I can get to the 
clock is through the hwmod.

>>
>>> +	if (soc_is_am33xx()) {
>>> +		clockevent_gpt.suspend = omap_clkevt_suspend;
>>> +		clockevent_gpt.resume = omap_clkevt_resume;
>>> +	}
>>> +
>>
>> Maybe try to set up things so we initialize the SoC specific
>> timer suspend and resume functions in mach-omap2/timer.c
>> in a way where eventually the device driver can easily use
>> them?
>>
> +1. I had similar comments on the previous version too.

This was my attempt to make things specific to only am335x based on Santosh's 
previous comments as last time they were populated for every device even when 
unneeded. These are not standard suspend/resume handlers, they are specific to 
clock event. I know there will always need to be at least some code here for the 
early timer init based on previous timer cleanup series I've seen, so perhaps I 
could hook it in there when the time comes?

Regards,
Dave

>
> Regards,
> Santosh
>


^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 03/11] ARM: OMAP2+: timer: Add suspend-resume callbacks for clkevent device
@ 2014-07-14 17:42         ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-14 17:42 UTC (permalink / raw)
  To: linux-arm-kernel

Santosh, Tony,

On 07/14/2014 09:37 AM, Santosh Shilimkar wrote:
> On Monday 14 July 2014 07:15 AM, Tony Lindgren wrote:
>> * Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
>>> From: Vaibhav Bedia <vaibhav.bedia@ti.com>
>>>
>>> OMAP timer code registers two timers - one as clocksource
>>> and one as clockevent. Since AM33XX has only one usable timer
>>> in the WKUP domain 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.
>>>
>>> Signed-off-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
>>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>>> ---
>>> v3->v4:
>>> 	Only use for am33xx soc now.
>>>
>>>   arch/arm/mach-omap2/timer.c | 28 ++++++++++++++++++++++++++++
>>>   1 file changed, 28 insertions(+)
>>>
>>> diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
>>> index 43d03fb..6fc1748 100644
>>> --- a/arch/arm/mach-omap2/timer.c
>>> +++ b/arch/arm/mach-omap2/timer.c
>>> @@ -128,6 +128,29 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
>>>   	}
>>>   }
>>>
>>> +static void omap_clkevt_suspend(struct clock_event_device *unused)
>>> +{
>>> +	struct omap_hwmod *oh;
>>> +
>>> +	oh = omap_hwmod_lookup(clockevent_gpt.name);
>>> +	if (!oh)
>>> +		return;
>>> +
>>> +	omap_hwmod_idle(oh);
>>> +}
>>> +
>>> +static void omap_clkevt_resume(struct clock_event_device *unused)
>>> +{
>>> +	struct omap_hwmod *oh;
>>> +
>>> +	oh = omap_hwmod_lookup(clockevent_gpt.name);
>>> +	if (!oh)
>>> +		return;
>>> +
>>> +	omap_hwmod_enable(oh);
>>> +	__omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);
>>> +}
>>> +
>>
>> This is going to make moving the timer code into drivers one step
>> tougher to do. And you don't need to look up the hwmod entry every
>> time, just initialize it during the init.

Yes you are right about looking up only at init I need to change that. I agree 
that this makes moving the timers harder but I'm not sure there's any way around 
this. I attempted to use the omap_device layer here but there is no device at 
all created here, it does not hook into the normal PM layer through omap_device. 
This clock must be shut off as it sits in the peripheral power domain and any 
active clock within the domain will prevent suspend from happening, so we end up 
with a platform specific issue here. It seems that the only way I can get to the 
clock is through the hwmod.

>>
>>> +	if (soc_is_am33xx()) {
>>> +		clockevent_gpt.suspend = omap_clkevt_suspend;
>>> +		clockevent_gpt.resume = omap_clkevt_resume;
>>> +	}
>>> +
>>
>> Maybe try to set up things so we initialize the SoC specific
>> timer suspend and resume functions in mach-omap2/timer.c
>> in a way where eventually the device driver can easily use
>> them?
>>
> +1. I had similar comments on the previous version too.

This was my attempt to make things specific to only am335x based on Santosh's 
previous comments as last time they were populated for every device even when 
unneeded. These are not standard suspend/resume handlers, they are specific to 
clock event. I know there will always need to be at least some code here for the 
early timer init based on previous timer cleanup series I've seen, so perhaps I 
could hook it in there when the time comes?

Regards,
Dave

>
> Regards,
> Santosh
>

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 02/11] memory: emif: Move EMIF register defines to include/linux/
  2014-07-14 11:12     ` Tony Lindgren
@ 2014-07-14 17:42       ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-14 17:42 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: linux-arm-kernel, linux-omap, Paul Walmsley, Kevin Hilman,
	Tero Kristo, Nishanth Menon, Russ Dill, Santosh Shilimkar,
	Daniel Mack, Suman Anna, Benoit Cousson

On 07/14/2014 06:12 AM, Tony Lindgren wrote:
> * Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
>> 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 register defines in the emif header file to include/linux so that
>> both the EMIF driver and the AM33XX PM code can benefit.
>>
>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>> Reviewed-by: Russ Dill <russ.dill@ti.com>
>> Acked-by: Santosh Shililmar <santosh.shilimkar@ti.com>
>> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
>> ---
>> v3->v4:
>> patch unchanged from original:
>> 	http://www.spinics.net/lists/linux-omap/msg95314.html
>>
>>   drivers/memory/emif.h   | 543 +---------------------------------------------
>>   include/linux/ti_emif.h | 558 ++++++++++++++++++++++++++++++++++++++++++++++++
>
> So far we've seen that exposing hardware registers like this
> will lead into various drivers misusing them. I think a better
> solution is to implement few targeted functions that allow
> sharing code between the platform idle code and memory driver.
>
> Maybe you can have the shared functions in in something like
> drivers/memory/ti-emif.c that's always built in? The idle
> code won't need any of that early on.

Well the reason it was done this way was to utilize all of the addresses of EMIF 
register in the ASM sleep code to do relative addressing from the EMIF base 
address. The ASM sleep code (patch 9) needs to save and restore emif context and 
set and unset self refresh in emif. The issues will come from the ASM being 
copied to and running from SRAM without the ability to access code in DDR 
(because we are shutting the EMIF off), so we would need to copy these functions 
as well and have to worry about any issues we introduce by relocating c code. Is 
it worth the added maintenance burden?

Regards,
Dave

> Regards,
>
> Tony
>


^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 02/11] memory: emif: Move EMIF register defines to include/linux/
@ 2014-07-14 17:42       ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-14 17:42 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/14/2014 06:12 AM, Tony Lindgren wrote:
> * Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
>> 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 register defines in the emif header file to include/linux so that
>> both the EMIF driver and the AM33XX PM code can benefit.
>>
>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>> Reviewed-by: Russ Dill <russ.dill@ti.com>
>> Acked-by: Santosh Shililmar <santosh.shilimkar@ti.com>
>> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
>> ---
>> v3->v4:
>> patch unchanged from original:
>> 	http://www.spinics.net/lists/linux-omap/msg95314.html
>>
>>   drivers/memory/emif.h   | 543 +---------------------------------------------
>>   include/linux/ti_emif.h | 558 ++++++++++++++++++++++++++++++++++++++++++++++++
>
> So far we've seen that exposing hardware registers like this
> will lead into various drivers misusing them. I think a better
> solution is to implement few targeted functions that allow
> sharing code between the platform idle code and memory driver.
>
> Maybe you can have the shared functions in in something like
> drivers/memory/ti-emif.c that's always built in? The idle
> code won't need any of that early on.

Well the reason it was done this way was to utilize all of the addresses of EMIF 
register in the ASM sleep code to do relative addressing from the EMIF base 
address. The ASM sleep code (patch 9) needs to save and restore emif context and 
set and unset self refresh in emif. The issues will come from the ASM being 
copied to and running from SRAM without the ability to access code in DDR 
(because we are shutting the EMIF off), so we would need to copy these functions 
as well and have to worry about any issues we introduce by relocating c code. Is 
it worth the added maintenance burden?

Regards,
Dave

> Regards,
>
> Tony
>

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 06/11] remoteproc: wkup_m3: Add wkup_m3 remote proc driver
  2014-07-14 14:54     ` Santosh Shilimkar
@ 2014-07-14 17:43       ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-14 17:43 UTC (permalink / raw)
  To: Santosh Shilimkar
  Cc: linux-arm-kernel, linux-omap, Paul Walmsley, Kevin Hilman,
	Tony Lindgren, Tero Kristo, Nishanth Menon, Russ Dill,
	Daniel Mack, Suman Anna, Benoit Cousson, Ohad Ben-Cohen

On 07/14/2014 09:54 AM, Santosh Shilimkar wrote:
> On Thursday 10 July 2014 10:55 PM, Dave Gerlach wrote:
>> Add a remoteproc driver to load the firmware for and boot the wkup_m3
>> present on am33xx. The wkup_m3 is an integrated Cortex M3 that allows
>> the SoC to enter the lowest possible power state by taking control from
>> the MPU after it has gone into its own low power state and shutting off
>> any additional peripherals.
>>
>> Communication between the MPU and CM3 is handled by several IPC
>> registers in the control module and a mailbox. An API is exposed for
>> programming the aforementioned IPC registers and notifying the wkup_m3
>> of pending data using the mailbox. The wkup_m3 has the ability to
>> trigger an interrupt back to the MPU to allow for bidirectional
>> communication through these registers.
>>
>> Two callbacks are provided. rproc_ready allows code to hook into the
>> driver to see when firmware has been loaded and execute other code and
>> txev_handler allows external code to run when the wkup_m3 triggers an
>> interrupt back to the m3.
>>
>> The driver expects a resource table to be present in the wkup_m3 to
>> define the required memory resources needed by wkup_m3, at least the
>> data memory so that the firmware can be copied for the proper place for
>> execution.
>>
>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>> CC: Ohad Ben-Cohen <ohad@wizery.com>
>> ---
>>   drivers/remoteproc/Kconfig         |  15 ++
>>   drivers/remoteproc/Makefile        |   1 +
>>   drivers/remoteproc/wkup_m3_rproc.c | 512 +++++++++++++++++++++++++++++++++++++
>>   include/linux/wkup_m3.h            |  71 +++++
>>   4 files changed, 599 insertions(+)
>>   create mode 100644 drivers/remoteproc/wkup_m3_rproc.c
>>   create mode 100644 include/linux/wkup_m3.h
>>
>> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
>> index 5e343ba..4b00c21 100644
>> --- a/drivers/remoteproc/Kconfig
>> +++ b/drivers/remoteproc/Kconfig
>> @@ -41,6 +41,21 @@ config STE_MODEM_RPROC
>>   	  This can be either built-in or a loadable module.
>>   	  If unsure say N.
>>
>> +config WKUP_M3_RPROC
>> +	bool "AM33xx wkup-m3 remoteproc support"
>> +        depends on SOC_AM33XX
>> +        select REMOTEPROC
>> +	select MAILBOX
>> +	select OMAP2PLUS_MBOX
> Please fix the indentation

Whoops, thanks for pointing this out, checkpatch didn't catch it.
.
>
>> +	default n
> Default is always 'n' so drop above.

No problem.

>> +	help
>> +	  Say y here to support AM33xx wkup-m3.
>> +
>> +	  Required for Suspend-to-ram and CPUIdle on AM33xx. Allows for
>> +	  loading of firmware and communication with CM3 PM coproccesor
>> +	  that is present on AM33xx family of SoCs
>> +	  If unsure say N.
>> +
>>   config DA8XX_REMOTEPROC
>>   	tristate "DA8xx/OMAP-L13x remoteproc support"
>>   	depends on ARCH_DAVINCI_DA8XX
>> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
>> index ac2ff75..81b04d1 100644
>> --- a/drivers/remoteproc/Makefile
>> +++ b/drivers/remoteproc/Makefile
>> @@ -9,4 +9,5 @@ remoteproc-y				+= remoteproc_virtio.o
>>   remoteproc-y				+= remoteproc_elf_loader.o
>>   obj-$(CONFIG_OMAP_REMOTEPROC)		+= omap_remoteproc.o
>>   obj-$(CONFIG_STE_MODEM_RPROC)	 	+= ste_modem_rproc.o
>> +obj-$(CONFIG_WKUP_M3_RPROC)		+= wkup_m3_rproc.o
>>   obj-$(CONFIG_DA8XX_REMOTEPROC)		+= da8xx_remoteproc.o
>> diff --git a/drivers/remoteproc/wkup_m3_rproc.c b/drivers/remoteproc/wkup_m3_rproc.c
>> new file mode 100644
>> index 0000000..58aeaf2
>> --- /dev/null
>> +++ b/drivers/remoteproc/wkup_m3_rproc.c
>> @@ -0,0 +1,512 @@
>> +/*
>> + * AMx3 Wkup M3 Remote Processor driver
>> + *
>> + * Copyright (C) 2014 Texas Instruments, Inc.
>> + *
>> + * Dave Gerlach <d-gerlach@ti.com>
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * version 2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/err.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/irq.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/elf.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/firmware.h>
>> +#include <linux/remoteproc.h>
>> +#include <linux/omap-mailbox.h>
>> +#include <linux/mailbox_client.h>
>> +#include <linux/wkup_m3.h>
>> +#include <linux/kthread.h>
>> +#include "remoteproc_internal.h"
>> +
>> +#include <linux/platform_data/wkup_m3.h>
>> +
>> +#define WKUP_M3_WAKE_SRC_MASK		0xFF
>> +
>> +#define WKUP_M3_STATUS_RESP_SHIFT	16
>> +#define WKUP_M3_STATUS_RESP_MASK	(0xffff << 16)
>> +
>> +#define WKUP_M3_FW_VERSION_SHIFT	0
>> +#define WKUP_M3_FW_VERSION_MASK		0xffff
>> +
>> +/* AM33XX M3_TXEV_EOI register */
>> +#define AM33XX_CONTROL_M3_TXEV_EOI	0x00
>> +
>> +#define AM33XX_M3_TXEV_ACK		(0x1 << 0)
>> +#define AM33XX_M3_TXEV_ENABLE		(0x0 << 0)
>> +
>> +/* AM33XX IPC message registers */
>> +#define AM33XX_CONTROL_IPC_MSG_REG0	0x04
>> +#define AM33XX_CONTROL_IPC_MSG_REG1	0x08
>> +#define AM33XX_CONTROL_IPC_MSG_REG2	0x0c
>> +#define AM33XX_CONTROL_IPC_MSG_REG3	0x10
>> +#define AM33XX_CONTROL_IPC_MSG_REG4	0x14
>> +#define AM33XX_CONTROL_IPC_MSG_REG5	0x18
>> +#define AM33XX_CONTROL_IPC_MSG_REG6	0x1c
>> +#define AM33XX_CONTROL_IPC_MSG_REG7	0x20
>> +
> Is this driver going to be AM33xx specific ?

Same driver will be used for am43xx in the future. I suppose the above macros 
would be more appropriately named WKUP_M3_*

>
>> +struct wkup_m3_rproc {
>> +	struct rproc *rproc;
>> +
>> +	void * __iomem dev_table_va;
>> +	void * __iomem ipc_mem_base;
>> +	struct platform_device *pdev;
>> +
>> +	struct mbox_client mbox_client;
>> +	struct mbox_chan *mbox;
>> +	struct wkup_m3_ops *ops;
>> +
>> +	bool is_active;
>> +};
>> +
>> +static struct wkup_m3_rproc *m3_rproc_static;
>> +
>> +static struct wkup_m3_wakeup_src wakeups[] = {
>> +	{.irq_nr = 35,	.src = "USB0_PHY"},
>> +	{.irq_nr = 36,	.src = "USB1_PHY"},
>> +	{.irq_nr = 40,	.src = "I2C0"},
>> +	{.irq_nr = 41,	.src = "RTC Timer"},
>> +	{.irq_nr = 42,	.src = "RTC Alarm"},
>> +	{.irq_nr = 43,	.src = "Timer0"},
>> +	{.irq_nr = 44,	.src = "Timer1"},
>> +	{.irq_nr = 45,	.src = "UART"},
>> +	{.irq_nr = 46,	.src = "GPIO0"},
>> +	{.irq_nr = 48,	.src = "MPU_WAKE"},
>> +	{.irq_nr = 49,	.src = "WDT0"},
>> +	{.irq_nr = 50,	.src = "WDT1"},
>> +	{.irq_nr = 51,	.src = "ADC_TSC"},
>> +	{.irq_nr = 0,	.src = "Unknown"},
>> +};
>> +
> const ?
>

Yes, that would be more appropriate thanks.

>> +static void am33xx_txev_eoi(struct wkup_m3_rproc *m3_rproc)
>> +{
>> +	writel(AM33XX_M3_TXEV_ACK,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
>> +}
>> +
>> +static void am33xx_txev_enable(struct wkup_m3_rproc *m3_rproc)
>> +{
>> +	writel(AM33XX_M3_TXEV_ENABLE,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
>> +}
>> +
>> +static void wkup_m3_ctrl_ipc_write(struct wkup_m3_rproc *m3_rproc,
>> +				   struct wkup_m3_ipc_regs *ipc_regs)
>> +{
>> +	writel(ipc_regs->reg0,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG0);
>> +	writel(ipc_regs->reg1,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG1);
>> +	writel(ipc_regs->reg2,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG2);
>> +	writel(ipc_regs->reg3,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG3);
>> +	writel(ipc_regs->reg4,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG4);
>> +	writel(ipc_regs->reg5,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG5);
>> +	writel(ipc_regs->reg6,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG6);
>> +	writel(ipc_regs->reg7,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG7);
>> +}
>> +
>> +static void wkup_m3_ctrl_ipc_read(struct wkup_m3_rproc *m3_rproc,
>> +				  struct wkup_m3_ipc_regs *ipc_regs)
>> +{
>> +	ipc_regs->reg0 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG0);
>> +	ipc_regs->reg1 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG1);
>> +	ipc_regs->reg2 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG2);
>> +	ipc_regs->reg3 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG3);
>> +	ipc_regs->reg4 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG4);
>> +	ipc_regs->reg5 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG5);
>> +	ipc_regs->reg6 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG6);
>> +	ipc_regs->reg7 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG7);
>> +}
>> +
>> +static irqreturn_t wkup_m3_txev_handler(int irq, void *unused)
>> +{
>> +	am33xx_txev_eoi(m3_rproc_static);
>> +
>> +	if (m3_rproc_static->ops && m3_rproc_static->ops->txev_handler)
>> +		m3_rproc_static->ops->txev_handler();
>> +
>> +	am33xx_txev_enable(m3_rproc_static);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +/**
>> + * wkup_m3_fw_version_clear - Clear FW version from ipc regs
>> + *
>> + * Invalidate M3 firmware version before hardreset.
>> + * Write invalid version in lower 4 nibbles of parameter
>> + * register (ipc_regs + 0x8).
>> + */
>> +
>> +static void wkup_m3_fw_version_clear(void)
>> +{
>> +	struct wkup_m3_ipc_regs ipc_regs;
>> +
>> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
>> +	ipc_regs.reg2 &= 0xFFFF0000;
> Probably define a macro and use it.

Yes I agree.

>> +	wkup_m3_ctrl_ipc_write(m3_rproc_static, &ipc_regs);
>> +}
>> +
>> +static void wkup_m3_mbox_callback(struct mbox_client *client, void *data)
>> +{
>> +	omap_mbox_disable_irq(m3_rproc_static->mbox, IRQ_RX);
>> +}
>> +
>> +static int wkup_m3_rproc_start(struct rproc *rproc)
>> +{
>> +	struct wkup_m3_rproc *m3_rproc = rproc->priv;
>> +	struct platform_device *pdev = m3_rproc->pdev;
>> +	struct device *dev = &pdev->dev;
>> +	struct wkup_m3_platform_data *pdata = dev->platform_data;
>> +	int ret;
>> +
>> +	wkup_m3_fw_version_clear();
>> +
>> +	if (pdata && pdata->deassert_reset) {
>> +		ret = pdata->deassert_reset(pdev, pdata->reset_name);
>> +		if (ret) {
>> +			dev_err(dev, "Unable to reset wkup_m3!\n");
>> +			return -ENODEV;
>> +		}
>> +	} else {
>> +		dev_err(dev, "Platform data missing deassert_reset!\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	m3_rproc->mbox_client.dev = dev;
>> +	m3_rproc->mbox_client.tx_done = NULL;
>> +	m3_rproc->mbox_client.rx_callback = wkup_m3_mbox_callback;
>> +	m3_rproc->mbox_client.tx_block = false;
>> +	m3_rproc->mbox_client.knows_txdone = false;
>> +
>> +	m3_rproc->mbox = mbox_request_channel(&m3_rproc->mbox_client);
>> +
>> +	if (IS_ERR(m3_rproc->mbox)) {
>> +		dev_err(dev, "IPC Request for A8->M3 Channel failed!\n");
>> +		ret = PTR_ERR(m3_rproc->mbox);
>> +		m3_rproc->mbox = NULL;
>> +		return ret;
>> +	}
>> +
>> +	if (m3_rproc_static->ops && m3_rproc_static->ops->rproc_ready)
>> +		m3_rproc_static->ops->rproc_ready();
>> +
>> +	m3_rproc_static->is_active = 1;
>> +
>> +	return 0;
>> +}
>> +
>> +
>> +static int wkup_m3_rproc_stop(struct rproc *rproc)
>> +{
>> +	return 0;
>> +}
>> +
>> +static void wkup_m3_rproc_kick(struct rproc *rproc, int vqid)
>> +{
>> +}
>> +
>> +static struct rproc_ops wkup_m3_rproc_ops = {
>> +	.start		= wkup_m3_rproc_start,
>> +	.stop		= wkup_m3_rproc_stop,
>> +	.kick		= wkup_m3_rproc_kick,
>> +};
>> +
>> +/* Public Functions */
>> +
>> +/**
>> + * wkup_m3_set_ops - Set callbacks for user of rproc
>> + * @ops - struct wkup_m3_ops *
>> + *
>> + * Registers callbacks to wkup_m3 to be invoked after rproc is ready to use
>> + * and after an interrupt is handled.
>> + */
>> +void wkup_m3_set_ops(struct wkup_m3_ops *ops)
>> +{
>> +	m3_rproc_static->ops = ops;
>> +
>> +	if (m3_rproc_static->is_active && m3_rproc_static->ops &&
>> +	    m3_rproc_static->ops->rproc_ready)
>> +		m3_rproc_static->ops->rproc_ready();
>> +}
>> +
>> +/**
>> + * wkup_m3_ping - Send a dummy msg to wkup_m3 to tell to to check IPC regs
>> + *
>> + * Returns the result of sending mbox msg or -EIO if no mbox handle is present
>> + */
>> +int wkup_m3_ping(void)
>> +{
>> +	int ret;
>> +	mbox_msg_t dummy_msg = 0;
>> +
>> +	if (!m3_rproc_static->mbox) {
>> +		dev_err(&m3_rproc_static->pdev->dev,
>> +			"No IPC channel to communicate with wkup_m3!\n");
>> +		return -EIO;
>> +	}
>> +
>> +	/*
>> +	 * Write a dummy message to the mailbox in order to trigger the RX
>> +	 * interrupt to alert the M3 that data is available in the IPC
>> +	 * registers. We must enable the IRQ here and disable it after in
>> +	 * the RX callback to avoid multiple interrupts being received
>> +	 * by the CM3.
>> +	 */
>> +	omap_mbox_enable_irq(m3_rproc_static->mbox, IRQ_RX);
>> +	ret = mbox_send_message(m3_rproc_static->mbox, (void *)dummy_msg);
>> +
>> +	if (ret < 0) {
>> +		pr_err("%s: mbox_send_message() failed: %d\n", __func__, ret);
>> +		return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * wkup_m3_wake_src - Get the wakeup source info passed from wkup_m3
>> + * @wkup_m3_wakeup: struct wkup_m3_wakeup_src * gets assigned the
>> + *		    wakeup src value
>> + */
>> +void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wkup_m3_wakeup)
>> +{
>> +	struct wkup_m3_ipc_regs ipc_regs;
>> +	unsigned int wakeup_src_idx;
>> +	int j;
>> +
>> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
>> +
>> +	wakeup_src_idx = ipc_regs.reg6 & WKUP_M3_WAKE_SRC_MASK;
>> +
>> +	for (j = 0; j < ARRAY_SIZE(wakeups)-1; j++) {
>> +		if (wakeups[j].irq_nr == wakeup_src_idx) {
>> +			*wkup_m3_wakeup = wakeups[j];
>> +			return;
>> +		}
>> +	}
>> +	*wkup_m3_wakeup = wakeups[j];
>> +}
>> +
>> +/**
>> + * wkup_m3_pm_status - Return the status code from wkup_m3 after sleep event
>> + *
>> + * Returns an error code that indicates whether or not the dsired sleep
>> + * action was a success or not.
>> + */
>> +int wkup_m3_pm_status(void)
>> +{
>> +	unsigned int i;
>> +	struct wkup_m3_ipc_regs ipc_regs;
>> +
>> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
>> +
>> +	i = WKUP_M3_STATUS_RESP_MASK & ipc_regs.reg1;
>> +	i >>= __ffs(WKUP_M3_STATUS_RESP_MASK);
>> +
>> +	return i;
>> +}
>> +
>> +/**
>> + * wkup_m3_fw_version_read - Return the fw version given by the wkup_m3
>> + *
>> + * After boot the fw version should be read to ensure it is compatible.
>> + */
>> +int wkup_m3_fw_version_read(void)
>> +{
>> +	struct wkup_m3_ipc_regs ipc_regs;
>> +
>> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
>> +
>> +	return ipc_regs.reg2 & WKUP_M3_FW_VERSION_MASK;
>> +}
>> +
>> +/**
>> + * wkup_m3_set_cmd - write contents of struct to ipc regs
>> + * @ipc_regs: struct wkup_m3_ipc_regs *
>> + */
>> +void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs)
>> +{
>> +	wkup_m3_ctrl_ipc_write(m3_rproc_static, ipc_regs);
>> +}
>> +
>> +static void wkup_m3_rproc_loader_thread(struct rproc *rproc)
>> +{
>> +	struct wkup_m3_rproc *m3_rproc = rproc->priv;
>> +	struct device *dev = &m3_rproc->pdev->dev;
>> +	int ret;
>> +
>> +	wait_for_completion(&rproc->firmware_loading_complete);
>> +
>> +	ret = rproc_boot(rproc);
>> +	if (ret)
>> +		dev_err(dev, "rproc_boot failed\n");
>> +
>> +	do_exit(0);
>> +}
>> +
>> +static int wkup_m3_rproc_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct device_node *np = pdev->dev.of_node;
>> +	struct wkup_m3_rproc *m3_rproc;
>> +	struct rproc *rproc;
>> +	int irq, ret;
>> +	struct resource *res;
>> +	struct task_struct *task;
>> +	const char *mbox_name;
>> +
>> +	pm_runtime_enable(&pdev->dev);
>> +
>> +	ret = pm_runtime_get_sync(&pdev->dev);
>> +	if (IS_ERR_VALUE(ret)) {
>> +		dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
>> +		return ret;
>> +	}
>> +
>> +	rproc = rproc_alloc(dev, "wkup_m3", &wkup_m3_rproc_ops,
>> +			    "am335x-pm-firmware.elf", sizeof(*m3_rproc));
>> +	if (!rproc)
>> +		return -ENOMEM;
>> +
>> +	m3_rproc = rproc->priv;
>> +	m3_rproc->rproc = rproc;
>> +	m3_rproc->pdev = pdev;
>> +
>> +	m3_rproc_static = m3_rproc;
>> +
>> +	irq = platform_get_irq(pdev, 0);
>> +	if (!irq) {
>> +		dev_err(&pdev->dev, "no irq resource\n");
>> +		ret = -ENXIO;
>> +		goto err;
>> +	}
>> +
>> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ipc_regs");
>> +	if (!res) {
>> +		dev_err(&pdev->dev, "no memory resource for ipc\n");
>> +		ret = -ENXIO;
>> +		goto err;
>> +	}
>> +
>> +	m3_rproc->ipc_mem_base = devm_request_and_ioremap(dev, res);
>> +	if (!m3_rproc->ipc_mem_base) {
>> +		dev_err(dev, "could not ioremap ipc_mem\n");
>> +		ret = -EADDRNOTAVAIL;
>> +		goto err;
>> +	}
>> +
>> +	ret = devm_request_irq(dev, irq, wkup_m3_txev_handler,
>> +			       IRQF_DISABLED, "wkup_m3_txev", m3_rproc);
>> +	if (ret) {
>> +		dev_err(dev, "request_irq failed\n");
>> +		goto err;
>> +	}
>> +
>> +	/* Get mbox name from device tree node */
>> +	ret = of_property_read_string(np, "mbox-names", &mbox_name);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "Unable to get mbox name from dt node: %d\n",
>> +			ret);
>> +			goto err;
>> +	};
>> +
>> +	m3_rproc->mbox_client.chan_name = mbox_name;
>> +
>> +	/* Register as a remoteproc device */
>> +	ret = rproc_add(rproc);
>> +	if (ret) {
>> +		dev_err(dev, "rproc_add failed\n");
>> +		goto err;
>> +	}
>> +
>> +	/*
>> +	 * Wait for firmware loading completion in a thread so we
>> +	 * can boot the wkup_m3 as soon as it's ready without holding
>> +	 * up kernel boot
>> +	 */
>> +	task = kthread_run((void *)wkup_m3_rproc_loader_thread, rproc,
>> +			   "wkup_m3_rproc_loader");
>> +
>> +	if (IS_ERR(task)) {
>> +		dev_err(dev, "can't create rproc_loader thread\n");
>> +		goto err;
>> +	}
>> +
>> +	return 0;
>> +
>> +err:
>> +	rproc_put(rproc);
> pm_runtime_put_sync() ?

Yes, I do need that.

>> +	return ret;
>> +}
>> +
>> +static int wkup_m3_rproc_remove(struct platform_device *pdev)
>> +{
>> +	struct rproc *rproc = platform_get_drvdata(pdev);
>> +
>> +	rproc_del(rproc);
>> +	rproc_put(rproc);
>> +
> Here too ?

Yes, again here.

>> +	return 0;
>> +}
>> +
>> +static int wkup_m3_rpm_suspend(struct device *dev)
>> +{
>> +	return -EBUSY;
>> +}
>> +
>> +static int wkup_m3_rpm_resume(struct device *dev)
>> +{
>> +	return 0;
>> +}
>> +
> If you are not doing any meaningfull in suspend/resume hooks,
> why are you even registering them ?

The suspend hook is needed to prevent omap_device from idling the wkup_m3 hwmod, 
without it we get a warning from omap_hwmod.

>
>> +static const struct dev_pm_ops wkup_m3_rproc_pm_ops = {
>> +	SET_RUNTIME_PM_OPS(wkup_m3_rpm_suspend, wkup_m3_rpm_resume, NULL)
>> +};
>> +
>> +static const struct of_device_id wkup_m3_rproc_of_match[] = {
>> +	{ .compatible = "ti,am3353-wkup-m3", .data = NULL, },
>> +	{},
>> +};
>> +
>> +static struct platform_driver wkup_m3_rproc_driver = {
>> +	.probe = wkup_m3_rproc_probe,
>> +	.remove = wkup_m3_rproc_remove,
>> +	.driver = {
>> +		.name = "wkup_m3",
>> +		.owner = THIS_MODULE,
>> +		.of_match_table = wkup_m3_rproc_of_match,
>> +		.pm = &wkup_m3_rproc_pm_ops,
>> +	},
>> +};
>> +
>> +module_platform_driver(wkup_m3_rproc_driver);
>> +
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_DESCRIPTION("wkup m3 remote processor control driver");
>> diff --git a/include/linux/wkup_m3.h b/include/linux/wkup_m3.h
>> new file mode 100644
>> index 0000000..1a2237f
>> --- /dev/null
>> +++ b/include/linux/wkup_m3.h
>> @@ -0,0 +1,71 @@
>> +/*
>> + * TI Wakeup M3 Power Management Routines
>> + *
>> + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
>> + * Dave Gerlach <d-gerlach@ti.com>
>> + *
>> + * 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 _LINUX_WKUP_M3_H
>> +#define _LINUX_WKUP_M3_H
>> +
>> +/**
>> + * struct wkup_m3_ops - Callbacks for allowing pm code to interact with wkup_m3.
>> + *
>> + * @txev_handler: Callback to allow pm code to react to response from wkup_m3
>> + *		  after pinging it using wkup_m3_ping.
>> + *
>> + * @firmware_loaded: Callback invoked when the firmware has been loaded to the
>> + *		     m3 to allow the pm code to enable suspend/resume ops.
>> + */
>> +
>> +struct wkup_m3_ops {
>> +	void (*txev_handler)(void);
>> +	void (*rproc_ready)(void);
>> +};
>> +
>> +struct wkup_m3_wakeup_src {
>> +	int irq_nr;
>> +	char src[10];
>> +};
>> +
>> +struct wkup_m3_ipc_regs {
>> +	u32 reg0;
>> +	u32 reg1;
>> +	u32 reg2;
>> +	u32 reg3;
>> +	u32 reg4;
>> +	u32 reg5;
>> +	u32 reg6;
>> +	u32 reg7;
>> +};
>> +
>> +#ifdef CONFIG_WKUP_M3_RPROC
>> +int wkup_m3_prepare(void);
>> +void wkup_m3_set_ops(struct wkup_m3_ops *ops);
>> +int wkup_m3_ping(void);
>> +void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wakeup_src);
>> +int wkup_m3_pm_status(void);
>> +int wkup_m3_is_valid(void);
>> +int wkup_m3_fw_version_read(void);
>> +void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs);
>> +
>> +#else
>> +
>> +int wkup_m3_prepare(void) { return -EINVAL; }
>> +void wkup_m3_set_ops(struct wkup_m3_ops *ops) { }
>> +int wkup_m3_ping(void) { return -EINVAL }
>> +void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wakeup_src) { }
>> +int wkup_m3_pm_status(void) { return -1; }
>> +int wkup_m3_fw_version_read(void) { return -1; }
>> +void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs) { }
>
> Mark all of above in else as static inline.

Ok, that makes sense.

Thanks for the comments.

Regards,
Dave

>
>> +#endif /* CONFIG_WKUP_M3_RPROC */
>> +#endif /* _LINUX_WKUP_M3_H */
>>
>


^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 06/11] remoteproc: wkup_m3: Add wkup_m3 remote proc driver
@ 2014-07-14 17:43       ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-14 17:43 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/14/2014 09:54 AM, Santosh Shilimkar wrote:
> On Thursday 10 July 2014 10:55 PM, Dave Gerlach wrote:
>> Add a remoteproc driver to load the firmware for and boot the wkup_m3
>> present on am33xx. The wkup_m3 is an integrated Cortex M3 that allows
>> the SoC to enter the lowest possible power state by taking control from
>> the MPU after it has gone into its own low power state and shutting off
>> any additional peripherals.
>>
>> Communication between the MPU and CM3 is handled by several IPC
>> registers in the control module and a mailbox. An API is exposed for
>> programming the aforementioned IPC registers and notifying the wkup_m3
>> of pending data using the mailbox. The wkup_m3 has the ability to
>> trigger an interrupt back to the MPU to allow for bidirectional
>> communication through these registers.
>>
>> Two callbacks are provided. rproc_ready allows code to hook into the
>> driver to see when firmware has been loaded and execute other code and
>> txev_handler allows external code to run when the wkup_m3 triggers an
>> interrupt back to the m3.
>>
>> The driver expects a resource table to be present in the wkup_m3 to
>> define the required memory resources needed by wkup_m3, at least the
>> data memory so that the firmware can be copied for the proper place for
>> execution.
>>
>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>> CC: Ohad Ben-Cohen <ohad@wizery.com>
>> ---
>>   drivers/remoteproc/Kconfig         |  15 ++
>>   drivers/remoteproc/Makefile        |   1 +
>>   drivers/remoteproc/wkup_m3_rproc.c | 512 +++++++++++++++++++++++++++++++++++++
>>   include/linux/wkup_m3.h            |  71 +++++
>>   4 files changed, 599 insertions(+)
>>   create mode 100644 drivers/remoteproc/wkup_m3_rproc.c
>>   create mode 100644 include/linux/wkup_m3.h
>>
>> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
>> index 5e343ba..4b00c21 100644
>> --- a/drivers/remoteproc/Kconfig
>> +++ b/drivers/remoteproc/Kconfig
>> @@ -41,6 +41,21 @@ config STE_MODEM_RPROC
>>   	  This can be either built-in or a loadable module.
>>   	  If unsure say N.
>>
>> +config WKUP_M3_RPROC
>> +	bool "AM33xx wkup-m3 remoteproc support"
>> +        depends on SOC_AM33XX
>> +        select REMOTEPROC
>> +	select MAILBOX
>> +	select OMAP2PLUS_MBOX
> Please fix the indentation

Whoops, thanks for pointing this out, checkpatch didn't catch it.
.
>
>> +	default n
> Default is always 'n' so drop above.

No problem.

>> +	help
>> +	  Say y here to support AM33xx wkup-m3.
>> +
>> +	  Required for Suspend-to-ram and CPUIdle on AM33xx. Allows for
>> +	  loading of firmware and communication with CM3 PM coproccesor
>> +	  that is present on AM33xx family of SoCs
>> +	  If unsure say N.
>> +
>>   config DA8XX_REMOTEPROC
>>   	tristate "DA8xx/OMAP-L13x remoteproc support"
>>   	depends on ARCH_DAVINCI_DA8XX
>> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
>> index ac2ff75..81b04d1 100644
>> --- a/drivers/remoteproc/Makefile
>> +++ b/drivers/remoteproc/Makefile
>> @@ -9,4 +9,5 @@ remoteproc-y				+= remoteproc_virtio.o
>>   remoteproc-y				+= remoteproc_elf_loader.o
>>   obj-$(CONFIG_OMAP_REMOTEPROC)		+= omap_remoteproc.o
>>   obj-$(CONFIG_STE_MODEM_RPROC)	 	+= ste_modem_rproc.o
>> +obj-$(CONFIG_WKUP_M3_RPROC)		+= wkup_m3_rproc.o
>>   obj-$(CONFIG_DA8XX_REMOTEPROC)		+= da8xx_remoteproc.o
>> diff --git a/drivers/remoteproc/wkup_m3_rproc.c b/drivers/remoteproc/wkup_m3_rproc.c
>> new file mode 100644
>> index 0000000..58aeaf2
>> --- /dev/null
>> +++ b/drivers/remoteproc/wkup_m3_rproc.c
>> @@ -0,0 +1,512 @@
>> +/*
>> + * AMx3 Wkup M3 Remote Processor driver
>> + *
>> + * Copyright (C) 2014 Texas Instruments, Inc.
>> + *
>> + * Dave Gerlach <d-gerlach@ti.com>
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * version 2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/err.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/irq.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/elf.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/firmware.h>
>> +#include <linux/remoteproc.h>
>> +#include <linux/omap-mailbox.h>
>> +#include <linux/mailbox_client.h>
>> +#include <linux/wkup_m3.h>
>> +#include <linux/kthread.h>
>> +#include "remoteproc_internal.h"
>> +
>> +#include <linux/platform_data/wkup_m3.h>
>> +
>> +#define WKUP_M3_WAKE_SRC_MASK		0xFF
>> +
>> +#define WKUP_M3_STATUS_RESP_SHIFT	16
>> +#define WKUP_M3_STATUS_RESP_MASK	(0xffff << 16)
>> +
>> +#define WKUP_M3_FW_VERSION_SHIFT	0
>> +#define WKUP_M3_FW_VERSION_MASK		0xffff
>> +
>> +/* AM33XX M3_TXEV_EOI register */
>> +#define AM33XX_CONTROL_M3_TXEV_EOI	0x00
>> +
>> +#define AM33XX_M3_TXEV_ACK		(0x1 << 0)
>> +#define AM33XX_M3_TXEV_ENABLE		(0x0 << 0)
>> +
>> +/* AM33XX IPC message registers */
>> +#define AM33XX_CONTROL_IPC_MSG_REG0	0x04
>> +#define AM33XX_CONTROL_IPC_MSG_REG1	0x08
>> +#define AM33XX_CONTROL_IPC_MSG_REG2	0x0c
>> +#define AM33XX_CONTROL_IPC_MSG_REG3	0x10
>> +#define AM33XX_CONTROL_IPC_MSG_REG4	0x14
>> +#define AM33XX_CONTROL_IPC_MSG_REG5	0x18
>> +#define AM33XX_CONTROL_IPC_MSG_REG6	0x1c
>> +#define AM33XX_CONTROL_IPC_MSG_REG7	0x20
>> +
> Is this driver going to be AM33xx specific ?

Same driver will be used for am43xx in the future. I suppose the above macros 
would be more appropriately named WKUP_M3_*

>
>> +struct wkup_m3_rproc {
>> +	struct rproc *rproc;
>> +
>> +	void * __iomem dev_table_va;
>> +	void * __iomem ipc_mem_base;
>> +	struct platform_device *pdev;
>> +
>> +	struct mbox_client mbox_client;
>> +	struct mbox_chan *mbox;
>> +	struct wkup_m3_ops *ops;
>> +
>> +	bool is_active;
>> +};
>> +
>> +static struct wkup_m3_rproc *m3_rproc_static;
>> +
>> +static struct wkup_m3_wakeup_src wakeups[] = {
>> +	{.irq_nr = 35,	.src = "USB0_PHY"},
>> +	{.irq_nr = 36,	.src = "USB1_PHY"},
>> +	{.irq_nr = 40,	.src = "I2C0"},
>> +	{.irq_nr = 41,	.src = "RTC Timer"},
>> +	{.irq_nr = 42,	.src = "RTC Alarm"},
>> +	{.irq_nr = 43,	.src = "Timer0"},
>> +	{.irq_nr = 44,	.src = "Timer1"},
>> +	{.irq_nr = 45,	.src = "UART"},
>> +	{.irq_nr = 46,	.src = "GPIO0"},
>> +	{.irq_nr = 48,	.src = "MPU_WAKE"},
>> +	{.irq_nr = 49,	.src = "WDT0"},
>> +	{.irq_nr = 50,	.src = "WDT1"},
>> +	{.irq_nr = 51,	.src = "ADC_TSC"},
>> +	{.irq_nr = 0,	.src = "Unknown"},
>> +};
>> +
> const ?
>

Yes, that would be more appropriate thanks.

>> +static void am33xx_txev_eoi(struct wkup_m3_rproc *m3_rproc)
>> +{
>> +	writel(AM33XX_M3_TXEV_ACK,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
>> +}
>> +
>> +static void am33xx_txev_enable(struct wkup_m3_rproc *m3_rproc)
>> +{
>> +	writel(AM33XX_M3_TXEV_ENABLE,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
>> +}
>> +
>> +static void wkup_m3_ctrl_ipc_write(struct wkup_m3_rproc *m3_rproc,
>> +				   struct wkup_m3_ipc_regs *ipc_regs)
>> +{
>> +	writel(ipc_regs->reg0,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG0);
>> +	writel(ipc_regs->reg1,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG1);
>> +	writel(ipc_regs->reg2,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG2);
>> +	writel(ipc_regs->reg3,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG3);
>> +	writel(ipc_regs->reg4,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG4);
>> +	writel(ipc_regs->reg5,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG5);
>> +	writel(ipc_regs->reg6,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG6);
>> +	writel(ipc_regs->reg7,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG7);
>> +}
>> +
>> +static void wkup_m3_ctrl_ipc_read(struct wkup_m3_rproc *m3_rproc,
>> +				  struct wkup_m3_ipc_regs *ipc_regs)
>> +{
>> +	ipc_regs->reg0 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG0);
>> +	ipc_regs->reg1 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG1);
>> +	ipc_regs->reg2 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG2);
>> +	ipc_regs->reg3 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG3);
>> +	ipc_regs->reg4 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG4);
>> +	ipc_regs->reg5 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG5);
>> +	ipc_regs->reg6 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG6);
>> +	ipc_regs->reg7 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG7);
>> +}
>> +
>> +static irqreturn_t wkup_m3_txev_handler(int irq, void *unused)
>> +{
>> +	am33xx_txev_eoi(m3_rproc_static);
>> +
>> +	if (m3_rproc_static->ops && m3_rproc_static->ops->txev_handler)
>> +		m3_rproc_static->ops->txev_handler();
>> +
>> +	am33xx_txev_enable(m3_rproc_static);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +/**
>> + * wkup_m3_fw_version_clear - Clear FW version from ipc regs
>> + *
>> + * Invalidate M3 firmware version before hardreset.
>> + * Write invalid version in lower 4 nibbles of parameter
>> + * register (ipc_regs + 0x8).
>> + */
>> +
>> +static void wkup_m3_fw_version_clear(void)
>> +{
>> +	struct wkup_m3_ipc_regs ipc_regs;
>> +
>> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
>> +	ipc_regs.reg2 &= 0xFFFF0000;
> Probably define a macro and use it.

Yes I agree.

>> +	wkup_m3_ctrl_ipc_write(m3_rproc_static, &ipc_regs);
>> +}
>> +
>> +static void wkup_m3_mbox_callback(struct mbox_client *client, void *data)
>> +{
>> +	omap_mbox_disable_irq(m3_rproc_static->mbox, IRQ_RX);
>> +}
>> +
>> +static int wkup_m3_rproc_start(struct rproc *rproc)
>> +{
>> +	struct wkup_m3_rproc *m3_rproc = rproc->priv;
>> +	struct platform_device *pdev = m3_rproc->pdev;
>> +	struct device *dev = &pdev->dev;
>> +	struct wkup_m3_platform_data *pdata = dev->platform_data;
>> +	int ret;
>> +
>> +	wkup_m3_fw_version_clear();
>> +
>> +	if (pdata && pdata->deassert_reset) {
>> +		ret = pdata->deassert_reset(pdev, pdata->reset_name);
>> +		if (ret) {
>> +			dev_err(dev, "Unable to reset wkup_m3!\n");
>> +			return -ENODEV;
>> +		}
>> +	} else {
>> +		dev_err(dev, "Platform data missing deassert_reset!\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	m3_rproc->mbox_client.dev = dev;
>> +	m3_rproc->mbox_client.tx_done = NULL;
>> +	m3_rproc->mbox_client.rx_callback = wkup_m3_mbox_callback;
>> +	m3_rproc->mbox_client.tx_block = false;
>> +	m3_rproc->mbox_client.knows_txdone = false;
>> +
>> +	m3_rproc->mbox = mbox_request_channel(&m3_rproc->mbox_client);
>> +
>> +	if (IS_ERR(m3_rproc->mbox)) {
>> +		dev_err(dev, "IPC Request for A8->M3 Channel failed!\n");
>> +		ret = PTR_ERR(m3_rproc->mbox);
>> +		m3_rproc->mbox = NULL;
>> +		return ret;
>> +	}
>> +
>> +	if (m3_rproc_static->ops && m3_rproc_static->ops->rproc_ready)
>> +		m3_rproc_static->ops->rproc_ready();
>> +
>> +	m3_rproc_static->is_active = 1;
>> +
>> +	return 0;
>> +}
>> +
>> +
>> +static int wkup_m3_rproc_stop(struct rproc *rproc)
>> +{
>> +	return 0;
>> +}
>> +
>> +static void wkup_m3_rproc_kick(struct rproc *rproc, int vqid)
>> +{
>> +}
>> +
>> +static struct rproc_ops wkup_m3_rproc_ops = {
>> +	.start		= wkup_m3_rproc_start,
>> +	.stop		= wkup_m3_rproc_stop,
>> +	.kick		= wkup_m3_rproc_kick,
>> +};
>> +
>> +/* Public Functions */
>> +
>> +/**
>> + * wkup_m3_set_ops - Set callbacks for user of rproc
>> + * @ops - struct wkup_m3_ops *
>> + *
>> + * Registers callbacks to wkup_m3 to be invoked after rproc is ready to use
>> + * and after an interrupt is handled.
>> + */
>> +void wkup_m3_set_ops(struct wkup_m3_ops *ops)
>> +{
>> +	m3_rproc_static->ops = ops;
>> +
>> +	if (m3_rproc_static->is_active && m3_rproc_static->ops &&
>> +	    m3_rproc_static->ops->rproc_ready)
>> +		m3_rproc_static->ops->rproc_ready();
>> +}
>> +
>> +/**
>> + * wkup_m3_ping - Send a dummy msg to wkup_m3 to tell to to check IPC regs
>> + *
>> + * Returns the result of sending mbox msg or -EIO if no mbox handle is present
>> + */
>> +int wkup_m3_ping(void)
>> +{
>> +	int ret;
>> +	mbox_msg_t dummy_msg = 0;
>> +
>> +	if (!m3_rproc_static->mbox) {
>> +		dev_err(&m3_rproc_static->pdev->dev,
>> +			"No IPC channel to communicate with wkup_m3!\n");
>> +		return -EIO;
>> +	}
>> +
>> +	/*
>> +	 * Write a dummy message to the mailbox in order to trigger the RX
>> +	 * interrupt to alert the M3 that data is available in the IPC
>> +	 * registers. We must enable the IRQ here and disable it after in
>> +	 * the RX callback to avoid multiple interrupts being received
>> +	 * by the CM3.
>> +	 */
>> +	omap_mbox_enable_irq(m3_rproc_static->mbox, IRQ_RX);
>> +	ret = mbox_send_message(m3_rproc_static->mbox, (void *)dummy_msg);
>> +
>> +	if (ret < 0) {
>> +		pr_err("%s: mbox_send_message() failed: %d\n", __func__, ret);
>> +		return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * wkup_m3_wake_src - Get the wakeup source info passed from wkup_m3
>> + * @wkup_m3_wakeup: struct wkup_m3_wakeup_src * gets assigned the
>> + *		    wakeup src value
>> + */
>> +void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wkup_m3_wakeup)
>> +{
>> +	struct wkup_m3_ipc_regs ipc_regs;
>> +	unsigned int wakeup_src_idx;
>> +	int j;
>> +
>> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
>> +
>> +	wakeup_src_idx = ipc_regs.reg6 & WKUP_M3_WAKE_SRC_MASK;
>> +
>> +	for (j = 0; j < ARRAY_SIZE(wakeups)-1; j++) {
>> +		if (wakeups[j].irq_nr == wakeup_src_idx) {
>> +			*wkup_m3_wakeup = wakeups[j];
>> +			return;
>> +		}
>> +	}
>> +	*wkup_m3_wakeup = wakeups[j];
>> +}
>> +
>> +/**
>> + * wkup_m3_pm_status - Return the status code from wkup_m3 after sleep event
>> + *
>> + * Returns an error code that indicates whether or not the dsired sleep
>> + * action was a success or not.
>> + */
>> +int wkup_m3_pm_status(void)
>> +{
>> +	unsigned int i;
>> +	struct wkup_m3_ipc_regs ipc_regs;
>> +
>> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
>> +
>> +	i = WKUP_M3_STATUS_RESP_MASK & ipc_regs.reg1;
>> +	i >>= __ffs(WKUP_M3_STATUS_RESP_MASK);
>> +
>> +	return i;
>> +}
>> +
>> +/**
>> + * wkup_m3_fw_version_read - Return the fw version given by the wkup_m3
>> + *
>> + * After boot the fw version should be read to ensure it is compatible.
>> + */
>> +int wkup_m3_fw_version_read(void)
>> +{
>> +	struct wkup_m3_ipc_regs ipc_regs;
>> +
>> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
>> +
>> +	return ipc_regs.reg2 & WKUP_M3_FW_VERSION_MASK;
>> +}
>> +
>> +/**
>> + * wkup_m3_set_cmd - write contents of struct to ipc regs
>> + * @ipc_regs: struct wkup_m3_ipc_regs *
>> + */
>> +void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs)
>> +{
>> +	wkup_m3_ctrl_ipc_write(m3_rproc_static, ipc_regs);
>> +}
>> +
>> +static void wkup_m3_rproc_loader_thread(struct rproc *rproc)
>> +{
>> +	struct wkup_m3_rproc *m3_rproc = rproc->priv;
>> +	struct device *dev = &m3_rproc->pdev->dev;
>> +	int ret;
>> +
>> +	wait_for_completion(&rproc->firmware_loading_complete);
>> +
>> +	ret = rproc_boot(rproc);
>> +	if (ret)
>> +		dev_err(dev, "rproc_boot failed\n");
>> +
>> +	do_exit(0);
>> +}
>> +
>> +static int wkup_m3_rproc_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct device_node *np = pdev->dev.of_node;
>> +	struct wkup_m3_rproc *m3_rproc;
>> +	struct rproc *rproc;
>> +	int irq, ret;
>> +	struct resource *res;
>> +	struct task_struct *task;
>> +	const char *mbox_name;
>> +
>> +	pm_runtime_enable(&pdev->dev);
>> +
>> +	ret = pm_runtime_get_sync(&pdev->dev);
>> +	if (IS_ERR_VALUE(ret)) {
>> +		dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
>> +		return ret;
>> +	}
>> +
>> +	rproc = rproc_alloc(dev, "wkup_m3", &wkup_m3_rproc_ops,
>> +			    "am335x-pm-firmware.elf", sizeof(*m3_rproc));
>> +	if (!rproc)
>> +		return -ENOMEM;
>> +
>> +	m3_rproc = rproc->priv;
>> +	m3_rproc->rproc = rproc;
>> +	m3_rproc->pdev = pdev;
>> +
>> +	m3_rproc_static = m3_rproc;
>> +
>> +	irq = platform_get_irq(pdev, 0);
>> +	if (!irq) {
>> +		dev_err(&pdev->dev, "no irq resource\n");
>> +		ret = -ENXIO;
>> +		goto err;
>> +	}
>> +
>> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ipc_regs");
>> +	if (!res) {
>> +		dev_err(&pdev->dev, "no memory resource for ipc\n");
>> +		ret = -ENXIO;
>> +		goto err;
>> +	}
>> +
>> +	m3_rproc->ipc_mem_base = devm_request_and_ioremap(dev, res);
>> +	if (!m3_rproc->ipc_mem_base) {
>> +		dev_err(dev, "could not ioremap ipc_mem\n");
>> +		ret = -EADDRNOTAVAIL;
>> +		goto err;
>> +	}
>> +
>> +	ret = devm_request_irq(dev, irq, wkup_m3_txev_handler,
>> +			       IRQF_DISABLED, "wkup_m3_txev", m3_rproc);
>> +	if (ret) {
>> +		dev_err(dev, "request_irq failed\n");
>> +		goto err;
>> +	}
>> +
>> +	/* Get mbox name from device tree node */
>> +	ret = of_property_read_string(np, "mbox-names", &mbox_name);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "Unable to get mbox name from dt node: %d\n",
>> +			ret);
>> +			goto err;
>> +	};
>> +
>> +	m3_rproc->mbox_client.chan_name = mbox_name;
>> +
>> +	/* Register as a remoteproc device */
>> +	ret = rproc_add(rproc);
>> +	if (ret) {
>> +		dev_err(dev, "rproc_add failed\n");
>> +		goto err;
>> +	}
>> +
>> +	/*
>> +	 * Wait for firmware loading completion in a thread so we
>> +	 * can boot the wkup_m3 as soon as it's ready without holding
>> +	 * up kernel boot
>> +	 */
>> +	task = kthread_run((void *)wkup_m3_rproc_loader_thread, rproc,
>> +			   "wkup_m3_rproc_loader");
>> +
>> +	if (IS_ERR(task)) {
>> +		dev_err(dev, "can't create rproc_loader thread\n");
>> +		goto err;
>> +	}
>> +
>> +	return 0;
>> +
>> +err:
>> +	rproc_put(rproc);
> pm_runtime_put_sync() ?

Yes, I do need that.

>> +	return ret;
>> +}
>> +
>> +static int wkup_m3_rproc_remove(struct platform_device *pdev)
>> +{
>> +	struct rproc *rproc = platform_get_drvdata(pdev);
>> +
>> +	rproc_del(rproc);
>> +	rproc_put(rproc);
>> +
> Here too ?

Yes, again here.

>> +	return 0;
>> +}
>> +
>> +static int wkup_m3_rpm_suspend(struct device *dev)
>> +{
>> +	return -EBUSY;
>> +}
>> +
>> +static int wkup_m3_rpm_resume(struct device *dev)
>> +{
>> +	return 0;
>> +}
>> +
> If you are not doing any meaningfull in suspend/resume hooks,
> why are you even registering them ?

The suspend hook is needed to prevent omap_device from idling the wkup_m3 hwmod, 
without it we get a warning from omap_hwmod.

>
>> +static const struct dev_pm_ops wkup_m3_rproc_pm_ops = {
>> +	SET_RUNTIME_PM_OPS(wkup_m3_rpm_suspend, wkup_m3_rpm_resume, NULL)
>> +};
>> +
>> +static const struct of_device_id wkup_m3_rproc_of_match[] = {
>> +	{ .compatible = "ti,am3353-wkup-m3", .data = NULL, },
>> +	{},
>> +};
>> +
>> +static struct platform_driver wkup_m3_rproc_driver = {
>> +	.probe = wkup_m3_rproc_probe,
>> +	.remove = wkup_m3_rproc_remove,
>> +	.driver = {
>> +		.name = "wkup_m3",
>> +		.owner = THIS_MODULE,
>> +		.of_match_table = wkup_m3_rproc_of_match,
>> +		.pm = &wkup_m3_rproc_pm_ops,
>> +	},
>> +};
>> +
>> +module_platform_driver(wkup_m3_rproc_driver);
>> +
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_DESCRIPTION("wkup m3 remote processor control driver");
>> diff --git a/include/linux/wkup_m3.h b/include/linux/wkup_m3.h
>> new file mode 100644
>> index 0000000..1a2237f
>> --- /dev/null
>> +++ b/include/linux/wkup_m3.h
>> @@ -0,0 +1,71 @@
>> +/*
>> + * TI Wakeup M3 Power Management Routines
>> + *
>> + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
>> + * Dave Gerlach <d-gerlach@ti.com>
>> + *
>> + * 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 _LINUX_WKUP_M3_H
>> +#define _LINUX_WKUP_M3_H
>> +
>> +/**
>> + * struct wkup_m3_ops - Callbacks for allowing pm code to interact with wkup_m3.
>> + *
>> + * @txev_handler: Callback to allow pm code to react to response from wkup_m3
>> + *		  after pinging it using wkup_m3_ping.
>> + *
>> + * @firmware_loaded: Callback invoked when the firmware has been loaded to the
>> + *		     m3 to allow the pm code to enable suspend/resume ops.
>> + */
>> +
>> +struct wkup_m3_ops {
>> +	void (*txev_handler)(void);
>> +	void (*rproc_ready)(void);
>> +};
>> +
>> +struct wkup_m3_wakeup_src {
>> +	int irq_nr;
>> +	char src[10];
>> +};
>> +
>> +struct wkup_m3_ipc_regs {
>> +	u32 reg0;
>> +	u32 reg1;
>> +	u32 reg2;
>> +	u32 reg3;
>> +	u32 reg4;
>> +	u32 reg5;
>> +	u32 reg6;
>> +	u32 reg7;
>> +};
>> +
>> +#ifdef CONFIG_WKUP_M3_RPROC
>> +int wkup_m3_prepare(void);
>> +void wkup_m3_set_ops(struct wkup_m3_ops *ops);
>> +int wkup_m3_ping(void);
>> +void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wakeup_src);
>> +int wkup_m3_pm_status(void);
>> +int wkup_m3_is_valid(void);
>> +int wkup_m3_fw_version_read(void);
>> +void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs);
>> +
>> +#else
>> +
>> +int wkup_m3_prepare(void) { return -EINVAL; }
>> +void wkup_m3_set_ops(struct wkup_m3_ops *ops) { }
>> +int wkup_m3_ping(void) { return -EINVAL }
>> +void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wakeup_src) { }
>> +int wkup_m3_pm_status(void) { return -1; }
>> +int wkup_m3_fw_version_read(void) { return -1; }
>> +void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs) { }
>
> Mark all of above in else as static inline.

Ok, that makes sense.

Thanks for the comments.

Regards,
Dave

>
>> +#endif /* CONFIG_WKUP_M3_RPROC */
>> +#endif /* _LINUX_WKUP_M3_H */
>>
>

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 05/11] Documentation: dt: add ti,am3353_wkup_m3 bindings
  2014-07-14 16:33       ` [PATCH v4 05/11] Documentation: dt: add ti, am3353_wkup_m3 bindings Suman Anna
@ 2014-07-14 17:45         ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-14 17:45 UTC (permalink / raw)
  To: Suman Anna
  Cc: Santosh Shilimkar, linux-arm-kernel, linux-omap, Paul Walmsley,
	Kevin Hilman, Tony Lindgren, Tero Kristo, Nishanth Menon,
	Russ Dill, Daniel Mack, Benoit Cousson, Ohad Ben-Cohen

Santosh, Suman,

On 07/14/2014 11:33 AM, Suman Anna wrote:
> Hi Dave,
>
> On 07/14/2014 09:41 AM, Santosh Shilimkar wrote:
>> On Thursday 10 July 2014 10:55 PM, Dave Gerlach wrote:
>>> Add the device tree bindings document for am3353 wkup_m3.
>>>
>>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>>> CC: Ohad Ben-Cohen <ohad@wizery.com>
>>> CC: Benoit Cousson <bcousson@baylibre.com>
>>> ---
>> Looks like you missed to copy device tree list and maintainers.
>> As Tony suggested, split up the series and send the wkup_m3 related
>> patches separately along with bindings and mark the DT folks on email.

Yes I did, I will fix this when I resend the split up series. Thanks.

>>
>>>   .../bindings/remoteproc/wkup_m3_rproc.txt          | 46 ++++++++++++++++++++++
>>>   1 file changed, 46 insertions(+)
>>>   create mode 100644 Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
>>>
>>> diff --git a/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt b/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
>>> new file mode 100644
>>> index 0000000..e9dd909
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
>>> @@ -0,0 +1,46 @@
>>> +Wakeup M3 Remote Proc Driver
>>> +=====================
>>> +
>>> +TI AMx3 Family devices use a Cortex M3 co-processor to help with various
>>> +low power tasks that cannot be controlled from the MPU. The CM3 requires
>>> +a firmware binary to accomplish this and communicates with the MPU through
>>> +IPC registers present in the SoCs control module. The wkup_m3 remoteproc
>>> +driver handles the loading of the firmware and exposes an API to
>>> +communicate with the wkup_m3 through the use of the IPC registers and a
>>> +mailbox.
>>> +
>>> +Wkup M3 Device Node:
>>> +====================
>>> +A wkup_m3 device node is used to represent a wakeup M3 IP instance within
>>> +a SoC. The sub-mailboxes are represented as child node of this parent node.
>>> +
>>> +Required properties:
>>> +--------------------
>>> +- compatible:		Should be "ti,am3353-wkup-m3" for AM33xx SoCs
>>> +- reg:			Contains the wkup_m3 register address ranges for
>>> +			umem, dmem, and ipc-regs.
>>> +- reg-names:		Names for reg addresses given above
>
> You should explicitly state the names here. Giving any different names
> will cause the driver to fail.

Alright I will move the names from the item above and add some details.

>
>>> +- interrupts:		Contains the interrupt information for the wkup_m3
>>> +			interrupt that signals the MPU.
>>> +- ti,hwmods:		Name of the hwmod associated with the mailbox
>
> hwmod is not associated with mailbox here, please fix the cut-copy-paste
> error.

Yep that's my bad, sorry about that.

Regards,
Dave

>
> regards
> Suman
>
>
>>> +- ti,no-reset-on-init:	Reset is handled after fw has been loaded, not at
>>> +			init of hwmod.
>>> +- mbox-names:		Name of the mbox channel for the IPC framework
>>> +- mbox:			Phandle used by IPC framework to get correct mbox
>>> +			channel for communication.
>>> +
>>> +Example:
>>> +--------
>>> +/* AM33xx */
>>> +wkup_m3: wkup_m3@44d00000 {
>>> +	compatible = "ti,am3353-wkup-m3";
>>> +	reg = <0x44d00000 0x4000
>>> +	       0x44d80000 0x2000
>>> +	       0x44e11324 0x0024>;
>>> +	reg-names = "m3_umem", "m3_dmem", "ipc_regs";
>>> +	interrupts = <78>;
>>> +	ti,hwmods = "wkup_m3";
>>> +	ti,no-reset-on-init;
>>> +	mbox-names = "wkup_m3";
>>> +	mbox = <&mailbox &mbox_wkupm3>;
>>> +};
>>>
>>
>


^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 05/11] Documentation: dt: add ti, am3353_wkup_m3 bindings
@ 2014-07-14 17:45         ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-14 17:45 UTC (permalink / raw)
  To: linux-arm-kernel

Santosh, Suman,

On 07/14/2014 11:33 AM, Suman Anna wrote:
> Hi Dave,
>
> On 07/14/2014 09:41 AM, Santosh Shilimkar wrote:
>> On Thursday 10 July 2014 10:55 PM, Dave Gerlach wrote:
>>> Add the device tree bindings document for am3353 wkup_m3.
>>>
>>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>>> CC: Ohad Ben-Cohen <ohad@wizery.com>
>>> CC: Benoit Cousson <bcousson@baylibre.com>
>>> ---
>> Looks like you missed to copy device tree list and maintainers.
>> As Tony suggested, split up the series and send the wkup_m3 related
>> patches separately along with bindings and mark the DT folks on email.

Yes I did, I will fix this when I resend the split up series. Thanks.

>>
>>>   .../bindings/remoteproc/wkup_m3_rproc.txt          | 46 ++++++++++++++++++++++
>>>   1 file changed, 46 insertions(+)
>>>   create mode 100644 Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
>>>
>>> diff --git a/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt b/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
>>> new file mode 100644
>>> index 0000000..e9dd909
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
>>> @@ -0,0 +1,46 @@
>>> +Wakeup M3 Remote Proc Driver
>>> +=====================
>>> +
>>> +TI AMx3 Family devices use a Cortex M3 co-processor to help with various
>>> +low power tasks that cannot be controlled from the MPU. The CM3 requires
>>> +a firmware binary to accomplish this and communicates with the MPU through
>>> +IPC registers present in the SoCs control module. The wkup_m3 remoteproc
>>> +driver handles the loading of the firmware and exposes an API to
>>> +communicate with the wkup_m3 through the use of the IPC registers and a
>>> +mailbox.
>>> +
>>> +Wkup M3 Device Node:
>>> +====================
>>> +A wkup_m3 device node is used to represent a wakeup M3 IP instance within
>>> +a SoC. The sub-mailboxes are represented as child node of this parent node.
>>> +
>>> +Required properties:
>>> +--------------------
>>> +- compatible:		Should be "ti,am3353-wkup-m3" for AM33xx SoCs
>>> +- reg:			Contains the wkup_m3 register address ranges for
>>> +			umem, dmem, and ipc-regs.
>>> +- reg-names:		Names for reg addresses given above
>
> You should explicitly state the names here. Giving any different names
> will cause the driver to fail.

Alright I will move the names from the item above and add some details.

>
>>> +- interrupts:		Contains the interrupt information for the wkup_m3
>>> +			interrupt that signals the MPU.
>>> +- ti,hwmods:		Name of the hwmod associated with the mailbox
>
> hwmod is not associated with mailbox here, please fix the cut-copy-paste
> error.

Yep that's my bad, sorry about that.

Regards,
Dave

>
> regards
> Suman
>
>
>>> +- ti,no-reset-on-init:	Reset is handled after fw has been loaded, not at
>>> +			init of hwmod.
>>> +- mbox-names:		Name of the mbox channel for the IPC framework
>>> +- mbox:			Phandle used by IPC framework to get correct mbox
>>> +			channel for communication.
>>> +
>>> +Example:
>>> +--------
>>> +/* AM33xx */
>>> +wkup_m3: wkup_m3 at 44d00000 {
>>> +	compatible = "ti,am3353-wkup-m3";
>>> +	reg = <0x44d00000 0x4000
>>> +	       0x44d80000 0x2000
>>> +	       0x44e11324 0x0024>;
>>> +	reg-names = "m3_umem", "m3_dmem", "ipc_regs";
>>> +	interrupts = <78>;
>>> +	ti,hwmods = "wkup_m3";
>>> +	ti,no-reset-on-init;
>>> +	mbox-names = "wkup_m3";
>>> +	mbox = <&mailbox &mbox_wkupm3>;
>>> +};
>>>
>>
>

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 11/11] ARM: OMAP2+: AM33XX: Hookup AM33XX PM code into OMAP builds
  2014-07-14 11:21     ` Tony Lindgren
@ 2014-07-14 17:46       ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-14 17:46 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: linux-arm-kernel, linux-omap, Paul Walmsley, Kevin Hilman,
	Tero Kristo, Nishanth Menon, Russ Dill, Santosh Shilimkar,
	Daniel Mack, Suman Anna, Benoit Cousson

On 07/14/2014 06:21 AM, Tony Lindgren wrote:
> * Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
>> With all the requisite changes in place we can now enable the basic
>> PM support for AM33xx. This patch updates the various OMAP files
>> to enable suspend-resume on AM33xx.
>>
>> Because the suspend resume functionality is different on AM33xx
>> than other OMAP platforms due to the need for M3 firmware and an
>> IPC channel to be in place, separate PM ops are used instead of
>> omap_pm_ops. These are now set using omap2_common_suspend_init
>> so the AM33xx can make a decision at runtime to enable suspend based
>> on the availabilty of aforementioned requirements.
>>
>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>> ---
>> v3->v4:
>> 	Updated for rproc usage now.
>>
>>   arch/arm/mach-omap2/Kconfig  |  1 +
>>   arch/arm/mach-omap2/Makefile |  2 ++
>>   arch/arm/mach-omap2/common.h |  9 +++++++++
>>   arch/arm/mach-omap2/io.c     |  1 +
>>   arch/arm/mach-omap2/pm.h     |  5 +++++
>>   arch/arm/mach-omap2/sram.c   | 10 +++++++++-
>>   arch/arm/mach-omap2/sram.h   |  2 ++
>>   7 files changed, 29 insertions(+), 1 deletion(-)
>>
>> diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
>> index 1c1ed73..f8a56e5 100644
>> --- a/arch/arm/mach-omap2/Kconfig
>> +++ b/arch/arm/mach-omap2/Kconfig
>> @@ -59,6 +59,7 @@ config SOC_AM33XX
>>   	select ARCH_OMAP2PLUS
>>   	select ARCH_HAS_OPP
>>   	select ARM_CPU_SUSPEND if PM
>> +	select WKUP_M3_RPROC if PM
>
> You should not do a select in Kconfig for a driver,
> that will lead into various randconfig build errors
> at some point. It's probably best to make the PM code
> depend on the WKUP_M3_RPROC driver instead.

Ok, I didn't consider that, thanks.

Regards,
Dave

>
> Regards,
>
> Tony
>


^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 11/11] ARM: OMAP2+: AM33XX: Hookup AM33XX PM code into OMAP builds
@ 2014-07-14 17:46       ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-14 17:46 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/14/2014 06:21 AM, Tony Lindgren wrote:
> * Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
>> With all the requisite changes in place we can now enable the basic
>> PM support for AM33xx. This patch updates the various OMAP files
>> to enable suspend-resume on AM33xx.
>>
>> Because the suspend resume functionality is different on AM33xx
>> than other OMAP platforms due to the need for M3 firmware and an
>> IPC channel to be in place, separate PM ops are used instead of
>> omap_pm_ops. These are now set using omap2_common_suspend_init
>> so the AM33xx can make a decision at runtime to enable suspend based
>> on the availabilty of aforementioned requirements.
>>
>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>> ---
>> v3->v4:
>> 	Updated for rproc usage now.
>>
>>   arch/arm/mach-omap2/Kconfig  |  1 +
>>   arch/arm/mach-omap2/Makefile |  2 ++
>>   arch/arm/mach-omap2/common.h |  9 +++++++++
>>   arch/arm/mach-omap2/io.c     |  1 +
>>   arch/arm/mach-omap2/pm.h     |  5 +++++
>>   arch/arm/mach-omap2/sram.c   | 10 +++++++++-
>>   arch/arm/mach-omap2/sram.h   |  2 ++
>>   7 files changed, 29 insertions(+), 1 deletion(-)
>>
>> diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
>> index 1c1ed73..f8a56e5 100644
>> --- a/arch/arm/mach-omap2/Kconfig
>> +++ b/arch/arm/mach-omap2/Kconfig
>> @@ -59,6 +59,7 @@ config SOC_AM33XX
>>   	select ARCH_OMAP2PLUS
>>   	select ARCH_HAS_OPP
>>   	select ARM_CPU_SUSPEND if PM
>> +	select WKUP_M3_RPROC if PM
>
> You should not do a select in Kconfig for a driver,
> that will lead into various randconfig build errors
> at some point. It's probably best to make the PM code
> depend on the WKUP_M3_RPROC driver instead.

Ok, I didn't consider that, thanks.

Regards,
Dave

>
> Regards,
>
> Tony
>

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 01/11] ARM: omap: edma: add suspend suspend/resume hooks
  2014-07-14 11:05     ` Tony Lindgren
@ 2014-07-14 17:47       ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-14 17:47 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: linux-arm-kernel, linux-omap, Paul Walmsley, Kevin Hilman,
	Tero Kristo, Nishanth Menon, Russ Dill, Santosh Shilimkar,
	Daniel Mack, Suman Anna, Benoit Cousson, Daniel Mack

On 07/14/2014 06:05 AM, Tony Lindgren wrote:
> * Dave Gerlach <d-gerlach@ti.com> [140710 19:58]:
>> From: Daniel Mack <zonque@gmail.com>
>>
>> This patch makes the edma driver resume correctly after suspend. Tested
>> on an AM33xx platform with cyclic audio streams and omap_hsmmc.
>>
>> All information can be reconstructed by already known runtime
>> information.
>>
>> As we now use some functions that were previously only used from __init
>> context, annotations had to be dropped.
>
> This is something Sekhar needs to look at, did you maybe forget
> to Cc him?

Yes I did, I will make sure it goes to the right people when it gets split off.

Regards,
Dave

>
> Regards,
>
> Tony
>


^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 01/11] ARM: omap: edma: add suspend suspend/resume hooks
@ 2014-07-14 17:47       ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-14 17:47 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/14/2014 06:05 AM, Tony Lindgren wrote:
> * Dave Gerlach <d-gerlach@ti.com> [140710 19:58]:
>> From: Daniel Mack <zonque@gmail.com>
>>
>> This patch makes the edma driver resume correctly after suspend. Tested
>> on an AM33xx platform with cyclic audio streams and omap_hsmmc.
>>
>> All information can be reconstructed by already known runtime
>> information.
>>
>> As we now use some functions that were previously only used from __init
>> context, annotations had to be dropped.
>
> This is something Sekhar needs to look at, did you maybe forget
> to Cc him?

Yes I did, I will make sure it goes to the right people when it gets split off.

Regards,
Dave

>
> Regards,
>
> Tony
>

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 00/11] ARM: OMAP2+: AM33XX: Add suspend-resume support
  2014-07-14 11:24   ` Tony Lindgren
@ 2014-07-14 17:47     ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-14 17:47 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: linux-arm-kernel, linux-omap, Paul Walmsley, Kevin Hilman,
	Tero Kristo, Nishanth Menon, Russ Dill, Santosh Shilimkar,
	Daniel Mack, Suman Anna, Benoit Cousson

Tony,

On 07/14/2014 06:24 AM, Tony Lindgren wrote:
> * Dave Gerlach <d-gerlach@ti.com> [140710 19:58]:
>> Hello,
>>
>> This series adds suspend/resume support for am335x. Version 3 of this
>> series can be found at [1]. I apologize for the large delay between this
>> and the previous revision. This code has been heavily refined
>> since the last version based on the various comments received for v3. The
>> major change from previous version is moving all wkup_m3 code into a
>> remoteproc based driver. The new driver handles all IPC and fw loading
>> and exposes a small API to be used by PM code to achieve low power states.
>
> Great, this is nice for all the am335x users. I made few comments
> on the series that should be quite simple to fix up.
>
> Then please split up your next version of this series into the
> following independet patches:
>
> 1. Changes to the EDMA driver for suspend and resume support
>
> 2. Patches to add the rproc driver
>
> 3. SoC related changes to enable PM support
>

Thanks for the feedback. I will give some more time for additional comments and 
then revise, split, and resend.

Regards,
Dave

> Regards,
>
> Tony
>


^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 00/11] ARM: OMAP2+: AM33XX: Add suspend-resume support
@ 2014-07-14 17:47     ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-14 17:47 UTC (permalink / raw)
  To: linux-arm-kernel

Tony,

On 07/14/2014 06:24 AM, Tony Lindgren wrote:
> * Dave Gerlach <d-gerlach@ti.com> [140710 19:58]:
>> Hello,
>>
>> This series adds suspend/resume support for am335x. Version 3 of this
>> series can be found at [1]. I apologize for the large delay between this
>> and the previous revision. This code has been heavily refined
>> since the last version based on the various comments received for v3. The
>> major change from previous version is moving all wkup_m3 code into a
>> remoteproc based driver. The new driver handles all IPC and fw loading
>> and exposes a small API to be used by PM code to achieve low power states.
>
> Great, this is nice for all the am335x users. I made few comments
> on the series that should be quite simple to fix up.
>
> Then please split up your next version of this series into the
> following independet patches:
>
> 1. Changes to the EDMA driver for suspend and resume support
>
> 2. Patches to add the rproc driver
>
> 3. SoC related changes to enable PM support
>

Thanks for the feedback. I will give some more time for additional comments and 
then revise, split, and resend.

Regards,
Dave

> Regards,
>
> Tony
>

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 04/11] ARM: OMAP2+: Use pdata-quirks for wkup_m3 deassert_hardreset
  2014-07-11  2:55   ` Dave Gerlach
@ 2014-07-14 18:48     ` Suman Anna
  -1 siblings, 0 replies; 106+ messages in thread
From: Suman Anna @ 2014-07-14 18:48 UTC (permalink / raw)
  To: Dave Gerlach, linux-arm-kernel, linux-omap
  Cc: Paul Walmsley, Kevin Hilman, Tony Lindgren, Tero Kristo,
	Nishanth Menon, Russ Dill, Santosh Shilimkar, Daniel Mack,
	Benoit Cousson

Hi Dave,

On 07/10/2014 09:55 PM, Dave Gerlach wrote:
> Use pdata-quirks to reset the wkup_m3 during boot.
> 
> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
> ---
>  arch/arm/mach-omap2/pdata-quirks.c    | 12 ++++++++++++
>  include/linux/platform_data/wkup_m3.h | 17 +++++++++++++++++
>  2 files changed, 29 insertions(+)
>  create mode 100644 include/linux/platform_data/wkup_m3.h
> 
> diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c
> index 90c88d4..8e4a541 100644
> --- a/arch/arm/mach-omap2/pdata-quirks.c
> +++ b/arch/arm/mach-omap2/pdata-quirks.c
> @@ -17,6 +17,7 @@
>  
>  #include <linux/platform_data/pinctrl-single.h>
>  #include <linux/platform_data/iommu-omap.h>
> +#include <linux/platform_data/wkup_m3.h>
>  
>  #include "am35xx.h"
>  #include "common.h"
> @@ -255,6 +256,13 @@ static void __init nokia_n900_legacy_init(void)
>  }
>  #endif /* CONFIG_ARCH_OMAP3 */
>  
> +#ifdef CONFIG_SOC_AM33XX
> +static struct wkup_m3_platform_data wkup_m3_data = {
> +	.reset_name = "wkup_m3",
> +	.deassert_reset = omap_device_deassert_hardreset,

Probably need to add assert_reset too

> +};
> +#endif
> +
>  #ifdef CONFIG_ARCH_OMAP4
>  static void __init omap4_sdp_legacy_init(void)
>  {
> @@ -348,6 +356,10 @@ struct of_dev_auxdata omap_auxdata_lookup[] __initdata = {
>  	OF_DEV_AUXDATA("ti,am3517-emac", 0x5c000000, "davinci_emac.0",
>  		       &am35xx_emac_pdata),
>  #endif
> +#ifdef CONFIG_SOC_AM33XX
> +	OF_DEV_AUXDATA("ti,am3353-wkup-m3", 0x44d00000, "44d00000.wkup_m3",
> +		       &wkup_m3_data),
> +#endif
>  #ifdef CONFIG_ARCH_OMAP4
>  	OF_DEV_AUXDATA("ti,omap4-padconf", 0x4a100040, "4a100040.pinmux", &pcs_pdata),
>  	OF_DEV_AUXDATA("ti,omap4-padconf", 0x4a31e040, "4a31e040.pinmux", &pcs_pdata),
> diff --git a/include/linux/platform_data/wkup_m3.h b/include/linux/platform_data/wkup_m3.h
> new file mode 100644
> index 0000000..07282bd
> --- /dev/null
> +++ b/include/linux/platform_data/wkup_m3.h
> @@ -0,0 +1,17 @@
> +/*
> + * omap wkup_m3: platform data
> + *
> + * Copyright (C) 2014 Texas Instruments, Inc.
> + *
> + * Dave Gerlach <d-gerlach@ti.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +

protect against double inclusion..

regards
Suman

> +struct wkup_m3_platform_data {
> +	const char *reset_name;
> +
> +	int (*deassert_reset)(struct platform_device *pdev, const char *name);
> +};
> 


^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 04/11] ARM: OMAP2+: Use pdata-quirks for wkup_m3 deassert_hardreset
@ 2014-07-14 18:48     ` Suman Anna
  0 siblings, 0 replies; 106+ messages in thread
From: Suman Anna @ 2014-07-14 18:48 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Dave,

On 07/10/2014 09:55 PM, Dave Gerlach wrote:
> Use pdata-quirks to reset the wkup_m3 during boot.
> 
> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
> ---
>  arch/arm/mach-omap2/pdata-quirks.c    | 12 ++++++++++++
>  include/linux/platform_data/wkup_m3.h | 17 +++++++++++++++++
>  2 files changed, 29 insertions(+)
>  create mode 100644 include/linux/platform_data/wkup_m3.h
> 
> diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c
> index 90c88d4..8e4a541 100644
> --- a/arch/arm/mach-omap2/pdata-quirks.c
> +++ b/arch/arm/mach-omap2/pdata-quirks.c
> @@ -17,6 +17,7 @@
>  
>  #include <linux/platform_data/pinctrl-single.h>
>  #include <linux/platform_data/iommu-omap.h>
> +#include <linux/platform_data/wkup_m3.h>
>  
>  #include "am35xx.h"
>  #include "common.h"
> @@ -255,6 +256,13 @@ static void __init nokia_n900_legacy_init(void)
>  }
>  #endif /* CONFIG_ARCH_OMAP3 */
>  
> +#ifdef CONFIG_SOC_AM33XX
> +static struct wkup_m3_platform_data wkup_m3_data = {
> +	.reset_name = "wkup_m3",
> +	.deassert_reset = omap_device_deassert_hardreset,

Probably need to add assert_reset too

> +};
> +#endif
> +
>  #ifdef CONFIG_ARCH_OMAP4
>  static void __init omap4_sdp_legacy_init(void)
>  {
> @@ -348,6 +356,10 @@ struct of_dev_auxdata omap_auxdata_lookup[] __initdata = {
>  	OF_DEV_AUXDATA("ti,am3517-emac", 0x5c000000, "davinci_emac.0",
>  		       &am35xx_emac_pdata),
>  #endif
> +#ifdef CONFIG_SOC_AM33XX
> +	OF_DEV_AUXDATA("ti,am3353-wkup-m3", 0x44d00000, "44d00000.wkup_m3",
> +		       &wkup_m3_data),
> +#endif
>  #ifdef CONFIG_ARCH_OMAP4
>  	OF_DEV_AUXDATA("ti,omap4-padconf", 0x4a100040, "4a100040.pinmux", &pcs_pdata),
>  	OF_DEV_AUXDATA("ti,omap4-padconf", 0x4a31e040, "4a31e040.pinmux", &pcs_pdata),
> diff --git a/include/linux/platform_data/wkup_m3.h b/include/linux/platform_data/wkup_m3.h
> new file mode 100644
> index 0000000..07282bd
> --- /dev/null
> +++ b/include/linux/platform_data/wkup_m3.h
> @@ -0,0 +1,17 @@
> +/*
> + * omap wkup_m3: platform data
> + *
> + * Copyright (C) 2014 Texas Instruments, Inc.
> + *
> + * Dave Gerlach <d-gerlach@ti.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +

protect against double inclusion..

regards
Suman

> +struct wkup_m3_platform_data {
> +	const char *reset_name;
> +
> +	int (*deassert_reset)(struct platform_device *pdev, const char *name);
> +};
> 

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 06/11] remoteproc: wkup_m3: Add wkup_m3 remote proc driver
  2014-07-14 14:54     ` Santosh Shilimkar
@ 2014-07-14 19:07       ` Suman Anna
  -1 siblings, 0 replies; 106+ messages in thread
From: Suman Anna @ 2014-07-14 19:07 UTC (permalink / raw)
  To: Santosh Shilimkar, Dave Gerlach, linux-arm-kernel, linux-omap
  Cc: Paul Walmsley, Kevin Hilman, Tony Lindgren, Tero Kristo,
	Nishanth Menon, Russ Dill, Daniel Mack, Benoit Cousson,
	Ohad Ben-Cohen

Hi Dave,

I have a few more comments in addition to Santosh's..

On 07/14/2014 09:54 AM, Santosh Shilimkar wrote:
> On Thursday 10 July 2014 10:55 PM, Dave Gerlach wrote:
>> Add a remoteproc driver to load the firmware for and boot the wkup_m3
>> present on am33xx. The wkup_m3 is an integrated Cortex M3 that allows
>> the SoC to enter the lowest possible power state by taking control from
>> the MPU after it has gone into its own low power state and shutting off
>> any additional peripherals.
>>
>> Communication between the MPU and CM3 is handled by several IPC
>> registers in the control module and a mailbox. An API is exposed for
>> programming the aforementioned IPC registers and notifying the wkup_m3
>> of pending data using the mailbox. The wkup_m3 has the ability to
>> trigger an interrupt back to the MPU to allow for bidirectional
>> communication through these registers.
>>
>> Two callbacks are provided. rproc_ready allows code to hook into the
>> driver to see when firmware has been loaded and execute other code and
>> txev_handler allows external code to run when the wkup_m3 triggers an
>> interrupt back to the m3.
>>
>> The driver expects a resource table to be present in the wkup_m3 to
>> define the required memory resources needed by wkup_m3, at least the
>> data memory so that the firmware can be copied for the proper place for
>> execution.
>>
>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>> CC: Ohad Ben-Cohen <ohad@wizery.com>
>> ---
>>  drivers/remoteproc/Kconfig         |  15 ++
>>  drivers/remoteproc/Makefile        |   1 +
>>  drivers/remoteproc/wkup_m3_rproc.c | 512 +++++++++++++++++++++++++++++++++++++
>>  include/linux/wkup_m3.h            |  71 +++++
>>  4 files changed, 599 insertions(+)
>>  create mode 100644 drivers/remoteproc/wkup_m3_rproc.c
>>  create mode 100644 include/linux/wkup_m3.h
>>
>> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
>> index 5e343ba..4b00c21 100644
>> --- a/drivers/remoteproc/Kconfig
>> +++ b/drivers/remoteproc/Kconfig
>> @@ -41,6 +41,21 @@ config STE_MODEM_RPROC
>>  	  This can be either built-in or a loadable module.
>>  	  If unsure say N.
>>  
>> +config WKUP_M3_RPROC
>> +	bool "AM33xx wkup-m3 remoteproc support"
>> +        depends on SOC_AM33XX
>> +        select REMOTEPROC
>> +	select MAILBOX
>> +	select OMAP2PLUS_MBOX
> Please fix the indentation.
> 
>> +	default n
> Default is always 'n' so drop above.
>> +	help
>> +	  Say y here to support AM33xx wkup-m3.
>> +
>> +	  Required for Suspend-to-ram and CPUIdle on AM33xx. Allows for
>> +	  loading of firmware and communication with CM3 PM coproccesor

typo - coprocessor*

>> +	  that is present on AM33xx family of SoCs
>> +	  If unsure say N.
>> +
>>  config DA8XX_REMOTEPROC
>>  	tristate "DA8xx/OMAP-L13x remoteproc support"
>>  	depends on ARCH_DAVINCI_DA8XX
>> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
>> index ac2ff75..81b04d1 100644
>> --- a/drivers/remoteproc/Makefile
>> +++ b/drivers/remoteproc/Makefile
>> @@ -9,4 +9,5 @@ remoteproc-y				+= remoteproc_virtio.o
>>  remoteproc-y				+= remoteproc_elf_loader.o
>>  obj-$(CONFIG_OMAP_REMOTEPROC)		+= omap_remoteproc.o
>>  obj-$(CONFIG_STE_MODEM_RPROC)	 	+= ste_modem_rproc.o
>> +obj-$(CONFIG_WKUP_M3_RPROC)		+= wkup_m3_rproc.o
>>  obj-$(CONFIG_DA8XX_REMOTEPROC)		+= da8xx_remoteproc.o
>> diff --git a/drivers/remoteproc/wkup_m3_rproc.c b/drivers/remoteproc/wkup_m3_rproc.c
>> new file mode 100644
>> index 0000000..58aeaf2
>> --- /dev/null
>> +++ b/drivers/remoteproc/wkup_m3_rproc.c
>> @@ -0,0 +1,512 @@
>> +/*
>> + * AMx3 Wkup M3 Remote Processor driver
>> + *
>> + * Copyright (C) 2014 Texas Instruments, Inc.
>> + *
>> + * Dave Gerlach <d-gerlach@ti.com>
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * version 2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/err.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/irq.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/elf.h>

Do you require this header?

>> +#include <linux/pm_runtime.h>
>> +#include <linux/firmware.h>
>> +#include <linux/remoteproc.h>
>> +#include <linux/omap-mailbox.h>
>> +#include <linux/mailbox_client.h>
>> +#include <linux/wkup_m3.h>
>> +#include <linux/kthread.h>
>> +#include "remoteproc_internal.h"

Suggest moving the internal header to after including the
standard headers..

>> +
>> +#include <linux/platform_data/wkup_m3.h>
>> +
>> +#define WKUP_M3_WAKE_SRC_MASK		0xFF
>> +
>> +#define WKUP_M3_STATUS_RESP_SHIFT	16
>> +#define WKUP_M3_STATUS_RESP_MASK	(0xffff << 16)
>> +
>> +#define WKUP_M3_FW_VERSION_SHIFT	0
>> +#define WKUP_M3_FW_VERSION_MASK		0xffff
>> +
>> +/* AM33XX M3_TXEV_EOI register */
>> +#define AM33XX_CONTROL_M3_TXEV_EOI	0x00
>> +
>> +#define AM33XX_M3_TXEV_ACK		(0x1 << 0)
>> +#define AM33XX_M3_TXEV_ENABLE		(0x0 << 0)
>> +
>> +/* AM33XX IPC message registers */
>> +#define AM33XX_CONTROL_IPC_MSG_REG0	0x04
>> +#define AM33XX_CONTROL_IPC_MSG_REG1	0x08
>> +#define AM33XX_CONTROL_IPC_MSG_REG2	0x0c
>> +#define AM33XX_CONTROL_IPC_MSG_REG3	0x10
>> +#define AM33XX_CONTROL_IPC_MSG_REG4	0x14
>> +#define AM33XX_CONTROL_IPC_MSG_REG5	0x18
>> +#define AM33XX_CONTROL_IPC_MSG_REG6	0x1c
>> +#define AM33XX_CONTROL_IPC_MSG_REG7	0x20

How about using a macro for these, especially given that the AM43xx has
a bit more registers.

>> +
> Is this driver going to be AM33xx specific ?
> 
>> +struct wkup_m3_rproc {
>> +	struct rproc *rproc;
>> +
>> +	void * __iomem dev_table_va;
>> +	void * __iomem ipc_mem_base;
>> +	struct platform_device *pdev;
>> +
>> +	struct mbox_client mbox_client;
>> +	struct mbox_chan *mbox;
>> +	struct wkup_m3_ops *ops;
>> +
>> +	bool is_active;
>> +};
>> +
>> +static struct wkup_m3_rproc *m3_rproc_static;
>> +
>> +static struct wkup_m3_wakeup_src wakeups[] = {
>> +	{.irq_nr = 35,	.src = "USB0_PHY"},
>> +	{.irq_nr = 36,	.src = "USB1_PHY"},
>> +	{.irq_nr = 40,	.src = "I2C0"},
>> +	{.irq_nr = 41,	.src = "RTC Timer"},
>> +	{.irq_nr = 42,	.src = "RTC Alarm"},
>> +	{.irq_nr = 43,	.src = "Timer0"},
>> +	{.irq_nr = 44,	.src = "Timer1"},
>> +	{.irq_nr = 45,	.src = "UART"},
>> +	{.irq_nr = 46,	.src = "GPIO0"},
>> +	{.irq_nr = 48,	.src = "MPU_WAKE"},
>> +	{.irq_nr = 49,	.src = "WDT0"},
>> +	{.irq_nr = 50,	.src = "WDT1"},
>> +	{.irq_nr = 51,	.src = "ADC_TSC"},
>> +	{.irq_nr = 0,	.src = "Unknown"},
>> +};

Is this table going to be same for AM43xx? If not, it is better to store
this as match data, so that a different table can easily be plugged in
based on compatible string.

>> +
> const ?
> 
>> +static void am33xx_txev_eoi(struct wkup_m3_rproc *m3_rproc)
>> +{
>> +	writel(AM33XX_M3_TXEV_ACK,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
>> +}
>> +
>> +static void am33xx_txev_enable(struct wkup_m3_rproc *m3_rproc)
>> +{
>> +	writel(AM33XX_M3_TXEV_ENABLE,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
>> +}
>> +
>> +static void wkup_m3_ctrl_ipc_write(struct wkup_m3_rproc *m3_rproc,
>> +				   struct wkup_m3_ipc_regs *ipc_regs)
>> +{
>> +	writel(ipc_regs->reg0,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG0);
>> +	writel(ipc_regs->reg1,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG1);
>> +	writel(ipc_regs->reg2,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG2);
>> +	writel(ipc_regs->reg3,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG3);
>> +	writel(ipc_regs->reg4,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG4);
>> +	writel(ipc_regs->reg5,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG5);
>> +	writel(ipc_regs->reg6,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG6);
>> +	writel(ipc_regs->reg7,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG7);

How about defining a static inline function for the read and write
operations?

>> +}
>> +
>> +static void wkup_m3_ctrl_ipc_read(struct wkup_m3_rproc *m3_rproc,
>> +				  struct wkup_m3_ipc_regs *ipc_regs)
>> +{
>> +	ipc_regs->reg0 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG0);
>> +	ipc_regs->reg1 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG1);
>> +	ipc_regs->reg2 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG2);
>> +	ipc_regs->reg3 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG3);
>> +	ipc_regs->reg4 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG4);
>> +	ipc_regs->reg5 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG5);
>> +	ipc_regs->reg6 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG6);
>> +	ipc_regs->reg7 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG7);
>> +}
>> +
>> +static irqreturn_t wkup_m3_txev_handler(int irq, void *unused)
>> +{
>> +	am33xx_txev_eoi(m3_rproc_static);
>> +
>> +	if (m3_rproc_static->ops && m3_rproc_static->ops->txev_handler)
>> +		m3_rproc_static->ops->txev_handler();
>> +
>> +	am33xx_txev_enable(m3_rproc_static);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +/**
>> + * wkup_m3_fw_version_clear - Clear FW version from ipc regs
>> + *
>> + * Invalidate M3 firmware version before hardreset.
>> + * Write invalid version in lower 4 nibbles of parameter
>> + * register (ipc_regs + 0x8).
>> + */
>> +
>> +static void wkup_m3_fw_version_clear(void)
>> +{
>> +	struct wkup_m3_ipc_regs ipc_regs;
>> +
>> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
>> +	ipc_regs.reg2 &= 0xFFFF0000;
> Probably define a macro and use it.
>> +	wkup_m3_ctrl_ipc_write(m3_rproc_static, &ipc_regs);
>> +}
>> +
>> +static void wkup_m3_mbox_callback(struct mbox_client *client, void *data)
>> +{
>> +	omap_mbox_disable_irq(m3_rproc_static->mbox, IRQ_RX);
>> +}
>> +
>> +static int wkup_m3_rproc_start(struct rproc *rproc)
>> +{
>> +	struct wkup_m3_rproc *m3_rproc = rproc->priv;
>> +	struct platform_device *pdev = m3_rproc->pdev;
>> +	struct device *dev = &pdev->dev;
>> +	struct wkup_m3_platform_data *pdata = dev->platform_data;
>> +	int ret;
>> +
>> +	wkup_m3_fw_version_clear();
>> +
>> +	if (pdata && pdata->deassert_reset) {
>> +		ret = pdata->deassert_reset(pdev, pdata->reset_name);
>> +		if (ret) {
>> +			dev_err(dev, "Unable to reset wkup_m3!\n");
>> +			return -ENODEV;
>> +		}
>> +	} else {
>> +		dev_err(dev, "Platform data missing deassert_reset!\n");
>> +		return -ENODEV;
>> +	}

You don't need a runtime check for this, check once in probe, this ops
is essential right?

>> +
>> +	m3_rproc->mbox_client.dev = dev;
>> +	m3_rproc->mbox_client.tx_done = NULL;
>> +	m3_rproc->mbox_client.rx_callback = wkup_m3_mbox_callback;
>> +	m3_rproc->mbox_client.tx_block = false;
>> +	m3_rproc->mbox_client.knows_txdone = false;
>> +
>> +	m3_rproc->mbox = mbox_request_channel(&m3_rproc->mbox_client);
>> +
nit, no need of an additional blank line

>> +	if (IS_ERR(m3_rproc->mbox)) {
>> +		dev_err(dev, "IPC Request for A8->M3 Channel failed!\n");
>> +		ret = PTR_ERR(m3_rproc->mbox);
>> +		m3_rproc->mbox = NULL;
>> +		return ret;
>> +	}
>> +
>> +	if (m3_rproc_static->ops && m3_rproc_static->ops->rproc_ready)
>> +		m3_rproc_static->ops->rproc_ready();
>> +
>> +	m3_rproc_static->is_active = 1;
>> +
>> +	return 0;
>> +}
>> +
>> +
>> +static int wkup_m3_rproc_stop(struct rproc *rproc)
>> +{
>> +	return 0;

Any reason this is a stub, don't you need to assert the reset in stop?

>> +}
>> +
>> +static void wkup_m3_rproc_kick(struct rproc *rproc, int vqid)
>> +{
>> +}

If you can remove this empty stub, please do so. I do not think we will
be using virtio for communicating with the WkupM3.

>> +
>> +static struct rproc_ops wkup_m3_rproc_ops = {
>> +	.start		= wkup_m3_rproc_start,
>> +	.stop		= wkup_m3_rproc_stop,
>> +	.kick		= wkup_m3_rproc_kick,
>> +};
>> +
>> +/* Public Functions */
>> +
>> +/**
>> + * wkup_m3_set_ops - Set callbacks for user of rproc
>> + * @ops - struct wkup_m3_ops *
>> + *
>> + * Registers callbacks to wkup_m3 to be invoked after rproc is ready to use
>> + * and after an interrupt is handled.
>> + */
>> +void wkup_m3_set_ops(struct wkup_m3_ops *ops)
>> +{
>> +	m3_rproc_static->ops = ops;
>> +
>> +	if (m3_rproc_static->is_active && m3_rproc_static->ops &&
>> +	    m3_rproc_static->ops->rproc_ready)
>> +		m3_rproc_static->ops->rproc_ready();
>> +}



>> +
>> +/**
>> + * wkup_m3_ping - Send a dummy msg to wkup_m3 to tell to to check IPC regs
>> + *
>> + * Returns the result of sending mbox msg or -EIO if no mbox handle is present
>> + */
>> +int wkup_m3_ping(void)
>> +{
>> +	int ret;
>> +	mbox_msg_t dummy_msg = 0;
>> +
>> +	if (!m3_rproc_static->mbox) {
>> +		dev_err(&m3_rproc_static->pdev->dev,
>> +			"No IPC channel to communicate with wkup_m3!\n");
>> +		return -EIO;
>> +	}
>> +
>> +	/*
>> +	 * Write a dummy message to the mailbox in order to trigger the RX
>> +	 * interrupt to alert the M3 that data is available in the IPC
>> +	 * registers. We must enable the IRQ here and disable it after in
>> +	 * the RX callback to avoid multiple interrupts being received
>> +	 * by the CM3.
>> +	 */
>> +	omap_mbox_enable_irq(m3_rproc_static->mbox, IRQ_RX);
>> +	ret = mbox_send_message(m3_rproc_static->mbox, (void *)dummy_msg);
>> +
nit, no need of an additional blank line

>> +	if (ret < 0) {
>> +		pr_err("%s: mbox_send_message() failed: %d\n", __func__, ret);
>> +		return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * wkup_m3_wake_src - Get the wakeup source info passed from wkup_m3
>> + * @wkup_m3_wakeup: struct wkup_m3_wakeup_src * gets assigned the
>> + *		    wakeup src value
>> + */
>> +void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wkup_m3_wakeup)
>> +{
>> +	struct wkup_m3_ipc_regs ipc_regs;
>> +	unsigned int wakeup_src_idx;
>> +	int j;
>> +
>> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
>> +
>> +	wakeup_src_idx = ipc_regs.reg6 & WKUP_M3_WAKE_SRC_MASK;
>> +
>> +	for (j = 0; j < ARRAY_SIZE(wakeups)-1; j++) {
>> +		if (wakeups[j].irq_nr == wakeup_src_idx) {
>> +			*wkup_m3_wakeup = wakeups[j];
>> +			return;
>> +		}
>> +	}
>> +	*wkup_m3_wakeup = wakeups[j];
>> +}
>> +
>> +/**
>> + * wkup_m3_pm_status - Return the status code from wkup_m3 after sleep event
>> + *
>> + * Returns an error code that indicates whether or not the dsired sleep
>> + * action was a success or not.
>> + */
>> +int wkup_m3_pm_status(void)
>> +{
>> +	unsigned int i;
>> +	struct wkup_m3_ipc_regs ipc_regs;
>> +
>> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);

Do you need to read back all the registers, given that this is a local
structure anyway?

>> +
>> +	i = WKUP_M3_STATUS_RESP_MASK & ipc_regs.reg1;
>> +	i >>= __ffs(WKUP_M3_STATUS_RESP_MASK);
>> +
>> +	return i;
>> +}
>> +
>> +/**
>> + * wkup_m3_fw_version_read - Return the fw version given by the wkup_m3
>> + *
>> + * After boot the fw version should be read to ensure it is compatible.
>> + */
>> +int wkup_m3_fw_version_read(void)
>> +{
>> +	struct wkup_m3_ipc_regs ipc_regs;
>> +
>> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
>> +
>> +	return ipc_regs.reg2 & WKUP_M3_FW_VERSION_MASK;
>> +}
>> +
>> +/**
>> + * wkup_m3_set_cmd - write contents of struct to ipc regs
>> + * @ipc_regs: struct wkup_m3_ipc_regs *
>> + */
>> +void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs)
>> +{
>> +	wkup_m3_ctrl_ipc_write(m3_rproc_static, ipc_regs);
>> +}
>> +
>> +static void wkup_m3_rproc_loader_thread(struct rproc *rproc)
>> +{
>> +	struct wkup_m3_rproc *m3_rproc = rproc->priv;
>> +	struct device *dev = &m3_rproc->pdev->dev;
>> +	int ret;
>> +
>> +	wait_for_completion(&rproc->firmware_loading_complete);
>> +
>> +	ret = rproc_boot(rproc);
>> +	if (ret)
>> +		dev_err(dev, "rproc_boot failed\n");
>> +
>> +	do_exit(0);
>> +}
>> +
>> +static int wkup_m3_rproc_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct device_node *np = pdev->dev.of_node;
>> +	struct wkup_m3_rproc *m3_rproc;
>> +	struct rproc *rproc;
>> +	int irq, ret;
>> +	struct resource *res;
>> +	struct task_struct *task;
>> +	const char *mbox_name;
>> +
>> +	pm_runtime_enable(&pdev->dev);
>> +
>> +	ret = pm_runtime_get_sync(&pdev->dev);
>> +	if (IS_ERR_VALUE(ret)) {
>> +		dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
>> +		return ret;
>> +	}
>> +
>> +	rproc = rproc_alloc(dev, "wkup_m3", &wkup_m3_rproc_ops,
>> +			    "am335x-pm-firmware.elf", sizeof(*m3_rproc));
>> +	if (!rproc)
>> +		return -ENOMEM;
>> +
>> +	m3_rproc = rproc->priv;
>> +	m3_rproc->rproc = rproc;
>> +	m3_rproc->pdev = pdev;
>> +
>> +	m3_rproc_static = m3_rproc;
>> +
>> +	irq = platform_get_irq(pdev, 0);
>> +	if (!irq) {
>> +		dev_err(&pdev->dev, "no irq resource\n");
>> +		ret = -ENXIO;
>> +		goto err;
>> +	}

Colocate this to the same place as the request_irq.

>> +
>> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ipc_regs");
>> +	if (!res) {

No need to check for error, the devm function following this should take
care of any errors.

>> +		dev_err(&pdev->dev, "no memory resource for ipc\n");
>> +		ret = -ENXIO;
>> +		goto err;
>> +	}
>> +
>> +	m3_rproc->ipc_mem_base = devm_request_and_ioremap(dev, res);

Is some one else doing an ioremap of this space, otherwise
devm_ioremap_resource is preferred.

>> +	if (!m3_rproc->ipc_mem_base) {
>> +		dev_err(dev, "could not ioremap ipc_mem\n");
>> +		ret = -EADDRNOTAVAIL;
>> +		goto err;
>> +	}

Don't you need to be retrieving umem and dmem?


>> +
>> +	ret = devm_request_irq(dev, irq, wkup_m3_txev_handler,
>> +			       IRQF_DISABLED, "wkup_m3_txev", m3_rproc);
>> +	if (ret) {
>> +		dev_err(dev, "request_irq failed\n");
>> +		goto err;
>> +	}
>> +
>> +	/* Get mbox name from device tree node */
>> +	ret = of_property_read_string(np, "mbox-names", &mbox_name);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "Unable to get mbox name from dt node: %d\n",
>> +			ret);
>> +			goto err;
>> +	};
>> +
>> +	m3_rproc->mbox_client.chan_name = mbox_name;
>> +
>> +	/* Register as a remoteproc device */
>> +	ret = rproc_add(rproc);
>> +	if (ret) {
>> +		dev_err(dev, "rproc_add failed\n");
>> +		goto err;
>> +	}
>> +
>> +	/*
>> +	 * Wait for firmware loading completion in a thread so we
>> +	 * can boot the wkup_m3 as soon as it's ready without holding
>> +	 * up kernel boot
>> +	 */
>> +	task = kthread_run((void *)wkup_m3_rproc_loader_thread, rproc,
>> +			   "wkup_m3_rproc_loader");
>> +
>> +	if (IS_ERR(task)) {
>> +		dev_err(dev, "can't create rproc_loader thread\n");
>> +		goto err;
>> +	}
>> +
>> +	return 0;
>> +
>> +err:
>> +	rproc_put(rproc);
> pm_runtime_put_sync() ?
>> +	return ret;
>> +}
>> +
>> +static int wkup_m3_rproc_remove(struct platform_device *pdev)
>> +{
>> +	struct rproc *rproc = platform_get_drvdata(pdev);
>> +
>> +	rproc_del(rproc);
>> +	rproc_put(rproc);

Need to reset m3_rproc_static?

>> +
> Here too ?
>> +	return 0;
>> +}
>> +
>> +static int wkup_m3_rpm_suspend(struct device *dev)
>> +{
>> +	return -EBUSY;
>> +}
>> +
>> +static int wkup_m3_rpm_resume(struct device *dev)
>> +{
>> +	return 0;
>> +}
>> +
> If you are not doing any meaningfull in suspend/resume hooks,
> why are you even registering them ?
> 
>> +static const struct dev_pm_ops wkup_m3_rproc_pm_ops = {
>> +	SET_RUNTIME_PM_OPS(wkup_m3_rpm_suspend, wkup_m3_rpm_resume, NULL)
>> +};
>> +
>> +static const struct of_device_id wkup_m3_rproc_of_match[] = {
>> +	{ .compatible = "ti,am3353-wkup-m3", .data = NULL, },
>> +	{},
>> +};
>> +
>> +static struct platform_driver wkup_m3_rproc_driver = {
>> +	.probe = wkup_m3_rproc_probe,
>> +	.remove = wkup_m3_rproc_remove,
>> +	.driver = {
>> +		.name = "wkup_m3",
>> +		.owner = THIS_MODULE,
>> +		.of_match_table = wkup_m3_rproc_of_match,
>> +		.pm = &wkup_m3_rproc_pm_ops,
>> +	},
>> +};
>> +
>> +module_platform_driver(wkup_m3_rproc_driver);
>> +
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_DESCRIPTION("wkup m3 remote processor control driver");
>> diff --git a/include/linux/wkup_m3.h b/include/linux/wkup_m3.h
>> new file mode 100644
>> index 0000000..1a2237f
>> --- /dev/null
>> +++ b/include/linux/wkup_m3.h
>> @@ -0,0 +1,71 @@
>> +/*
>> + * TI Wakeup M3 Power Management Routines

Suggest adding for AMx3x SoCs

>> + *
>> + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
>> + * Dave Gerlach <d-gerlach@ti.com>
>> + *
>> + * 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 _LINUX_WKUP_M3_H
>> +#define _LINUX_WKUP_M3_H
>> +
>> +/**
>> + * struct wkup_m3_ops - Callbacks for allowing pm code to interact with wkup_m3.
>> + *
>> + * @txev_handler: Callback to allow pm code to react to response from wkup_m3
>> + *		  after pinging it using wkup_m3_ping.
>> + *
>> + * @firmware_loaded: Callback invoked when the firmware has been loaded to the
>> + *		     m3 to allow the pm code to enable suspend/resume ops.

firmware_loaded ops does not exist

>> + */
>> +
>> +struct wkup_m3_ops {
>> +	void (*txev_handler)(void);
>> +	void (*rproc_ready)(void);
>> +};
>> +
>> +struct wkup_m3_wakeup_src {
>> +	int irq_nr;
>> +	char src[10];

What is the @src used for, debug? Don't see anywhere this is getting used.

regards
Suman

>> +};
>> +
>> +struct wkup_m3_ipc_regs {
>> +	u32 reg0;
>> +	u32 reg1;
>> +	u32 reg2;
>> +	u32 reg3;
>> +	u32 reg4;
>> +	u32 reg5;
>> +	u32 reg6;
>> +	u32 reg7;
>> +};
>> +
>> +#ifdef CONFIG_WKUP_M3_RPROC
>> +int wkup_m3_prepare(void);
>> +void wkup_m3_set_ops(struct wkup_m3_ops *ops);
>> +int wkup_m3_ping(void);
>> +void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wakeup_src);
>> +int wkup_m3_pm_status(void);
>> +int wkup_m3_is_valid(void);
>> +int wkup_m3_fw_version_read(void);
>> +void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs);
>> +
>> +#else
>> +
>> +int wkup_m3_prepare(void) { return -EINVAL; }
>> +void wkup_m3_set_ops(struct wkup_m3_ops *ops) { }
>> +int wkup_m3_ping(void) { return -EINVAL }
>> +void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wakeup_src) { }
>> +int wkup_m3_pm_status(void) { return -1; }
>> +int wkup_m3_fw_version_read(void) { return -1; }
>> +void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs) { }
> 
> Mark all of above in else as static inline.
> 
>> +#endif /* CONFIG_WKUP_M3_RPROC */
>> +#endif /* _LINUX_WKUP_M3_H */
>>
> 


^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 06/11] remoteproc: wkup_m3: Add wkup_m3 remote proc driver
@ 2014-07-14 19:07       ` Suman Anna
  0 siblings, 0 replies; 106+ messages in thread
From: Suman Anna @ 2014-07-14 19:07 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Dave,

I have a few more comments in addition to Santosh's..

On 07/14/2014 09:54 AM, Santosh Shilimkar wrote:
> On Thursday 10 July 2014 10:55 PM, Dave Gerlach wrote:
>> Add a remoteproc driver to load the firmware for and boot the wkup_m3
>> present on am33xx. The wkup_m3 is an integrated Cortex M3 that allows
>> the SoC to enter the lowest possible power state by taking control from
>> the MPU after it has gone into its own low power state and shutting off
>> any additional peripherals.
>>
>> Communication between the MPU and CM3 is handled by several IPC
>> registers in the control module and a mailbox. An API is exposed for
>> programming the aforementioned IPC registers and notifying the wkup_m3
>> of pending data using the mailbox. The wkup_m3 has the ability to
>> trigger an interrupt back to the MPU to allow for bidirectional
>> communication through these registers.
>>
>> Two callbacks are provided. rproc_ready allows code to hook into the
>> driver to see when firmware has been loaded and execute other code and
>> txev_handler allows external code to run when the wkup_m3 triggers an
>> interrupt back to the m3.
>>
>> The driver expects a resource table to be present in the wkup_m3 to
>> define the required memory resources needed by wkup_m3, at least the
>> data memory so that the firmware can be copied for the proper place for
>> execution.
>>
>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>> CC: Ohad Ben-Cohen <ohad@wizery.com>
>> ---
>>  drivers/remoteproc/Kconfig         |  15 ++
>>  drivers/remoteproc/Makefile        |   1 +
>>  drivers/remoteproc/wkup_m3_rproc.c | 512 +++++++++++++++++++++++++++++++++++++
>>  include/linux/wkup_m3.h            |  71 +++++
>>  4 files changed, 599 insertions(+)
>>  create mode 100644 drivers/remoteproc/wkup_m3_rproc.c
>>  create mode 100644 include/linux/wkup_m3.h
>>
>> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
>> index 5e343ba..4b00c21 100644
>> --- a/drivers/remoteproc/Kconfig
>> +++ b/drivers/remoteproc/Kconfig
>> @@ -41,6 +41,21 @@ config STE_MODEM_RPROC
>>  	  This can be either built-in or a loadable module.
>>  	  If unsure say N.
>>  
>> +config WKUP_M3_RPROC
>> +	bool "AM33xx wkup-m3 remoteproc support"
>> +        depends on SOC_AM33XX
>> +        select REMOTEPROC
>> +	select MAILBOX
>> +	select OMAP2PLUS_MBOX
> Please fix the indentation.
> 
>> +	default n
> Default is always 'n' so drop above.
>> +	help
>> +	  Say y here to support AM33xx wkup-m3.
>> +
>> +	  Required for Suspend-to-ram and CPUIdle on AM33xx. Allows for
>> +	  loading of firmware and communication with CM3 PM coproccesor

typo - coprocessor*

>> +	  that is present on AM33xx family of SoCs
>> +	  If unsure say N.
>> +
>>  config DA8XX_REMOTEPROC
>>  	tristate "DA8xx/OMAP-L13x remoteproc support"
>>  	depends on ARCH_DAVINCI_DA8XX
>> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
>> index ac2ff75..81b04d1 100644
>> --- a/drivers/remoteproc/Makefile
>> +++ b/drivers/remoteproc/Makefile
>> @@ -9,4 +9,5 @@ remoteproc-y				+= remoteproc_virtio.o
>>  remoteproc-y				+= remoteproc_elf_loader.o
>>  obj-$(CONFIG_OMAP_REMOTEPROC)		+= omap_remoteproc.o
>>  obj-$(CONFIG_STE_MODEM_RPROC)	 	+= ste_modem_rproc.o
>> +obj-$(CONFIG_WKUP_M3_RPROC)		+= wkup_m3_rproc.o
>>  obj-$(CONFIG_DA8XX_REMOTEPROC)		+= da8xx_remoteproc.o
>> diff --git a/drivers/remoteproc/wkup_m3_rproc.c b/drivers/remoteproc/wkup_m3_rproc.c
>> new file mode 100644
>> index 0000000..58aeaf2
>> --- /dev/null
>> +++ b/drivers/remoteproc/wkup_m3_rproc.c
>> @@ -0,0 +1,512 @@
>> +/*
>> + * AMx3 Wkup M3 Remote Processor driver
>> + *
>> + * Copyright (C) 2014 Texas Instruments, Inc.
>> + *
>> + * Dave Gerlach <d-gerlach@ti.com>
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * version 2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/err.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/irq.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/elf.h>

Do you require this header?

>> +#include <linux/pm_runtime.h>
>> +#include <linux/firmware.h>
>> +#include <linux/remoteproc.h>
>> +#include <linux/omap-mailbox.h>
>> +#include <linux/mailbox_client.h>
>> +#include <linux/wkup_m3.h>
>> +#include <linux/kthread.h>
>> +#include "remoteproc_internal.h"

Suggest moving the internal header to after including the
standard headers..

>> +
>> +#include <linux/platform_data/wkup_m3.h>
>> +
>> +#define WKUP_M3_WAKE_SRC_MASK		0xFF
>> +
>> +#define WKUP_M3_STATUS_RESP_SHIFT	16
>> +#define WKUP_M3_STATUS_RESP_MASK	(0xffff << 16)
>> +
>> +#define WKUP_M3_FW_VERSION_SHIFT	0
>> +#define WKUP_M3_FW_VERSION_MASK		0xffff
>> +
>> +/* AM33XX M3_TXEV_EOI register */
>> +#define AM33XX_CONTROL_M3_TXEV_EOI	0x00
>> +
>> +#define AM33XX_M3_TXEV_ACK		(0x1 << 0)
>> +#define AM33XX_M3_TXEV_ENABLE		(0x0 << 0)
>> +
>> +/* AM33XX IPC message registers */
>> +#define AM33XX_CONTROL_IPC_MSG_REG0	0x04
>> +#define AM33XX_CONTROL_IPC_MSG_REG1	0x08
>> +#define AM33XX_CONTROL_IPC_MSG_REG2	0x0c
>> +#define AM33XX_CONTROL_IPC_MSG_REG3	0x10
>> +#define AM33XX_CONTROL_IPC_MSG_REG4	0x14
>> +#define AM33XX_CONTROL_IPC_MSG_REG5	0x18
>> +#define AM33XX_CONTROL_IPC_MSG_REG6	0x1c
>> +#define AM33XX_CONTROL_IPC_MSG_REG7	0x20

How about using a macro for these, especially given that the AM43xx has
a bit more registers.

>> +
> Is this driver going to be AM33xx specific ?
> 
>> +struct wkup_m3_rproc {
>> +	struct rproc *rproc;
>> +
>> +	void * __iomem dev_table_va;
>> +	void * __iomem ipc_mem_base;
>> +	struct platform_device *pdev;
>> +
>> +	struct mbox_client mbox_client;
>> +	struct mbox_chan *mbox;
>> +	struct wkup_m3_ops *ops;
>> +
>> +	bool is_active;
>> +};
>> +
>> +static struct wkup_m3_rproc *m3_rproc_static;
>> +
>> +static struct wkup_m3_wakeup_src wakeups[] = {
>> +	{.irq_nr = 35,	.src = "USB0_PHY"},
>> +	{.irq_nr = 36,	.src = "USB1_PHY"},
>> +	{.irq_nr = 40,	.src = "I2C0"},
>> +	{.irq_nr = 41,	.src = "RTC Timer"},
>> +	{.irq_nr = 42,	.src = "RTC Alarm"},
>> +	{.irq_nr = 43,	.src = "Timer0"},
>> +	{.irq_nr = 44,	.src = "Timer1"},
>> +	{.irq_nr = 45,	.src = "UART"},
>> +	{.irq_nr = 46,	.src = "GPIO0"},
>> +	{.irq_nr = 48,	.src = "MPU_WAKE"},
>> +	{.irq_nr = 49,	.src = "WDT0"},
>> +	{.irq_nr = 50,	.src = "WDT1"},
>> +	{.irq_nr = 51,	.src = "ADC_TSC"},
>> +	{.irq_nr = 0,	.src = "Unknown"},
>> +};

Is this table going to be same for AM43xx? If not, it is better to store
this as match data, so that a different table can easily be plugged in
based on compatible string.

>> +
> const ?
> 
>> +static void am33xx_txev_eoi(struct wkup_m3_rproc *m3_rproc)
>> +{
>> +	writel(AM33XX_M3_TXEV_ACK,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
>> +}
>> +
>> +static void am33xx_txev_enable(struct wkup_m3_rproc *m3_rproc)
>> +{
>> +	writel(AM33XX_M3_TXEV_ENABLE,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
>> +}
>> +
>> +static void wkup_m3_ctrl_ipc_write(struct wkup_m3_rproc *m3_rproc,
>> +				   struct wkup_m3_ipc_regs *ipc_regs)
>> +{
>> +	writel(ipc_regs->reg0,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG0);
>> +	writel(ipc_regs->reg1,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG1);
>> +	writel(ipc_regs->reg2,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG2);
>> +	writel(ipc_regs->reg3,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG3);
>> +	writel(ipc_regs->reg4,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG4);
>> +	writel(ipc_regs->reg5,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG5);
>> +	writel(ipc_regs->reg6,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG6);
>> +	writel(ipc_regs->reg7,
>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG7);

How about defining a static inline function for the read and write
operations?

>> +}
>> +
>> +static void wkup_m3_ctrl_ipc_read(struct wkup_m3_rproc *m3_rproc,
>> +				  struct wkup_m3_ipc_regs *ipc_regs)
>> +{
>> +	ipc_regs->reg0 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG0);
>> +	ipc_regs->reg1 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG1);
>> +	ipc_regs->reg2 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG2);
>> +	ipc_regs->reg3 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG3);
>> +	ipc_regs->reg4 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG4);
>> +	ipc_regs->reg5 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG5);
>> +	ipc_regs->reg6 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG6);
>> +	ipc_regs->reg7 = readl(m3_rproc->ipc_mem_base
>> +			       + AM33XX_CONTROL_IPC_MSG_REG7);
>> +}
>> +
>> +static irqreturn_t wkup_m3_txev_handler(int irq, void *unused)
>> +{
>> +	am33xx_txev_eoi(m3_rproc_static);
>> +
>> +	if (m3_rproc_static->ops && m3_rproc_static->ops->txev_handler)
>> +		m3_rproc_static->ops->txev_handler();
>> +
>> +	am33xx_txev_enable(m3_rproc_static);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +/**
>> + * wkup_m3_fw_version_clear - Clear FW version from ipc regs
>> + *
>> + * Invalidate M3 firmware version before hardreset.
>> + * Write invalid version in lower 4 nibbles of parameter
>> + * register (ipc_regs + 0x8).
>> + */
>> +
>> +static void wkup_m3_fw_version_clear(void)
>> +{
>> +	struct wkup_m3_ipc_regs ipc_regs;
>> +
>> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
>> +	ipc_regs.reg2 &= 0xFFFF0000;
> Probably define a macro and use it.
>> +	wkup_m3_ctrl_ipc_write(m3_rproc_static, &ipc_regs);
>> +}
>> +
>> +static void wkup_m3_mbox_callback(struct mbox_client *client, void *data)
>> +{
>> +	omap_mbox_disable_irq(m3_rproc_static->mbox, IRQ_RX);
>> +}
>> +
>> +static int wkup_m3_rproc_start(struct rproc *rproc)
>> +{
>> +	struct wkup_m3_rproc *m3_rproc = rproc->priv;
>> +	struct platform_device *pdev = m3_rproc->pdev;
>> +	struct device *dev = &pdev->dev;
>> +	struct wkup_m3_platform_data *pdata = dev->platform_data;
>> +	int ret;
>> +
>> +	wkup_m3_fw_version_clear();
>> +
>> +	if (pdata && pdata->deassert_reset) {
>> +		ret = pdata->deassert_reset(pdev, pdata->reset_name);
>> +		if (ret) {
>> +			dev_err(dev, "Unable to reset wkup_m3!\n");
>> +			return -ENODEV;
>> +		}
>> +	} else {
>> +		dev_err(dev, "Platform data missing deassert_reset!\n");
>> +		return -ENODEV;
>> +	}

You don't need a runtime check for this, check once in probe, this ops
is essential right?

>> +
>> +	m3_rproc->mbox_client.dev = dev;
>> +	m3_rproc->mbox_client.tx_done = NULL;
>> +	m3_rproc->mbox_client.rx_callback = wkup_m3_mbox_callback;
>> +	m3_rproc->mbox_client.tx_block = false;
>> +	m3_rproc->mbox_client.knows_txdone = false;
>> +
>> +	m3_rproc->mbox = mbox_request_channel(&m3_rproc->mbox_client);
>> +
nit, no need of an additional blank line

>> +	if (IS_ERR(m3_rproc->mbox)) {
>> +		dev_err(dev, "IPC Request for A8->M3 Channel failed!\n");
>> +		ret = PTR_ERR(m3_rproc->mbox);
>> +		m3_rproc->mbox = NULL;
>> +		return ret;
>> +	}
>> +
>> +	if (m3_rproc_static->ops && m3_rproc_static->ops->rproc_ready)
>> +		m3_rproc_static->ops->rproc_ready();
>> +
>> +	m3_rproc_static->is_active = 1;
>> +
>> +	return 0;
>> +}
>> +
>> +
>> +static int wkup_m3_rproc_stop(struct rproc *rproc)
>> +{
>> +	return 0;

Any reason this is a stub, don't you need to assert the reset in stop?

>> +}
>> +
>> +static void wkup_m3_rproc_kick(struct rproc *rproc, int vqid)
>> +{
>> +}

If you can remove this empty stub, please do so. I do not think we will
be using virtio for communicating with the WkupM3.

>> +
>> +static struct rproc_ops wkup_m3_rproc_ops = {
>> +	.start		= wkup_m3_rproc_start,
>> +	.stop		= wkup_m3_rproc_stop,
>> +	.kick		= wkup_m3_rproc_kick,
>> +};
>> +
>> +/* Public Functions */
>> +
>> +/**
>> + * wkup_m3_set_ops - Set callbacks for user of rproc
>> + * @ops - struct wkup_m3_ops *
>> + *
>> + * Registers callbacks to wkup_m3 to be invoked after rproc is ready to use
>> + * and after an interrupt is handled.
>> + */
>> +void wkup_m3_set_ops(struct wkup_m3_ops *ops)
>> +{
>> +	m3_rproc_static->ops = ops;
>> +
>> +	if (m3_rproc_static->is_active && m3_rproc_static->ops &&
>> +	    m3_rproc_static->ops->rproc_ready)
>> +		m3_rproc_static->ops->rproc_ready();
>> +}



>> +
>> +/**
>> + * wkup_m3_ping - Send a dummy msg to wkup_m3 to tell to to check IPC regs
>> + *
>> + * Returns the result of sending mbox msg or -EIO if no mbox handle is present
>> + */
>> +int wkup_m3_ping(void)
>> +{
>> +	int ret;
>> +	mbox_msg_t dummy_msg = 0;
>> +
>> +	if (!m3_rproc_static->mbox) {
>> +		dev_err(&m3_rproc_static->pdev->dev,
>> +			"No IPC channel to communicate with wkup_m3!\n");
>> +		return -EIO;
>> +	}
>> +
>> +	/*
>> +	 * Write a dummy message to the mailbox in order to trigger the RX
>> +	 * interrupt to alert the M3 that data is available in the IPC
>> +	 * registers. We must enable the IRQ here and disable it after in
>> +	 * the RX callback to avoid multiple interrupts being received
>> +	 * by the CM3.
>> +	 */
>> +	omap_mbox_enable_irq(m3_rproc_static->mbox, IRQ_RX);
>> +	ret = mbox_send_message(m3_rproc_static->mbox, (void *)dummy_msg);
>> +
nit, no need of an additional blank line

>> +	if (ret < 0) {
>> +		pr_err("%s: mbox_send_message() failed: %d\n", __func__, ret);
>> +		return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * wkup_m3_wake_src - Get the wakeup source info passed from wkup_m3
>> + * @wkup_m3_wakeup: struct wkup_m3_wakeup_src * gets assigned the
>> + *		    wakeup src value
>> + */
>> +void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wkup_m3_wakeup)
>> +{
>> +	struct wkup_m3_ipc_regs ipc_regs;
>> +	unsigned int wakeup_src_idx;
>> +	int j;
>> +
>> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
>> +
>> +	wakeup_src_idx = ipc_regs.reg6 & WKUP_M3_WAKE_SRC_MASK;
>> +
>> +	for (j = 0; j < ARRAY_SIZE(wakeups)-1; j++) {
>> +		if (wakeups[j].irq_nr == wakeup_src_idx) {
>> +			*wkup_m3_wakeup = wakeups[j];
>> +			return;
>> +		}
>> +	}
>> +	*wkup_m3_wakeup = wakeups[j];
>> +}
>> +
>> +/**
>> + * wkup_m3_pm_status - Return the status code from wkup_m3 after sleep event
>> + *
>> + * Returns an error code that indicates whether or not the dsired sleep
>> + * action was a success or not.
>> + */
>> +int wkup_m3_pm_status(void)
>> +{
>> +	unsigned int i;
>> +	struct wkup_m3_ipc_regs ipc_regs;
>> +
>> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);

Do you need to read back all the registers, given that this is a local
structure anyway?

>> +
>> +	i = WKUP_M3_STATUS_RESP_MASK & ipc_regs.reg1;
>> +	i >>= __ffs(WKUP_M3_STATUS_RESP_MASK);
>> +
>> +	return i;
>> +}
>> +
>> +/**
>> + * wkup_m3_fw_version_read - Return the fw version given by the wkup_m3
>> + *
>> + * After boot the fw version should be read to ensure it is compatible.
>> + */
>> +int wkup_m3_fw_version_read(void)
>> +{
>> +	struct wkup_m3_ipc_regs ipc_regs;
>> +
>> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
>> +
>> +	return ipc_regs.reg2 & WKUP_M3_FW_VERSION_MASK;
>> +}
>> +
>> +/**
>> + * wkup_m3_set_cmd - write contents of struct to ipc regs
>> + * @ipc_regs: struct wkup_m3_ipc_regs *
>> + */
>> +void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs)
>> +{
>> +	wkup_m3_ctrl_ipc_write(m3_rproc_static, ipc_regs);
>> +}
>> +
>> +static void wkup_m3_rproc_loader_thread(struct rproc *rproc)
>> +{
>> +	struct wkup_m3_rproc *m3_rproc = rproc->priv;
>> +	struct device *dev = &m3_rproc->pdev->dev;
>> +	int ret;
>> +
>> +	wait_for_completion(&rproc->firmware_loading_complete);
>> +
>> +	ret = rproc_boot(rproc);
>> +	if (ret)
>> +		dev_err(dev, "rproc_boot failed\n");
>> +
>> +	do_exit(0);
>> +}
>> +
>> +static int wkup_m3_rproc_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct device_node *np = pdev->dev.of_node;
>> +	struct wkup_m3_rproc *m3_rproc;
>> +	struct rproc *rproc;
>> +	int irq, ret;
>> +	struct resource *res;
>> +	struct task_struct *task;
>> +	const char *mbox_name;
>> +
>> +	pm_runtime_enable(&pdev->dev);
>> +
>> +	ret = pm_runtime_get_sync(&pdev->dev);
>> +	if (IS_ERR_VALUE(ret)) {
>> +		dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
>> +		return ret;
>> +	}
>> +
>> +	rproc = rproc_alloc(dev, "wkup_m3", &wkup_m3_rproc_ops,
>> +			    "am335x-pm-firmware.elf", sizeof(*m3_rproc));
>> +	if (!rproc)
>> +		return -ENOMEM;
>> +
>> +	m3_rproc = rproc->priv;
>> +	m3_rproc->rproc = rproc;
>> +	m3_rproc->pdev = pdev;
>> +
>> +	m3_rproc_static = m3_rproc;
>> +
>> +	irq = platform_get_irq(pdev, 0);
>> +	if (!irq) {
>> +		dev_err(&pdev->dev, "no irq resource\n");
>> +		ret = -ENXIO;
>> +		goto err;
>> +	}

Colocate this to the same place as the request_irq.

>> +
>> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ipc_regs");
>> +	if (!res) {

No need to check for error, the devm function following this should take
care of any errors.

>> +		dev_err(&pdev->dev, "no memory resource for ipc\n");
>> +		ret = -ENXIO;
>> +		goto err;
>> +	}
>> +
>> +	m3_rproc->ipc_mem_base = devm_request_and_ioremap(dev, res);

Is some one else doing an ioremap of this space, otherwise
devm_ioremap_resource is preferred.

>> +	if (!m3_rproc->ipc_mem_base) {
>> +		dev_err(dev, "could not ioremap ipc_mem\n");
>> +		ret = -EADDRNOTAVAIL;
>> +		goto err;
>> +	}

Don't you need to be retrieving umem and dmem?


>> +
>> +	ret = devm_request_irq(dev, irq, wkup_m3_txev_handler,
>> +			       IRQF_DISABLED, "wkup_m3_txev", m3_rproc);
>> +	if (ret) {
>> +		dev_err(dev, "request_irq failed\n");
>> +		goto err;
>> +	}
>> +
>> +	/* Get mbox name from device tree node */
>> +	ret = of_property_read_string(np, "mbox-names", &mbox_name);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "Unable to get mbox name from dt node: %d\n",
>> +			ret);
>> +			goto err;
>> +	};
>> +
>> +	m3_rproc->mbox_client.chan_name = mbox_name;
>> +
>> +	/* Register as a remoteproc device */
>> +	ret = rproc_add(rproc);
>> +	if (ret) {
>> +		dev_err(dev, "rproc_add failed\n");
>> +		goto err;
>> +	}
>> +
>> +	/*
>> +	 * Wait for firmware loading completion in a thread so we
>> +	 * can boot the wkup_m3 as soon as it's ready without holding
>> +	 * up kernel boot
>> +	 */
>> +	task = kthread_run((void *)wkup_m3_rproc_loader_thread, rproc,
>> +			   "wkup_m3_rproc_loader");
>> +
>> +	if (IS_ERR(task)) {
>> +		dev_err(dev, "can't create rproc_loader thread\n");
>> +		goto err;
>> +	}
>> +
>> +	return 0;
>> +
>> +err:
>> +	rproc_put(rproc);
> pm_runtime_put_sync() ?
>> +	return ret;
>> +}
>> +
>> +static int wkup_m3_rproc_remove(struct platform_device *pdev)
>> +{
>> +	struct rproc *rproc = platform_get_drvdata(pdev);
>> +
>> +	rproc_del(rproc);
>> +	rproc_put(rproc);

Need to reset m3_rproc_static?

>> +
> Here too ?
>> +	return 0;
>> +}
>> +
>> +static int wkup_m3_rpm_suspend(struct device *dev)
>> +{
>> +	return -EBUSY;
>> +}
>> +
>> +static int wkup_m3_rpm_resume(struct device *dev)
>> +{
>> +	return 0;
>> +}
>> +
> If you are not doing any meaningfull in suspend/resume hooks,
> why are you even registering them ?
> 
>> +static const struct dev_pm_ops wkup_m3_rproc_pm_ops = {
>> +	SET_RUNTIME_PM_OPS(wkup_m3_rpm_suspend, wkup_m3_rpm_resume, NULL)
>> +};
>> +
>> +static const struct of_device_id wkup_m3_rproc_of_match[] = {
>> +	{ .compatible = "ti,am3353-wkup-m3", .data = NULL, },
>> +	{},
>> +};
>> +
>> +static struct platform_driver wkup_m3_rproc_driver = {
>> +	.probe = wkup_m3_rproc_probe,
>> +	.remove = wkup_m3_rproc_remove,
>> +	.driver = {
>> +		.name = "wkup_m3",
>> +		.owner = THIS_MODULE,
>> +		.of_match_table = wkup_m3_rproc_of_match,
>> +		.pm = &wkup_m3_rproc_pm_ops,
>> +	},
>> +};
>> +
>> +module_platform_driver(wkup_m3_rproc_driver);
>> +
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_DESCRIPTION("wkup m3 remote processor control driver");
>> diff --git a/include/linux/wkup_m3.h b/include/linux/wkup_m3.h
>> new file mode 100644
>> index 0000000..1a2237f
>> --- /dev/null
>> +++ b/include/linux/wkup_m3.h
>> @@ -0,0 +1,71 @@
>> +/*
>> + * TI Wakeup M3 Power Management Routines

Suggest adding for AMx3x SoCs

>> + *
>> + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
>> + * Dave Gerlach <d-gerlach@ti.com>
>> + *
>> + * 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 _LINUX_WKUP_M3_H
>> +#define _LINUX_WKUP_M3_H
>> +
>> +/**
>> + * struct wkup_m3_ops - Callbacks for allowing pm code to interact with wkup_m3.
>> + *
>> + * @txev_handler: Callback to allow pm code to react to response from wkup_m3
>> + *		  after pinging it using wkup_m3_ping.
>> + *
>> + * @firmware_loaded: Callback invoked when the firmware has been loaded to the
>> + *		     m3 to allow the pm code to enable suspend/resume ops.

firmware_loaded ops does not exist

>> + */
>> +
>> +struct wkup_m3_ops {
>> +	void (*txev_handler)(void);
>> +	void (*rproc_ready)(void);
>> +};
>> +
>> +struct wkup_m3_wakeup_src {
>> +	int irq_nr;
>> +	char src[10];

What is the @src used for, debug? Don't see anywhere this is getting used.

regards
Suman

>> +};
>> +
>> +struct wkup_m3_ipc_regs {
>> +	u32 reg0;
>> +	u32 reg1;
>> +	u32 reg2;
>> +	u32 reg3;
>> +	u32 reg4;
>> +	u32 reg5;
>> +	u32 reg6;
>> +	u32 reg7;
>> +};
>> +
>> +#ifdef CONFIG_WKUP_M3_RPROC
>> +int wkup_m3_prepare(void);
>> +void wkup_m3_set_ops(struct wkup_m3_ops *ops);
>> +int wkup_m3_ping(void);
>> +void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wakeup_src);
>> +int wkup_m3_pm_status(void);
>> +int wkup_m3_is_valid(void);
>> +int wkup_m3_fw_version_read(void);
>> +void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs);
>> +
>> +#else
>> +
>> +int wkup_m3_prepare(void) { return -EINVAL; }
>> +void wkup_m3_set_ops(struct wkup_m3_ops *ops) { }
>> +int wkup_m3_ping(void) { return -EINVAL }
>> +void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wakeup_src) { }
>> +int wkup_m3_pm_status(void) { return -1; }
>> +int wkup_m3_fw_version_read(void) { return -1; }
>> +void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs) { }
> 
> Mark all of above in else as static inline.
> 
>> +#endif /* CONFIG_WKUP_M3_RPROC */
>> +#endif /* _LINUX_WKUP_M3_H */
>>
> 

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 06/11] remoteproc: wkup_m3: Add wkup_m3 remote proc driver
  2014-07-14 19:07       ` Suman Anna
@ 2014-07-14 21:09         ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-14 21:09 UTC (permalink / raw)
  To: Suman Anna
  Cc: Santosh Shilimkar, linux-arm-kernel, linux-omap, Paul Walmsley,
	Kevin Hilman, Tony Lindgren, Tero Kristo, Nishanth Menon,
	Russ Dill, Daniel Mack, Benoit Cousson, Ohad Ben-Cohen

Suman,

On 07/14/2014 02:07 PM, Suman Anna wrote:
> Hi Dave,
>
> I have a few more comments in addition to Santosh's..
>
> On 07/14/2014 09:54 AM, Santosh Shilimkar wrote:
>> On Thursday 10 July 2014 10:55 PM, Dave Gerlach wrote:
>>> Add a remoteproc driver to load the firmware for and boot the wkup_m3
>>> present on am33xx. The wkup_m3 is an integrated Cortex M3 that allows
>>> the SoC to enter the lowest possible power state by taking control from
>>> the MPU after it has gone into its own low power state and shutting off
>>> any additional peripherals.
>>>
>>> Communication between the MPU and CM3 is handled by several IPC
>>> registers in the control module and a mailbox. An API is exposed for
>>> programming the aforementioned IPC registers and notifying the wkup_m3
>>> of pending data using the mailbox. The wkup_m3 has the ability to
>>> trigger an interrupt back to the MPU to allow for bidirectional
>>> communication through these registers.
>>>
>>> Two callbacks are provided. rproc_ready allows code to hook into the
>>> driver to see when firmware has been loaded and execute other code and
>>> txev_handler allows external code to run when the wkup_m3 triggers an
>>> interrupt back to the m3.
>>>
>>> The driver expects a resource table to be present in the wkup_m3 to
>>> define the required memory resources needed by wkup_m3, at least the
>>> data memory so that the firmware can be copied for the proper place for
>>> execution.
>>>
>>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>>> CC: Ohad Ben-Cohen <ohad@wizery.com>
>>> ---
>>>   drivers/remoteproc/Kconfig         |  15 ++
>>>   drivers/remoteproc/Makefile        |   1 +
>>>   drivers/remoteproc/wkup_m3_rproc.c | 512 +++++++++++++++++++++++++++++++++++++
>>>   include/linux/wkup_m3.h            |  71 +++++
>>>   4 files changed, 599 insertions(+)
>>>   create mode 100644 drivers/remoteproc/wkup_m3_rproc.c
>>>   create mode 100644 include/linux/wkup_m3.h
>>>
>>> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
>>> index 5e343ba..4b00c21 100644
>>> --- a/drivers/remoteproc/Kconfig
>>> +++ b/drivers/remoteproc/Kconfig
>>> @@ -41,6 +41,21 @@ config STE_MODEM_RPROC
>>>   	  This can be either built-in or a loadable module.
>>>   	  If unsure say N.
>>>
>>> +config WKUP_M3_RPROC
>>> +	bool "AM33xx wkup-m3 remoteproc support"
>>> +        depends on SOC_AM33XX
>>> +        select REMOTEPROC
>>> +	select MAILBOX
>>> +	select OMAP2PLUS_MBOX
>> Please fix the indentation.
>>
>>> +	default n
>> Default is always 'n' so drop above.
>>> +	help
>>> +	  Say y here to support AM33xx wkup-m3.
>>> +
>>> +	  Required for Suspend-to-ram and CPUIdle on AM33xx. Allows for
>>> +	  loading of firmware and communication with CM3 PM coproccesor
>
> typo - coprocessor*

Whoops thanks.

>
>>> +	  that is present on AM33xx family of SoCs
>>> +	  If unsure say N.
>>> +
>>>   config DA8XX_REMOTEPROC
>>>   	tristate "DA8xx/OMAP-L13x remoteproc support"
>>>   	depends on ARCH_DAVINCI_DA8XX
>>> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
>>> index ac2ff75..81b04d1 100644
>>> --- a/drivers/remoteproc/Makefile
>>> +++ b/drivers/remoteproc/Makefile
>>> @@ -9,4 +9,5 @@ remoteproc-y				+= remoteproc_virtio.o
>>>   remoteproc-y				+= remoteproc_elf_loader.o
>>>   obj-$(CONFIG_OMAP_REMOTEPROC)		+= omap_remoteproc.o
>>>   obj-$(CONFIG_STE_MODEM_RPROC)	 	+= ste_modem_rproc.o
>>> +obj-$(CONFIG_WKUP_M3_RPROC)		+= wkup_m3_rproc.o
>>>   obj-$(CONFIG_DA8XX_REMOTEPROC)		+= da8xx_remoteproc.o
>>> diff --git a/drivers/remoteproc/wkup_m3_rproc.c b/drivers/remoteproc/wkup_m3_rproc.c
>>> new file mode 100644
>>> index 0000000..58aeaf2
>>> --- /dev/null
>>> +++ b/drivers/remoteproc/wkup_m3_rproc.c
>>> @@ -0,0 +1,512 @@
>>> +/*
>>> + * AMx3 Wkup M3 Remote Processor driver
>>> + *
>>> + * Copyright (C) 2014 Texas Instruments, Inc.
>>> + *
>>> + * Dave Gerlach <d-gerlach@ti.com>
>>> + *
>>> + * This program is free software; you can redistribute it and/or
>>> + * modify it under the terms of the GNU General Public License
>>> + * version 2 as published by the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>> + * GNU General Public License for more details.
>>> + */
>>> +
>>> +#include <linux/kernel.h>
>>> +#include <linux/module.h>
>>> +#include <linux/err.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/irq.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/elf.h>
>
> Do you require this header?

No, seems I forgot to remove it after moving to common remoteproc_elf_ops.

>
>>> +#include <linux/pm_runtime.h>
>>> +#include <linux/firmware.h>
>>> +#include <linux/remoteproc.h>
>>> +#include <linux/omap-mailbox.h>
>>> +#include <linux/mailbox_client.h>
>>> +#include <linux/wkup_m3.h>
>>> +#include <linux/kthread.h>
>>> +#include "remoteproc_internal.h"
>
> Suggest moving the internal header to after including the
> standard headers..

Ok.

>
>>> +
>>> +#include <linux/platform_data/wkup_m3.h>
>>> +
>>> +#define WKUP_M3_WAKE_SRC_MASK		0xFF
>>> +
>>> +#define WKUP_M3_STATUS_RESP_SHIFT	16
>>> +#define WKUP_M3_STATUS_RESP_MASK	(0xffff << 16)
>>> +
>>> +#define WKUP_M3_FW_VERSION_SHIFT	0
>>> +#define WKUP_M3_FW_VERSION_MASK		0xffff
>>> +
>>> +/* AM33XX M3_TXEV_EOI register */
>>> +#define AM33XX_CONTROL_M3_TXEV_EOI	0x00
>>> +
>>> +#define AM33XX_M3_TXEV_ACK		(0x1 << 0)
>>> +#define AM33XX_M3_TXEV_ENABLE		(0x0 << 0)
>>> +
>>> +/* AM33XX IPC message registers */
>>> +#define AM33XX_CONTROL_IPC_MSG_REG0	0x04
>>> +#define AM33XX_CONTROL_IPC_MSG_REG1	0x08
>>> +#define AM33XX_CONTROL_IPC_MSG_REG2	0x0c
>>> +#define AM33XX_CONTROL_IPC_MSG_REG3	0x10
>>> +#define AM33XX_CONTROL_IPC_MSG_REG4	0x14
>>> +#define AM33XX_CONTROL_IPC_MSG_REG5	0x18
>>> +#define AM33XX_CONTROL_IPC_MSG_REG6	0x1c
>>> +#define AM33XX_CONTROL_IPC_MSG_REG7	0x20
>
> How about using a macro for these, especially given that the AM43xx has
> a bit more registers.

Yeah after thinking about it I will clean this up a bit. More explanation in 
later comment.

>
>>> +
>> Is this driver going to be AM33xx specific ?
>>
>>> +struct wkup_m3_rproc {
>>> +	struct rproc *rproc;
>>> +
>>> +	void * __iomem dev_table_va;
>>> +	void * __iomem ipc_mem_base;
>>> +	struct platform_device *pdev;
>>> +
>>> +	struct mbox_client mbox_client;
>>> +	struct mbox_chan *mbox;
>>> +	struct wkup_m3_ops *ops;
>>> +
>>> +	bool is_active;
>>> +};
>>> +
>>> +static struct wkup_m3_rproc *m3_rproc_static;
>>> +
>>> +static struct wkup_m3_wakeup_src wakeups[] = {
>>> +	{.irq_nr = 35,	.src = "USB0_PHY"},
>>> +	{.irq_nr = 36,	.src = "USB1_PHY"},
>>> +	{.irq_nr = 40,	.src = "I2C0"},
>>> +	{.irq_nr = 41,	.src = "RTC Timer"},
>>> +	{.irq_nr = 42,	.src = "RTC Alarm"},
>>> +	{.irq_nr = 43,	.src = "Timer0"},
>>> +	{.irq_nr = 44,	.src = "Timer1"},
>>> +	{.irq_nr = 45,	.src = "UART"},
>>> +	{.irq_nr = 46,	.src = "GPIO0"},
>>> +	{.irq_nr = 48,	.src = "MPU_WAKE"},
>>> +	{.irq_nr = 49,	.src = "WDT0"},
>>> +	{.irq_nr = 50,	.src = "WDT1"},
>>> +	{.irq_nr = 51,	.src = "ADC_TSC"},
>>> +	{.irq_nr = 0,	.src = "Unknown"},
>>> +};
>
> Is this table going to be same for AM43xx? If not, it is better to store
> this as match data, so that a different table can easily be plugged in
> based on compatible string.

The table actually is the same for am43xx with the addition of a few wake 
sources that aren't possible on am335x, so it'll work like this.

>
>>> +
>> const ?
>>
>>> +static void am33xx_txev_eoi(struct wkup_m3_rproc *m3_rproc)
>>> +{
>>> +	writel(AM33XX_M3_TXEV_ACK,
>>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
>>> +}
>>> +
>>> +static void am33xx_txev_enable(struct wkup_m3_rproc *m3_rproc)
>>> +{
>>> +	writel(AM33XX_M3_TXEV_ENABLE,
>>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
>>> +}
>>> +
>>> +static void wkup_m3_ctrl_ipc_write(struct wkup_m3_rproc *m3_rproc,
>>> +				   struct wkup_m3_ipc_regs *ipc_regs)
>>> +{
>>> +	writel(ipc_regs->reg0,
>>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG0);
>>> +	writel(ipc_regs->reg1,
>>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG1);
>>> +	writel(ipc_regs->reg2,
>>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG2);
>>> +	writel(ipc_regs->reg3,
>>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG3);
>>> +	writel(ipc_regs->reg4,
>>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG4);
>>> +	writel(ipc_regs->reg5,
>>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG5);
>>> +	writel(ipc_regs->reg6,
>>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG6);
>>> +	writel(ipc_regs->reg7,
>>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG7);
>
> How about defining a static inline function for the read and write
> operations?

Again, I plan to clean up these reads and writes now. More explanation in a 
later comment.

>
>>> +}
>>> +
>>> +static void wkup_m3_ctrl_ipc_read(struct wkup_m3_rproc *m3_rproc,
>>> +				  struct wkup_m3_ipc_regs *ipc_regs)
>>> +{
>>> +	ipc_regs->reg0 = readl(m3_rproc->ipc_mem_base
>>> +			       + AM33XX_CONTROL_IPC_MSG_REG0);
>>> +	ipc_regs->reg1 = readl(m3_rproc->ipc_mem_base
>>> +			       + AM33XX_CONTROL_IPC_MSG_REG1);
>>> +	ipc_regs->reg2 = readl(m3_rproc->ipc_mem_base
>>> +			       + AM33XX_CONTROL_IPC_MSG_REG2);
>>> +	ipc_regs->reg3 = readl(m3_rproc->ipc_mem_base
>>> +			       + AM33XX_CONTROL_IPC_MSG_REG3);
>>> +	ipc_regs->reg4 = readl(m3_rproc->ipc_mem_base
>>> +			       + AM33XX_CONTROL_IPC_MSG_REG4);
>>> +	ipc_regs->reg5 = readl(m3_rproc->ipc_mem_base
>>> +			       + AM33XX_CONTROL_IPC_MSG_REG5);
>>> +	ipc_regs->reg6 = readl(m3_rproc->ipc_mem_base
>>> +			       + AM33XX_CONTROL_IPC_MSG_REG6);
>>> +	ipc_regs->reg7 = readl(m3_rproc->ipc_mem_base
>>> +			       + AM33XX_CONTROL_IPC_MSG_REG7);
>>> +}
>>> +
>>> +static irqreturn_t wkup_m3_txev_handler(int irq, void *unused)
>>> +{
>>> +	am33xx_txev_eoi(m3_rproc_static);
>>> +
>>> +	if (m3_rproc_static->ops && m3_rproc_static->ops->txev_handler)
>>> +		m3_rproc_static->ops->txev_handler();
>>> +
>>> +	am33xx_txev_enable(m3_rproc_static);
>>> +
>>> +	return IRQ_HANDLED;
>>> +}
>>> +
>>> +/**
>>> + * wkup_m3_fw_version_clear - Clear FW version from ipc regs
>>> + *
>>> + * Invalidate M3 firmware version before hardreset.
>>> + * Write invalid version in lower 4 nibbles of parameter
>>> + * register (ipc_regs + 0x8).
>>> + */
>>> +
>>> +static void wkup_m3_fw_version_clear(void)
>>> +{
>>> +	struct wkup_m3_ipc_regs ipc_regs;
>>> +
>>> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
>>> +	ipc_regs.reg2 &= 0xFFFF0000;
>> Probably define a macro and use it.
>>> +	wkup_m3_ctrl_ipc_write(m3_rproc_static, &ipc_regs);
>>> +}
>>> +
>>> +static void wkup_m3_mbox_callback(struct mbox_client *client, void *data)
>>> +{
>>> +	omap_mbox_disable_irq(m3_rproc_static->mbox, IRQ_RX);
>>> +}
>>> +
>>> +static int wkup_m3_rproc_start(struct rproc *rproc)
>>> +{
>>> +	struct wkup_m3_rproc *m3_rproc = rproc->priv;
>>> +	struct platform_device *pdev = m3_rproc->pdev;
>>> +	struct device *dev = &pdev->dev;
>>> +	struct wkup_m3_platform_data *pdata = dev->platform_data;
>>> +	int ret;
>>> +
>>> +	wkup_m3_fw_version_clear();
>>> +
>>> +	if (pdata && pdata->deassert_reset) {
>>> +		ret = pdata->deassert_reset(pdev, pdata->reset_name);
>>> +		if (ret) {
>>> +			dev_err(dev, "Unable to reset wkup_m3!\n");
>>> +			return -ENODEV;
>>> +		}
>>> +	} else {
>>> +		dev_err(dev, "Platform data missing deassert_reset!\n");
>>> +		return -ENODEV;
>>> +	}
>
> You don't need a runtime check for this, check once in probe, this ops
> is essential right?

Yes you are right, better that way.

>
>>> +
>>> +	m3_rproc->mbox_client.dev = dev;
>>> +	m3_rproc->mbox_client.tx_done = NULL;
>>> +	m3_rproc->mbox_client.rx_callback = wkup_m3_mbox_callback;
>>> +	m3_rproc->mbox_client.tx_block = false;
>>> +	m3_rproc->mbox_client.knows_txdone = false;
>>> +
>>> +	m3_rproc->mbox = mbox_request_channel(&m3_rproc->mbox_client);
>>> +
> nit, no need of an additional blank line

Ok.

>
>>> +	if (IS_ERR(m3_rproc->mbox)) {
>>> +		dev_err(dev, "IPC Request for A8->M3 Channel failed!\n");
>>> +		ret = PTR_ERR(m3_rproc->mbox);
>>> +		m3_rproc->mbox = NULL;
>>> +		return ret;
>>> +	}
>>> +
>>> +	if (m3_rproc_static->ops && m3_rproc_static->ops->rproc_ready)
>>> +		m3_rproc_static->ops->rproc_ready();
>>> +
>>> +	m3_rproc_static->is_active = 1;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +
>>> +static int wkup_m3_rproc_stop(struct rproc *rproc)
>>> +{
>>> +	return 0;
>
> Any reason this is a stub, don't you need to assert the reset in stop?

Well, at this point I don't think there is any reason you would want to stop the 
wkup_m3, it will certainly cause issues with PM. That would be the most 
appropriate action here though.

>
>>> +}
>>> +
>>> +static void wkup_m3_rproc_kick(struct rproc *rproc, int vqid)
>>> +{
>>> +}
>
> If you can remove this empty stub, please do so. I do not think we will
> be using virtio for communicating with the WkupM3.

We don't need it, Ill remove it.

>
>>> +
>>> +static struct rproc_ops wkup_m3_rproc_ops = {
>>> +	.start		= wkup_m3_rproc_start,
>>> +	.stop		= wkup_m3_rproc_stop,
>>> +	.kick		= wkup_m3_rproc_kick,
>>> +};
>>> +
>>> +/* Public Functions */
>>> +
>>> +/**
>>> + * wkup_m3_set_ops - Set callbacks for user of rproc
>>> + * @ops - struct wkup_m3_ops *
>>> + *
>>> + * Registers callbacks to wkup_m3 to be invoked after rproc is ready to use
>>> + * and after an interrupt is handled.
>>> + */
>>> +void wkup_m3_set_ops(struct wkup_m3_ops *ops)
>>> +{
>>> +	m3_rproc_static->ops = ops;
>>> +
>>> +	if (m3_rproc_static->is_active && m3_rproc_static->ops &&
>>> +	    m3_rproc_static->ops->rproc_ready)
>>> +		m3_rproc_static->ops->rproc_ready();
>>> +}
>
>
>
>>> +
>>> +/**
>>> + * wkup_m3_ping - Send a dummy msg to wkup_m3 to tell to to check IPC regs
>>> + *
>>> + * Returns the result of sending mbox msg or -EIO if no mbox handle is present
>>> + */
>>> +int wkup_m3_ping(void)
>>> +{
>>> +	int ret;
>>> +	mbox_msg_t dummy_msg = 0;
>>> +
>>> +	if (!m3_rproc_static->mbox) {
>>> +		dev_err(&m3_rproc_static->pdev->dev,
>>> +			"No IPC channel to communicate with wkup_m3!\n");
>>> +		return -EIO;
>>> +	}
>>> +
>>> +	/*
>>> +	 * Write a dummy message to the mailbox in order to trigger the RX
>>> +	 * interrupt to alert the M3 that data is available in the IPC
>>> +	 * registers. We must enable the IRQ here and disable it after in
>>> +	 * the RX callback to avoid multiple interrupts being received
>>> +	 * by the CM3.
>>> +	 */
>>> +	omap_mbox_enable_irq(m3_rproc_static->mbox, IRQ_RX);
>>> +	ret = mbox_send_message(m3_rproc_static->mbox, (void *)dummy_msg);
>>> +
> nit, no need of an additional blank line

Ok.

>
>>> +	if (ret < 0) {
>>> +		pr_err("%s: mbox_send_message() failed: %d\n", __func__, ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/**
>>> + * wkup_m3_wake_src - Get the wakeup source info passed from wkup_m3
>>> + * @wkup_m3_wakeup: struct wkup_m3_wakeup_src * gets assigned the
>>> + *		    wakeup src value
>>> + */
>>> +void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wkup_m3_wakeup)
>>> +{
>>> +	struct wkup_m3_ipc_regs ipc_regs;
>>> +	unsigned int wakeup_src_idx;
>>> +	int j;
>>> +
>>> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
>>> +
>>> +	wakeup_src_idx = ipc_regs.reg6 & WKUP_M3_WAKE_SRC_MASK;
>>> +
>>> +	for (j = 0; j < ARRAY_SIZE(wakeups)-1; j++) {
>>> +		if (wakeups[j].irq_nr == wakeup_src_idx) {
>>> +			*wkup_m3_wakeup = wakeups[j];
>>> +			return;
>>> +		}
>>> +	}
>>> +	*wkup_m3_wakeup = wakeups[j];
>>> +}
>>> +
>>> +/**
>>> + * wkup_m3_pm_status - Return the status code from wkup_m3 after sleep event
>>> + *
>>> + * Returns an error code that indicates whether or not the dsired sleep
>>> + * action was a success or not.
>>> + */
>>> +int wkup_m3_pm_status(void)
>>> +{
>>> +	unsigned int i;
>>> +	struct wkup_m3_ipc_regs ipc_regs;
>>> +
>>> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
>
> Do you need to read back all the registers, given that this is a local
> structure anyway?

Well, at least one of the registers must be read back (reg1) in order to get the 
PM status value returned by the wkup_m3. The ipc read/write functions had been 
rewritten to be more "generic" in order to avoid exposing a bunch of specific 
APIs from the control module. The reads/writes are all internal to the driver 
now though so it probably makes sense to do away with that entirely now and be 
more specific in what we read and write.

>
>>> +
>>> +	i = WKUP_M3_STATUS_RESP_MASK & ipc_regs.reg1;
>>> +	i >>= __ffs(WKUP_M3_STATUS_RESP_MASK);
>>> +
>>> +	return i;
>>> +}
>>> +
>>> +/**
>>> + * wkup_m3_fw_version_read - Return the fw version given by the wkup_m3
>>> + *
>>> + * After boot the fw version should be read to ensure it is compatible.
>>> + */
>>> +int wkup_m3_fw_version_read(void)
>>> +{
>>> +	struct wkup_m3_ipc_regs ipc_regs;
>>> +
>>> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
>>> +
>>> +	return ipc_regs.reg2 & WKUP_M3_FW_VERSION_MASK;
>>> +}
>>> +
>>> +/**
>>> + * wkup_m3_set_cmd - write contents of struct to ipc regs
>>> + * @ipc_regs: struct wkup_m3_ipc_regs *
>>> + */
>>> +void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs)
>>> +{
>>> +	wkup_m3_ctrl_ipc_write(m3_rproc_static, ipc_regs);
>>> +}
>>> +
>>> +static void wkup_m3_rproc_loader_thread(struct rproc *rproc)
>>> +{
>>> +	struct wkup_m3_rproc *m3_rproc = rproc->priv;
>>> +	struct device *dev = &m3_rproc->pdev->dev;
>>> +	int ret;
>>> +
>>> +	wait_for_completion(&rproc->firmware_loading_complete);
>>> +
>>> +	ret = rproc_boot(rproc);
>>> +	if (ret)
>>> +		dev_err(dev, "rproc_boot failed\n");
>>> +
>>> +	do_exit(0);
>>> +}
>>> +
>>> +static int wkup_m3_rproc_probe(struct platform_device *pdev)
>>> +{
>>> +	struct device *dev = &pdev->dev;
>>> +	struct device_node *np = pdev->dev.of_node;
>>> +	struct wkup_m3_rproc *m3_rproc;
>>> +	struct rproc *rproc;
>>> +	int irq, ret;
>>> +	struct resource *res;
>>> +	struct task_struct *task;
>>> +	const char *mbox_name;
>>> +
>>> +	pm_runtime_enable(&pdev->dev);
>>> +
>>> +	ret = pm_runtime_get_sync(&pdev->dev);
>>> +	if (IS_ERR_VALUE(ret)) {
>>> +		dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	rproc = rproc_alloc(dev, "wkup_m3", &wkup_m3_rproc_ops,
>>> +			    "am335x-pm-firmware.elf", sizeof(*m3_rproc));
>>> +	if (!rproc)
>>> +		return -ENOMEM;
>>> +
>>> +	m3_rproc = rproc->priv;
>>> +	m3_rproc->rproc = rproc;
>>> +	m3_rproc->pdev = pdev;
>>> +
>>> +	m3_rproc_static = m3_rproc;
>>> +
>>> +	irq = platform_get_irq(pdev, 0);
>>> +	if (!irq) {
>>> +		dev_err(&pdev->dev, "no irq resource\n");
>>> +		ret = -ENXIO;
>>> +		goto err;
>>> +	}
>
> Colocate this to the same place as the request_irq.

Ok.

>
>>> +
>>> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ipc_regs");
>>> +	if (!res) {
>
> No need to check for error, the devm function following this should take
> care of any errors.
>
>>> +		dev_err(&pdev->dev, "no memory resource for ipc\n");
>>> +		ret = -ENXIO;
>>> +		goto err;
>>> +	}
>>> +
>>> +	m3_rproc->ipc_mem_base = devm_request_and_ioremap(dev, res);
>
> Is some one else doing an ioremap of this space, otherwise
> devm_ioremap_resource is preferred.

I will update driver to do it this way, was unfamiliar with this approach.

>
>>> +	if (!m3_rproc->ipc_mem_base) {
>>> +		dev_err(dev, "could not ioremap ipc_mem\n");
>>> +		ret = -EADDRNOTAVAIL;
>>> +		goto err;
>>> +	}
>
> Don't you need to be retrieving umem and dmem?

We don't need to do that here as remoteproc handles it for us behind the scenes. 
They are left in the DT as they had already been added and to keep the address 
space consistent.

>
>
>>> +
>>> +	ret = devm_request_irq(dev, irq, wkup_m3_txev_handler,
>>> +			       IRQF_DISABLED, "wkup_m3_txev", m3_rproc);
>>> +	if (ret) {
>>> +		dev_err(dev, "request_irq failed\n");
>>> +		goto err;
>>> +	}
>>> +
>>> +	/* Get mbox name from device tree node */
>>> +	ret = of_property_read_string(np, "mbox-names", &mbox_name);
>>> +	if (ret) {
>>> +		dev_err(&pdev->dev, "Unable to get mbox name from dt node: %d\n",
>>> +			ret);
>>> +			goto err;
>>> +	};
>>> +
>>> +	m3_rproc->mbox_client.chan_name = mbox_name;
>>> +
>>> +	/* Register as a remoteproc device */
>>> +	ret = rproc_add(rproc);
>>> +	if (ret) {
>>> +		dev_err(dev, "rproc_add failed\n");
>>> +		goto err;
>>> +	}
>>> +
>>> +	/*
>>> +	 * Wait for firmware loading completion in a thread so we
>>> +	 * can boot the wkup_m3 as soon as it's ready without holding
>>> +	 * up kernel boot
>>> +	 */
>>> +	task = kthread_run((void *)wkup_m3_rproc_loader_thread, rproc,
>>> +			   "wkup_m3_rproc_loader");
>>> +
>>> +	if (IS_ERR(task)) {
>>> +		dev_err(dev, "can't create rproc_loader thread\n");
>>> +		goto err;
>>> +	}
>>> +
>>> +	return 0;
>>> +
>>> +err:
>>> +	rproc_put(rproc);
>> pm_runtime_put_sync() ?
>>> +	return ret;
>>> +}
>>> +
>>> +static int wkup_m3_rproc_remove(struct platform_device *pdev)
>>> +{
>>> +	struct rproc *rproc = platform_get_drvdata(pdev);
>>> +
>>> +	rproc_del(rproc);
>>> +	rproc_put(rproc);
>
> Need to reset m3_rproc_static?

Yes I need to take care of that.

>
>>> +
>> Here too ?
>>> +	return 0;
>>> +}
>>> +
>>> +static int wkup_m3_rpm_suspend(struct device *dev)
>>> +{
>>> +	return -EBUSY;
>>> +}
>>> +
>>> +static int wkup_m3_rpm_resume(struct device *dev)
>>> +{
>>> +	return 0;
>>> +}
>>> +
>> If you are not doing any meaningfull in suspend/resume hooks,
>> why are you even registering them ?
>>
>>> +static const struct dev_pm_ops wkup_m3_rproc_pm_ops = {
>>> +	SET_RUNTIME_PM_OPS(wkup_m3_rpm_suspend, wkup_m3_rpm_resume, NULL)
>>> +};
>>> +
>>> +static const struct of_device_id wkup_m3_rproc_of_match[] = {
>>> +	{ .compatible = "ti,am3353-wkup-m3", .data = NULL, },
>>> +	{},
>>> +};
>>> +
>>> +static struct platform_driver wkup_m3_rproc_driver = {
>>> +	.probe = wkup_m3_rproc_probe,
>>> +	.remove = wkup_m3_rproc_remove,
>>> +	.driver = {
>>> +		.name = "wkup_m3",
>>> +		.owner = THIS_MODULE,
>>> +		.of_match_table = wkup_m3_rproc_of_match,
>>> +		.pm = &wkup_m3_rproc_pm_ops,
>>> +	},
>>> +};
>>> +
>>> +module_platform_driver(wkup_m3_rproc_driver);
>>> +
>>> +MODULE_LICENSE("GPL v2");
>>> +MODULE_DESCRIPTION("wkup m3 remote processor control driver");
>>> diff --git a/include/linux/wkup_m3.h b/include/linux/wkup_m3.h
>>> new file mode 100644
>>> index 0000000..1a2237f
>>> --- /dev/null
>>> +++ b/include/linux/wkup_m3.h
>>> @@ -0,0 +1,71 @@
>>> +/*
>>> + * TI Wakeup M3 Power Management Routines
>
> Suggest adding for AMx3x SoCs

Good idea.

>
>>> + *
>>> + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
>>> + * Dave Gerlach <d-gerlach@ti.com>
>>> + *
>>> + * 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 _LINUX_WKUP_M3_H
>>> +#define _LINUX_WKUP_M3_H
>>> +
>>> +/**
>>> + * struct wkup_m3_ops - Callbacks for allowing pm code to interact with wkup_m3.
>>> + *
>>> + * @txev_handler: Callback to allow pm code to react to response from wkup_m3
>>> + *		  after pinging it using wkup_m3_ping.
>>> + *
>>> + * @firmware_loaded: Callback invoked when the firmware has been loaded to the
>>> + *		     m3 to allow the pm code to enable suspend/resume ops.
>
> firmware_loaded ops does not exist

Ah yes, seems I renamed the function pointer but failed to update the comment.

>
>>> + */
>>> +
>>> +struct wkup_m3_ops {
>>> +	void (*txev_handler)(void);
>>> +	void (*rproc_ready)(void);
>>> +};
>>> +
>>> +struct wkup_m3_wakeup_src {
>>> +	int irq_nr;
>>> +	char src[10];
>
> What is the @src used for, debug? Don't see anywhere this is getting used.

It is used to provide a string that names the wakeup source, the table is 
defined at the top of wkup_m3_rproc.c in this patch.

Thanks for the comments.

Regards,
Dave

>
> regards
> Suman
>
>>> +};
>>> +
>>> +struct wkup_m3_ipc_regs {
>>> +	u32 reg0;
>>> +	u32 reg1;
>>> +	u32 reg2;
>>> +	u32 reg3;
>>> +	u32 reg4;
>>> +	u32 reg5;
>>> +	u32 reg6;
>>> +	u32 reg7;
>>> +};
>>> +
>>> +#ifdef CONFIG_WKUP_M3_RPROC
>>> +int wkup_m3_prepare(void);
>>> +void wkup_m3_set_ops(struct wkup_m3_ops *ops);
>>> +int wkup_m3_ping(void);
>>> +void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wakeup_src);
>>> +int wkup_m3_pm_status(void);
>>> +int wkup_m3_is_valid(void);
>>> +int wkup_m3_fw_version_read(void);
>>> +void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs);
>>> +
>>> +#else
>>> +
>>> +int wkup_m3_prepare(void) { return -EINVAL; }
>>> +void wkup_m3_set_ops(struct wkup_m3_ops *ops) { }
>>> +int wkup_m3_ping(void) { return -EINVAL }
>>> +void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wakeup_src) { }
>>> +int wkup_m3_pm_status(void) { return -1; }
>>> +int wkup_m3_fw_version_read(void) { return -1; }
>>> +void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs) { }
>>
>> Mark all of above in else as static inline.
>>
>>> +#endif /* CONFIG_WKUP_M3_RPROC */
>>> +#endif /* _LINUX_WKUP_M3_H */
>>>
>>
>


^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 06/11] remoteproc: wkup_m3: Add wkup_m3 remote proc driver
@ 2014-07-14 21:09         ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-14 21:09 UTC (permalink / raw)
  To: linux-arm-kernel

Suman,

On 07/14/2014 02:07 PM, Suman Anna wrote:
> Hi Dave,
>
> I have a few more comments in addition to Santosh's..
>
> On 07/14/2014 09:54 AM, Santosh Shilimkar wrote:
>> On Thursday 10 July 2014 10:55 PM, Dave Gerlach wrote:
>>> Add a remoteproc driver to load the firmware for and boot the wkup_m3
>>> present on am33xx. The wkup_m3 is an integrated Cortex M3 that allows
>>> the SoC to enter the lowest possible power state by taking control from
>>> the MPU after it has gone into its own low power state and shutting off
>>> any additional peripherals.
>>>
>>> Communication between the MPU and CM3 is handled by several IPC
>>> registers in the control module and a mailbox. An API is exposed for
>>> programming the aforementioned IPC registers and notifying the wkup_m3
>>> of pending data using the mailbox. The wkup_m3 has the ability to
>>> trigger an interrupt back to the MPU to allow for bidirectional
>>> communication through these registers.
>>>
>>> Two callbacks are provided. rproc_ready allows code to hook into the
>>> driver to see when firmware has been loaded and execute other code and
>>> txev_handler allows external code to run when the wkup_m3 triggers an
>>> interrupt back to the m3.
>>>
>>> The driver expects a resource table to be present in the wkup_m3 to
>>> define the required memory resources needed by wkup_m3, at least the
>>> data memory so that the firmware can be copied for the proper place for
>>> execution.
>>>
>>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>>> CC: Ohad Ben-Cohen <ohad@wizery.com>
>>> ---
>>>   drivers/remoteproc/Kconfig         |  15 ++
>>>   drivers/remoteproc/Makefile        |   1 +
>>>   drivers/remoteproc/wkup_m3_rproc.c | 512 +++++++++++++++++++++++++++++++++++++
>>>   include/linux/wkup_m3.h            |  71 +++++
>>>   4 files changed, 599 insertions(+)
>>>   create mode 100644 drivers/remoteproc/wkup_m3_rproc.c
>>>   create mode 100644 include/linux/wkup_m3.h
>>>
>>> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
>>> index 5e343ba..4b00c21 100644
>>> --- a/drivers/remoteproc/Kconfig
>>> +++ b/drivers/remoteproc/Kconfig
>>> @@ -41,6 +41,21 @@ config STE_MODEM_RPROC
>>>   	  This can be either built-in or a loadable module.
>>>   	  If unsure say N.
>>>
>>> +config WKUP_M3_RPROC
>>> +	bool "AM33xx wkup-m3 remoteproc support"
>>> +        depends on SOC_AM33XX
>>> +        select REMOTEPROC
>>> +	select MAILBOX
>>> +	select OMAP2PLUS_MBOX
>> Please fix the indentation.
>>
>>> +	default n
>> Default is always 'n' so drop above.
>>> +	help
>>> +	  Say y here to support AM33xx wkup-m3.
>>> +
>>> +	  Required for Suspend-to-ram and CPUIdle on AM33xx. Allows for
>>> +	  loading of firmware and communication with CM3 PM coproccesor
>
> typo - coprocessor*

Whoops thanks.

>
>>> +	  that is present on AM33xx family of SoCs
>>> +	  If unsure say N.
>>> +
>>>   config DA8XX_REMOTEPROC
>>>   	tristate "DA8xx/OMAP-L13x remoteproc support"
>>>   	depends on ARCH_DAVINCI_DA8XX
>>> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
>>> index ac2ff75..81b04d1 100644
>>> --- a/drivers/remoteproc/Makefile
>>> +++ b/drivers/remoteproc/Makefile
>>> @@ -9,4 +9,5 @@ remoteproc-y				+= remoteproc_virtio.o
>>>   remoteproc-y				+= remoteproc_elf_loader.o
>>>   obj-$(CONFIG_OMAP_REMOTEPROC)		+= omap_remoteproc.o
>>>   obj-$(CONFIG_STE_MODEM_RPROC)	 	+= ste_modem_rproc.o
>>> +obj-$(CONFIG_WKUP_M3_RPROC)		+= wkup_m3_rproc.o
>>>   obj-$(CONFIG_DA8XX_REMOTEPROC)		+= da8xx_remoteproc.o
>>> diff --git a/drivers/remoteproc/wkup_m3_rproc.c b/drivers/remoteproc/wkup_m3_rproc.c
>>> new file mode 100644
>>> index 0000000..58aeaf2
>>> --- /dev/null
>>> +++ b/drivers/remoteproc/wkup_m3_rproc.c
>>> @@ -0,0 +1,512 @@
>>> +/*
>>> + * AMx3 Wkup M3 Remote Processor driver
>>> + *
>>> + * Copyright (C) 2014 Texas Instruments, Inc.
>>> + *
>>> + * Dave Gerlach <d-gerlach@ti.com>
>>> + *
>>> + * This program is free software; you can redistribute it and/or
>>> + * modify it under the terms of the GNU General Public License
>>> + * version 2 as published by the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>> + * GNU General Public License for more details.
>>> + */
>>> +
>>> +#include <linux/kernel.h>
>>> +#include <linux/module.h>
>>> +#include <linux/err.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/irq.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/elf.h>
>
> Do you require this header?

No, seems I forgot to remove it after moving to common remoteproc_elf_ops.

>
>>> +#include <linux/pm_runtime.h>
>>> +#include <linux/firmware.h>
>>> +#include <linux/remoteproc.h>
>>> +#include <linux/omap-mailbox.h>
>>> +#include <linux/mailbox_client.h>
>>> +#include <linux/wkup_m3.h>
>>> +#include <linux/kthread.h>
>>> +#include "remoteproc_internal.h"
>
> Suggest moving the internal header to after including the
> standard headers..

Ok.

>
>>> +
>>> +#include <linux/platform_data/wkup_m3.h>
>>> +
>>> +#define WKUP_M3_WAKE_SRC_MASK		0xFF
>>> +
>>> +#define WKUP_M3_STATUS_RESP_SHIFT	16
>>> +#define WKUP_M3_STATUS_RESP_MASK	(0xffff << 16)
>>> +
>>> +#define WKUP_M3_FW_VERSION_SHIFT	0
>>> +#define WKUP_M3_FW_VERSION_MASK		0xffff
>>> +
>>> +/* AM33XX M3_TXEV_EOI register */
>>> +#define AM33XX_CONTROL_M3_TXEV_EOI	0x00
>>> +
>>> +#define AM33XX_M3_TXEV_ACK		(0x1 << 0)
>>> +#define AM33XX_M3_TXEV_ENABLE		(0x0 << 0)
>>> +
>>> +/* AM33XX IPC message registers */
>>> +#define AM33XX_CONTROL_IPC_MSG_REG0	0x04
>>> +#define AM33XX_CONTROL_IPC_MSG_REG1	0x08
>>> +#define AM33XX_CONTROL_IPC_MSG_REG2	0x0c
>>> +#define AM33XX_CONTROL_IPC_MSG_REG3	0x10
>>> +#define AM33XX_CONTROL_IPC_MSG_REG4	0x14
>>> +#define AM33XX_CONTROL_IPC_MSG_REG5	0x18
>>> +#define AM33XX_CONTROL_IPC_MSG_REG6	0x1c
>>> +#define AM33XX_CONTROL_IPC_MSG_REG7	0x20
>
> How about using a macro for these, especially given that the AM43xx has
> a bit more registers.

Yeah after thinking about it I will clean this up a bit. More explanation in 
later comment.

>
>>> +
>> Is this driver going to be AM33xx specific ?
>>
>>> +struct wkup_m3_rproc {
>>> +	struct rproc *rproc;
>>> +
>>> +	void * __iomem dev_table_va;
>>> +	void * __iomem ipc_mem_base;
>>> +	struct platform_device *pdev;
>>> +
>>> +	struct mbox_client mbox_client;
>>> +	struct mbox_chan *mbox;
>>> +	struct wkup_m3_ops *ops;
>>> +
>>> +	bool is_active;
>>> +};
>>> +
>>> +static struct wkup_m3_rproc *m3_rproc_static;
>>> +
>>> +static struct wkup_m3_wakeup_src wakeups[] = {
>>> +	{.irq_nr = 35,	.src = "USB0_PHY"},
>>> +	{.irq_nr = 36,	.src = "USB1_PHY"},
>>> +	{.irq_nr = 40,	.src = "I2C0"},
>>> +	{.irq_nr = 41,	.src = "RTC Timer"},
>>> +	{.irq_nr = 42,	.src = "RTC Alarm"},
>>> +	{.irq_nr = 43,	.src = "Timer0"},
>>> +	{.irq_nr = 44,	.src = "Timer1"},
>>> +	{.irq_nr = 45,	.src = "UART"},
>>> +	{.irq_nr = 46,	.src = "GPIO0"},
>>> +	{.irq_nr = 48,	.src = "MPU_WAKE"},
>>> +	{.irq_nr = 49,	.src = "WDT0"},
>>> +	{.irq_nr = 50,	.src = "WDT1"},
>>> +	{.irq_nr = 51,	.src = "ADC_TSC"},
>>> +	{.irq_nr = 0,	.src = "Unknown"},
>>> +};
>
> Is this table going to be same for AM43xx? If not, it is better to store
> this as match data, so that a different table can easily be plugged in
> based on compatible string.

The table actually is the same for am43xx with the addition of a few wake 
sources that aren't possible on am335x, so it'll work like this.

>
>>> +
>> const ?
>>
>>> +static void am33xx_txev_eoi(struct wkup_m3_rproc *m3_rproc)
>>> +{
>>> +	writel(AM33XX_M3_TXEV_ACK,
>>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
>>> +}
>>> +
>>> +static void am33xx_txev_enable(struct wkup_m3_rproc *m3_rproc)
>>> +{
>>> +	writel(AM33XX_M3_TXEV_ENABLE,
>>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
>>> +}
>>> +
>>> +static void wkup_m3_ctrl_ipc_write(struct wkup_m3_rproc *m3_rproc,
>>> +				   struct wkup_m3_ipc_regs *ipc_regs)
>>> +{
>>> +	writel(ipc_regs->reg0,
>>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG0);
>>> +	writel(ipc_regs->reg1,
>>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG1);
>>> +	writel(ipc_regs->reg2,
>>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG2);
>>> +	writel(ipc_regs->reg3,
>>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG3);
>>> +	writel(ipc_regs->reg4,
>>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG4);
>>> +	writel(ipc_regs->reg5,
>>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG5);
>>> +	writel(ipc_regs->reg6,
>>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG6);
>>> +	writel(ipc_regs->reg7,
>>> +	       m3_rproc->ipc_mem_base + AM33XX_CONTROL_IPC_MSG_REG7);
>
> How about defining a static inline function for the read and write
> operations?

Again, I plan to clean up these reads and writes now. More explanation in a 
later comment.

>
>>> +}
>>> +
>>> +static void wkup_m3_ctrl_ipc_read(struct wkup_m3_rproc *m3_rproc,
>>> +				  struct wkup_m3_ipc_regs *ipc_regs)
>>> +{
>>> +	ipc_regs->reg0 = readl(m3_rproc->ipc_mem_base
>>> +			       + AM33XX_CONTROL_IPC_MSG_REG0);
>>> +	ipc_regs->reg1 = readl(m3_rproc->ipc_mem_base
>>> +			       + AM33XX_CONTROL_IPC_MSG_REG1);
>>> +	ipc_regs->reg2 = readl(m3_rproc->ipc_mem_base
>>> +			       + AM33XX_CONTROL_IPC_MSG_REG2);
>>> +	ipc_regs->reg3 = readl(m3_rproc->ipc_mem_base
>>> +			       + AM33XX_CONTROL_IPC_MSG_REG3);
>>> +	ipc_regs->reg4 = readl(m3_rproc->ipc_mem_base
>>> +			       + AM33XX_CONTROL_IPC_MSG_REG4);
>>> +	ipc_regs->reg5 = readl(m3_rproc->ipc_mem_base
>>> +			       + AM33XX_CONTROL_IPC_MSG_REG5);
>>> +	ipc_regs->reg6 = readl(m3_rproc->ipc_mem_base
>>> +			       + AM33XX_CONTROL_IPC_MSG_REG6);
>>> +	ipc_regs->reg7 = readl(m3_rproc->ipc_mem_base
>>> +			       + AM33XX_CONTROL_IPC_MSG_REG7);
>>> +}
>>> +
>>> +static irqreturn_t wkup_m3_txev_handler(int irq, void *unused)
>>> +{
>>> +	am33xx_txev_eoi(m3_rproc_static);
>>> +
>>> +	if (m3_rproc_static->ops && m3_rproc_static->ops->txev_handler)
>>> +		m3_rproc_static->ops->txev_handler();
>>> +
>>> +	am33xx_txev_enable(m3_rproc_static);
>>> +
>>> +	return IRQ_HANDLED;
>>> +}
>>> +
>>> +/**
>>> + * wkup_m3_fw_version_clear - Clear FW version from ipc regs
>>> + *
>>> + * Invalidate M3 firmware version before hardreset.
>>> + * Write invalid version in lower 4 nibbles of parameter
>>> + * register (ipc_regs + 0x8).
>>> + */
>>> +
>>> +static void wkup_m3_fw_version_clear(void)
>>> +{
>>> +	struct wkup_m3_ipc_regs ipc_regs;
>>> +
>>> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
>>> +	ipc_regs.reg2 &= 0xFFFF0000;
>> Probably define a macro and use it.
>>> +	wkup_m3_ctrl_ipc_write(m3_rproc_static, &ipc_regs);
>>> +}
>>> +
>>> +static void wkup_m3_mbox_callback(struct mbox_client *client, void *data)
>>> +{
>>> +	omap_mbox_disable_irq(m3_rproc_static->mbox, IRQ_RX);
>>> +}
>>> +
>>> +static int wkup_m3_rproc_start(struct rproc *rproc)
>>> +{
>>> +	struct wkup_m3_rproc *m3_rproc = rproc->priv;
>>> +	struct platform_device *pdev = m3_rproc->pdev;
>>> +	struct device *dev = &pdev->dev;
>>> +	struct wkup_m3_platform_data *pdata = dev->platform_data;
>>> +	int ret;
>>> +
>>> +	wkup_m3_fw_version_clear();
>>> +
>>> +	if (pdata && pdata->deassert_reset) {
>>> +		ret = pdata->deassert_reset(pdev, pdata->reset_name);
>>> +		if (ret) {
>>> +			dev_err(dev, "Unable to reset wkup_m3!\n");
>>> +			return -ENODEV;
>>> +		}
>>> +	} else {
>>> +		dev_err(dev, "Platform data missing deassert_reset!\n");
>>> +		return -ENODEV;
>>> +	}
>
> You don't need a runtime check for this, check once in probe, this ops
> is essential right?

Yes you are right, better that way.

>
>>> +
>>> +	m3_rproc->mbox_client.dev = dev;
>>> +	m3_rproc->mbox_client.tx_done = NULL;
>>> +	m3_rproc->mbox_client.rx_callback = wkup_m3_mbox_callback;
>>> +	m3_rproc->mbox_client.tx_block = false;
>>> +	m3_rproc->mbox_client.knows_txdone = false;
>>> +
>>> +	m3_rproc->mbox = mbox_request_channel(&m3_rproc->mbox_client);
>>> +
> nit, no need of an additional blank line

Ok.

>
>>> +	if (IS_ERR(m3_rproc->mbox)) {
>>> +		dev_err(dev, "IPC Request for A8->M3 Channel failed!\n");
>>> +		ret = PTR_ERR(m3_rproc->mbox);
>>> +		m3_rproc->mbox = NULL;
>>> +		return ret;
>>> +	}
>>> +
>>> +	if (m3_rproc_static->ops && m3_rproc_static->ops->rproc_ready)
>>> +		m3_rproc_static->ops->rproc_ready();
>>> +
>>> +	m3_rproc_static->is_active = 1;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +
>>> +static int wkup_m3_rproc_stop(struct rproc *rproc)
>>> +{
>>> +	return 0;
>
> Any reason this is a stub, don't you need to assert the reset in stop?

Well, at this point I don't think there is any reason you would want to stop the 
wkup_m3, it will certainly cause issues with PM. That would be the most 
appropriate action here though.

>
>>> +}
>>> +
>>> +static void wkup_m3_rproc_kick(struct rproc *rproc, int vqid)
>>> +{
>>> +}
>
> If you can remove this empty stub, please do so. I do not think we will
> be using virtio for communicating with the WkupM3.

We don't need it, Ill remove it.

>
>>> +
>>> +static struct rproc_ops wkup_m3_rproc_ops = {
>>> +	.start		= wkup_m3_rproc_start,
>>> +	.stop		= wkup_m3_rproc_stop,
>>> +	.kick		= wkup_m3_rproc_kick,
>>> +};
>>> +
>>> +/* Public Functions */
>>> +
>>> +/**
>>> + * wkup_m3_set_ops - Set callbacks for user of rproc
>>> + * @ops - struct wkup_m3_ops *
>>> + *
>>> + * Registers callbacks to wkup_m3 to be invoked after rproc is ready to use
>>> + * and after an interrupt is handled.
>>> + */
>>> +void wkup_m3_set_ops(struct wkup_m3_ops *ops)
>>> +{
>>> +	m3_rproc_static->ops = ops;
>>> +
>>> +	if (m3_rproc_static->is_active && m3_rproc_static->ops &&
>>> +	    m3_rproc_static->ops->rproc_ready)
>>> +		m3_rproc_static->ops->rproc_ready();
>>> +}
>
>
>
>>> +
>>> +/**
>>> + * wkup_m3_ping - Send a dummy msg to wkup_m3 to tell to to check IPC regs
>>> + *
>>> + * Returns the result of sending mbox msg or -EIO if no mbox handle is present
>>> + */
>>> +int wkup_m3_ping(void)
>>> +{
>>> +	int ret;
>>> +	mbox_msg_t dummy_msg = 0;
>>> +
>>> +	if (!m3_rproc_static->mbox) {
>>> +		dev_err(&m3_rproc_static->pdev->dev,
>>> +			"No IPC channel to communicate with wkup_m3!\n");
>>> +		return -EIO;
>>> +	}
>>> +
>>> +	/*
>>> +	 * Write a dummy message to the mailbox in order to trigger the RX
>>> +	 * interrupt to alert the M3 that data is available in the IPC
>>> +	 * registers. We must enable the IRQ here and disable it after in
>>> +	 * the RX callback to avoid multiple interrupts being received
>>> +	 * by the CM3.
>>> +	 */
>>> +	omap_mbox_enable_irq(m3_rproc_static->mbox, IRQ_RX);
>>> +	ret = mbox_send_message(m3_rproc_static->mbox, (void *)dummy_msg);
>>> +
> nit, no need of an additional blank line

Ok.

>
>>> +	if (ret < 0) {
>>> +		pr_err("%s: mbox_send_message() failed: %d\n", __func__, ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/**
>>> + * wkup_m3_wake_src - Get the wakeup source info passed from wkup_m3
>>> + * @wkup_m3_wakeup: struct wkup_m3_wakeup_src * gets assigned the
>>> + *		    wakeup src value
>>> + */
>>> +void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wkup_m3_wakeup)
>>> +{
>>> +	struct wkup_m3_ipc_regs ipc_regs;
>>> +	unsigned int wakeup_src_idx;
>>> +	int j;
>>> +
>>> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
>>> +
>>> +	wakeup_src_idx = ipc_regs.reg6 & WKUP_M3_WAKE_SRC_MASK;
>>> +
>>> +	for (j = 0; j < ARRAY_SIZE(wakeups)-1; j++) {
>>> +		if (wakeups[j].irq_nr == wakeup_src_idx) {
>>> +			*wkup_m3_wakeup = wakeups[j];
>>> +			return;
>>> +		}
>>> +	}
>>> +	*wkup_m3_wakeup = wakeups[j];
>>> +}
>>> +
>>> +/**
>>> + * wkup_m3_pm_status - Return the status code from wkup_m3 after sleep event
>>> + *
>>> + * Returns an error code that indicates whether or not the dsired sleep
>>> + * action was a success or not.
>>> + */
>>> +int wkup_m3_pm_status(void)
>>> +{
>>> +	unsigned int i;
>>> +	struct wkup_m3_ipc_regs ipc_regs;
>>> +
>>> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
>
> Do you need to read back all the registers, given that this is a local
> structure anyway?

Well, at least one of the registers must be read back (reg1) in order to get the 
PM status value returned by the wkup_m3. The ipc read/write functions had been 
rewritten to be more "generic" in order to avoid exposing a bunch of specific 
APIs from the control module. The reads/writes are all internal to the driver 
now though so it probably makes sense to do away with that entirely now and be 
more specific in what we read and write.

>
>>> +
>>> +	i = WKUP_M3_STATUS_RESP_MASK & ipc_regs.reg1;
>>> +	i >>= __ffs(WKUP_M3_STATUS_RESP_MASK);
>>> +
>>> +	return i;
>>> +}
>>> +
>>> +/**
>>> + * wkup_m3_fw_version_read - Return the fw version given by the wkup_m3
>>> + *
>>> + * After boot the fw version should be read to ensure it is compatible.
>>> + */
>>> +int wkup_m3_fw_version_read(void)
>>> +{
>>> +	struct wkup_m3_ipc_regs ipc_regs;
>>> +
>>> +	wkup_m3_ctrl_ipc_read(m3_rproc_static, &ipc_regs);
>>> +
>>> +	return ipc_regs.reg2 & WKUP_M3_FW_VERSION_MASK;
>>> +}
>>> +
>>> +/**
>>> + * wkup_m3_set_cmd - write contents of struct to ipc regs
>>> + * @ipc_regs: struct wkup_m3_ipc_regs *
>>> + */
>>> +void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs)
>>> +{
>>> +	wkup_m3_ctrl_ipc_write(m3_rproc_static, ipc_regs);
>>> +}
>>> +
>>> +static void wkup_m3_rproc_loader_thread(struct rproc *rproc)
>>> +{
>>> +	struct wkup_m3_rproc *m3_rproc = rproc->priv;
>>> +	struct device *dev = &m3_rproc->pdev->dev;
>>> +	int ret;
>>> +
>>> +	wait_for_completion(&rproc->firmware_loading_complete);
>>> +
>>> +	ret = rproc_boot(rproc);
>>> +	if (ret)
>>> +		dev_err(dev, "rproc_boot failed\n");
>>> +
>>> +	do_exit(0);
>>> +}
>>> +
>>> +static int wkup_m3_rproc_probe(struct platform_device *pdev)
>>> +{
>>> +	struct device *dev = &pdev->dev;
>>> +	struct device_node *np = pdev->dev.of_node;
>>> +	struct wkup_m3_rproc *m3_rproc;
>>> +	struct rproc *rproc;
>>> +	int irq, ret;
>>> +	struct resource *res;
>>> +	struct task_struct *task;
>>> +	const char *mbox_name;
>>> +
>>> +	pm_runtime_enable(&pdev->dev);
>>> +
>>> +	ret = pm_runtime_get_sync(&pdev->dev);
>>> +	if (IS_ERR_VALUE(ret)) {
>>> +		dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	rproc = rproc_alloc(dev, "wkup_m3", &wkup_m3_rproc_ops,
>>> +			    "am335x-pm-firmware.elf", sizeof(*m3_rproc));
>>> +	if (!rproc)
>>> +		return -ENOMEM;
>>> +
>>> +	m3_rproc = rproc->priv;
>>> +	m3_rproc->rproc = rproc;
>>> +	m3_rproc->pdev = pdev;
>>> +
>>> +	m3_rproc_static = m3_rproc;
>>> +
>>> +	irq = platform_get_irq(pdev, 0);
>>> +	if (!irq) {
>>> +		dev_err(&pdev->dev, "no irq resource\n");
>>> +		ret = -ENXIO;
>>> +		goto err;
>>> +	}
>
> Colocate this to the same place as the request_irq.

Ok.

>
>>> +
>>> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ipc_regs");
>>> +	if (!res) {
>
> No need to check for error, the devm function following this should take
> care of any errors.
>
>>> +		dev_err(&pdev->dev, "no memory resource for ipc\n");
>>> +		ret = -ENXIO;
>>> +		goto err;
>>> +	}
>>> +
>>> +	m3_rproc->ipc_mem_base = devm_request_and_ioremap(dev, res);
>
> Is some one else doing an ioremap of this space, otherwise
> devm_ioremap_resource is preferred.

I will update driver to do it this way, was unfamiliar with this approach.

>
>>> +	if (!m3_rproc->ipc_mem_base) {
>>> +		dev_err(dev, "could not ioremap ipc_mem\n");
>>> +		ret = -EADDRNOTAVAIL;
>>> +		goto err;
>>> +	}
>
> Don't you need to be retrieving umem and dmem?

We don't need to do that here as remoteproc handles it for us behind the scenes. 
They are left in the DT as they had already been added and to keep the address 
space consistent.

>
>
>>> +
>>> +	ret = devm_request_irq(dev, irq, wkup_m3_txev_handler,
>>> +			       IRQF_DISABLED, "wkup_m3_txev", m3_rproc);
>>> +	if (ret) {
>>> +		dev_err(dev, "request_irq failed\n");
>>> +		goto err;
>>> +	}
>>> +
>>> +	/* Get mbox name from device tree node */
>>> +	ret = of_property_read_string(np, "mbox-names", &mbox_name);
>>> +	if (ret) {
>>> +		dev_err(&pdev->dev, "Unable to get mbox name from dt node: %d\n",
>>> +			ret);
>>> +			goto err;
>>> +	};
>>> +
>>> +	m3_rproc->mbox_client.chan_name = mbox_name;
>>> +
>>> +	/* Register as a remoteproc device */
>>> +	ret = rproc_add(rproc);
>>> +	if (ret) {
>>> +		dev_err(dev, "rproc_add failed\n");
>>> +		goto err;
>>> +	}
>>> +
>>> +	/*
>>> +	 * Wait for firmware loading completion in a thread so we
>>> +	 * can boot the wkup_m3 as soon as it's ready without holding
>>> +	 * up kernel boot
>>> +	 */
>>> +	task = kthread_run((void *)wkup_m3_rproc_loader_thread, rproc,
>>> +			   "wkup_m3_rproc_loader");
>>> +
>>> +	if (IS_ERR(task)) {
>>> +		dev_err(dev, "can't create rproc_loader thread\n");
>>> +		goto err;
>>> +	}
>>> +
>>> +	return 0;
>>> +
>>> +err:
>>> +	rproc_put(rproc);
>> pm_runtime_put_sync() ?
>>> +	return ret;
>>> +}
>>> +
>>> +static int wkup_m3_rproc_remove(struct platform_device *pdev)
>>> +{
>>> +	struct rproc *rproc = platform_get_drvdata(pdev);
>>> +
>>> +	rproc_del(rproc);
>>> +	rproc_put(rproc);
>
> Need to reset m3_rproc_static?

Yes I need to take care of that.

>
>>> +
>> Here too ?
>>> +	return 0;
>>> +}
>>> +
>>> +static int wkup_m3_rpm_suspend(struct device *dev)
>>> +{
>>> +	return -EBUSY;
>>> +}
>>> +
>>> +static int wkup_m3_rpm_resume(struct device *dev)
>>> +{
>>> +	return 0;
>>> +}
>>> +
>> If you are not doing any meaningfull in suspend/resume hooks,
>> why are you even registering them ?
>>
>>> +static const struct dev_pm_ops wkup_m3_rproc_pm_ops = {
>>> +	SET_RUNTIME_PM_OPS(wkup_m3_rpm_suspend, wkup_m3_rpm_resume, NULL)
>>> +};
>>> +
>>> +static const struct of_device_id wkup_m3_rproc_of_match[] = {
>>> +	{ .compatible = "ti,am3353-wkup-m3", .data = NULL, },
>>> +	{},
>>> +};
>>> +
>>> +static struct platform_driver wkup_m3_rproc_driver = {
>>> +	.probe = wkup_m3_rproc_probe,
>>> +	.remove = wkup_m3_rproc_remove,
>>> +	.driver = {
>>> +		.name = "wkup_m3",
>>> +		.owner = THIS_MODULE,
>>> +		.of_match_table = wkup_m3_rproc_of_match,
>>> +		.pm = &wkup_m3_rproc_pm_ops,
>>> +	},
>>> +};
>>> +
>>> +module_platform_driver(wkup_m3_rproc_driver);
>>> +
>>> +MODULE_LICENSE("GPL v2");
>>> +MODULE_DESCRIPTION("wkup m3 remote processor control driver");
>>> diff --git a/include/linux/wkup_m3.h b/include/linux/wkup_m3.h
>>> new file mode 100644
>>> index 0000000..1a2237f
>>> --- /dev/null
>>> +++ b/include/linux/wkup_m3.h
>>> @@ -0,0 +1,71 @@
>>> +/*
>>> + * TI Wakeup M3 Power Management Routines
>
> Suggest adding for AMx3x SoCs

Good idea.

>
>>> + *
>>> + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
>>> + * Dave Gerlach <d-gerlach@ti.com>
>>> + *
>>> + * 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 _LINUX_WKUP_M3_H
>>> +#define _LINUX_WKUP_M3_H
>>> +
>>> +/**
>>> + * struct wkup_m3_ops - Callbacks for allowing pm code to interact with wkup_m3.
>>> + *
>>> + * @txev_handler: Callback to allow pm code to react to response from wkup_m3
>>> + *		  after pinging it using wkup_m3_ping.
>>> + *
>>> + * @firmware_loaded: Callback invoked when the firmware has been loaded to the
>>> + *		     m3 to allow the pm code to enable suspend/resume ops.
>
> firmware_loaded ops does not exist

Ah yes, seems I renamed the function pointer but failed to update the comment.

>
>>> + */
>>> +
>>> +struct wkup_m3_ops {
>>> +	void (*txev_handler)(void);
>>> +	void (*rproc_ready)(void);
>>> +};
>>> +
>>> +struct wkup_m3_wakeup_src {
>>> +	int irq_nr;
>>> +	char src[10];
>
> What is the @src used for, debug? Don't see anywhere this is getting used.

It is used to provide a string that names the wakeup source, the table is 
defined at the top of wkup_m3_rproc.c in this patch.

Thanks for the comments.

Regards,
Dave

>
> regards
> Suman
>
>>> +};
>>> +
>>> +struct wkup_m3_ipc_regs {
>>> +	u32 reg0;
>>> +	u32 reg1;
>>> +	u32 reg2;
>>> +	u32 reg3;
>>> +	u32 reg4;
>>> +	u32 reg5;
>>> +	u32 reg6;
>>> +	u32 reg7;
>>> +};
>>> +
>>> +#ifdef CONFIG_WKUP_M3_RPROC
>>> +int wkup_m3_prepare(void);
>>> +void wkup_m3_set_ops(struct wkup_m3_ops *ops);
>>> +int wkup_m3_ping(void);
>>> +void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wakeup_src);
>>> +int wkup_m3_pm_status(void);
>>> +int wkup_m3_is_valid(void);
>>> +int wkup_m3_fw_version_read(void);
>>> +void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs);
>>> +
>>> +#else
>>> +
>>> +int wkup_m3_prepare(void) { return -EINVAL; }
>>> +void wkup_m3_set_ops(struct wkup_m3_ops *ops) { }
>>> +int wkup_m3_ping(void) { return -EINVAL }
>>> +void wkup_m3_wake_src(struct wkup_m3_wakeup_src *wakeup_src) { }
>>> +int wkup_m3_pm_status(void) { return -1; }
>>> +int wkup_m3_fw_version_read(void) { return -1; }
>>> +void wkup_m3_set_cmd(struct wkup_m3_ipc_regs *ipc_regs) { }
>>
>> Mark all of above in else as static inline.
>>
>>> +#endif /* CONFIG_WKUP_M3_RPROC */
>>> +#endif /* _LINUX_WKUP_M3_H */
>>>
>>
>

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 02/11] memory: emif: Move EMIF register defines to include/linux/
  2014-07-14 17:42       ` Dave Gerlach
@ 2014-07-15  6:38         ` Tony Lindgren
  -1 siblings, 0 replies; 106+ messages in thread
From: Tony Lindgren @ 2014-07-15  6:38 UTC (permalink / raw)
  To: Dave Gerlach
  Cc: linux-arm-kernel, linux-omap, Paul Walmsley, Kevin Hilman,
	Tero Kristo, Nishanth Menon, Russ Dill, Santosh Shilimkar,
	Daniel Mack, Suman Anna, Benoit Cousson

* Dave Gerlach <d-gerlach@ti.com> [140714 10:44]:
> On 07/14/2014 06:12 AM, Tony Lindgren wrote:
> >* Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
> >>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 register defines in the emif header file to include/linux so that
> >>both the EMIF driver and the AM33XX PM code can benefit.
> >>
> >>Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
> >>Reviewed-by: Russ Dill <russ.dill@ti.com>
> >>Acked-by: Santosh Shililmar <santosh.shilimkar@ti.com>
> >>Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> >>---
> >>v3->v4:
> >>patch unchanged from original:
> >>	http://www.spinics.net/lists/linux-omap/msg95314.html
> >>
> >>  drivers/memory/emif.h   | 543 +---------------------------------------------
> >>  include/linux/ti_emif.h | 558 ++++++++++++++++++++++++++++++++++++++++++++++++
> >
> >So far we've seen that exposing hardware registers like this
> >will lead into various drivers misusing them. I think a better
> >solution is to implement few targeted functions that allow
> >sharing code between the platform idle code and memory driver.
> >
> >Maybe you can have the shared functions in in something like
> >drivers/memory/ti-emif.c that's always built in? The idle
> >code won't need any of that early on.
> 
> Well the reason it was done this way was to utilize all of the addresses of
> EMIF register in the ASM sleep code to do relative addressing from the EMIF
> base address. The ASM sleep code (patch 9) needs to save and restore emif
> context and set and unset self refresh in emif. The issues will come from
> the ASM being copied to and running from SRAM without the ability to access
> code in DDR (because we are shutting the EMIF off), so we would need to copy
> these functions as well and have to worry about any issues we introduce by
> relocating c code. Is it worth the added maintenance burden?

Ah right it needs to run in SRAM. There were some relocatable
c code patches posted a while back, so it might be worth
revisiting that.

I think it can also be done with assembly with something like
this:

1. Make am335x idle code depends on TI_EMIF && WKUP_M3_RPROC

2. Add the memory save and restore assembly functions into
   drivers/memory/ti-emif-sram.S

3. Allocate the SRAM preferrably with drivers/misc/sram.c
   instead of the legacy mach-omap2/sram.c

4. Map the idle assembly code and EMIF save and restore
   functions into SRAM

5. Call the EMIF save and restore functions from the idle
   assembly code at the SRAM locations and pass the save and
   restore area in a register

So basically we need to figure out a generic way to do driver
hooks in the PM idle code even very late and early in the
assembly code so we can keep most of the code in drivers.
Eventually also the idle assembly code should be in the drivers
too..

Regards,

Tony

^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 02/11] memory: emif: Move EMIF register defines to include/linux/
@ 2014-07-15  6:38         ` Tony Lindgren
  0 siblings, 0 replies; 106+ messages in thread
From: Tony Lindgren @ 2014-07-15  6:38 UTC (permalink / raw)
  To: linux-arm-kernel

* Dave Gerlach <d-gerlach@ti.com> [140714 10:44]:
> On 07/14/2014 06:12 AM, Tony Lindgren wrote:
> >* Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
> >>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 register defines in the emif header file to include/linux so that
> >>both the EMIF driver and the AM33XX PM code can benefit.
> >>
> >>Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
> >>Reviewed-by: Russ Dill <russ.dill@ti.com>
> >>Acked-by: Santosh Shililmar <santosh.shilimkar@ti.com>
> >>Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> >>---
> >>v3->v4:
> >>patch unchanged from original:
> >>	http://www.spinics.net/lists/linux-omap/msg95314.html
> >>
> >>  drivers/memory/emif.h   | 543 +---------------------------------------------
> >>  include/linux/ti_emif.h | 558 ++++++++++++++++++++++++++++++++++++++++++++++++
> >
> >So far we've seen that exposing hardware registers like this
> >will lead into various drivers misusing them. I think a better
> >solution is to implement few targeted functions that allow
> >sharing code between the platform idle code and memory driver.
> >
> >Maybe you can have the shared functions in in something like
> >drivers/memory/ti-emif.c that's always built in? The idle
> >code won't need any of that early on.
> 
> Well the reason it was done this way was to utilize all of the addresses of
> EMIF register in the ASM sleep code to do relative addressing from the EMIF
> base address. The ASM sleep code (patch 9) needs to save and restore emif
> context and set and unset self refresh in emif. The issues will come from
> the ASM being copied to and running from SRAM without the ability to access
> code in DDR (because we are shutting the EMIF off), so we would need to copy
> these functions as well and have to worry about any issues we introduce by
> relocating c code. Is it worth the added maintenance burden?

Ah right it needs to run in SRAM. There were some relocatable
c code patches posted a while back, so it might be worth
revisiting that.

I think it can also be done with assembly with something like
this:

1. Make am335x idle code depends on TI_EMIF && WKUP_M3_RPROC

2. Add the memory save and restore assembly functions into
   drivers/memory/ti-emif-sram.S

3. Allocate the SRAM preferrably with drivers/misc/sram.c
   instead of the legacy mach-omap2/sram.c

4. Map the idle assembly code and EMIF save and restore
   functions into SRAM

5. Call the EMIF save and restore functions from the idle
   assembly code at the SRAM locations and pass the save and
   restore area in a register

So basically we need to figure out a generic way to do driver
hooks in the PM idle code even very late and early in the
assembly code so we can keep most of the code in drivers.
Eventually also the idle assembly code should be in the drivers
too..

Regards,

Tony

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 03/11] ARM: OMAP2+: timer: Add suspend-resume callbacks for clkevent device
  2014-07-14 17:42         ` Dave Gerlach
@ 2014-07-15  6:48           ` Tony Lindgren
  -1 siblings, 0 replies; 106+ messages in thread
From: Tony Lindgren @ 2014-07-15  6:48 UTC (permalink / raw)
  To: Dave Gerlach
  Cc: Santosh Shilimkar, linux-arm-kernel, linux-omap, Paul Walmsley,
	Kevin Hilman, Tero Kristo, Nishanth Menon, Russ Dill,
	Daniel Mack, Suman Anna, Benoit Cousson, Vaibhav Bedia

* Dave Gerlach <d-gerlach@ti.com> [140714 10:44]:
> Santosh, Tony,
> 
> On 07/14/2014 09:37 AM, Santosh Shilimkar wrote:
> >On Monday 14 July 2014 07:15 AM, Tony Lindgren wrote:
> >>* Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
> >>>From: Vaibhav Bedia <vaibhav.bedia@ti.com>
> >>>
> >>>OMAP timer code registers two timers - one as clocksource
> >>>and one as clockevent. Since AM33XX has only one usable timer
> >>>in the WKUP domain 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.
> >>>
> >>>Signed-off-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
> >>>Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
> >>>---
> >>>v3->v4:
> >>>	Only use for am33xx soc now.
> >>>
> >>>  arch/arm/mach-omap2/timer.c | 28 ++++++++++++++++++++++++++++
> >>>  1 file changed, 28 insertions(+)
> >>>
> >>>diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
> >>>index 43d03fb..6fc1748 100644
> >>>--- a/arch/arm/mach-omap2/timer.c
> >>>+++ b/arch/arm/mach-omap2/timer.c
> >>>@@ -128,6 +128,29 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
> >>>  	}
> >>>  }
> >>>
> >>>+static void omap_clkevt_suspend(struct clock_event_device *unused)
> >>>+{
> >>>+	struct omap_hwmod *oh;
> >>>+
> >>>+	oh = omap_hwmod_lookup(clockevent_gpt.name);
> >>>+	if (!oh)
> >>>+		return;
> >>>+
> >>>+	omap_hwmod_idle(oh);
> >>>+}
> >>>+
> >>>+static void omap_clkevt_resume(struct clock_event_device *unused)
> >>>+{
> >>>+	struct omap_hwmod *oh;
> >>>+
> >>>+	oh = omap_hwmod_lookup(clockevent_gpt.name);
> >>>+	if (!oh)
> >>>+		return;
> >>>+
> >>>+	omap_hwmod_enable(oh);
> >>>+	__omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);
> >>>+}
> >>>+
> >>
> >>This is going to make moving the timer code into drivers one step
> >>tougher to do. And you don't need to look up the hwmod entry every
> >>time, just initialize it during the init.
> 
> Yes you are right about looking up only at init I need to change that. I
> agree that this makes moving the timers harder but I'm not sure there's any
> way around this. I attempted to use the omap_device layer here but there is
> no device at all created here, it does not hook into the normal PM layer
> through omap_device. This clock must be shut off as it sits in the
> peripheral power domain and any active clock within the domain will prevent
> suspend from happening, so we end up with a platform specific issue here. It
> seems that the only way I can get to the clock is through the hwmod.

It's best to register these kind of functions as platform_data
in pdata-quirks.c auxdata. That way when this moves to live
in drivers/clocksource the driver does not need to know anything
about the interconnect specific registers.

Also, please don't use Linux generic names here.. Maybe use
omap_clkevt_idle/unidle? The linux suspend and resume hooks
and runtime PM could all use these functions then.

> >>>+	if (soc_is_am33xx()) {
> >>>+		clockevent_gpt.suspend = omap_clkevt_suspend;
> >>>+		clockevent_gpt.resume = omap_clkevt_resume;
> >>>+	}
> >>>+
> >>
> >>Maybe try to set up things so we initialize the SoC specific
> >>timer suspend and resume functions in mach-omap2/timer.c
> >>in a way where eventually the device driver can easily use
> >>them?
> >>
> >+1. I had similar comments on the previous version too.
> 
> This was my attempt to make things specific to only am335x based on
> Santosh's previous comments as last time they were populated for every
> device even when unneeded. These are not standard suspend/resume handlers,
> they are specific to clock event. I know there will always need to be at
> least some code here for the early timer init based on previous timer
> cleanup series I've seen, so perhaps I could hook it in there when the time
> comes?

Well just adding a minimal include/linux/platform_data/timer-omap.h
to pass those function pointers to the driver should do the trick.

Regards,

Tony

^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 03/11] ARM: OMAP2+: timer: Add suspend-resume callbacks for clkevent device
@ 2014-07-15  6:48           ` Tony Lindgren
  0 siblings, 0 replies; 106+ messages in thread
From: Tony Lindgren @ 2014-07-15  6:48 UTC (permalink / raw)
  To: linux-arm-kernel

* Dave Gerlach <d-gerlach@ti.com> [140714 10:44]:
> Santosh, Tony,
> 
> On 07/14/2014 09:37 AM, Santosh Shilimkar wrote:
> >On Monday 14 July 2014 07:15 AM, Tony Lindgren wrote:
> >>* Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
> >>>From: Vaibhav Bedia <vaibhav.bedia@ti.com>
> >>>
> >>>OMAP timer code registers two timers - one as clocksource
> >>>and one as clockevent. Since AM33XX has only one usable timer
> >>>in the WKUP domain 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.
> >>>
> >>>Signed-off-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
> >>>Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
> >>>---
> >>>v3->v4:
> >>>	Only use for am33xx soc now.
> >>>
> >>>  arch/arm/mach-omap2/timer.c | 28 ++++++++++++++++++++++++++++
> >>>  1 file changed, 28 insertions(+)
> >>>
> >>>diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
> >>>index 43d03fb..6fc1748 100644
> >>>--- a/arch/arm/mach-omap2/timer.c
> >>>+++ b/arch/arm/mach-omap2/timer.c
> >>>@@ -128,6 +128,29 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
> >>>  	}
> >>>  }
> >>>
> >>>+static void omap_clkevt_suspend(struct clock_event_device *unused)
> >>>+{
> >>>+	struct omap_hwmod *oh;
> >>>+
> >>>+	oh = omap_hwmod_lookup(clockevent_gpt.name);
> >>>+	if (!oh)
> >>>+		return;
> >>>+
> >>>+	omap_hwmod_idle(oh);
> >>>+}
> >>>+
> >>>+static void omap_clkevt_resume(struct clock_event_device *unused)
> >>>+{
> >>>+	struct omap_hwmod *oh;
> >>>+
> >>>+	oh = omap_hwmod_lookup(clockevent_gpt.name);
> >>>+	if (!oh)
> >>>+		return;
> >>>+
> >>>+	omap_hwmod_enable(oh);
> >>>+	__omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);
> >>>+}
> >>>+
> >>
> >>This is going to make moving the timer code into drivers one step
> >>tougher to do. And you don't need to look up the hwmod entry every
> >>time, just initialize it during the init.
> 
> Yes you are right about looking up only at init I need to change that. I
> agree that this makes moving the timers harder but I'm not sure there's any
> way around this. I attempted to use the omap_device layer here but there is
> no device at all created here, it does not hook into the normal PM layer
> through omap_device. This clock must be shut off as it sits in the
> peripheral power domain and any active clock within the domain will prevent
> suspend from happening, so we end up with a platform specific issue here. It
> seems that the only way I can get to the clock is through the hwmod.

It's best to register these kind of functions as platform_data
in pdata-quirks.c auxdata. That way when this moves to live
in drivers/clocksource the driver does not need to know anything
about the interconnect specific registers.

Also, please don't use Linux generic names here.. Maybe use
omap_clkevt_idle/unidle? The linux suspend and resume hooks
and runtime PM could all use these functions then.

> >>>+	if (soc_is_am33xx()) {
> >>>+		clockevent_gpt.suspend = omap_clkevt_suspend;
> >>>+		clockevent_gpt.resume = omap_clkevt_resume;
> >>>+	}
> >>>+
> >>
> >>Maybe try to set up things so we initialize the SoC specific
> >>timer suspend and resume functions in mach-omap2/timer.c
> >>in a way where eventually the device driver can easily use
> >>them?
> >>
> >+1. I had similar comments on the previous version too.
> 
> This was my attempt to make things specific to only am335x based on
> Santosh's previous comments as last time they were populated for every
> device even when unneeded. These are not standard suspend/resume handlers,
> they are specific to clock event. I know there will always need to be at
> least some code here for the early timer init based on previous timer
> cleanup series I've seen, so perhaps I could hook it in there when the time
> comes?

Well just adding a minimal include/linux/platform_data/timer-omap.h
to pass those function pointers to the driver should do the trick.

Regards,

Tony

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 03/11] ARM: OMAP2+: timer: Add suspend-resume callbacks for clkevent device
  2014-07-15  6:48           ` Tony Lindgren
@ 2014-07-15 19:10             ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-15 19:10 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Santosh Shilimkar, linux-arm-kernel, linux-omap, Paul Walmsley,
	Kevin Hilman, Tero Kristo, Nishanth Menon, Russ Dill,
	Daniel Mack, Suman Anna, Benoit Cousson, Vaibhav Bedia

On 07/15/2014 01:48 AM, Tony Lindgren wrote:
> * Dave Gerlach <d-gerlach@ti.com> [140714 10:44]:
>> Santosh, Tony,
>>
>> On 07/14/2014 09:37 AM, Santosh Shilimkar wrote:
>>> On Monday 14 July 2014 07:15 AM, Tony Lindgren wrote:
>>>> * Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
>>>>> From: Vaibhav Bedia <vaibhav.bedia@ti.com>
>>>>>
>>>>> OMAP timer code registers two timers - one as clocksource
>>>>> and one as clockevent. Since AM33XX has only one usable timer
>>>>> in the WKUP domain 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.
>>>>>
>>>>> Signed-off-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
>>>>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>>>>> ---
>>>>> v3->v4:
>>>>> 	Only use for am33xx soc now.
>>>>>
>>>>>   arch/arm/mach-omap2/timer.c | 28 ++++++++++++++++++++++++++++
>>>>>   1 file changed, 28 insertions(+)
>>>>>
>>>>> diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
>>>>> index 43d03fb..6fc1748 100644
>>>>> --- a/arch/arm/mach-omap2/timer.c
>>>>> +++ b/arch/arm/mach-omap2/timer.c
>>>>> @@ -128,6 +128,29 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
>>>>>   	}
>>>>>   }
>>>>>
>>>>> +static void omap_clkevt_suspend(struct clock_event_device *unused)
>>>>> +{
>>>>> +	struct omap_hwmod *oh;
>>>>> +
>>>>> +	oh = omap_hwmod_lookup(clockevent_gpt.name);
>>>>> +	if (!oh)
>>>>> +		return;
>>>>> +
>>>>> +	omap_hwmod_idle(oh);
>>>>> +}
>>>>> +
>>>>> +static void omap_clkevt_resume(struct clock_event_device *unused)
>>>>> +{
>>>>> +	struct omap_hwmod *oh;
>>>>> +
>>>>> +	oh = omap_hwmod_lookup(clockevent_gpt.name);
>>>>> +	if (!oh)
>>>>> +		return;
>>>>> +
>>>>> +	omap_hwmod_enable(oh);
>>>>> +	__omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);
>>>>> +}
>>>>> +
>>>>
>>>> This is going to make moving the timer code into drivers one step
>>>> tougher to do. And you don't need to look up the hwmod entry every
>>>> time, just initialize it during the init.
>>
>> Yes you are right about looking up only at init I need to change that. I
>> agree that this makes moving the timers harder but I'm not sure there's any
>> way around this. I attempted to use the omap_device layer here but there is
>> no device at all created here, it does not hook into the normal PM layer
>> through omap_device. This clock must be shut off as it sits in the
>> peripheral power domain and any active clock within the domain will prevent
>> suspend from happening, so we end up with a platform specific issue here. It
>> seems that the only way I can get to the clock is through the hwmod.
>
> It's best to register these kind of functions as platform_data
> in pdata-quirks.c auxdata. That way when this moves to live
> in drivers/clocksource the driver does not need to know anything
> about the interconnect specific registers.
>
> Also, please don't use Linux generic names here.. Maybe use
> omap_clkevt_idle/unidle? The linux suspend and resume hooks
> and runtime PM could all use these functions then.

Ok to both of the above, I definitely like your suggestion better.

Regards,
Dave

>
>>>>> +	if (soc_is_am33xx()) {
>>>>> +		clockevent_gpt.suspend = omap_clkevt_suspend;
>>>>> +		clockevent_gpt.resume = omap_clkevt_resume;
>>>>> +	}
>>>>> +
>>>>
>>>> Maybe try to set up things so we initialize the SoC specific
>>>> timer suspend and resume functions in mach-omap2/timer.c
>>>> in a way where eventually the device driver can easily use
>>>> them?
>>>>
>>> +1. I had similar comments on the previous version too.
>>
>> This was my attempt to make things specific to only am335x based on
>> Santosh's previous comments as last time they were populated for every
>> device even when unneeded. These are not standard suspend/resume handlers,
>> they are specific to clock event. I know there will always need to be at
>> least some code here for the early timer init based on previous timer
>> cleanup series I've seen, so perhaps I could hook it in there when the time
>> comes?
>
> Well just adding a minimal include/linux/platform_data/timer-omap.h
> to pass those function pointers to the driver should do the trick.
>
> Regards,
>
> Tony
>


^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 03/11] ARM: OMAP2+: timer: Add suspend-resume callbacks for clkevent device
@ 2014-07-15 19:10             ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-15 19:10 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/15/2014 01:48 AM, Tony Lindgren wrote:
> * Dave Gerlach <d-gerlach@ti.com> [140714 10:44]:
>> Santosh, Tony,
>>
>> On 07/14/2014 09:37 AM, Santosh Shilimkar wrote:
>>> On Monday 14 July 2014 07:15 AM, Tony Lindgren wrote:
>>>> * Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
>>>>> From: Vaibhav Bedia <vaibhav.bedia@ti.com>
>>>>>
>>>>> OMAP timer code registers two timers - one as clocksource
>>>>> and one as clockevent. Since AM33XX has only one usable timer
>>>>> in the WKUP domain 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.
>>>>>
>>>>> Signed-off-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
>>>>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>>>>> ---
>>>>> v3->v4:
>>>>> 	Only use for am33xx soc now.
>>>>>
>>>>>   arch/arm/mach-omap2/timer.c | 28 ++++++++++++++++++++++++++++
>>>>>   1 file changed, 28 insertions(+)
>>>>>
>>>>> diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
>>>>> index 43d03fb..6fc1748 100644
>>>>> --- a/arch/arm/mach-omap2/timer.c
>>>>> +++ b/arch/arm/mach-omap2/timer.c
>>>>> @@ -128,6 +128,29 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
>>>>>   	}
>>>>>   }
>>>>>
>>>>> +static void omap_clkevt_suspend(struct clock_event_device *unused)
>>>>> +{
>>>>> +	struct omap_hwmod *oh;
>>>>> +
>>>>> +	oh = omap_hwmod_lookup(clockevent_gpt.name);
>>>>> +	if (!oh)
>>>>> +		return;
>>>>> +
>>>>> +	omap_hwmod_idle(oh);
>>>>> +}
>>>>> +
>>>>> +static void omap_clkevt_resume(struct clock_event_device *unused)
>>>>> +{
>>>>> +	struct omap_hwmod *oh;
>>>>> +
>>>>> +	oh = omap_hwmod_lookup(clockevent_gpt.name);
>>>>> +	if (!oh)
>>>>> +		return;
>>>>> +
>>>>> +	omap_hwmod_enable(oh);
>>>>> +	__omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);
>>>>> +}
>>>>> +
>>>>
>>>> This is going to make moving the timer code into drivers one step
>>>> tougher to do. And you don't need to look up the hwmod entry every
>>>> time, just initialize it during the init.
>>
>> Yes you are right about looking up only at init I need to change that. I
>> agree that this makes moving the timers harder but I'm not sure there's any
>> way around this. I attempted to use the omap_device layer here but there is
>> no device at all created here, it does not hook into the normal PM layer
>> through omap_device. This clock must be shut off as it sits in the
>> peripheral power domain and any active clock within the domain will prevent
>> suspend from happening, so we end up with a platform specific issue here. It
>> seems that the only way I can get to the clock is through the hwmod.
>
> It's best to register these kind of functions as platform_data
> in pdata-quirks.c auxdata. That way when this moves to live
> in drivers/clocksource the driver does not need to know anything
> about the interconnect specific registers.
>
> Also, please don't use Linux generic names here.. Maybe use
> omap_clkevt_idle/unidle? The linux suspend and resume hooks
> and runtime PM could all use these functions then.

Ok to both of the above, I definitely like your suggestion better.

Regards,
Dave

>
>>>>> +	if (soc_is_am33xx()) {
>>>>> +		clockevent_gpt.suspend = omap_clkevt_suspend;
>>>>> +		clockevent_gpt.resume = omap_clkevt_resume;
>>>>> +	}
>>>>> +
>>>>
>>>> Maybe try to set up things so we initialize the SoC specific
>>>> timer suspend and resume functions in mach-omap2/timer.c
>>>> in a way where eventually the device driver can easily use
>>>> them?
>>>>
>>> +1. I had similar comments on the previous version too.
>>
>> This was my attempt to make things specific to only am335x based on
>> Santosh's previous comments as last time they were populated for every
>> device even when unneeded. These are not standard suspend/resume handlers,
>> they are specific to clock event. I know there will always need to be at
>> least some code here for the early timer init based on previous timer
>> cleanup series I've seen, so perhaps I could hook it in there when the time
>> comes?
>
> Well just adding a minimal include/linux/platform_data/timer-omap.h
> to pass those function pointers to the driver should do the trick.
>
> Regards,
>
> Tony
>

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 02/11] memory: emif: Move EMIF register defines to include/linux/
  2014-07-15  6:38         ` Tony Lindgren
@ 2014-07-16  2:44           ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-16  2:44 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: linux-arm-kernel, linux-omap, Paul Walmsley, Kevin Hilman,
	Tero Kristo, Nishanth Menon, Russ Dill, Santosh Shilimkar,
	Daniel Mack, Suman Anna, Benoit Cousson

Tony,
On 07/15/2014 01:38 AM, Tony Lindgren wrote:
> * Dave Gerlach <d-gerlach@ti.com> [140714 10:44]:
>> On 07/14/2014 06:12 AM, Tony Lindgren wrote:
>>> * Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
>>>> 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 register defines in the emif header file to include/linux so that
>>>> both the EMIF driver and the AM33XX PM code can benefit.
>>>>
>>>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>>>> Reviewed-by: Russ Dill <russ.dill@ti.com>
>>>> Acked-by: Santosh Shililmar <santosh.shilimkar@ti.com>
>>>> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
>>>> ---
>>>> v3->v4:
>>>> patch unchanged from original:
>>>> 	http://www.spinics.net/lists/linux-omap/msg95314.html
>>>>
>>>>   drivers/memory/emif.h   | 543 +---------------------------------------------
>>>>   include/linux/ti_emif.h | 558 ++++++++++++++++++++++++++++++++++++++++++++++++
>>>
>>> So far we've seen that exposing hardware registers like this
>>> will lead into various drivers misusing them. I think a better
>>> solution is to implement few targeted functions that allow
>>> sharing code between the platform idle code and memory driver.
>>>
>>> Maybe you can have the shared functions in in something like
>>> drivers/memory/ti-emif.c that's always built in? The idle
>>> code won't need any of that early on.
>>
>> Well the reason it was done this way was to utilize all of the addresses of
>> EMIF register in the ASM sleep code to do relative addressing from the EMIF
>> base address. The ASM sleep code (patch 9) needs to save and restore emif
>> context and set and unset self refresh in emif. The issues will come from
>> the ASM being copied to and running from SRAM without the ability to access
>> code in DDR (because we are shutting the EMIF off), so we would need to copy
>> these functions as well and have to worry about any issues we introduce by
>> relocating c code. Is it worth the added maintenance burden?
>
> Ah right it needs to run in SRAM. There were some relocatable
> c code patches posted a while back, so it might be worth
> revisiting that.
>
> I think it can also be done with assembly with something like
> this:
>
> 1. Make am335x idle code depends on TI_EMIF && WKUP_M3_RPROC
>
> 2. Add the memory save and restore assembly functions into
>     drivers/memory/ti-emif-sram.S
>
> 3. Allocate the SRAM preferrably with drivers/misc/sram.c
>     instead of the legacy mach-omap2/sram.c
>

This I can do, I will just need to make a change somewhere to make generic sram 
driver provide sram allocations marked for exec.

> 4. Map the idle assembly code and EMIF save and restore
>     functions into SRAM
>
> 5. Call the EMIF save and restore functions from the idle
>     assembly code at the SRAM locations and pass the save and
>     restore area in a register
>
> So basically we need to figure out a generic way to do driver
> hooks in the PM idle code even very late and early in the
> assembly code so we can keep most of the code in drivers.
> Eventually also the idle assembly code should be in the drivers
> too..

I did not consider this earlier but the cpuidle code will use the same path in 
the assembly code. The cpuidle configures the suspend path to make the emif 
actions optional (save and restore with shut off, and self refresh), so a 
generic solution probably isn't possible here as we (will) need a certain level 
of granularity of control over the emif actions, and that will be difficult to 
maintain while keeping the pm functionality out of the EMIF code.

Regards,
Dave

>
> Regards,
>
> Tony
>


^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 02/11] memory: emif: Move EMIF register defines to include/linux/
@ 2014-07-16  2:44           ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-16  2:44 UTC (permalink / raw)
  To: linux-arm-kernel

Tony,
On 07/15/2014 01:38 AM, Tony Lindgren wrote:
> * Dave Gerlach <d-gerlach@ti.com> [140714 10:44]:
>> On 07/14/2014 06:12 AM, Tony Lindgren wrote:
>>> * Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
>>>> 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 register defines in the emif header file to include/linux so that
>>>> both the EMIF driver and the AM33XX PM code can benefit.
>>>>
>>>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>>>> Reviewed-by: Russ Dill <russ.dill@ti.com>
>>>> Acked-by: Santosh Shililmar <santosh.shilimkar@ti.com>
>>>> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
>>>> ---
>>>> v3->v4:
>>>> patch unchanged from original:
>>>> 	http://www.spinics.net/lists/linux-omap/msg95314.html
>>>>
>>>>   drivers/memory/emif.h   | 543 +---------------------------------------------
>>>>   include/linux/ti_emif.h | 558 ++++++++++++++++++++++++++++++++++++++++++++++++
>>>
>>> So far we've seen that exposing hardware registers like this
>>> will lead into various drivers misusing them. I think a better
>>> solution is to implement few targeted functions that allow
>>> sharing code between the platform idle code and memory driver.
>>>
>>> Maybe you can have the shared functions in in something like
>>> drivers/memory/ti-emif.c that's always built in? The idle
>>> code won't need any of that early on.
>>
>> Well the reason it was done this way was to utilize all of the addresses of
>> EMIF register in the ASM sleep code to do relative addressing from the EMIF
>> base address. The ASM sleep code (patch 9) needs to save and restore emif
>> context and set and unset self refresh in emif. The issues will come from
>> the ASM being copied to and running from SRAM without the ability to access
>> code in DDR (because we are shutting the EMIF off), so we would need to copy
>> these functions as well and have to worry about any issues we introduce by
>> relocating c code. Is it worth the added maintenance burden?
>
> Ah right it needs to run in SRAM. There were some relocatable
> c code patches posted a while back, so it might be worth
> revisiting that.
>
> I think it can also be done with assembly with something like
> this:
>
> 1. Make am335x idle code depends on TI_EMIF && WKUP_M3_RPROC
>
> 2. Add the memory save and restore assembly functions into
>     drivers/memory/ti-emif-sram.S
>
> 3. Allocate the SRAM preferrably with drivers/misc/sram.c
>     instead of the legacy mach-omap2/sram.c
>

This I can do, I will just need to make a change somewhere to make generic sram 
driver provide sram allocations marked for exec.

> 4. Map the idle assembly code and EMIF save and restore
>     functions into SRAM
>
> 5. Call the EMIF save and restore functions from the idle
>     assembly code at the SRAM locations and pass the save and
>     restore area in a register
>
> So basically we need to figure out a generic way to do driver
> hooks in the PM idle code even very late and early in the
> assembly code so we can keep most of the code in drivers.
> Eventually also the idle assembly code should be in the drivers
> too..

I did not consider this earlier but the cpuidle code will use the same path in 
the assembly code. The cpuidle configures the suspend path to make the emif 
actions optional (save and restore with shut off, and self refresh), so a 
generic solution probably isn't possible here as we (will) need a certain level 
of granularity of control over the emif actions, and that will be difficult to 
maintain while keeping the pm functionality out of the EMIF code.

Regards,
Dave

>
> Regards,
>
> Tony
>

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 02/11] memory: emif: Move EMIF register defines to include/linux/
  2014-07-16  2:44           ` Dave Gerlach
@ 2014-07-16  8:33             ` Tony Lindgren
  -1 siblings, 0 replies; 106+ messages in thread
From: Tony Lindgren @ 2014-07-16  8:33 UTC (permalink / raw)
  To: Dave Gerlach
  Cc: linux-arm-kernel, linux-omap, Paul Walmsley, Kevin Hilman,
	Tero Kristo, Nishanth Menon, Russ Dill, Santosh Shilimkar,
	Daniel Mack, Suman Anna, Benoit Cousson

* Dave Gerlach <d-gerlach@ti.com> [140715 19:46]:
> Tony,
> On 07/15/2014 01:38 AM, Tony Lindgren wrote:
> >* Dave Gerlach <d-gerlach@ti.com> [140714 10:44]:
> >>On 07/14/2014 06:12 AM, Tony Lindgren wrote:
> >>>* Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
> >>>>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 register defines in the emif header file to include/linux so that
> >>>>both the EMIF driver and the AM33XX PM code can benefit.
> >>>>
> >>>>Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
> >>>>Reviewed-by: Russ Dill <russ.dill@ti.com>
> >>>>Acked-by: Santosh Shililmar <santosh.shilimkar@ti.com>
> >>>>Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> >>>>---
> >>>>v3->v4:
> >>>>patch unchanged from original:
> >>>>	http://www.spinics.net/lists/linux-omap/msg95314.html
> >>>>
> >>>>  drivers/memory/emif.h   | 543 +---------------------------------------------
> >>>>  include/linux/ti_emif.h | 558 ++++++++++++++++++++++++++++++++++++++++++++++++
> >>>
> >>>So far we've seen that exposing hardware registers like this
> >>>will lead into various drivers misusing them. I think a better
> >>>solution is to implement few targeted functions that allow
> >>>sharing code between the platform idle code and memory driver.
> >>>
> >>>Maybe you can have the shared functions in in something like
> >>>drivers/memory/ti-emif.c that's always built in? The idle
> >>>code won't need any of that early on.
> >>
> >>Well the reason it was done this way was to utilize all of the addresses of
> >>EMIF register in the ASM sleep code to do relative addressing from the EMIF
> >>base address. The ASM sleep code (patch 9) needs to save and restore emif
> >>context and set and unset self refresh in emif. The issues will come from
> >>the ASM being copied to and running from SRAM without the ability to access
> >>code in DDR (because we are shutting the EMIF off), so we would need to copy
> >>these functions as well and have to worry about any issues we introduce by
> >>relocating c code. Is it worth the added maintenance burden?
> >
> >Ah right it needs to run in SRAM. There were some relocatable
> >c code patches posted a while back, so it might be worth
> >revisiting that.
> >
> >I think it can also be done with assembly with something like
> >this:
> >
> >1. Make am335x idle code depends on TI_EMIF && WKUP_M3_RPROC
> >
> >2. Add the memory save and restore assembly functions into
> >    drivers/memory/ti-emif-sram.S
> >
> >3. Allocate the SRAM preferrably with drivers/misc/sram.c
> >    instead of the legacy mach-omap2/sram.c
> >
> 
> This I can do, I will just need to make a change somewhere to make generic
>  sram driver provide sram allocations marked for exec.

OK great, that will make things easier for us in the long run.
 
> >4. Map the idle assembly code and EMIF save and restore
> >    functions into SRAM
> >
> >5. Call the EMIF save and restore functions from the idle
> >    assembly code at the SRAM locations and pass the save and
> >    restore area in a register
> >
> >So basically we need to figure out a generic way to do driver
> >hooks in the PM idle code even very late and early in the
> >assembly code so we can keep most of the code in drivers.
> >Eventually also the idle assembly code should be in the drivers
> >too..
> 
> I did not consider this earlier but the cpuidle code will use the same path
> in the assembly code. The cpuidle configures the suspend path to make the
> emif actions optional (save and restore with shut off, and self refresh), so
> a generic solution probably isn't possible here as we (will) need a certain
> level of granularity of control over the emif actions, and that will be
> difficult to maintain while keeping the pm functionality out of the EMIF
> code.

OK

Regards,

Tony

^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 02/11] memory: emif: Move EMIF register defines to include/linux/
@ 2014-07-16  8:33             ` Tony Lindgren
  0 siblings, 0 replies; 106+ messages in thread
From: Tony Lindgren @ 2014-07-16  8:33 UTC (permalink / raw)
  To: linux-arm-kernel

* Dave Gerlach <d-gerlach@ti.com> [140715 19:46]:
> Tony,
> On 07/15/2014 01:38 AM, Tony Lindgren wrote:
> >* Dave Gerlach <d-gerlach@ti.com> [140714 10:44]:
> >>On 07/14/2014 06:12 AM, Tony Lindgren wrote:
> >>>* Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
> >>>>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 register defines in the emif header file to include/linux so that
> >>>>both the EMIF driver and the AM33XX PM code can benefit.
> >>>>
> >>>>Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
> >>>>Reviewed-by: Russ Dill <russ.dill@ti.com>
> >>>>Acked-by: Santosh Shililmar <santosh.shilimkar@ti.com>
> >>>>Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> >>>>---
> >>>>v3->v4:
> >>>>patch unchanged from original:
> >>>>	http://www.spinics.net/lists/linux-omap/msg95314.html
> >>>>
> >>>>  drivers/memory/emif.h   | 543 +---------------------------------------------
> >>>>  include/linux/ti_emif.h | 558 ++++++++++++++++++++++++++++++++++++++++++++++++
> >>>
> >>>So far we've seen that exposing hardware registers like this
> >>>will lead into various drivers misusing them. I think a better
> >>>solution is to implement few targeted functions that allow
> >>>sharing code between the platform idle code and memory driver.
> >>>
> >>>Maybe you can have the shared functions in in something like
> >>>drivers/memory/ti-emif.c that's always built in? The idle
> >>>code won't need any of that early on.
> >>
> >>Well the reason it was done this way was to utilize all of the addresses of
> >>EMIF register in the ASM sleep code to do relative addressing from the EMIF
> >>base address. The ASM sleep code (patch 9) needs to save and restore emif
> >>context and set and unset self refresh in emif. The issues will come from
> >>the ASM being copied to and running from SRAM without the ability to access
> >>code in DDR (because we are shutting the EMIF off), so we would need to copy
> >>these functions as well and have to worry about any issues we introduce by
> >>relocating c code. Is it worth the added maintenance burden?
> >
> >Ah right it needs to run in SRAM. There were some relocatable
> >c code patches posted a while back, so it might be worth
> >revisiting that.
> >
> >I think it can also be done with assembly with something like
> >this:
> >
> >1. Make am335x idle code depends on TI_EMIF && WKUP_M3_RPROC
> >
> >2. Add the memory save and restore assembly functions into
> >    drivers/memory/ti-emif-sram.S
> >
> >3. Allocate the SRAM preferrably with drivers/misc/sram.c
> >    instead of the legacy mach-omap2/sram.c
> >
> 
> This I can do, I will just need to make a change somewhere to make generic
>  sram driver provide sram allocations marked for exec.

OK great, that will make things easier for us in the long run.
 
> >4. Map the idle assembly code and EMIF save and restore
> >    functions into SRAM
> >
> >5. Call the EMIF save and restore functions from the idle
> >    assembly code at the SRAM locations and pass the save and
> >    restore area in a register
> >
> >So basically we need to figure out a generic way to do driver
> >hooks in the PM idle code even very late and early in the
> >assembly code so we can keep most of the code in drivers.
> >Eventually also the idle assembly code should be in the drivers
> >too..
> 
> I did not consider this earlier but the cpuidle code will use the same path
> in the assembly code. The cpuidle configures the suspend path to make the
> emif actions optional (save and restore with shut off, and self refresh), so
> a generic solution probably isn't possible here as we (will) need a certain
> level of granularity of control over the emif actions, and that will be
> difficult to maintain while keeping the pm functionality out of the EMIF
> code.

OK

Regards,

Tony

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 03/11] ARM: OMAP2+: timer: Add suspend-resume callbacks for clkevent device
  2014-07-15  6:48           ` Tony Lindgren
@ 2014-07-16 20:17             ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-16 20:17 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Santosh Shilimkar, linux-arm-kernel, linux-omap, Paul Walmsley,
	Kevin Hilman, Tero Kristo, Nishanth Menon, Russ Dill,
	Daniel Mack, Suman Anna, Benoit Cousson, Vaibhav Bedia

On 07/15/2014 01:48 AM, Tony Lindgren wrote:
> * Dave Gerlach <d-gerlach@ti.com> [140714 10:44]:
>> Santosh, Tony,
>>
>> On 07/14/2014 09:37 AM, Santosh Shilimkar wrote:
>>> On Monday 14 July 2014 07:15 AM, Tony Lindgren wrote:
>>>> * Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
>>>>> From: Vaibhav Bedia <vaibhav.bedia@ti.com>
>>>>>
>>>>> OMAP timer code registers two timers - one as clocksource
>>>>> and one as clockevent. Since AM33XX has only one usable timer
>>>>> in the WKUP domain 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.
>>>>>
>>>>> Signed-off-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
>>>>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>>>>> ---
>>>>> v3->v4:
>>>>> 	Only use for am33xx soc now.
>>>>>
>>>>>  arch/arm/mach-omap2/timer.c | 28 ++++++++++++++++++++++++++++
>>>>>  1 file changed, 28 insertions(+)
>>>>>
>>>>> diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
>>>>> index 43d03fb..6fc1748 100644
>>>>> --- a/arch/arm/mach-omap2/timer.c
>>>>> +++ b/arch/arm/mach-omap2/timer.c
>>>>> @@ -128,6 +128,29 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
>>>>>  	}
>>>>>  }
>>>>>
>>>>> +static void omap_clkevt_suspend(struct clock_event_device *unused)
>>>>> +{
>>>>> +	struct omap_hwmod *oh;
>>>>> +
>>>>> +	oh = omap_hwmod_lookup(clockevent_gpt.name);
>>>>> +	if (!oh)
>>>>> +		return;
>>>>> +
>>>>> +	omap_hwmod_idle(oh);
>>>>> +}
>>>>> +
>>>>> +static void omap_clkevt_resume(struct clock_event_device *unused)
>>>>> +{
>>>>> +	struct omap_hwmod *oh;
>>>>> +
>>>>> +	oh = omap_hwmod_lookup(clockevent_gpt.name);
>>>>> +	if (!oh)
>>>>> +		return;
>>>>> +
>>>>> +	omap_hwmod_enable(oh);
>>>>> +	__omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);
>>>>> +}
>>>>> +
>>>>
>>>> This is going to make moving the timer code into drivers one step
>>>> tougher to do. And you don't need to look up the hwmod entry every
>>>> time, just initialize it during the init.
>>
>> Yes you are right about looking up only at init I need to change that. I
>> agree that this makes moving the timers harder but I'm not sure there's any
>> way around this. I attempted to use the omap_device layer here but there is
>> no device at all created here, it does not hook into the normal PM layer
>> through omap_device. This clock must be shut off as it sits in the
>> peripheral power domain and any active clock within the domain will prevent
>> suspend from happening, so we end up with a platform specific issue here. It
>> seems that the only way I can get to the clock is through the hwmod.
> 
> It's best to register these kind of functions as platform_data
> in pdata-quirks.c auxdata. That way when this moves to live
> in drivers/clocksource the driver does not need to know anything
> about the interconnect specific registers.

Perhaps I spoke too soon on this, I can't use pdata-quirks right now to pass the
idle/unidle functions. The clockevent source is special and reads the dt node
itself and then marks it as disabled so nothing else can touch it. No device is
created and no auxdata matched. I will gladly fix it up once the driver is ready
to move but right now I can't use any existing functionality to do it.

Regards,
Dave

> 
> Also, please don't use Linux generic names here.. Maybe use
> omap_clkevt_idle/unidle? The linux suspend and resume hooks
> and runtime PM could all use these functions then.
> 
>>>>> +	if (soc_is_am33xx()) {
>>>>> +		clockevent_gpt.suspend = omap_clkevt_suspend;
>>>>> +		clockevent_gpt.resume = omap_clkevt_resume;
>>>>> +	}
>>>>> +
>>>>
>>>> Maybe try to set up things so we initialize the SoC specific
>>>> timer suspend and resume functions in mach-omap2/timer.c
>>>> in a way where eventually the device driver can easily use
>>>> them?
>>>>
>>> +1. I had similar comments on the previous version too.
>>
>> This was my attempt to make things specific to only am335x based on
>> Santosh's previous comments as last time they were populated for every
>> device even when unneeded. These are not standard suspend/resume handlers,
>> they are specific to clock event. I know there will always need to be at
>> least some code here for the early timer init based on previous timer
>> cleanup series I've seen, so perhaps I could hook it in there when the time
>> comes?
> 
> Well just adding a minimal include/linux/platform_data/timer-omap.h
> to pass those function pointers to the driver should do the trick.
> 
> Regards,
> 
> Tony
> 


^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 03/11] ARM: OMAP2+: timer: Add suspend-resume callbacks for clkevent device
@ 2014-07-16 20:17             ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-07-16 20:17 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/15/2014 01:48 AM, Tony Lindgren wrote:
> * Dave Gerlach <d-gerlach@ti.com> [140714 10:44]:
>> Santosh, Tony,
>>
>> On 07/14/2014 09:37 AM, Santosh Shilimkar wrote:
>>> On Monday 14 July 2014 07:15 AM, Tony Lindgren wrote:
>>>> * Dave Gerlach <d-gerlach@ti.com> [140710 19:59]:
>>>>> From: Vaibhav Bedia <vaibhav.bedia@ti.com>
>>>>>
>>>>> OMAP timer code registers two timers - one as clocksource
>>>>> and one as clockevent. Since AM33XX has only one usable timer
>>>>> in the WKUP domain 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.
>>>>>
>>>>> Signed-off-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
>>>>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>>>>> ---
>>>>> v3->v4:
>>>>> 	Only use for am33xx soc now.
>>>>>
>>>>>  arch/arm/mach-omap2/timer.c | 28 ++++++++++++++++++++++++++++
>>>>>  1 file changed, 28 insertions(+)
>>>>>
>>>>> diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
>>>>> index 43d03fb..6fc1748 100644
>>>>> --- a/arch/arm/mach-omap2/timer.c
>>>>> +++ b/arch/arm/mach-omap2/timer.c
>>>>> @@ -128,6 +128,29 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
>>>>>  	}
>>>>>  }
>>>>>
>>>>> +static void omap_clkevt_suspend(struct clock_event_device *unused)
>>>>> +{
>>>>> +	struct omap_hwmod *oh;
>>>>> +
>>>>> +	oh = omap_hwmod_lookup(clockevent_gpt.name);
>>>>> +	if (!oh)
>>>>> +		return;
>>>>> +
>>>>> +	omap_hwmod_idle(oh);
>>>>> +}
>>>>> +
>>>>> +static void omap_clkevt_resume(struct clock_event_device *unused)
>>>>> +{
>>>>> +	struct omap_hwmod *oh;
>>>>> +
>>>>> +	oh = omap_hwmod_lookup(clockevent_gpt.name);
>>>>> +	if (!oh)
>>>>> +		return;
>>>>> +
>>>>> +	omap_hwmod_enable(oh);
>>>>> +	__omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);
>>>>> +}
>>>>> +
>>>>
>>>> This is going to make moving the timer code into drivers one step
>>>> tougher to do. And you don't need to look up the hwmod entry every
>>>> time, just initialize it during the init.
>>
>> Yes you are right about looking up only at init I need to change that. I
>> agree that this makes moving the timers harder but I'm not sure there's any
>> way around this. I attempted to use the omap_device layer here but there is
>> no device at all created here, it does not hook into the normal PM layer
>> through omap_device. This clock must be shut off as it sits in the
>> peripheral power domain and any active clock within the domain will prevent
>> suspend from happening, so we end up with a platform specific issue here. It
>> seems that the only way I can get to the clock is through the hwmod.
> 
> It's best to register these kind of functions as platform_data
> in pdata-quirks.c auxdata. That way when this moves to live
> in drivers/clocksource the driver does not need to know anything
> about the interconnect specific registers.

Perhaps I spoke too soon on this, I can't use pdata-quirks right now to pass the
idle/unidle functions. The clockevent source is special and reads the dt node
itself and then marks it as disabled so nothing else can touch it. No device is
created and no auxdata matched. I will gladly fix it up once the driver is ready
to move but right now I can't use any existing functionality to do it.

Regards,
Dave

> 
> Also, please don't use Linux generic names here.. Maybe use
> omap_clkevt_idle/unidle? The linux suspend and resume hooks
> and runtime PM could all use these functions then.
> 
>>>>> +	if (soc_is_am33xx()) {
>>>>> +		clockevent_gpt.suspend = omap_clkevt_suspend;
>>>>> +		clockevent_gpt.resume = omap_clkevt_resume;
>>>>> +	}
>>>>> +
>>>>
>>>> Maybe try to set up things so we initialize the SoC specific
>>>> timer suspend and resume functions in mach-omap2/timer.c
>>>> in a way where eventually the device driver can easily use
>>>> them?
>>>>
>>> +1. I had similar comments on the previous version too.
>>
>> This was my attempt to make things specific to only am335x based on
>> Santosh's previous comments as last time they were populated for every
>> device even when unneeded. These are not standard suspend/resume handlers,
>> they are specific to clock event. I know there will always need to be at
>> least some code here for the early timer init based on previous timer
>> cleanup series I've seen, so perhaps I could hook it in there when the time
>> comes?
> 
> Well just adding a minimal include/linux/platform_data/timer-omap.h
> to pass those function pointers to the driver should do the trick.
> 
> Regards,
> 
> Tony
> 

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 03/11] ARM: OMAP2+: timer: Add suspend-resume callbacks for clkevent device
  2014-07-16 20:17             ` Dave Gerlach
@ 2014-07-17  8:16               ` Tony Lindgren
  -1 siblings, 0 replies; 106+ messages in thread
From: Tony Lindgren @ 2014-07-17  8:16 UTC (permalink / raw)
  To: Dave Gerlach
  Cc: Santosh Shilimkar, linux-arm-kernel, linux-omap, Paul Walmsley,
	Kevin Hilman, Tero Kristo, Nishanth Menon, Russ Dill,
	Daniel Mack, Suman Anna, Benoit Cousson, Vaibhav Bedia

* Dave Gerlach <d-gerlach@ti.com> [140716 13:19]:
> On 07/15/2014 01:48 AM, Tony Lindgren wrote:
> > 
> > It's best to register these kind of functions as platform_data
> > in pdata-quirks.c auxdata. That way when this moves to live
> > in drivers/clocksource the driver does not need to know anything
> > about the interconnect specific registers.
> 
> Perhaps I spoke too soon on this, I can't use pdata-quirks right now to pass the
> idle/unidle functions. The clockevent source is special and reads the dt node
> itself and then marks it as disabled so nothing else can touch it. No device is
> created and no auxdata matched. I will gladly fix it up once the driver is ready
> to move but right now I can't use any existing functionality to do it.

OK. So maybe just initialize the function pointers during init and
use appropriate naming so the function pointers will stay usable
later on too once it's a driver.

Regards,

Tony

^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 03/11] ARM: OMAP2+: timer: Add suspend-resume callbacks for clkevent device
@ 2014-07-17  8:16               ` Tony Lindgren
  0 siblings, 0 replies; 106+ messages in thread
From: Tony Lindgren @ 2014-07-17  8:16 UTC (permalink / raw)
  To: linux-arm-kernel

* Dave Gerlach <d-gerlach@ti.com> [140716 13:19]:
> On 07/15/2014 01:48 AM, Tony Lindgren wrote:
> > 
> > It's best to register these kind of functions as platform_data
> > in pdata-quirks.c auxdata. That way when this moves to live
> > in drivers/clocksource the driver does not need to know anything
> > about the interconnect specific registers.
> 
> Perhaps I spoke too soon on this, I can't use pdata-quirks right now to pass the
> idle/unidle functions. The clockevent source is special and reads the dt node
> itself and then marks it as disabled so nothing else can touch it. No device is
> created and no auxdata matched. I will gladly fix it up once the driver is ready
> to move but right now I can't use any existing functionality to do it.

OK. So maybe just initialize the function pointers during init and
use appropriate naming so the function pointers will stay usable
later on too once it's a driver.

Regards,

Tony

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 10/11] ARM: OMAP2+: AM33XX: Basic suspend resume support
  2014-07-11  2:55   ` Dave Gerlach
@ 2014-09-08 22:30     ` Kevin Hilman
  -1 siblings, 0 replies; 106+ messages in thread
From: Kevin Hilman @ 2014-09-08 22:30 UTC (permalink / raw)
  To: Dave Gerlach
  Cc: linux-arm-kernel, linux-omap, Paul Walmsley, Tony Lindgren,
	Tero Kristo, Nishanth Menon, Russ Dill, Santosh Shilimkar,
	Daniel Mack, Suman Anna, Benoit Cousson, Ohad Ben-Cohen

+Ohad

Dave Gerlach <d-gerlach@ti.com> writes:

> AM335x supports various low power modes as documented
> in section 8.1.4.3 of the AM335x Technical Reference Manual.
>
> 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 the resume process. As part of
> the boot process, the assembly code is copied over to OCMCRAM
> using the OMAP SRAM code so it can be executed to turn of the
> EMIF.
>
> AM335x has a Cortex-M3 (WKUP_M3) which assists the MPU
> in DeepSleep0 and Standby 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 WKUP_M3 is managed by a remoteproc driver. The PM code hooks
> into the remoteproc driver through an rproc_ready callback to
> allow PM features to become available once the firmware for the
> wkup_m3 has been loaded. This code maintains its own state machine
> for the wkup_m3 so that it can be used in the manner intended for
> low power modes.
>
> 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 external RAM in self-refresh
> mode, gates the MPU clock, and then finally executes the WFI
> instruction. Execution of the WFI instruction with MPU clock gated
> triggers another interrupt to the WKUP_M3 which then continues
> with 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 and from here system remains in sleep state
> until a wake source brings the system into resume path.
>
> 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.
>
> Code is based on work by Vaibhav Bedia.
>
> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
> ---
> v3->v4:
> 	Remove all direct wkup_m3 usage and moved to rproc driver introduced
> 	in previous patch.

This statement is rather confusing as there's still quite a bit of
direct wkup_m3 usage, including the guts of the protocal for message
passing.  I thought you had agreed based on earilier reviews to split
out the wkup_m3 into it's on little driver with a clear/clean API which
could be called from here?

To me, it's not terribly clear how you made the split between this PM
core code an the remoteproc code.  In the changelog for the remoteproc
patch, it states it's to "load the firmware for and boot the wkup_m3".
But, while parts of the IPC are here in pm33xx.c, parts of the IPC are
also inside the remoteproc driver, so I'm quite curious if that's OK
with the remoteproc maintainers.  Either way, please make it clearer how
and why you made the split, and please isolate the wkup_m3 IPC/protocol
from this code.  Think of people wanting to rework/extend the wkup_m3
firmware.  They shouldn't be messing around in here, but rather inside a
driver specificaly for the wkup_m3.

Also, I'll beat this drum again even though nobody is listening: it's
still very fuzzy to me how this approach is going to be used to manage
low-power idle?  Is low-power idle just being completely ignored for
this SoC?

Kevin

^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 10/11] ARM: OMAP2+: AM33XX: Basic suspend resume support
@ 2014-09-08 22:30     ` Kevin Hilman
  0 siblings, 0 replies; 106+ messages in thread
From: Kevin Hilman @ 2014-09-08 22:30 UTC (permalink / raw)
  To: linux-arm-kernel

+Ohad

Dave Gerlach <d-gerlach@ti.com> writes:

> AM335x supports various low power modes as documented
> in section 8.1.4.3 of the AM335x Technical Reference Manual.
>
> 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 the resume process. As part of
> the boot process, the assembly code is copied over to OCMCRAM
> using the OMAP SRAM code so it can be executed to turn of the
> EMIF.
>
> AM335x has a Cortex-M3 (WKUP_M3) which assists the MPU
> in DeepSleep0 and Standby 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 WKUP_M3 is managed by a remoteproc driver. The PM code hooks
> into the remoteproc driver through an rproc_ready callback to
> allow PM features to become available once the firmware for the
> wkup_m3 has been loaded. This code maintains its own state machine
> for the wkup_m3 so that it can be used in the manner intended for
> low power modes.
>
> 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 external RAM in self-refresh
> mode, gates the MPU clock, and then finally executes the WFI
> instruction. Execution of the WFI instruction with MPU clock gated
> triggers another interrupt to the WKUP_M3 which then continues
> with 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 and from here system remains in sleep state
> until a wake source brings the system into resume path.
>
> 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.
>
> Code is based on work by Vaibhav Bedia.
>
> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
> ---
> v3->v4:
> 	Remove all direct wkup_m3 usage and moved to rproc driver introduced
> 	in previous patch.

This statement is rather confusing as there's still quite a bit of
direct wkup_m3 usage, including the guts of the protocal for message
passing.  I thought you had agreed based on earilier reviews to split
out the wkup_m3 into it's on little driver with a clear/clean API which
could be called from here?

To me, it's not terribly clear how you made the split between this PM
core code an the remoteproc code.  In the changelog for the remoteproc
patch, it states it's to "load the firmware for and boot the wkup_m3".
But, while parts of the IPC are here in pm33xx.c, parts of the IPC are
also inside the remoteproc driver, so I'm quite curious if that's OK
with the remoteproc maintainers.  Either way, please make it clearer how
and why you made the split, and please isolate the wkup_m3 IPC/protocol
from this code.  Think of people wanting to rework/extend the wkup_m3
firmware.  They shouldn't be messing around in here, but rather inside a
driver specificaly for the wkup_m3.

Also, I'll beat this drum again even though nobody is listening: it's
still very fuzzy to me how this approach is going to be used to manage
low-power idle?  Is low-power idle just being completely ignored for
this SoC?

Kevin

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 10/11] ARM: OMAP2+: AM33XX: Basic suspend resume support
  2014-09-08 22:30     ` Kevin Hilman
@ 2014-09-09 10:31       ` Ohad Ben-Cohen
  -1 siblings, 0 replies; 106+ messages in thread
From: Ohad Ben-Cohen @ 2014-09-09 10:31 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Dave Gerlach, linux-arm, linux-omap, Paul Walmsley,
	Tony Lindgren, Tero Kristo, Nishanth Menon, Russ Dill,
	Santosh Shilimkar, Daniel Mack, Suman Anna, Benoit Cousson

On Tue, Sep 9, 2014 at 1:30 AM, Kevin Hilman <khilman@linaro.org> wrote:
> To me, it's not terribly clear how you made the split between this PM
> core code an the remoteproc code.  In the changelog for the remoteproc
> patch, it states it's to "load the firmware for and boot the wkup_m3".
> But, while parts of the IPC are here in pm33xx.c, parts of the IPC are
> also inside the remoteproc driver, so I'm quite curious if that's OK
> with the remoteproc maintainers.  Either way, please make it clearer how
> and why you made the split, and please isolate the wkup_m3 IPC/protocol
> from this code.  Think of people wanting to rework/extend the wkup_m3
> firmware.  They shouldn't be messing around in here, but rather inside a
> driver specificaly for the wkup_m3.

I haven't looked at the code very thoroughly yet, but generally a
remoteproc driver should only implement the three start/stop/kick
rproc_ops, and then register them via the remoteproc framework.
Exposing additional API directly from that driver isn't something we
immediately want to accept.

If relevant, we would generally prefer to extend remoteproc instead,
so other platform-specific drivers could utilize that functionality as
well. Or rpmsg - if we're missing some IPC functionality.

Thanks,
Ohad.

^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 10/11] ARM: OMAP2+: AM33XX: Basic suspend resume support
@ 2014-09-09 10:31       ` Ohad Ben-Cohen
  0 siblings, 0 replies; 106+ messages in thread
From: Ohad Ben-Cohen @ 2014-09-09 10:31 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Sep 9, 2014 at 1:30 AM, Kevin Hilman <khilman@linaro.org> wrote:
> To me, it's not terribly clear how you made the split between this PM
> core code an the remoteproc code.  In the changelog for the remoteproc
> patch, it states it's to "load the firmware for and boot the wkup_m3".
> But, while parts of the IPC are here in pm33xx.c, parts of the IPC are
> also inside the remoteproc driver, so I'm quite curious if that's OK
> with the remoteproc maintainers.  Either way, please make it clearer how
> and why you made the split, and please isolate the wkup_m3 IPC/protocol
> from this code.  Think of people wanting to rework/extend the wkup_m3
> firmware.  They shouldn't be messing around in here, but rather inside a
> driver specificaly for the wkup_m3.

I haven't looked at the code very thoroughly yet, but generally a
remoteproc driver should only implement the three start/stop/kick
rproc_ops, and then register them via the remoteproc framework.
Exposing additional API directly from that driver isn't something we
immediately want to accept.

If relevant, we would generally prefer to extend remoteproc instead,
so other platform-specific drivers could utilize that functionality as
well. Or rpmsg - if we're missing some IPC functionality.

Thanks,
Ohad.

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 10/11] ARM: OMAP2+: AM33XX: Basic suspend resume support
  2014-09-09 10:31       ` Ohad Ben-Cohen
@ 2014-09-09 19:59         ` Suman Anna
  -1 siblings, 0 replies; 106+ messages in thread
From: Suman Anna @ 2014-09-09 19:59 UTC (permalink / raw)
  To: Ohad Ben-Cohen, Kevin Hilman
  Cc: Dave Gerlach, linux-arm, linux-omap, Paul Walmsley,
	Tony Lindgren, Tero Kristo, Nishanth Menon, Russ Dill,
	Santosh Shilimkar, Daniel Mack, Benoit Cousson

Hi Ohad,

On 09/09/2014 05:31 AM, Ohad Ben-Cohen wrote:
> On Tue, Sep 9, 2014 at 1:30 AM, Kevin Hilman <khilman@linaro.org> wrote:
>> To me, it's not terribly clear how you made the split between this PM
>> core code an the remoteproc code.  In the changelog for the remoteproc
>> patch, it states it's to "load the firmware for and boot the wkup_m3".
>> But, while parts of the IPC are here in pm33xx.c, parts of the IPC are
>> also inside the remoteproc driver, so I'm quite curious if that's OK
>> with the remoteproc maintainers.  Either way, please make it clearer how
>> and why you made the split, and please isolate the wkup_m3 IPC/protocol
>> from this code.  Think of people wanting to rework/extend the wkup_m3
>> firmware.  They shouldn't be messing around in here, but rather inside a
>> driver specificaly for the wkup_m3.
> 
> I haven't looked at the code very thoroughly yet, but generally a
> remoteproc driver should only implement the three start/stop/kick
> rproc_ops, and then register them via the remoteproc framework.
> Exposing additional API directly from that driver isn't something we
> immediately want to accept.
> 
> If relevant, we would generally prefer to extend remoteproc instead,
> so other platform-specific drivers could utilize that functionality as
> well. Or rpmsg - if we're missing some IPC functionality.

The WkupM3 cannot access DDR, and so we don't intend to use rpmsg. The
IPC with wkup_m3 is usually one of the last steps for putting the SoC
into a desired low-power state either during suspend or cpuidle, and the
communication uses a bank of fixed registers. The .kick is specific
to virtio-based communication, and so this is not gonna be used.

If you can take a closer look at the wkup_m3 remoteproc driver and give
your comments, then we can plan on the next steps. Especially as there
are also pieces pertaining to the PM layer knowing the WkupM3 has been
loaded and booted. There are already some pending comments on code
fragments from Santosh and myself, but let us know your inputs on the
integration aspects on PM, remoteproc and IPC with WkupM3.

regards
Suman

^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 10/11] ARM: OMAP2+: AM33XX: Basic suspend resume support
@ 2014-09-09 19:59         ` Suman Anna
  0 siblings, 0 replies; 106+ messages in thread
From: Suman Anna @ 2014-09-09 19:59 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Ohad,

On 09/09/2014 05:31 AM, Ohad Ben-Cohen wrote:
> On Tue, Sep 9, 2014 at 1:30 AM, Kevin Hilman <khilman@linaro.org> wrote:
>> To me, it's not terribly clear how you made the split between this PM
>> core code an the remoteproc code.  In the changelog for the remoteproc
>> patch, it states it's to "load the firmware for and boot the wkup_m3".
>> But, while parts of the IPC are here in pm33xx.c, parts of the IPC are
>> also inside the remoteproc driver, so I'm quite curious if that's OK
>> with the remoteproc maintainers.  Either way, please make it clearer how
>> and why you made the split, and please isolate the wkup_m3 IPC/protocol
>> from this code.  Think of people wanting to rework/extend the wkup_m3
>> firmware.  They shouldn't be messing around in here, but rather inside a
>> driver specificaly for the wkup_m3.
> 
> I haven't looked at the code very thoroughly yet, but generally a
> remoteproc driver should only implement the three start/stop/kick
> rproc_ops, and then register them via the remoteproc framework.
> Exposing additional API directly from that driver isn't something we
> immediately want to accept.
> 
> If relevant, we would generally prefer to extend remoteproc instead,
> so other platform-specific drivers could utilize that functionality as
> well. Or rpmsg - if we're missing some IPC functionality.

The WkupM3 cannot access DDR, and so we don't intend to use rpmsg. The
IPC with wkup_m3 is usually one of the last steps for putting the SoC
into a desired low-power state either during suspend or cpuidle, and the
communication uses a bank of fixed registers. The .kick is specific
to virtio-based communication, and so this is not gonna be used.

If you can take a closer look at the wkup_m3 remoteproc driver and give
your comments, then we can plan on the next steps. Especially as there
are also pieces pertaining to the PM layer knowing the WkupM3 has been
loaded and booted. There are already some pending comments on code
fragments from Santosh and myself, but let us know your inputs on the
integration aspects on PM, remoteproc and IPC with WkupM3.

regards
Suman

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 10/11] ARM: OMAP2+: AM33XX: Basic suspend resume support
  2014-09-09 19:59         ` Suman Anna
@ 2014-09-09 20:28           ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-09-09 20:28 UTC (permalink / raw)
  To: Suman Anna, Ohad Ben-Cohen, Kevin Hilman
  Cc: linux-arm, linux-omap, Paul Walmsley, Tony Lindgren, Tero Kristo,
	Nishanth Menon, Russ Dill, Santosh Shilimkar, Daniel Mack,
	Benoit Cousson

Kevin/Ohad,
On 09/09/2014 02:59 PM, Suman Anna wrote:
> Hi Ohad,
> 
> On 09/09/2014 05:31 AM, Ohad Ben-Cohen wrote:
>> On Tue, Sep 9, 2014 at 1:30 AM, Kevin Hilman <khilman@linaro.org> wrote:
>>> To me, it's not terribly clear how you made the split between this PM
>>> core code an the remoteproc code.  In the changelog for the remoteproc
>>> patch, it states it's to "load the firmware for and boot the wkup_m3".
>>> But, while parts of the IPC are here in pm33xx.c, parts of the IPC are
>>> also inside the remoteproc driver, so I'm quite curious if that's OK
>>> with the remoteproc maintainers.  Either way, please make it clearer how
>>> and why you made the split, and please isolate the wkup_m3 IPC/protocol
>>> from this code.  Think of people wanting to rework/extend the wkup_m3
>>> firmware.  They shouldn't be messing around in here, but rather inside a
>>> driver specificaly for the wkup_m3.
>>
>> I haven't looked at the code very thoroughly yet, but generally a
>> remoteproc driver should only implement the three start/stop/kick
>> rproc_ops, and then register them via the remoteproc framework.
>> Exposing additional API directly from that driver isn't something we
>> immediately want to accept.
>>
>> If relevant, we would generally prefer to extend remoteproc instead,
>> so other platform-specific drivers could utilize that functionality as
>> well. Or rpmsg - if we're missing some IPC functionality.
> 
> The WkupM3 cannot access DDR, and so we don't intend to use rpmsg. The
> IPC with wkup_m3 is usually one of the last steps for putting the SoC
> into a desired low-power state either during suspend or cpuidle, and the
> communication uses a bank of fixed registers. The .kick is specific
> to virtio-based communication, and so this is not gonna be used.
> 
> If you can take a closer look at the wkup_m3 remoteproc driver and give
> your comments, then we can plan on the next steps. Especially as there
> are also pieces pertaining to the PM layer knowing the WkupM3 has been
> loaded and booted. There are already some pending comments on code
> fragments from Santosh and myself, but let us know your inputs on the
> integration aspects on PM, remoteproc and IPC with WkupM3.
>

The split was defined by putting all the application specific (to the
firmware in use) code in the platform pm code while trying to keep all the
IPC code within the wkup_m3_rproc driver. The exposed API is definitely
heavily biased towards the intended use-case, but the CM3 was designed with
this exact purpose in mind and not much else, and due to the limited IPC
registers we have to work with there isn't a whole lot of flexibility. Only IPC
reg 0 is always used as the resume address, the usage of the other registers is
defined by the firmware and pm code.

Just as a refresher for those not familiar with it, the IPC mechanism works
like this: we load the ipc registers (8 for am33xx, 16 for am43xx) with any
information we want to communicate to the CM3, then we make a dummy write to
the Mailbox which triggers an interrupt on the CM3, the CM3 does what it
needs to with the info passed in the IPC regs and writes anything it wants to
communicate back to these registers, and then triggers a different interrupt
(not related to mailbox) to let the MPU know it is done. It's kind of a mess so
I figured one driver was the best way to encapsulate it all, and I still had to
introduce callbacks within the wkup_m3_rproc driver so it could let the pm code
know when the FW loaded (to actually enable pm) and when an interrupt was
received from the wkup_m3 (so the pm code can process the response).

As Suman stated, this sequence is part of the suspend path and also will be part
of the lower c-states for cpuidle, so we need something fast and lightweight.
RPMsg is way more than we need and it doesn't really fit the use case, so I'm
not sure what makes the most sense, extending remoteproc in some way to support
IPC communication like described above or leaving the basic FW loading
functionality in place in remoteproc but moving the IPC and wkup_m3
functionality back into the platform pm33xx code as it's so specific to that
use-case anyway.

Regards,
Dave

> regards
> Suman
> 


^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 10/11] ARM: OMAP2+: AM33XX: Basic suspend resume support
@ 2014-09-09 20:28           ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-09-09 20:28 UTC (permalink / raw)
  To: linux-arm-kernel

Kevin/Ohad,
On 09/09/2014 02:59 PM, Suman Anna wrote:
> Hi Ohad,
> 
> On 09/09/2014 05:31 AM, Ohad Ben-Cohen wrote:
>> On Tue, Sep 9, 2014 at 1:30 AM, Kevin Hilman <khilman@linaro.org> wrote:
>>> To me, it's not terribly clear how you made the split between this PM
>>> core code an the remoteproc code.  In the changelog for the remoteproc
>>> patch, it states it's to "load the firmware for and boot the wkup_m3".
>>> But, while parts of the IPC are here in pm33xx.c, parts of the IPC are
>>> also inside the remoteproc driver, so I'm quite curious if that's OK
>>> with the remoteproc maintainers.  Either way, please make it clearer how
>>> and why you made the split, and please isolate the wkup_m3 IPC/protocol
>>> from this code.  Think of people wanting to rework/extend the wkup_m3
>>> firmware.  They shouldn't be messing around in here, but rather inside a
>>> driver specificaly for the wkup_m3.
>>
>> I haven't looked at the code very thoroughly yet, but generally a
>> remoteproc driver should only implement the three start/stop/kick
>> rproc_ops, and then register them via the remoteproc framework.
>> Exposing additional API directly from that driver isn't something we
>> immediately want to accept.
>>
>> If relevant, we would generally prefer to extend remoteproc instead,
>> so other platform-specific drivers could utilize that functionality as
>> well. Or rpmsg - if we're missing some IPC functionality.
> 
> The WkupM3 cannot access DDR, and so we don't intend to use rpmsg. The
> IPC with wkup_m3 is usually one of the last steps for putting the SoC
> into a desired low-power state either during suspend or cpuidle, and the
> communication uses a bank of fixed registers. The .kick is specific
> to virtio-based communication, and so this is not gonna be used.
> 
> If you can take a closer look at the wkup_m3 remoteproc driver and give
> your comments, then we can plan on the next steps. Especially as there
> are also pieces pertaining to the PM layer knowing the WkupM3 has been
> loaded and booted. There are already some pending comments on code
> fragments from Santosh and myself, but let us know your inputs on the
> integration aspects on PM, remoteproc and IPC with WkupM3.
>

The split was defined by putting all the application specific (to the
firmware in use) code in the platform pm code while trying to keep all the
IPC code within the wkup_m3_rproc driver. The exposed API is definitely
heavily biased towards the intended use-case, but the CM3 was designed with
this exact purpose in mind and not much else, and due to the limited IPC
registers we have to work with there isn't a whole lot of flexibility. Only IPC
reg 0 is always used as the resume address, the usage of the other registers is
defined by the firmware and pm code.

Just as a refresher for those not familiar with it, the IPC mechanism works
like this: we load the ipc registers (8 for am33xx, 16 for am43xx) with any
information we want to communicate to the CM3, then we make a dummy write to
the Mailbox which triggers an interrupt on the CM3, the CM3 does what it
needs to with the info passed in the IPC regs and writes anything it wants to
communicate back to these registers, and then triggers a different interrupt
(not related to mailbox) to let the MPU know it is done. It's kind of a mess so
I figured one driver was the best way to encapsulate it all, and I still had to
introduce callbacks within the wkup_m3_rproc driver so it could let the pm code
know when the FW loaded (to actually enable pm) and when an interrupt was
received from the wkup_m3 (so the pm code can process the response).

As Suman stated, this sequence is part of the suspend path and also will be part
of the lower c-states for cpuidle, so we need something fast and lightweight.
RPMsg is way more than we need and it doesn't really fit the use case, so I'm
not sure what makes the most sense, extending remoteproc in some way to support
IPC communication like described above or leaving the basic FW loading
functionality in place in remoteproc but moving the IPC and wkup_m3
functionality back into the platform pm33xx code as it's so specific to that
use-case anyway.

Regards,
Dave

> regards
> Suman
> 

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 10/11] ARM: OMAP2+: AM33XX: Basic suspend resume support
  2014-09-09 20:28           ` Dave Gerlach
@ 2014-09-09 21:10             ` Kevin Hilman
  -1 siblings, 0 replies; 106+ messages in thread
From: Kevin Hilman @ 2014-09-09 21:10 UTC (permalink / raw)
  To: Dave Gerlach
  Cc: Suman Anna, Ohad Ben-Cohen, linux-arm, linux-omap, Paul Walmsley,
	Tony Lindgren, Tero Kristo, Nishanth Menon, Russ Dill,
	Santosh Shilimkar, Daniel Mack, Benoit Cousson

Dave Gerlach <d-gerlach@ti.com> writes:

> Kevin/Ohad,
> On 09/09/2014 02:59 PM, Suman Anna wrote:
>> Hi Ohad,
>> 
>> On 09/09/2014 05:31 AM, Ohad Ben-Cohen wrote:
>>> On Tue, Sep 9, 2014 at 1:30 AM, Kevin Hilman <khilman@linaro.org> wrote:
>>>> To me, it's not terribly clear how you made the split between this PM
>>>> core code an the remoteproc code.  In the changelog for the remoteproc
>>>> patch, it states it's to "load the firmware for and boot the wkup_m3".
>>>> But, while parts of the IPC are here in pm33xx.c, parts of the IPC are
>>>> also inside the remoteproc driver, so I'm quite curious if that's OK
>>>> with the remoteproc maintainers.  Either way, please make it clearer how
>>>> and why you made the split, and please isolate the wkup_m3 IPC/protocol
>>>> from this code.  Think of people wanting to rework/extend the wkup_m3
>>>> firmware.  They shouldn't be messing around in here, but rather inside a
>>>> driver specificaly for the wkup_m3.
>>>
>>> I haven't looked at the code very thoroughly yet, but generally a
>>> remoteproc driver should only implement the three start/stop/kick
>>> rproc_ops, and then register them via the remoteproc framework.
>>> Exposing additional API directly from that driver isn't something we
>>> immediately want to accept.
>>>
>>> If relevant, we would generally prefer to extend remoteproc instead,
>>> so other platform-specific drivers could utilize that functionality as
>>> well. Or rpmsg - if we're missing some IPC functionality.
>> 
>> The WkupM3 cannot access DDR, and so we don't intend to use rpmsg. The
>> IPC with wkup_m3 is usually one of the last steps for putting the SoC
>> into a desired low-power state either during suspend or cpuidle, and the
>> communication uses a bank of fixed registers. The .kick is specific
>> to virtio-based communication, and so this is not gonna be used.
>> 
>> If you can take a closer look at the wkup_m3 remoteproc driver and give
>> your comments, then we can plan on the next steps. Especially as there
>> are also pieces pertaining to the PM layer knowing the WkupM3 has been
>> loaded and booted. There are already some pending comments on code
>> fragments from Santosh and myself, but let us know your inputs on the
>> integration aspects on PM, remoteproc and IPC with WkupM3.
>>
>
> The split was defined by putting all the application specific (to the
> firmware in use) code in the platform pm code while trying to keep all the
> IPC code within the wkup_m3_rproc driver. 

I don't even see that split.  I see the platform PM code directly
setting IPC register values, but then rproc driver actually sends the
mailbox command.

> The exposed API is definitely heavily biased towards the intended
> use-case, 

Maybe if the API was actually documented, it would be easier for us to
review it.

> but the CM3 was designed with this exact purpose in mind and
> not much else, and due to the limited IPC registers we have to work
> with there isn't a whole lot of flexibility. Only IPC reg 0 is always
> used as the resume address, the usage of the other registers is
> defined by the firmware and pm code.
>
> Just as a refresher for those not familiar with it, the IPC mechanism works
> like this: we load the ipc registers (8 for am33xx, 16 for am43xx) with any
> information we want to communicate to the CM3, 

OK, and this happens currently in the platform PM code, right?

> then we make a dummy write to
> the Mailbox which triggers an interrupt on the CM3, the CM3 does what it
> needs to with the info passed in the IPC regs and writes anything it wants to
> communicate back to these registers, and then triggers a different interrupt
> (not related to mailbox) to let the MPU know it is done. 

And this part happens in the rproc driver, right?

> It's kind of a mess so I figured one driver was the best way to
> encapsulate it all,

So where is this "one driver" that encapsulates it all?  

> and I still had to
> introduce callbacks within the wkup_m3_rproc driver so it could let the pm code
> know when the FW loaded (to actually enable pm) and when an interrupt was
> received from the wkup_m3 (so the pm code can process the response).

> As Suman stated, this sequence is part of the suspend path and also will be part
> of the lower c-states for cpuidle, so we need something fast and lightweight.
> RPMsg is way more than we need and it doesn't really fit the use case, so I'm
> not sure what makes the most sense, extending remoteproc in some way to support
> IPC communication like described above or leaving the basic FW loading
> functionality in place in remoteproc but moving the IPC and wkup_m3
> functionality back into the platform pm33xx code as it's so specific to that
> use-case anyway.

I'm not advocating for using rpmsg (anymore).  But I dont' think shoving
your rpmsg-lite IPC into your rproc driver is the right answer either
(and Ohad's repsonse confirmed my suspicion.)

What I think you need to do (and what I've recommended at least once in
earlier reviews) put all the (non-rproc) wkup_m3 IPC into into one
driver and create a well-described, well-documented API that the
platform PM code will use.

IMO, the current "split" is very difficult to read/understand, which
means it will even more difficult to maintain.

Kevin

^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 10/11] ARM: OMAP2+: AM33XX: Basic suspend resume support
@ 2014-09-09 21:10             ` Kevin Hilman
  0 siblings, 0 replies; 106+ messages in thread
From: Kevin Hilman @ 2014-09-09 21:10 UTC (permalink / raw)
  To: linux-arm-kernel

Dave Gerlach <d-gerlach@ti.com> writes:

> Kevin/Ohad,
> On 09/09/2014 02:59 PM, Suman Anna wrote:
>> Hi Ohad,
>> 
>> On 09/09/2014 05:31 AM, Ohad Ben-Cohen wrote:
>>> On Tue, Sep 9, 2014 at 1:30 AM, Kevin Hilman <khilman@linaro.org> wrote:
>>>> To me, it's not terribly clear how you made the split between this PM
>>>> core code an the remoteproc code.  In the changelog for the remoteproc
>>>> patch, it states it's to "load the firmware for and boot the wkup_m3".
>>>> But, while parts of the IPC are here in pm33xx.c, parts of the IPC are
>>>> also inside the remoteproc driver, so I'm quite curious if that's OK
>>>> with the remoteproc maintainers.  Either way, please make it clearer how
>>>> and why you made the split, and please isolate the wkup_m3 IPC/protocol
>>>> from this code.  Think of people wanting to rework/extend the wkup_m3
>>>> firmware.  They shouldn't be messing around in here, but rather inside a
>>>> driver specificaly for the wkup_m3.
>>>
>>> I haven't looked at the code very thoroughly yet, but generally a
>>> remoteproc driver should only implement the three start/stop/kick
>>> rproc_ops, and then register them via the remoteproc framework.
>>> Exposing additional API directly from that driver isn't something we
>>> immediately want to accept.
>>>
>>> If relevant, we would generally prefer to extend remoteproc instead,
>>> so other platform-specific drivers could utilize that functionality as
>>> well. Or rpmsg - if we're missing some IPC functionality.
>> 
>> The WkupM3 cannot access DDR, and so we don't intend to use rpmsg. The
>> IPC with wkup_m3 is usually one of the last steps for putting the SoC
>> into a desired low-power state either during suspend or cpuidle, and the
>> communication uses a bank of fixed registers. The .kick is specific
>> to virtio-based communication, and so this is not gonna be used.
>> 
>> If you can take a closer look at the wkup_m3 remoteproc driver and give
>> your comments, then we can plan on the next steps. Especially as there
>> are also pieces pertaining to the PM layer knowing the WkupM3 has been
>> loaded and booted. There are already some pending comments on code
>> fragments from Santosh and myself, but let us know your inputs on the
>> integration aspects on PM, remoteproc and IPC with WkupM3.
>>
>
> The split was defined by putting all the application specific (to the
> firmware in use) code in the platform pm code while trying to keep all the
> IPC code within the wkup_m3_rproc driver. 

I don't even see that split.  I see the platform PM code directly
setting IPC register values, but then rproc driver actually sends the
mailbox command.

> The exposed API is definitely heavily biased towards the intended
> use-case, 

Maybe if the API was actually documented, it would be easier for us to
review it.

> but the CM3 was designed with this exact purpose in mind and
> not much else, and due to the limited IPC registers we have to work
> with there isn't a whole lot of flexibility. Only IPC reg 0 is always
> used as the resume address, the usage of the other registers is
> defined by the firmware and pm code.
>
> Just as a refresher for those not familiar with it, the IPC mechanism works
> like this: we load the ipc registers (8 for am33xx, 16 for am43xx) with any
> information we want to communicate to the CM3, 

OK, and this happens currently in the platform PM code, right?

> then we make a dummy write to
> the Mailbox which triggers an interrupt on the CM3, the CM3 does what it
> needs to with the info passed in the IPC regs and writes anything it wants to
> communicate back to these registers, and then triggers a different interrupt
> (not related to mailbox) to let the MPU know it is done. 

And this part happens in the rproc driver, right?

> It's kind of a mess so I figured one driver was the best way to
> encapsulate it all,

So where is this "one driver" that encapsulates it all?  

> and I still had to
> introduce callbacks within the wkup_m3_rproc driver so it could let the pm code
> know when the FW loaded (to actually enable pm) and when an interrupt was
> received from the wkup_m3 (so the pm code can process the response).

> As Suman stated, this sequence is part of the suspend path and also will be part
> of the lower c-states for cpuidle, so we need something fast and lightweight.
> RPMsg is way more than we need and it doesn't really fit the use case, so I'm
> not sure what makes the most sense, extending remoteproc in some way to support
> IPC communication like described above or leaving the basic FW loading
> functionality in place in remoteproc but moving the IPC and wkup_m3
> functionality back into the platform pm33xx code as it's so specific to that
> use-case anyway.

I'm not advocating for using rpmsg (anymore).  But I dont' think shoving
your rpmsg-lite IPC into your rproc driver is the right answer either
(and Ohad's repsonse confirmed my suspicion.)

What I think you need to do (and what I've recommended at least once in
earlier reviews) put all the (non-rproc) wkup_m3 IPC into into one
driver and create a well-described, well-documented API that the
platform PM code will use.

IMO, the current "split" is very difficult to read/understand, which
means it will even more difficult to maintain.

Kevin

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 10/11] ARM: OMAP2+: AM33XX: Basic suspend resume support
  2014-09-09 21:10             ` Kevin Hilman
@ 2014-09-10 21:19               ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-09-10 21:19 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Suman Anna, Ohad Ben-Cohen, linux-arm, linux-omap, Paul Walmsley,
	Tony Lindgren, Tero Kristo, Nishanth Menon, Russ Dill,
	Santosh Shilimkar, Daniel Mack, Benoit Cousson

Kevin,
On 09/09/2014 04:10 PM, Kevin Hilman wrote:
> Dave Gerlach <d-gerlach@ti.com> writes:
> 
>> Kevin/Ohad,
>> On 09/09/2014 02:59 PM, Suman Anna wrote:
>>> Hi Ohad,
>>>
>>> On 09/09/2014 05:31 AM, Ohad Ben-Cohen wrote:
>>>> On Tue, Sep 9, 2014 at 1:30 AM, Kevin Hilman <khilman@linaro.org> wrote:
>>>>> To me, it's not terribly clear how you made the split between this PM
>>>>> core code an the remoteproc code.  In the changelog for the remoteproc
>>>>> patch, it states it's to "load the firmware for and boot the wkup_m3".
>>>>> But, while parts of the IPC are here in pm33xx.c, parts of the IPC are
>>>>> also inside the remoteproc driver, so I'm quite curious if that's OK
>>>>> with the remoteproc maintainers.  Either way, please make it clearer how
>>>>> and why you made the split, and please isolate the wkup_m3 IPC/protocol
>>>>> from this code.  Think of people wanting to rework/extend the wkup_m3
>>>>> firmware.  They shouldn't be messing around in here, but rather inside a
>>>>> driver specificaly for the wkup_m3.
>>>>
>>>> I haven't looked at the code very thoroughly yet, but generally a
>>>> remoteproc driver should only implement the three start/stop/kick
>>>> rproc_ops, and then register them via the remoteproc framework.
>>>> Exposing additional API directly from that driver isn't something we
>>>> immediately want to accept.
>>>>
>>>> If relevant, we would generally prefer to extend remoteproc instead,
>>>> so other platform-specific drivers could utilize that functionality as
>>>> well. Or rpmsg - if we're missing some IPC functionality.
>>>
>>> The WkupM3 cannot access DDR, and so we don't intend to use rpmsg. The
>>> IPC with wkup_m3 is usually one of the last steps for putting the SoC
>>> into a desired low-power state either during suspend or cpuidle, and the
>>> communication uses a bank of fixed registers. The .kick is specific
>>> to virtio-based communication, and so this is not gonna be used.
>>>
>>> If you can take a closer look at the wkup_m3 remoteproc driver and give
>>> your comments, then we can plan on the next steps. Especially as there
>>> are also pieces pertaining to the PM layer knowing the WkupM3 has been
>>> loaded and booted. There are already some pending comments on code
>>> fragments from Santosh and myself, but let us know your inputs on the
>>> integration aspects on PM, remoteproc and IPC with WkupM3.
>>>
>>
>> The split was defined by putting all the application specific (to the
>> firmware in use) code in the platform pm code while trying to keep all the
>> IPC code within the wkup_m3_rproc driver. 
> 
> I don't even see that split.  I see the platform PM code directly
> setting IPC register values, but then rproc driver actually sends the
> mailbox command.

Well, really the pm code is setting a structure which gets passed to the
wkup_m3_rproc API and that's what does the write. I suppose the naming of the
structure is misleading though. However, the wkup_m3 driver isn't aware of the
protocol or what is going into these registers for the writes, the PM code
defines the usage.

> 
>> The exposed API is definitely heavily biased towards the intended
>> use-case, 
> 
> Maybe if the API was actually documented, it would be easier for us to
> review it.
> 
>> but the CM3 was designed with this exact purpose in mind and
>> not much else, and due to the limited IPC registers we have to work
>> with there isn't a whole lot of flexibility. Only IPC reg 0 is always
>> used as the resume address, the usage of the other registers is
>> defined by the firmware and pm code.
>>
>> Just as a refresher for those not familiar with it, the IPC mechanism works
>> like this: we load the ipc registers (8 for am33xx, 16 for am43xx) with any
>> information we want to communicate to the CM3, 
> 
> OK, and this happens currently in the platform PM code, right?
> 
>> then we make a dummy write to
>> the Mailbox which triggers an interrupt on the CM3, the CM3 does what it
>> needs to with the info passed in the IPC regs and writes anything it wants to
>> communicate back to these registers, and then triggers a different interrupt
>> (not related to mailbox) to let the MPU know it is done. 
> 
> And this part happens in the rproc driver, right?
> 
>> It's kind of a mess so I figured one driver was the best way to
>> encapsulate it all,
> 
> So where is this "one driver" that encapsulates it all?  
>

Sp my thinking was that I put the IPC writing in the wkup_m3_rproc driver, but
the actual configuration of what gets written in the PM platform code, to at
least try to keep things generic. Still, I do agree now that the split is not
that clear.

>> and I still had to
>> introduce callbacks within the wkup_m3_rproc driver so it could let the pm code
>> know when the FW loaded (to actually enable pm) and when an interrupt was
>> received from the wkup_m3 (so the pm code can process the response).
> 
>> As Suman stated, this sequence is part of the suspend path and also will be part
>> of the lower c-states for cpuidle, so we need something fast and lightweight.
>> RPMsg is way more than we need and it doesn't really fit the use case, so I'm
>> not sure what makes the most sense, extending remoteproc in some way to support
>> IPC communication like described above or leaving the basic FW loading
>> functionality in place in remoteproc but moving the IPC and wkup_m3
>> functionality back into the platform pm33xx code as it's so specific to that
>> use-case anyway.
> 
> I'm not advocating for using rpmsg (anymore).  But I dont' think shoving
> your rpmsg-lite IPC into your rproc driver is the right answer either
> (and Ohad's repsonse confirmed my suspicion.)
> 
> What I think you need to do (and what I've recommended at least once in
> earlier reviews) put all the (non-rproc) wkup_m3 IPC into into one
> driver and create a well-described, well-documented API that the
> platform PM code will use.
> 
> IMO, the current "split" is very difficult to read/understand, which
> means it will even more difficult to maintain.

I dont think I entirely understand your vision for the API. I see it going in
one of two directions:

PM/Application agnostic: provide ability to write/read wkup_m3 (mailbox ping is
handled automatically by the driver also) and then callbacks for rproc being
ready and handling response from wkup_m3 for the PM code to use, and that's it.
Well let the PM code sort out how it uses everything. This means that there is a
payload (similar to the structure in place now that takes the register values)
that gets configured and then the wkup_m3 driver just passes the info back and
forth between the MPU and wkup_m3 without the driver ever knowing what's
actually happening.

OR

Application specific: Provide ability to set use-case specific functionality,
i.e. wkup_m3_set_low_power_mode, wkup_m3_set_resume_address, etc... which
exposes the high level functions provided by the wkup_m3 firmware (which are
limited to the functionality in the TI provided firmware, which is all that is
intended anyway), and the pm code uses this to accomplish what it wants without
any knowledge of the actual communication or configuration.

I think the first version is more scalable and maintainable for future
applications, but perhaps I am still not aligned with your vision. Thoughts?

Regards,
Dave

> 
> Kevin
> 


^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 10/11] ARM: OMAP2+: AM33XX: Basic suspend resume support
@ 2014-09-10 21:19               ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-09-10 21:19 UTC (permalink / raw)
  To: linux-arm-kernel

Kevin,
On 09/09/2014 04:10 PM, Kevin Hilman wrote:
> Dave Gerlach <d-gerlach@ti.com> writes:
> 
>> Kevin/Ohad,
>> On 09/09/2014 02:59 PM, Suman Anna wrote:
>>> Hi Ohad,
>>>
>>> On 09/09/2014 05:31 AM, Ohad Ben-Cohen wrote:
>>>> On Tue, Sep 9, 2014 at 1:30 AM, Kevin Hilman <khilman@linaro.org> wrote:
>>>>> To me, it's not terribly clear how you made the split between this PM
>>>>> core code an the remoteproc code.  In the changelog for the remoteproc
>>>>> patch, it states it's to "load the firmware for and boot the wkup_m3".
>>>>> But, while parts of the IPC are here in pm33xx.c, parts of the IPC are
>>>>> also inside the remoteproc driver, so I'm quite curious if that's OK
>>>>> with the remoteproc maintainers.  Either way, please make it clearer how
>>>>> and why you made the split, and please isolate the wkup_m3 IPC/protocol
>>>>> from this code.  Think of people wanting to rework/extend the wkup_m3
>>>>> firmware.  They shouldn't be messing around in here, but rather inside a
>>>>> driver specificaly for the wkup_m3.
>>>>
>>>> I haven't looked at the code very thoroughly yet, but generally a
>>>> remoteproc driver should only implement the three start/stop/kick
>>>> rproc_ops, and then register them via the remoteproc framework.
>>>> Exposing additional API directly from that driver isn't something we
>>>> immediately want to accept.
>>>>
>>>> If relevant, we would generally prefer to extend remoteproc instead,
>>>> so other platform-specific drivers could utilize that functionality as
>>>> well. Or rpmsg - if we're missing some IPC functionality.
>>>
>>> The WkupM3 cannot access DDR, and so we don't intend to use rpmsg. The
>>> IPC with wkup_m3 is usually one of the last steps for putting the SoC
>>> into a desired low-power state either during suspend or cpuidle, and the
>>> communication uses a bank of fixed registers. The .kick is specific
>>> to virtio-based communication, and so this is not gonna be used.
>>>
>>> If you can take a closer look at the wkup_m3 remoteproc driver and give
>>> your comments, then we can plan on the next steps. Especially as there
>>> are also pieces pertaining to the PM layer knowing the WkupM3 has been
>>> loaded and booted. There are already some pending comments on code
>>> fragments from Santosh and myself, but let us know your inputs on the
>>> integration aspects on PM, remoteproc and IPC with WkupM3.
>>>
>>
>> The split was defined by putting all the application specific (to the
>> firmware in use) code in the platform pm code while trying to keep all the
>> IPC code within the wkup_m3_rproc driver. 
> 
> I don't even see that split.  I see the platform PM code directly
> setting IPC register values, but then rproc driver actually sends the
> mailbox command.

Well, really the pm code is setting a structure which gets passed to the
wkup_m3_rproc API and that's what does the write. I suppose the naming of the
structure is misleading though. However, the wkup_m3 driver isn't aware of the
protocol or what is going into these registers for the writes, the PM code
defines the usage.

> 
>> The exposed API is definitely heavily biased towards the intended
>> use-case, 
> 
> Maybe if the API was actually documented, it would be easier for us to
> review it.
> 
>> but the CM3 was designed with this exact purpose in mind and
>> not much else, and due to the limited IPC registers we have to work
>> with there isn't a whole lot of flexibility. Only IPC reg 0 is always
>> used as the resume address, the usage of the other registers is
>> defined by the firmware and pm code.
>>
>> Just as a refresher for those not familiar with it, the IPC mechanism works
>> like this: we load the ipc registers (8 for am33xx, 16 for am43xx) with any
>> information we want to communicate to the CM3, 
> 
> OK, and this happens currently in the platform PM code, right?
> 
>> then we make a dummy write to
>> the Mailbox which triggers an interrupt on the CM3, the CM3 does what it
>> needs to with the info passed in the IPC regs and writes anything it wants to
>> communicate back to these registers, and then triggers a different interrupt
>> (not related to mailbox) to let the MPU know it is done. 
> 
> And this part happens in the rproc driver, right?
> 
>> It's kind of a mess so I figured one driver was the best way to
>> encapsulate it all,
> 
> So where is this "one driver" that encapsulates it all?  
>

Sp my thinking was that I put the IPC writing in the wkup_m3_rproc driver, but
the actual configuration of what gets written in the PM platform code, to at
least try to keep things generic. Still, I do agree now that the split is not
that clear.

>> and I still had to
>> introduce callbacks within the wkup_m3_rproc driver so it could let the pm code
>> know when the FW loaded (to actually enable pm) and when an interrupt was
>> received from the wkup_m3 (so the pm code can process the response).
> 
>> As Suman stated, this sequence is part of the suspend path and also will be part
>> of the lower c-states for cpuidle, so we need something fast and lightweight.
>> RPMsg is way more than we need and it doesn't really fit the use case, so I'm
>> not sure what makes the most sense, extending remoteproc in some way to support
>> IPC communication like described above or leaving the basic FW loading
>> functionality in place in remoteproc but moving the IPC and wkup_m3
>> functionality back into the platform pm33xx code as it's so specific to that
>> use-case anyway.
> 
> I'm not advocating for using rpmsg (anymore).  But I dont' think shoving
> your rpmsg-lite IPC into your rproc driver is the right answer either
> (and Ohad's repsonse confirmed my suspicion.)
> 
> What I think you need to do (and what I've recommended at least once in
> earlier reviews) put all the (non-rproc) wkup_m3 IPC into into one
> driver and create a well-described, well-documented API that the
> platform PM code will use.
> 
> IMO, the current "split" is very difficult to read/understand, which
> means it will even more difficult to maintain.

I dont think I entirely understand your vision for the API. I see it going in
one of two directions:

PM/Application agnostic: provide ability to write/read wkup_m3 (mailbox ping is
handled automatically by the driver also) and then callbacks for rproc being
ready and handling response from wkup_m3 for the PM code to use, and that's it.
Well let the PM code sort out how it uses everything. This means that there is a
payload (similar to the structure in place now that takes the register values)
that gets configured and then the wkup_m3 driver just passes the info back and
forth between the MPU and wkup_m3 without the driver ever knowing what's
actually happening.

OR

Application specific: Provide ability to set use-case specific functionality,
i.e. wkup_m3_set_low_power_mode, wkup_m3_set_resume_address, etc... which
exposes the high level functions provided by the wkup_m3 firmware (which are
limited to the functionality in the TI provided firmware, which is all that is
intended anyway), and the pm code uses this to accomplish what it wants without
any knowledge of the actual communication or configuration.

I think the first version is more scalable and maintainable for future
applications, but perhaps I am still not aligned with your vision. Thoughts?

Regards,
Dave

> 
> Kevin
> 

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 10/11] ARM: OMAP2+: AM33XX: Basic suspend resume support
  2014-09-09 21:10             ` Kevin Hilman
@ 2014-09-16 15:08               ` Ohad Ben-Cohen
  -1 siblings, 0 replies; 106+ messages in thread
From: Ohad Ben-Cohen @ 2014-09-16 15:08 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Dave Gerlach, Suman Anna, linux-arm, linux-omap, Paul Walmsley,
	Tony Lindgren, Tero Kristo, Nishanth Menon, Russ Dill,
	Santosh Shilimkar, Daniel Mack, Benoit Cousson

On Wed, Sep 10, 2014 at 12:10 AM, Kevin Hilman <khilman@kernel.org> wrote:
> What I think you need to do (and what I've recommended at least once in
> earlier reviews) put all the (non-rproc) wkup_m3 IPC into into one
> driver and create a well-described, well-documented API that the
> platform PM code will use.
>
> IMO, the current "split" is very difficult to read/understand, which
> means it will even more difficult to maintain.

I strongly agree.

A remoteproc driver should generally only register its
hardware-specific implementation of the rproc_ops via the remoteproc
framework. It is not expected to expose public API of its own - that's
why we have the generic remoteproc layer for. It makes perfect sense
not to use rpmsg for communications if it's not lightweight enough,
but exposing a new set of IPC API should take place in a separate
well-documented driver.

In addition, the suggested remoteproc driver seems to act both as a
low-level hardware-specific driver that plugs into remoteproc (by
registering rproc_ops via the remoteproc framework), and also as a
high-level driver that utilizes the remoteproc public API (by calling
rproc_boot). This alone calls for scrutinizing the design as this is
not very intuitive.

At least for the remoteproc part: if you could provide a simple and
straight-forward remoteproc driver that just implements the rproc_ops,
this could be merged very quickly. The rest of the code most probably
belongs in a different entity, just like Kevin suggested.

Thanks,
Ohad.

^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 10/11] ARM: OMAP2+: AM33XX: Basic suspend resume support
@ 2014-09-16 15:08               ` Ohad Ben-Cohen
  0 siblings, 0 replies; 106+ messages in thread
From: Ohad Ben-Cohen @ 2014-09-16 15:08 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Sep 10, 2014 at 12:10 AM, Kevin Hilman <khilman@kernel.org> wrote:
> What I think you need to do (and what I've recommended at least once in
> earlier reviews) put all the (non-rproc) wkup_m3 IPC into into one
> driver and create a well-described, well-documented API that the
> platform PM code will use.
>
> IMO, the current "split" is very difficult to read/understand, which
> means it will even more difficult to maintain.

I strongly agree.

A remoteproc driver should generally only register its
hardware-specific implementation of the rproc_ops via the remoteproc
framework. It is not expected to expose public API of its own - that's
why we have the generic remoteproc layer for. It makes perfect sense
not to use rpmsg for communications if it's not lightweight enough,
but exposing a new set of IPC API should take place in a separate
well-documented driver.

In addition, the suggested remoteproc driver seems to act both as a
low-level hardware-specific driver that plugs into remoteproc (by
registering rproc_ops via the remoteproc framework), and also as a
high-level driver that utilizes the remoteproc public API (by calling
rproc_boot). This alone calls for scrutinizing the design as this is
not very intuitive.

At least for the remoteproc part: if you could provide a simple and
straight-forward remoteproc driver that just implements the rproc_ops,
this could be merged very quickly. The rest of the code most probably
belongs in a different entity, just like Kevin suggested.

Thanks,
Ohad.

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 10/11] ARM: OMAP2+: AM33XX: Basic suspend resume support
  2014-09-16 15:08               ` Ohad Ben-Cohen
@ 2014-09-16 16:14                 ` Suman Anna
  -1 siblings, 0 replies; 106+ messages in thread
From: Suman Anna @ 2014-09-16 16:14 UTC (permalink / raw)
  To: Ohad Ben-Cohen, Kevin Hilman
  Cc: Dave Gerlach, linux-arm, linux-omap, Paul Walmsley,
	Tony Lindgren, Tero Kristo, Nishanth Menon, Russ Dill,
	Santosh Shilimkar, Daniel Mack, Benoit Cousson

Hi Ohad,

On 09/16/2014 10:08 AM, Ohad Ben-Cohen wrote:
> On Wed, Sep 10, 2014 at 12:10 AM, Kevin Hilman <khilman@kernel.org> wrote:
>> What I think you need to do (and what I've recommended at least once in
>> earlier reviews) put all the (non-rproc) wkup_m3 IPC into into one
>> driver and create a well-described, well-documented API that the
>> platform PM code will use.
>>
>> IMO, the current "split" is very difficult to read/understand, which
>> means it will even more difficult to maintain.
> 
> I strongly agree.
> 
> A remoteproc driver should generally only register its
> hardware-specific implementation of the rproc_ops via the remoteproc
> framework. It is not expected to expose public API of its own - that's
> why we have the generic remoteproc layer for. It makes perfect sense
> not to use rpmsg for communications if it's not lightweight enough,
> but exposing a new set of IPC API should take place in a separate
> well-documented driver.
> 
> In addition, the suggested remoteproc driver seems to act both as a
> low-level hardware-specific driver that plugs into remoteproc (by
> registering rproc_ops via the remoteproc framework), and also as a
> high-level driver that utilizes the remoteproc public API (by calling
> rproc_boot). This alone calls for scrutinizing the design as this is
> not very intuitive.

The current remoteproc infrastructure automatically calls rproc_boot
only as part of the rpmsg/virtio stack (in remoteproc_virtio.c), but
since the WkupM3 does not use rpmsg, there is no automatic booting of
the WkupM3 processor. This is the reason why rproc_boot is called as
part of the WkupM3 driver probe sequence. What are your concerns here,
and if you think this is not the right place do invoke rproc_boot, where
do you expect it to be called? Also do note that, there is no way
at present to retrieve the struct rproc for a given remote processor, to
be able to invoke the rproc_boot from a different layer.

> At least for the remoteproc part: if you could provide a simple and
> straight-forward remoteproc driver that just implements the rproc_ops,
> this could be merged very quickly. The rest of the code most probably
> belongs in a different entity, just like Kevin suggested.
> 

Splitting this would still require some kind of notifier from remoteproc
for the other layers for them to know that the WkupM3 remote processor
has been loaded and booted successfully. This is also done as part of
the WkupM3 driver at the moment.

regards
Suman


^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 10/11] ARM: OMAP2+: AM33XX: Basic suspend resume support
@ 2014-09-16 16:14                 ` Suman Anna
  0 siblings, 0 replies; 106+ messages in thread
From: Suman Anna @ 2014-09-16 16:14 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Ohad,

On 09/16/2014 10:08 AM, Ohad Ben-Cohen wrote:
> On Wed, Sep 10, 2014 at 12:10 AM, Kevin Hilman <khilman@kernel.org> wrote:
>> What I think you need to do (and what I've recommended at least once in
>> earlier reviews) put all the (non-rproc) wkup_m3 IPC into into one
>> driver and create a well-described, well-documented API that the
>> platform PM code will use.
>>
>> IMO, the current "split" is very difficult to read/understand, which
>> means it will even more difficult to maintain.
> 
> I strongly agree.
> 
> A remoteproc driver should generally only register its
> hardware-specific implementation of the rproc_ops via the remoteproc
> framework. It is not expected to expose public API of its own - that's
> why we have the generic remoteproc layer for. It makes perfect sense
> not to use rpmsg for communications if it's not lightweight enough,
> but exposing a new set of IPC API should take place in a separate
> well-documented driver.
> 
> In addition, the suggested remoteproc driver seems to act both as a
> low-level hardware-specific driver that plugs into remoteproc (by
> registering rproc_ops via the remoteproc framework), and also as a
> high-level driver that utilizes the remoteproc public API (by calling
> rproc_boot). This alone calls for scrutinizing the design as this is
> not very intuitive.

The current remoteproc infrastructure automatically calls rproc_boot
only as part of the rpmsg/virtio stack (in remoteproc_virtio.c), but
since the WkupM3 does not use rpmsg, there is no automatic booting of
the WkupM3 processor. This is the reason why rproc_boot is called as
part of the WkupM3 driver probe sequence. What are your concerns here,
and if you think this is not the right place do invoke rproc_boot, where
do you expect it to be called? Also do note that, there is no way
at present to retrieve the struct rproc for a given remote processor, to
be able to invoke the rproc_boot from a different layer.

> At least for the remoteproc part: if you could provide a simple and
> straight-forward remoteproc driver that just implements the rproc_ops,
> this could be merged very quickly. The rest of the code most probably
> belongs in a different entity, just like Kevin suggested.
> 

Splitting this would still require some kind of notifier from remoteproc
for the other layers for them to know that the WkupM3 remote processor
has been loaded and booted successfully. This is also done as part of
the WkupM3 driver at the moment.

regards
Suman

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 10/11] ARM: OMAP2+: AM33XX: Basic suspend resume support
  2014-09-16 16:14                 ` Suman Anna
@ 2014-09-17 13:37                   ` Ohad Ben-Cohen
  -1 siblings, 0 replies; 106+ messages in thread
From: Ohad Ben-Cohen @ 2014-09-17 13:37 UTC (permalink / raw)
  To: Suman Anna
  Cc: Kevin Hilman, Dave Gerlach, linux-arm, linux-omap, Paul Walmsley,
	Tony Lindgren, Tero Kristo, Nishanth Menon, Russ Dill,
	Santosh Shilimkar, Daniel Mack, Benoit Cousson

Hi Suman,

On Tue, Sep 16, 2014 at 7:14 PM, Suman Anna <s-anna@ti.com> wrote:
> The current remoteproc infrastructure automatically calls rproc_boot
> only as part of the rpmsg/virtio stack (in remoteproc_virtio.c), but
> since the WkupM3 does not use rpmsg, there is no automatic booting of
> the WkupM3 processor.  This is the reason why rproc_boot is called as
> part of the WkupM3 driver probe sequence. What are your concerns here,
> and if you think this is not the right place do invoke rproc_boot, where
> do you expect it to be called?

The remoteproc layer is meant to hide hardware-specific details, and
allow high-level hardware-agnostic code to boot a remote processor, in
order to achieve some task, without even caring what kind of hardware
it is booting.

So generally we have some consumer driver asking remoteproc to boot a
remote processor, and in turn, remoteproc asking a hardware-specific
vendor driver to take care of the hardware details like actually
taking the remote processor out of reset.

The consumer driver is some code that deals with some hardware
agnostic task. Today the only consumer we have is rpmsg, so it's
perfectly reasonable if remoteproc needs to be adapted a bit to
accommodate other consumers as they show up.

Can you think of any component in your code that is not necessarily
hardware specific, and that one day might be useful for other vendors?
Can you describe the task you're trying to achieve, the entities
involved and the flow between them?

> Also do note that, there is no way
> at present to retrieve the struct rproc for a given remote processor, to
> be able to invoke the rproc_boot from a different layer.

It's perfectly ok to make struct rproc public if we have a consumer
that requires it.

> Splitting this would still require some kind of notifier from remoteproc
> for the other layers for them to know that the WkupM3 remote processor
> has been loaded and booted successfully. This is also done as part of
> the WkupM3 driver at the moment.

Are there entities, other than the one that calls rproc_boot, that
needs to know that the WkupM3 is up? if so, let's discuss who should
notify them - remoteproc or the actual invoker of rproc_boot. It
probably depends on who those entities are and what's their relation,
if any, to the WkupM3 driver.

Thanks,
Ohad.

^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 10/11] ARM: OMAP2+: AM33XX: Basic suspend resume support
@ 2014-09-17 13:37                   ` Ohad Ben-Cohen
  0 siblings, 0 replies; 106+ messages in thread
From: Ohad Ben-Cohen @ 2014-09-17 13:37 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Suman,

On Tue, Sep 16, 2014 at 7:14 PM, Suman Anna <s-anna@ti.com> wrote:
> The current remoteproc infrastructure automatically calls rproc_boot
> only as part of the rpmsg/virtio stack (in remoteproc_virtio.c), but
> since the WkupM3 does not use rpmsg, there is no automatic booting of
> the WkupM3 processor.  This is the reason why rproc_boot is called as
> part of the WkupM3 driver probe sequence. What are your concerns here,
> and if you think this is not the right place do invoke rproc_boot, where
> do you expect it to be called?

The remoteproc layer is meant to hide hardware-specific details, and
allow high-level hardware-agnostic code to boot a remote processor, in
order to achieve some task, without even caring what kind of hardware
it is booting.

So generally we have some consumer driver asking remoteproc to boot a
remote processor, and in turn, remoteproc asking a hardware-specific
vendor driver to take care of the hardware details like actually
taking the remote processor out of reset.

The consumer driver is some code that deals with some hardware
agnostic task. Today the only consumer we have is rpmsg, so it's
perfectly reasonable if remoteproc needs to be adapted a bit to
accommodate other consumers as they show up.

Can you think of any component in your code that is not necessarily
hardware specific, and that one day might be useful for other vendors?
Can you describe the task you're trying to achieve, the entities
involved and the flow between them?

> Also do note that, there is no way
> at present to retrieve the struct rproc for a given remote processor, to
> be able to invoke the rproc_boot from a different layer.

It's perfectly ok to make struct rproc public if we have a consumer
that requires it.

> Splitting this would still require some kind of notifier from remoteproc
> for the other layers for them to know that the WkupM3 remote processor
> has been loaded and booted successfully. This is also done as part of
> the WkupM3 driver at the moment.

Are there entities, other than the one that calls rproc_boot, that
needs to know that the WkupM3 is up? if so, let's discuss who should
notify them - remoteproc or the actual invoker of rproc_boot. It
probably depends on who those entities are and what's their relation,
if any, to the WkupM3 driver.

Thanks,
Ohad.

^ permalink raw reply	[flat|nested] 106+ messages in thread

* Re: [PATCH v4 10/11] ARM: OMAP2+: AM33XX: Basic suspend resume support
  2014-09-17 13:37                   ` Ohad Ben-Cohen
@ 2014-09-25 19:42                     ` Dave Gerlach
  -1 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-09-25 19:42 UTC (permalink / raw)
  To: Ohad Ben-Cohen, Suman Anna
  Cc: Kevin Hilman, linux-arm, linux-omap, Paul Walmsley,
	Tony Lindgren, Tero Kristo, Nishanth Menon, Russ Dill,
	Santosh Shilimkar, Daniel Mack, Benoit Cousson

Ohad,

On 09/17/2014 08:37 AM, Ohad Ben-Cohen wrote:> Hi Suman,
>
> On Tue, Sep 16, 2014 at 7:14 PM, Suman Anna <s-anna@ti.com> wrote:
>> The current remoteproc infrastructure automatically calls rproc_boot only
>> as part of the rpmsg/virtio stack (in remoteproc_virtio.c), but since the
>> WkupM3 does not use rpmsg, there is no automatic booting of the WkupM3
>> processor.  This is the reason why rproc_boot is called as part of the
>> WkupM3 driver probe sequence. What are your concerns here, and if you think
>> this is not the right place do invoke rproc_boot, where do you expect it to
>> be called?
>
> The remoteproc layer is meant to hide hardware-specific details, and allow
> high-level hardware-agnostic code to boot a remote processor, in order to
> achieve some task, without even caring what kind of hardware it is booting.
>
> So generally we have some consumer driver asking remoteproc to boot a remote
>  processor, and in turn, remoteproc asking a hardware-specific vendor driver
>  to take care of the hardware details like actually taking the remote
> processor out of reset.
>
> The consumer driver is some code that deals with some hardware agnostic task.
> Today the only consumer we have is rpmsg, so it's perfectly reasonable if
> remoteproc needs to be adapted a bit to accommodate other consumers as they
> show up.
>
> Can you think of any component in your code that is not necessarily hardware
>  specific, and that one day might be useful for other vendors? Can you
> describe the task you're trying to achieve, the entities involved and the
> flow between them?
>
>> Also do note that, there is no way at present to retrieve the struct rproc
>>  for a given remote processor, to be able to invoke the rproc_boot from a
>> different layer.
>
> It's perfectly ok to make struct rproc public if we have a consumer that
> requires it.
>
>> Splitting this would still require some kind of notifier from remoteproc
>> for the other layers for them to know that the WkupM3 remote processor has
>>  been loaded and booted successfully. This is also done as part of the
>> WkupM3 driver at the moment.
>
> Are there entities, other than the one that calls rproc_boot, that needs to
> know that the WkupM3 is up? if so, let's discuss who should notify them -
> remoteproc or the actual invoker of rproc_boot. It probably depends on who
> those entities are and what's their relation, if any, to the WkupM3 driver.

There are three layers at play here. The pm layer, the ipc layer, and the rproc
layer. As we know, currently the problem is that the ipc and rproc layer are
combined.

PM on am33xx is ENTIRELY dependent on the wkup_m3. It can't enable any PM OPs
until the FW is ready. So that is one place where the PM layer must be notified.
The other instance is for IPC, the wkup_m3 triggers an interrupt back to the MPU
when it it is done with it's work after an IPC event (explained more later), so
the PM code must be notified of this as well.

Here is the exact flow between PM, IPC, and wkup_m3 during a suspend cycle:

Boot:
*Firmware gets loaded and wkup_m3 has hard reset deasserted and starts executing
(Definite wkup_m3 rproc responsibility).

*PM code is notified that wkup_m3 is awake (currently by wkup_m3_rproc start
hook) and calls into pm code with rproc_ready hook and uses IPC registers (which
were filled by wkup_m3) to check version number and make sure it's compatible.
This sounds like IPC layer responsibility, but does the IPC layer just handle
reading back the registers and give these values for the PM layer to interpret?
Or does it do the interpreting and gives back a specific PM value? (In this case
fw version.)

Suspend:
*PM code tells wkup_m3_rproc driver to place the command for the desired PM
state in the IPC registers along with any other info needed for suspend
(determined by PM code) and then ping the wkup_m3 using the mailbox, which is
just a dummy write, the mailbox is not actually used just the interrupt. Once
this happens the wkup_m3 itself runs, prepares for the desired PM state, and
triggers it's own wkup_m3 interrupt, unrelated to the mailbox interrupt.

*Once this interrupt is sent the wkup_m3_rproc has the irq handler for the
interrupt and calls into pm code using txev_handler hook I defined, and with
this, the PM code proceeds, and the wkup_m3 just waits for MPU clock gate
(unrelated to IPC or wkup_m3, triggered by other SoC functionality).

*On resume, no interrupt is generated from wkup_m3 but the PM code, in the
standard resume path, reads from the IPC registers to check a status value
(without receiving an interrupt like before, just assumes there is data because
a resume is happening, interrupts are disabled still at this point) and then the
PM code interprets the status value and a wake source value and does whatever it
wants with it. This also sounds like IPC layer responsibility but again the
question comes up do we provide generic IPC reg reading functionality or let the
IPC layer do more, like with the version number read?

I am not sure exactly how the layers should be divided. Does remoteproc notify
an IPC layer which notifies the PM code that the rproc is booted and the IPC
data is ready? Or does the remoteproc notify PM code directly and then that uses
the IPC layer to read the IPC registers?

However the wkup_m3 does trigger the wkup_m3 interrupt after it is done booting,
so perhaps that could be used as the trigger instead, which fits better into the
IPC layer. There are two different hooks back to PM code. One that indicates
when wkup_m3 has booted, and one that indicates when the wkup_m3 interrupt has
been received. As I mentioned before, perhaps we can get away with just using
the wkup_m3 interrupt to indicate when the rproc has booted because it won't be
triggered unless the fw runs from the start.

Perhaps we can live with one notifier hook (for the interrupt from the wkup_m3),
but I am still unclear on how generic the IPC layer should be. A very generic
layer that just reads the IPC registers and handlers the interrupts could be
reused, leaving the PM code to interprets those values while a more PM specific
IPC layer would have to evolve with the PM layer as functionality changes, so I
would definitely lean towards the highly generic IPC layer.

Regards,
Dave


>
> Thanks, Ohad.
>


^ permalink raw reply	[flat|nested] 106+ messages in thread

* [PATCH v4 10/11] ARM: OMAP2+: AM33XX: Basic suspend resume support
@ 2014-09-25 19:42                     ` Dave Gerlach
  0 siblings, 0 replies; 106+ messages in thread
From: Dave Gerlach @ 2014-09-25 19:42 UTC (permalink / raw)
  To: linux-arm-kernel

Ohad,

On 09/17/2014 08:37 AM, Ohad Ben-Cohen wrote:> Hi Suman,
>
> On Tue, Sep 16, 2014 at 7:14 PM, Suman Anna <s-anna@ti.com> wrote:
>> The current remoteproc infrastructure automatically calls rproc_boot only
>> as part of the rpmsg/virtio stack (in remoteproc_virtio.c), but since the
>> WkupM3 does not use rpmsg, there is no automatic booting of the WkupM3
>> processor.  This is the reason why rproc_boot is called as part of the
>> WkupM3 driver probe sequence. What are your concerns here, and if you think
>> this is not the right place do invoke rproc_boot, where do you expect it to
>> be called?
>
> The remoteproc layer is meant to hide hardware-specific details, and allow
> high-level hardware-agnostic code to boot a remote processor, in order to
> achieve some task, without even caring what kind of hardware it is booting.
>
> So generally we have some consumer driver asking remoteproc to boot a remote
>  processor, and in turn, remoteproc asking a hardware-specific vendor driver
>  to take care of the hardware details like actually taking the remote
> processor out of reset.
>
> The consumer driver is some code that deals with some hardware agnostic task.
> Today the only consumer we have is rpmsg, so it's perfectly reasonable if
> remoteproc needs to be adapted a bit to accommodate other consumers as they
> show up.
>
> Can you think of any component in your code that is not necessarily hardware
>  specific, and that one day might be useful for other vendors? Can you
> describe the task you're trying to achieve, the entities involved and the
> flow between them?
>
>> Also do note that, there is no way at present to retrieve the struct rproc
>>  for a given remote processor, to be able to invoke the rproc_boot from a
>> different layer.
>
> It's perfectly ok to make struct rproc public if we have a consumer that
> requires it.
>
>> Splitting this would still require some kind of notifier from remoteproc
>> for the other layers for them to know that the WkupM3 remote processor has
>>  been loaded and booted successfully. This is also done as part of the
>> WkupM3 driver at the moment.
>
> Are there entities, other than the one that calls rproc_boot, that needs to
> know that the WkupM3 is up? if so, let's discuss who should notify them -
> remoteproc or the actual invoker of rproc_boot. It probably depends on who
> those entities are and what's their relation, if any, to the WkupM3 driver.

There are three layers at play here. The pm layer, the ipc layer, and the rproc
layer. As we know, currently the problem is that the ipc and rproc layer are
combined.

PM on am33xx is ENTIRELY dependent on the wkup_m3. It can't enable any PM OPs
until the FW is ready. So that is one place where the PM layer must be notified.
The other instance is for IPC, the wkup_m3 triggers an interrupt back to the MPU
when it it is done with it's work after an IPC event (explained more later), so
the PM code must be notified of this as well.

Here is the exact flow between PM, IPC, and wkup_m3 during a suspend cycle:

Boot:
*Firmware gets loaded and wkup_m3 has hard reset deasserted and starts executing
(Definite wkup_m3 rproc responsibility).

*PM code is notified that wkup_m3 is awake (currently by wkup_m3_rproc start
hook) and calls into pm code with rproc_ready hook and uses IPC registers (which
were filled by wkup_m3) to check version number and make sure it's compatible.
This sounds like IPC layer responsibility, but does the IPC layer just handle
reading back the registers and give these values for the PM layer to interpret?
Or does it do the interpreting and gives back a specific PM value? (In this case
fw version.)

Suspend:
*PM code tells wkup_m3_rproc driver to place the command for the desired PM
state in the IPC registers along with any other info needed for suspend
(determined by PM code) and then ping the wkup_m3 using the mailbox, which is
just a dummy write, the mailbox is not actually used just the interrupt. Once
this happens the wkup_m3 itself runs, prepares for the desired PM state, and
triggers it's own wkup_m3 interrupt, unrelated to the mailbox interrupt.

*Once this interrupt is sent the wkup_m3_rproc has the irq handler for the
interrupt and calls into pm code using txev_handler hook I defined, and with
this, the PM code proceeds, and the wkup_m3 just waits for MPU clock gate
(unrelated to IPC or wkup_m3, triggered by other SoC functionality).

*On resume, no interrupt is generated from wkup_m3 but the PM code, in the
standard resume path, reads from the IPC registers to check a status value
(without receiving an interrupt like before, just assumes there is data because
a resume is happening, interrupts are disabled still at this point) and then the
PM code interprets the status value and a wake source value and does whatever it
wants with it. This also sounds like IPC layer responsibility but again the
question comes up do we provide generic IPC reg reading functionality or let the
IPC layer do more, like with the version number read?

I am not sure exactly how the layers should be divided. Does remoteproc notify
an IPC layer which notifies the PM code that the rproc is booted and the IPC
data is ready? Or does the remoteproc notify PM code directly and then that uses
the IPC layer to read the IPC registers?

However the wkup_m3 does trigger the wkup_m3 interrupt after it is done booting,
so perhaps that could be used as the trigger instead, which fits better into the
IPC layer. There are two different hooks back to PM code. One that indicates
when wkup_m3 has booted, and one that indicates when the wkup_m3 interrupt has
been received. As I mentioned before, perhaps we can get away with just using
the wkup_m3 interrupt to indicate when the rproc has booted because it won't be
triggered unless the fw runs from the start.

Perhaps we can live with one notifier hook (for the interrupt from the wkup_m3),
but I am still unclear on how generic the IPC layer should be. A very generic
layer that just reads the IPC registers and handlers the interrupts could be
reused, leaving the PM code to interprets those values while a more PM specific
IPC layer would have to evolve with the PM layer as functionality changes, so I
would definitely lean towards the highly generic IPC layer.

Regards,
Dave


>
> Thanks, Ohad.
>

^ permalink raw reply	[flat|nested] 106+ messages in thread

end of thread, other threads:[~2014-09-25 19:43 UTC | newest]

Thread overview: 106+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-07-11  2:55 [PATCH v4 00/11] ARM: OMAP2+: AM33XX: Add suspend-resume support Dave Gerlach
2014-07-11  2:55 ` Dave Gerlach
2014-07-11  2:55 ` [PATCH v4 01/11] ARM: omap: edma: add suspend suspend/resume hooks Dave Gerlach
2014-07-11  2:55   ` Dave Gerlach
2014-07-14 11:05   ` Tony Lindgren
2014-07-14 11:05     ` Tony Lindgren
2014-07-14 17:47     ` Dave Gerlach
2014-07-14 17:47       ` Dave Gerlach
2014-07-11  2:55 ` [PATCH v4 02/11] memory: emif: Move EMIF register defines to include/linux/ Dave Gerlach
2014-07-11  2:55   ` Dave Gerlach
2014-07-14 11:12   ` Tony Lindgren
2014-07-14 11:12     ` Tony Lindgren
2014-07-14 17:42     ` Dave Gerlach
2014-07-14 17:42       ` Dave Gerlach
2014-07-15  6:38       ` Tony Lindgren
2014-07-15  6:38         ` Tony Lindgren
2014-07-16  2:44         ` Dave Gerlach
2014-07-16  2:44           ` Dave Gerlach
2014-07-16  8:33           ` Tony Lindgren
2014-07-16  8:33             ` Tony Lindgren
2014-07-11  2:55 ` [PATCH v4 03/11] ARM: OMAP2+: timer: Add suspend-resume callbacks for clkevent device Dave Gerlach
2014-07-11  2:55   ` Dave Gerlach
2014-07-14 11:15   ` Tony Lindgren
2014-07-14 11:15     ` Tony Lindgren
2014-07-14 14:37     ` Santosh Shilimkar
2014-07-14 14:37       ` Santosh Shilimkar
2014-07-14 17:42       ` Dave Gerlach
2014-07-14 17:42         ` Dave Gerlach
2014-07-15  6:48         ` Tony Lindgren
2014-07-15  6:48           ` Tony Lindgren
2014-07-15 19:10           ` Dave Gerlach
2014-07-15 19:10             ` Dave Gerlach
2014-07-16 20:17           ` Dave Gerlach
2014-07-16 20:17             ` Dave Gerlach
2014-07-17  8:16             ` Tony Lindgren
2014-07-17  8:16               ` Tony Lindgren
2014-07-11  2:55 ` [PATCH v4 04/11] ARM: OMAP2+: Use pdata-quirks for wkup_m3 deassert_hardreset Dave Gerlach
2014-07-11  2:55   ` Dave Gerlach
2014-07-14 18:48   ` Suman Anna
2014-07-14 18:48     ` Suman Anna
2014-07-11  2:55 ` [PATCH v4 05/11] Documentation: dt: add ti,am3353_wkup_m3 bindings Dave Gerlach
2014-07-11  2:55   ` Dave Gerlach
2014-07-14 14:41   ` Santosh Shilimkar
2014-07-14 14:41     ` [PATCH v4 05/11] Documentation: dt: add ti, am3353_wkup_m3 bindings Santosh Shilimkar
2014-07-14 16:33     ` [PATCH v4 05/11] Documentation: dt: add ti,am3353_wkup_m3 bindings Suman Anna
2014-07-14 16:33       ` [PATCH v4 05/11] Documentation: dt: add ti, am3353_wkup_m3 bindings Suman Anna
2014-07-14 17:45       ` [PATCH v4 05/11] Documentation: dt: add ti,am3353_wkup_m3 bindings Dave Gerlach
2014-07-14 17:45         ` [PATCH v4 05/11] Documentation: dt: add ti, am3353_wkup_m3 bindings Dave Gerlach
2014-07-11  2:55 ` [PATCH v4 06/11] remoteproc: wkup_m3: Add wkup_m3 remote proc driver Dave Gerlach
2014-07-11  2:55   ` Dave Gerlach
2014-07-14 14:54   ` Santosh Shilimkar
2014-07-14 14:54     ` Santosh Shilimkar
2014-07-14 17:43     ` Dave Gerlach
2014-07-14 17:43       ` Dave Gerlach
2014-07-14 19:07     ` Suman Anna
2014-07-14 19:07       ` Suman Anna
2014-07-14 21:09       ` Dave Gerlach
2014-07-14 21:09         ` Dave Gerlach
2014-07-11  2:55 ` [PATCH v4 07/11] ARM: dts: am33xx: Update wkup_m3 node Dave Gerlach
2014-07-11  2:55   ` Dave Gerlach
2014-07-11  2:55 ` [PATCH v4 08/11] ARM: OMAP2+: AM33XX: Reserve memory to comply with EMIF spec Dave Gerlach
2014-07-11  2:55   ` Dave Gerlach
2014-07-11  2:55 ` [PATCH v4 09/11] ARM: OMAP2+: AM33XX: Add assembly code for PM operations Dave Gerlach
2014-07-11  2:55   ` Dave Gerlach
2014-07-11  2:55 ` [PATCH v4 10/11] ARM: OMAP2+: AM33XX: Basic suspend resume support Dave Gerlach
2014-07-11  2:55   ` Dave Gerlach
2014-09-08 22:30   ` Kevin Hilman
2014-09-08 22:30     ` Kevin Hilman
2014-09-09 10:31     ` Ohad Ben-Cohen
2014-09-09 10:31       ` Ohad Ben-Cohen
2014-09-09 19:59       ` Suman Anna
2014-09-09 19:59         ` Suman Anna
2014-09-09 20:28         ` Dave Gerlach
2014-09-09 20:28           ` Dave Gerlach
2014-09-09 21:10           ` Kevin Hilman
2014-09-09 21:10             ` Kevin Hilman
2014-09-10 21:19             ` Dave Gerlach
2014-09-10 21:19               ` Dave Gerlach
2014-09-16 15:08             ` Ohad Ben-Cohen
2014-09-16 15:08               ` Ohad Ben-Cohen
2014-09-16 16:14               ` Suman Anna
2014-09-16 16:14                 ` Suman Anna
2014-09-17 13:37                 ` Ohad Ben-Cohen
2014-09-17 13:37                   ` Ohad Ben-Cohen
2014-09-25 19:42                   ` Dave Gerlach
2014-09-25 19:42                     ` Dave Gerlach
2014-07-11  2:55 ` [PATCH v4 11/11] ARM: OMAP2+: AM33XX: Hookup AM33XX PM code into OMAP builds Dave Gerlach
2014-07-11  2:55   ` Dave Gerlach
2014-07-14 11:21   ` Tony Lindgren
2014-07-14 11:21     ` Tony Lindgren
2014-07-14 17:46     ` Dave Gerlach
2014-07-14 17:46       ` Dave Gerlach
2014-07-11  7:59 ` [PATCH v4 00/11] ARM: OMAP2+: AM33XX: Add suspend-resume support Daniel Mack
2014-07-11  7:59   ` Daniel Mack
2014-07-11 17:24   ` Dave Gerlach
2014-07-11 17:24     ` Dave Gerlach
2014-07-11 15:30 ` Andre Heider
2014-07-11 15:30   ` Andre Heider
2014-07-11 17:31   ` Dave Gerlach
2014-07-11 17:31     ` Dave Gerlach
2014-07-14  9:37     ` Andre Heider
2014-07-14  9:37       ` Andre Heider
2014-07-14 11:24 ` Tony Lindgren
2014-07-14 11:24   ` Tony Lindgren
2014-07-14 17:47   ` Dave Gerlach
2014-07-14 17:47     ` Dave Gerlach

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.