linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 0/3] remoteproc: Introduce wkup_m3_rproc driver
@ 2014-11-26 21:27 Dave Gerlach
  2014-11-26 21:27 ` [RFC PATCH 1/3] ARM: OMAP2+: Use pdata-quirks for wkup_m3 deassert_hardreset Dave Gerlach
                   ` (8 more replies)
  0 siblings, 9 replies; 12+ messages in thread
From: Dave Gerlach @ 2014-11-26 21:27 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, linux-omap, devicetree
  Cc: Tony Lindgren, Paul Walmsley, Benoit Cousson, Kevin Hilman,
	Ohad Ben-Cohen, Suman Anna, Dave Gerlach

Hi,
This patch series adds a wkup_m3_rproc driver for TI AM335x SoCs.
This family of SoCs contains an ARM Cortex M3 coprocessor that is
responsible for low-level power tasks that cannot be handled by the
main ARM Cortex A8 so firmware running from the CM3 can be used instead.
This driver handles loading of the firmware and reset of the CM3
once it is booted.

This patch was split off from v4 of the am335x suspend series, found
here [1]. Because of this it is a dependency for v5 of that series. I have
pushed a branch based on v3.18-rc6 containing all dependencies here [2] for
am33xx suspend for a higher level view of the entire series of patch sets.

This patch set depends on series "couple of generic remoteproc enhancements"
by Suman Anna found here [3] (Included already in previously mentioned branch)

Regards,
Dave

[1] http://www.spinics.net/lists/linux-omap/msg109331.html
[2] https://github.com/dgerlach/linux-pm/tree/rfc-pm-am335x-v3.18-rc6
[3] http://www.spinics.net/lists/arm-kernel/msg362961.html

Dave Gerlach (3):
  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

 .../bindings/remoteproc/wkup_m3_rproc.txt          |  32 ++++
 arch/arm/mach-omap2/pdata-quirks.c                 |  13 ++
 drivers/remoteproc/Kconfig                         |  12 ++
 drivers/remoteproc/Makefile                        |   1 +
 drivers/remoteproc/wkup_m3_rproc.c                 | 175 +++++++++++++++++++++
 include/linux/platform_data/wkup_m3.h              |  23 +++
 6 files changed, 256 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
 create mode 100644 drivers/remoteproc/wkup_m3_rproc.c
 create mode 100644 include/linux/platform_data/wkup_m3.h

-- 
2.1.0


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

* [RFC PATCH 1/3] ARM: OMAP2+: Use pdata-quirks for wkup_m3 deassert_hardreset
  2014-11-26 21:27 [RFC PATCH 0/3] remoteproc: Introduce wkup_m3_rproc driver Dave Gerlach
@ 2014-11-26 21:27 ` Dave Gerlach
  2014-11-26 21:27 ` [RFC PATCH 2/3] Documentation: dt: add ti,am3353-wkup-m3 bindings Dave Gerlach
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Dave Gerlach @ 2014-11-26 21:27 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, linux-omap, devicetree
  Cc: Tony Lindgren, Paul Walmsley, Benoit Cousson, Kevin Hilman,
	Ohad Ben-Cohen, Suman Anna, Dave Gerlach

Use pdata-quirks to provide platform data required for reset
of the wkup_m3 during boot.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
---
 arch/arm/mach-omap2/pdata-quirks.c    | 13 +++++++++++++
 include/linux/platform_data/wkup_m3.h | 23 +++++++++++++++++++++++
 2 files changed, 36 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 cec9d6c..1c0bcdb 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"
@@ -260,6 +261,14 @@ static void __init omap3_tao3530_legacy_init(void)
 }
 #endif /* CONFIG_ARCH_OMAP3 */
 
+#ifdef CONFIG_SOC_AM33XX
+static struct wkup_m3_platform_data wkup_m3_data = {
+	.reset_name = "wkup_m3",
+	.assert_reset = omap_device_assert_hardreset,
+	.deassert_reset = omap_device_deassert_hardreset,
+};
+#endif
+
 #ifdef CONFIG_ARCH_OMAP4
 static void __init omap4_sdp_legacy_init(void)
 {
@@ -355,6 +364,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..6ee33d7
--- /dev/null
+++ b/include/linux/platform_data/wkup_m3.h
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+#ifndef _LINUX_PLATFORM_DATA_WKUP_M3_H
+#define _LINUX_PLATFORM_DATA_WKUP_M3_H
+
+struct wkup_m3_platform_data {
+	const char *reset_name;
+
+	int (*assert_reset)(struct platform_device *pdev, const char *name);
+	int (*deassert_reset)(struct platform_device *pdev, const char *name);
+};
+
+#endif /* _LINUX_PLATFORM_DATA_WKUP_M3_H */
-- 
2.1.0


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

* [RFC PATCH 2/3] Documentation: dt: add ti,am3353-wkup-m3 bindings
  2014-11-26 21:27 [RFC PATCH 0/3] remoteproc: Introduce wkup_m3_rproc driver Dave Gerlach
  2014-11-26 21:27 ` [RFC PATCH 1/3] ARM: OMAP2+: Use pdata-quirks for wkup_m3 deassert_hardreset Dave Gerlach
@ 2014-11-26 21:27 ` Dave Gerlach
  2014-11-26 21:27 ` [RFC PATCH 3/3] remoteproc: wkup_m3: Add wkup_m3 remote proc driver Dave Gerlach
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Dave Gerlach @ 2014-11-26 21:27 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, linux-omap, devicetree
  Cc: Tony Lindgren, Paul Walmsley, Benoit Cousson, Kevin Hilman,
	Ohad Ben-Cohen, Suman Anna, Dave Gerlach

Add the device tree bindings document for ti,am3353-wkup-m3 which is
used by the wkup_m3_rproc driver.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
---
 .../bindings/remoteproc/wkup_m3_rproc.txt          | 32 ++++++++++++++++++++++
 1 file changed, 32 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..4d64a23
--- /dev/null
+++ b/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt
@@ -0,0 +1,32 @@
+Wakeup M3 Remote Proc Driver
+=====================
+
+TI AMx3 family of 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. The wkup_m3 remoteproc driver handles
+the loading of the firmware and booting of the CM3.
+
+Wkup M3 Device Node:
+====================
+A wkup_m3 device node is used to represent a wakeup M3 IP instance within
+a SoC.
+
+Required properties:
+--------------------
+- compatible:		Should be "ti,am3353-wkup-m3" for AM33xx SoCs
+- reg:			Contains the wkup_m3 register address ranges for
+			umem and dmem.
+- 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.
+
+Example:
+--------
+/* AM33xx */
+wkup_m3: wkup_m3@44d00000 {
+	compatible = "ti,am3353-wkup-m3";
+	reg = <0x44d00000 0x4000	/* M3 UMEM */
+	       0x44d80000 0x2000>;	/* M3 DMEM */
+	ti,hwmods = "wkup_m3";
+	ti,no-reset-on-init;
+};
-- 
2.1.0


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

* [RFC PATCH 3/3] remoteproc: wkup_m3: Add wkup_m3 remote proc driver
  2014-11-26 21:27 [RFC PATCH 0/3] remoteproc: Introduce wkup_m3_rproc driver Dave Gerlach
  2014-11-26 21:27 ` [RFC PATCH 1/3] ARM: OMAP2+: Use pdata-quirks for wkup_m3 deassert_hardreset Dave Gerlach
  2014-11-26 21:27 ` [RFC PATCH 2/3] Documentation: dt: add ti,am3353-wkup-m3 bindings Dave Gerlach
@ 2014-11-26 21:27 ` Dave Gerlach
  2014-11-26 21:38 ` [RFC PATCH 1/3] Documentation: dt: add ti,am3353-wkup-m3-ipc bindings Dave Gerlach
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Dave Gerlach @ 2014-11-26 21:27 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, linux-omap, devicetree
  Cc: Tony Lindgren, Paul Walmsley, Benoit Cousson, Kevin Hilman,
	Ohad Ben-Cohen, Suman Anna, Dave Gerlach

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.

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

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
---
 drivers/remoteproc/Kconfig         |  12 +++
 drivers/remoteproc/Makefile        |   1 +
 drivers/remoteproc/wkup_m3_rproc.c | 175 +++++++++++++++++++++++++++++++++++++
 3 files changed, 188 insertions(+)
 create mode 100644 drivers/remoteproc/wkup_m3_rproc.c

diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 5e343ba..7fbdb53 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -41,6 +41,18 @@ 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
+	help
+	  Say y here to support AM33xx wkup-m3.
+
+	  Required for Suspend-to-ram and CPUIdle on AM33xx. Allows for
+	  loading of firmware of CM3 PM 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..8686ca2
--- /dev/null
+++ b/drivers/remoteproc/wkup_m3_rproc.c
@@ -0,0 +1,175 @@
+/*
+ * 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/err.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/remoteproc.h>
+
+#include <linux/platform_data/wkup_m3.h>
+
+#include "remoteproc_internal.h"
+
+struct wkup_m3_rproc {
+	struct rproc *rproc;
+	struct platform_device *pdev;
+};
+
+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;
+
+	ret = pdata->deassert_reset(pdev, pdata->reset_name);
+	if (ret) {
+		dev_err(dev, "Unable to reset wkup_m3!\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int wkup_m3_rproc_stop(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;
+
+	ret = pdata->assert_reset(pdev, pdata->reset_name);
+	if (ret) {
+		dev_err(dev, "Unable to assert reset of wkup_m3!\n");
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static struct rproc_ops wkup_m3_rproc_ops = {
+	.start		= wkup_m3_rproc_start,
+	.stop		= wkup_m3_rproc_stop,
+};
+
+static const struct of_device_id wkup_m3_rproc_of_match[] = {
+	{
+		.compatible = "ti,am3353-wkup-m3",
+		.data = (void *)"am335x-pm-firmware.elf",
+	},
+	{},
+};
+
+static int wkup_m3_rproc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const char *fw_name;
+	struct wkup_m3_platform_data *pdata = dev->platform_data;
+	struct wkup_m3_rproc *m3_rproc;
+	const struct of_device_id *match;
+	struct rproc *rproc;
+	int ret;
+
+	if (!(pdata && pdata->deassert_reset && pdata->assert_reset &&
+	      pdata->reset_name)) {
+		dev_err(dev, "Platform data missing!\n");
+		return -ENODEV;
+	}
+
+	match = of_match_device(wkup_m3_rproc_of_match, &pdev->dev);
+	if (!match)
+		return -ENODEV;
+	fw_name = (char *)match->data;
+
+	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,
+			    fw_name, sizeof(*m3_rproc));
+	if (!rproc)
+		return -ENOMEM;
+
+	m3_rproc = rproc->priv;
+	m3_rproc->rproc = rproc;
+	m3_rproc->pdev = pdev;
+
+	dev_set_drvdata(dev, rproc);
+
+	/* Register as a remoteproc device */
+	ret = rproc_add(rproc);
+	if (ret) {
+		dev_err(dev, "rproc_add failed\n");
+		goto err;
+	}
+
+	return 0;
+
+err:
+	rproc_put(rproc);
+	pm_runtime_put_sync(&pdev->dev);
+	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);
+	pm_runtime_put_sync(&pdev->dev);
+
+	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 struct platform_driver wkup_m3_rproc_driver = {
+	.probe = wkup_m3_rproc_probe,
+	.remove = wkup_m3_rproc_remove,
+	.driver = {
+		.name = "wkup_m3",
+		.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");
-- 
2.1.0


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

* [RFC PATCH 1/3] Documentation: dt: add ti,am3353-wkup-m3-ipc bindings
  2014-11-26 21:27 [RFC PATCH 0/3] remoteproc: Introduce wkup_m3_rproc driver Dave Gerlach
                   ` (2 preceding siblings ...)
  2014-11-26 21:27 ` [RFC PATCH 3/3] remoteproc: wkup_m3: Add wkup_m3 remote proc driver Dave Gerlach
@ 2014-11-26 21:38 ` Dave Gerlach
  2014-11-26 21:38 ` [RFC PATCH 2/3] soc: ti: Add wkup_m3_ipc driver Dave Gerlach
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Dave Gerlach @ 2014-11-26 21:38 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, linux-omap, devicetree
  Cc: Tony Lindgren, Paul Walmsley, Benoit Cousson, Kevin Hilman,
	Ohad Ben-Cohen, Suman Anna, Dave Gerlach

Add the device tree bindings document for ti,am3353-wkup-m3-ipc which
is used by the wkup_m3_ipc driver.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
---
 .../devicetree/bindings/soc/ti/wkup_m3_ipc.txt     | 41 ++++++++++++++++++++++
 1 file changed, 41 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/soc/ti/wkup_m3_ipc.txt

diff --git a/Documentation/devicetree/bindings/soc/ti/wkup_m3_ipc.txt b/Documentation/devicetree/bindings/soc/ti/wkup_m3_ipc.txt
new file mode 100644
index 0000000..ceb6acf
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/ti/wkup_m3_ipc.txt
@@ -0,0 +1,41 @@
+Wakeup M3 IPC Driver
+=====================
+
+TI AMx3 family of devices use a Cortex M3 co-processor to help with various
+low power tasks that cannot be controlled from the MPU like suspend/resume
+and certain deep C-states for CPU Idle. Once the wkup_m3_ipc driver uses the
+wkup_m3_rproc driver to boot the wkup_m3, it handles communication with the
+CM3 using IPC registers present in the SoC's control module and a mailbox.
+The wkup_m3_ipc exposes an API to allow the SoC PM code to execute specific
+PM tasks.
+
+Wkup M3 Device Node:
+====================
+A wkup_m3_ipc device node is used to represent a wakeup M3 IP instance within
+an SoC. The sub-mailboxes are represented as child node of this parent node.
+
+Required properties:
+--------------------
+- compatible:		Should be "ti,am3353-wkup-m3-ipc" for AM33xx SoCs
+- reg:			Contains the wkup_m3 register address ranges for
+			ipc-regs.
+- reg-names:		Name for ipc-regs given above
+- interrupts:		Contains the interrupt information for the wkup_m3
+			interrupt that signals the MPU.
+- ti,rproc:		Phandle to the wkup_m3 rproc node so the IPC driver
+			can boot it.
+- mboxes:		Phandles used by IPC framework to get correct mbox
+			channel for communication. Must point to appropriate
+			mbox_wkupm3 child node.
+
+Example:
+--------
+/* AM33xx */
+wkup_m3_ipc: wkup_m3_ipc@44e11324 {
+	compatible = "ti,am3353-wkup-m3-ipc";
+	reg = <0x44e11324 0x0024>;
+	reg-names = "ipc_regs";
+	interrupts = <78>;
+	ti,rproc = <&wkup_m3>;
+	mboxes = <&mailbox &mbox_wkupm3>;
+};
-- 
2.1.0


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

* [RFC PATCH 2/3] soc: ti: Add wkup_m3_ipc driver
  2014-11-26 21:27 [RFC PATCH 0/3] remoteproc: Introduce wkup_m3_rproc driver Dave Gerlach
                   ` (3 preceding siblings ...)
  2014-11-26 21:38 ` [RFC PATCH 1/3] Documentation: dt: add ti,am3353-wkup-m3-ipc bindings Dave Gerlach
@ 2014-11-26 21:38 ` Dave Gerlach
  2014-11-26 21:51   ` Arnd Bergmann
  2014-11-26 21:38 ` [RFC PATCH 3/3] ARM: dts: am33xx: Add wkup_m3_ipc node Dave Gerlach
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 12+ messages in thread
From: Dave Gerlach @ 2014-11-26 21:38 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, linux-omap, devicetree
  Cc: Tony Lindgren, Paul Walmsley, Benoit Cousson, Kevin Hilman,
	Ohad Ben-Cohen, Suman Anna, Dave Gerlach

Introduce a wkup_m3_ipc driver to handle communication between the MPU
and Cortex M3 wkup_m3 present on am335x.

This driver is responsible for actually booting the wkup_m3_rproc and
also handling all IPC which is done using the IPC registers in the control
module, a mailbox, and a separate interrupt back from the wkup_m3. A small
API is exposed for executing specific power commands, which include
configuring for low power mode, request a transition to a low power mode,
and status info on a previous transition.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
---
 drivers/soc/ti/Kconfig       |  11 +
 drivers/soc/ti/Makefile      |   1 +
 drivers/soc/ti/wkup_m3_ipc.c | 503 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/wkup_m3_ipc.h  |  33 +++
 4 files changed, 548 insertions(+)
 create mode 100644 drivers/soc/ti/wkup_m3_ipc.c
 create mode 100644 include/linux/wkup_m3_ipc.h

diff --git a/drivers/soc/ti/Kconfig b/drivers/soc/ti/Kconfig
index 7266b21..61cda85 100644
--- a/drivers/soc/ti/Kconfig
+++ b/drivers/soc/ti/Kconfig
@@ -28,4 +28,15 @@ config KEYSTONE_NAVIGATOR_DMA
 
 	  If unsure, say N.
 
+config WKUP_M3_IPC
+	bool "TI AM33XX Wkup-M3 IPC Driver"
+	depends on WKUP_M3_RPROC
+	select MAILBOX
+	select OMAP2PLUS_MBOX
+	help
+	  TI AM33XX has a Cortex M3 to handle low power transitions. This IPC
+	  driver provides the necessary API to communicate and use the wkup m3
+	  for PM features like Suspend/Resume and boots the wkup_m3 using
+	  wkup_m3_rproc driver.
+
 endif # SOC_TI
diff --git a/drivers/soc/ti/Makefile b/drivers/soc/ti/Makefile
index 6bed611..b6b8c8b 100644
--- a/drivers/soc/ti/Makefile
+++ b/drivers/soc/ti/Makefile
@@ -3,3 +3,4 @@
 #
 obj-$(CONFIG_KEYSTONE_NAVIGATOR_QMSS)	+= knav_qmss_queue.o knav_qmss_acc.o
 obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA)	+= knav_dma.o
+obj-$(CONFIG_WKUP_M3_IPC)		+= wkup_m3_ipc.o
diff --git a/drivers/soc/ti/wkup_m3_ipc.c b/drivers/soc/ti/wkup_m3_ipc.c
new file mode 100644
index 0000000..f915da6
--- /dev/null
+++ b/drivers/soc/ti/wkup_m3_ipc.c
@@ -0,0 +1,503 @@
+/*
+ * AMx3 Wkup M3 IPC 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/err.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mailbox_client.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/omap-mailbox.h>
+#include <linux/platform_device.h>
+#include <linux/remoteproc.h>
+#include <linux/suspend.h>
+#include <linux/wkup_m3_ipc.h>
+
+#define AM33XX_CTRL_IPC_REG_COUNT	0x8
+#define AM33XX_CTRL_IPC_REG_OFFSET(m)	(0x4 + 4 * (m))
+
+/* 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)
+
+#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_WAKE_SRC_MASK		0xFF
+#define M3_STATUS_RESP_MASK		(0xffff << 16)
+#define M3_FW_VERSION_MASK		0xffff
+
+#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
+
+struct wkup_m3_wakeup_src {
+	int irq_nr;
+	char src[10];
+};
+
+struct wkup_m3_ipc {
+	struct rproc *rproc;
+
+	void __iomem *ipc_mem_base;
+	struct device *dev;
+
+	int mem_type;
+	unsigned long resume_addr;
+	int state;
+
+	struct mbox_client mbox_client;
+	struct mbox_chan *mbox;
+};
+
+static struct wkup_m3_ipc m3_ipc_state;
+
+static DECLARE_COMPLETION(m3_ipc_sync);
+
+static const 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 inline void am33xx_txev_eoi(struct wkup_m3_ipc *m3_ipc)
+{
+	writel(AM33XX_M3_TXEV_ACK,
+	       m3_ipc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
+}
+
+static inline void am33xx_txev_enable(struct wkup_m3_ipc *m3_ipc)
+{
+	writel(AM33XX_M3_TXEV_ENABLE,
+	       m3_ipc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
+}
+
+static inline void wkup_m3_ctrl_ipc_write(struct wkup_m3_ipc *m3_ipc,
+					  u32 val, int ipc_reg_num)
+{
+	if (ipc_reg_num < 0 || ipc_reg_num > AM33XX_CTRL_IPC_REG_COUNT)
+		return;
+
+	writel(val, m3_ipc->ipc_mem_base +
+	       AM33XX_CTRL_IPC_REG_OFFSET(ipc_reg_num));
+}
+
+static inline unsigned int wkup_m3_ctrl_ipc_read(struct wkup_m3_ipc *m3_ipc,
+						 int ipc_reg_num)
+{
+	if (ipc_reg_num < 0 || ipc_reg_num > AM33XX_CTRL_IPC_REG_COUNT)
+		return 0;
+
+	return readl(m3_ipc->ipc_mem_base +
+		     AM33XX_CTRL_IPC_REG_OFFSET(ipc_reg_num));
+}
+
+static int wkup_m3_fw_version_read(void)
+{
+	int val;
+
+	val = wkup_m3_ctrl_ipc_read(&m3_ipc_state, 2);
+
+	return val & M3_FW_VERSION_MASK;
+}
+
+static irqreturn_t wkup_m3_txev_handler(int irq, void *ipc_data)
+{
+	struct wkup_m3_ipc *m3_ipc = (struct wkup_m3_ipc *)ipc_data;
+	struct device *dev = m3_ipc->dev;
+	int ver = 0;
+
+	am33xx_txev_eoi(m3_ipc);
+
+	switch (m3_ipc->state) {
+	case M3_STATE_RESET:
+		ver = wkup_m3_fw_version_read();
+
+		if (ver == M3_VERSION_UNKNOWN ||
+		    ver < M3_BASELINE_VERSION) {
+			dev_warn(dev, "CM3 Firmware Version %x not supported\n",
+				 ver);
+		} else {
+			dev_info(dev, "CM3 Firmware Version = 0x%x\n", ver);
+		}
+
+		m3_ipc->state = M3_STATE_INITED;
+		complete(&m3_ipc_sync);
+		break;
+	case M3_STATE_MSG_FOR_RESET:
+		m3_ipc->state = M3_STATE_INITED;
+		complete(&m3_ipc_sync);
+		break;
+	case M3_STATE_MSG_FOR_LP:
+		complete(&m3_ipc_sync);
+		break;
+	case M3_STATE_UNKNOWN:
+		dev_warn(dev, "Unknown CM3 State\n");
+	}
+
+	am33xx_txev_enable(m3_ipc);
+
+	return IRQ_HANDLED;
+}
+
+static void wkup_m3_mbox_callback(struct mbox_client *client, void *data)
+{
+	struct wkup_m3_ipc *m3_ipc = container_of(client, struct wkup_m3_ipc,
+						  mbox_client);
+
+	omap_mbox_disable_irq(m3_ipc->mbox, IRQ_RX);
+}
+
+static int wkup_m3_ping(void)
+{
+	struct device *dev = m3_ipc_state.dev;
+	mbox_msg_t dummy_msg = 0;
+	int ret;
+
+	if (!m3_ipc_state.mbox) {
+		dev_err(m3_ipc_state.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_ipc_state.mbox, IRQ_RX);
+	ret = mbox_send_message(m3_ipc_state.mbox, (void *)dummy_msg);
+	if (ret < 0) {
+		dev_err(dev, "%s: mbox_send_message() failed: %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	ret = wait_for_completion_timeout(&m3_ipc_sync,
+					  msecs_to_jiffies(500));
+	if (!ret) {
+		dev_err(dev, "MPU<->CM3 sync failure\n");
+		m3_ipc_state.state = M3_STATE_UNKNOWN;
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static const char *wkup_m3_request_wake_src(void)
+{
+	unsigned int wakeup_src_idx;
+	int j, val;
+
+	val = wkup_m3_ctrl_ipc_read(&m3_ipc_state, 6);
+
+	wakeup_src_idx = val & M3_WAKE_SRC_MASK;
+
+	for (j = 0; j < ARRAY_SIZE(wakeups)-1; j++) {
+		if (wakeups[j].irq_nr == wakeup_src_idx)
+			return wakeups[j].src;
+	}
+	return wakeups[j].src;
+}
+
+static int wkup_m3_is_available(void)
+{
+	return (m3_ipc_state.state != M3_STATE_RESET);
+}
+
+/* Public functions */
+/**
+ * wkup_m3_set_mem_type - Pass wkup_m3 which type of memory is in use
+ * @mem_type: memory type value read directly from emif
+ *
+ * wkup_m3 must know what memory type is in use to properly suspend
+ * and resume.
+ */
+void wkup_m3_set_mem_type(int mem_type)
+{
+	m3_ipc_state.mem_type = mem_type;
+}
+
+/**
+ * wkup_m3_set_resume_address - Pass wkup_m3 resume address
+ * @addr: Physical address from which resume code should execute
+ */
+void wkup_m3_set_resume_address(void *addr)
+{
+	m3_ipc_state.resume_addr = (unsigned long)addr;
+}
+
+/**
+ * wkup_m3_set_resume_address - Retrieve wkup_m3 status code after suspend
+ *
+ * Returns code representing the status of a low power mode transition.
+ *	0 - Successful transition
+ *	1 - Failure to transition to low power state
+ */
+int wkup_m3_request_pm_status(void)
+{
+	struct device *dev = m3_ipc_state.dev;
+	unsigned int i;
+	int val;
+	const char *wake_src;
+
+	/* print the wakeup reason */
+	wake_src = wkup_m3_request_wake_src();
+	dev_dbg(dev, "Wakeup source %s\n", wake_src);
+
+	val = wkup_m3_ctrl_ipc_read(&m3_ipc_state, 1);
+
+	i = M3_STATUS_RESP_MASK & val;
+	i >>= __ffs(M3_STATUS_RESP_MASK);
+
+	return i;
+}
+
+/**
+ * wkup_m3_prepare_low_power - Request preparation for transition to
+ *			       low power state
+ * @state: A kernel suspend state to enter, either MEM or STANDBY
+ *
+ * Returns 0 if preparation was successful, otherwise returns error code
+ */
+int wkup_m3_prepare_low_power(int state)
+{
+	struct device *dev = m3_ipc_state.dev;
+	int m3_power_state;
+	int ret = 0;
+
+	if (!wkup_m3_is_available()) {
+		dev_err(dev, "wkup_m3 not available. DeepSleep entry not possible.\n");
+		return -ENODEV;
+	}
+
+	switch (state) {
+	case PM_SUSPEND_MEM:
+		m3_power_state = IPC_CMD_DS0;
+		break;
+	default:
+		return 1;
+	}
+
+	/* Program each required IPC register then write defaults to others */
+	wkup_m3_ctrl_ipc_write(&m3_ipc_state, m3_ipc_state.resume_addr, 0);
+	wkup_m3_ctrl_ipc_write(&m3_ipc_state, m3_power_state, 1);
+	wkup_m3_ctrl_ipc_write(&m3_ipc_state, m3_ipc_state.mem_type, 4);
+
+	wkup_m3_ctrl_ipc_write(&m3_ipc_state, DS_IPC_DEFAULT, 2);
+	wkup_m3_ctrl_ipc_write(&m3_ipc_state, DS_IPC_DEFAULT, 3);
+	wkup_m3_ctrl_ipc_write(&m3_ipc_state, DS_IPC_DEFAULT, 5);
+	wkup_m3_ctrl_ipc_write(&m3_ipc_state, DS_IPC_DEFAULT, 6);
+	wkup_m3_ctrl_ipc_write(&m3_ipc_state, DS_IPC_DEFAULT, 7);
+
+	m3_ipc_state.state = M3_STATE_MSG_FOR_LP;
+
+	ret = wkup_m3_ping();
+	if (ret) {
+		dev_err(dev, "Unable to ping CM3\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * wkup_m3_finish_low_power - Return m3 to reset state
+ *
+ * Returns 0 if reset was successful, otherwise returns error code
+ */
+int wkup_m3_finish_low_power(void)
+{
+	struct device *dev = m3_ipc_state.dev;
+	int ret = 0;
+
+	if (!wkup_m3_is_available())
+		return -ENODEV;
+
+	wkup_m3_ctrl_ipc_write(&m3_ipc_state, IPC_CMD_RESET, 1);
+	wkup_m3_ctrl_ipc_write(&m3_ipc_state, DS_IPC_DEFAULT, 2);
+
+	m3_ipc_state.state = M3_STATE_MSG_FOR_RESET;
+
+	ret = wkup_m3_ping();
+	if (ret) {
+		dev_err(dev, "Unable to ping CM3\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int match(struct device *dev, void *data)
+{
+	struct device_node *node = (struct device_node *)data;
+
+	return dev->of_node == node;
+}
+
+static struct rproc *wkup_m3_get_rproc(struct device *dev)
+{
+	struct device_node *node;
+	struct rproc *rp;
+
+	node = of_parse_phandle(dev->of_node, "ti,rproc", 0);
+	if (!node)
+		return NULL;
+
+	dev = bus_find_device(&platform_bus_type, NULL, node, match);
+	if (!dev)
+		return NULL;
+
+	rp = dev_get_drvdata(dev);
+	return rp;
+}
+
+static void wkup_m3_rproc_boot_thread(struct rproc *rproc)
+{
+	struct device *dev = &rproc->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_ipc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	int irq, ret;
+	struct rproc *m3_rproc;
+	struct resource *res;
+	struct task_struct *task;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ipc_regs");
+	m3_ipc_state.ipc_mem_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(m3_ipc_state.ipc_mem_base)) {
+		dev_err(dev, "could not ioremap ipc_mem\n");
+		ret = PTR_ERR(m3_ipc_state.ipc_mem_base);
+		goto err;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (!irq) {
+		dev_err(&pdev->dev, "no irq resource\n");
+		ret = -ENXIO;
+		goto err;
+	}
+
+	ret = devm_request_irq(dev, irq, wkup_m3_txev_handler,
+			       IRQF_DISABLED, "wkup_m3_txev", &m3_ipc_state);
+	if (ret) {
+		dev_err(dev, "request_irq failed\n");
+		goto err;
+	}
+
+	m3_ipc_state.mbox_client.dev = dev;
+	m3_ipc_state.mbox_client.tx_done = NULL;
+	m3_ipc_state.mbox_client.rx_callback = wkup_m3_mbox_callback;
+	m3_ipc_state.mbox_client.tx_block = false;
+	m3_ipc_state.mbox_client.knows_txdone = false;
+
+	m3_ipc_state.mbox = mbox_request_channel(&m3_ipc_state.mbox_client, 0);
+	if (IS_ERR(m3_ipc_state.mbox)) {
+		dev_err(dev, "IPC Request for A8->M3 Channel failed! %ld\n",
+			PTR_ERR(m3_ipc_state.mbox));
+		ret = PTR_ERR(m3_ipc_state.mbox);
+		m3_ipc_state.mbox = NULL;
+		return ret;
+	}
+
+	m3_rproc = wkup_m3_get_rproc(dev);
+	if (!m3_rproc) {
+		ret = -EPROBE_DEFER;
+		goto err;
+	}
+
+	m3_ipc_state.dev = dev;
+	m3_ipc_state.state = M3_STATE_RESET;
+	/*
+	 * 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_boot_thread, m3_rproc,
+			   "wkup_m3_rproc_loader");
+
+	if (IS_ERR(task)) {
+		dev_err(dev, "can't create rproc_boot thread\n");
+		goto err;
+	}
+
+	return 0;
+
+err:
+	mbox_free_channel(m3_ipc_state.mbox);
+	return ret;
+}
+
+static int wkup_m3_ipc_remove(struct platform_device *pdev)
+{
+	if (m3_ipc_state.mbox)
+		mbox_free_channel(m3_ipc_state.mbox);
+
+	return 0;
+}
+
+static const struct of_device_id wkup_m3_ipc_of_match[] = {
+	{ .compatible = "ti,am3353-wkup-m3-ipc", .data = NULL, },
+	{},
+};
+
+static struct platform_driver wkup_m3_ipc_driver = {
+	.probe = wkup_m3_ipc_probe,
+	.remove = wkup_m3_ipc_remove,
+	.driver = {
+		.name = "wkup_m3_ipc",
+		.of_match_table = wkup_m3_ipc_of_match,
+	},
+};
+
+module_platform_driver(wkup_m3_ipc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("wkup m3 remote processor ipc driver");
diff --git a/include/linux/wkup_m3_ipc.h b/include/linux/wkup_m3_ipc.h
new file mode 100644
index 0000000..a0c2722
--- /dev/null
+++ b/include/linux/wkup_m3_ipc.h
@@ -0,0 +1,33 @@
+/*
+ * TI Wakeup M3 for AMx3 SoCs 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
+
+#ifdef CONFIG_WKUP_M3_IPC
+void wkup_m3_set_mem_type(int mem_type);
+void wkup_m3_set_resume_address(void *addr);
+int wkup_m3_prepare_low_power(int state);
+int wkup_m3_finish_low_power(void);
+int wkup_m3_request_pm_status(void);
+#else
+static inline void wkup_m3_set_mem_type(int mem_type) { }
+static inline void wkup_m3_set_resume_address(void *addr) { }
+static inline int wkup_m3_prepare_low_power(int state) { return -1; }
+static inline int wkup_m3_finish_low_power(void) { return -1; }
+static inline int wkup_m3_request_pm_status(void) { return -1; }
+#endif /* CONFIG_WKUP_M3_IPC */
+#endif /* _LINUX_WKUP_M3_H */
-- 
2.1.0


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

* [RFC PATCH 3/3] ARM: dts: am33xx: Add wkup_m3_ipc node
  2014-11-26 21:27 [RFC PATCH 0/3] remoteproc: Introduce wkup_m3_rproc driver Dave Gerlach
                   ` (4 preceding siblings ...)
  2014-11-26 21:38 ` [RFC PATCH 2/3] soc: ti: Add wkup_m3_ipc driver Dave Gerlach
@ 2014-11-26 21:38 ` Dave Gerlach
  2014-11-26 21:39 ` [RFC PATCH 1/3] Documentation: dt: add ti,am3352-emif bindings Dave Gerlach
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Dave Gerlach @ 2014-11-26 21:38 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, linux-omap, devicetree
  Cc: Tony Lindgren, Paul Walmsley, Benoit Cousson, Kevin Hilman,
	Ohad Ben-Cohen, Suman Anna, Dave Gerlach

Add wkup_m3_ipc node for wkup_m3_ipc driver.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
---
 arch/arm/boot/dts/am33xx.dtsi | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
index 41659df..b86d8c0 100644
--- a/arch/arm/boot/dts/am33xx.dtsi
+++ b/arch/arm/boot/dts/am33xx.dtsi
@@ -848,6 +848,15 @@
 			reg = <0x48310000 0x2000>;
 			interrupts = <111>;
 		};
+
+		wkup_m3_ipc: wkup_m3_ipc@44e11324 {
+			compatible = "ti,am3353-wkup-m3-ipc";
+			reg = <0x44e11324 0x0024>;
+			reg-names = "ipc_regs";
+			interrupts = <78>;
+			ti,rproc = <&wkup_m3>;
+			mboxes = <&mailbox &mbox_wkupm3>;
+		};
 	};
 };
 
-- 
2.1.0


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

* [RFC PATCH 1/3] Documentation: dt: add ti,am3352-emif bindings
  2014-11-26 21:27 [RFC PATCH 0/3] remoteproc: Introduce wkup_m3_rproc driver Dave Gerlach
                   ` (5 preceding siblings ...)
  2014-11-26 21:38 ` [RFC PATCH 3/3] ARM: dts: am33xx: Add wkup_m3_ipc node Dave Gerlach
@ 2014-11-26 21:39 ` Dave Gerlach
  2014-11-26 21:39 ` [RFC PATCH 2/3] memory: ti-emif-sram: introduce relocatable suspend/resume handlers Dave Gerlach
  2014-11-26 21:39 ` [RFC PATCH 3/3] ARM: dts: am33xx: Add emif node Dave Gerlach
  8 siblings, 0 replies; 12+ messages in thread
From: Dave Gerlach @ 2014-11-26 21:39 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, linux-omap, devicetree
  Cc: Tony Lindgren, Benoit Cousson, Kevin Hilman, Greg Kroah-Hartman,
	Dave Gerlach

Add the device tree bindings document for ti,am3352-emif which
is used by the ti-emif-sram driver to provide low-level PM
functionality.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
---
 .../bindings/memory-controllers/ti/emif-sram.txt   | 31 ++++++++++++++++++++++
 1 file changed, 31 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/memory-controllers/ti/emif-sram.txt

diff --git a/Documentation/devicetree/bindings/memory-controllers/ti/emif-sram.txt b/Documentation/devicetree/bindings/memory-controllers/ti/emif-sram.txt
new file mode 100644
index 0000000..72d6db0
--- /dev/null
+++ b/Documentation/devicetree/bindings/memory-controllers/ti/emif-sram.txt
@@ -0,0 +1,31 @@
+EMIF SRAM Driver
+=====================
+
+TI AMx3 family of devices use a similar EMIF to other TI SoCs but have
+different PM requirements. Late suspend code runs from SRAM and requires
+save and restore of EMIF context and placing the SDRAM in and out of
+self-refresh. Because of this, the ti-emif-sram driver introduces
+relocatable PM function that can run from SRAM and place the EMIF in
+the proper state for low-power mode transition.
+
+EMIF Device Node:
+====================
+A emif node is used to represent an EMIF IP instance within an SoC. The node
+must contain a phandle to an sram node so the ti-emif-sram driver can allocate
+space within the sram and copy the relocatable PM functions.
+
+Required properties:
+--------------------
+- compatible:		Should be "ti,am3352-emif" for AM33xx SoCs
+- reg:			Contains the emif register address ranges.
+- sram:			Phandle for generic sram node for the driver
+			to use to copy PM functions to.
+
+Example:
+--------
+/* AM33xx */
+emif: emif@4c000000 {
+	compatible = "ti,am3352-emif";
+	reg =	<0x4C000000 0x1000>;
+	sram = <&ocmcram>;
+};
-- 
2.1.0


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

* [RFC PATCH 2/3] memory: ti-emif-sram: introduce relocatable suspend/resume handlers
  2014-11-26 21:27 [RFC PATCH 0/3] remoteproc: Introduce wkup_m3_rproc driver Dave Gerlach
                   ` (6 preceding siblings ...)
  2014-11-26 21:39 ` [RFC PATCH 1/3] Documentation: dt: add ti,am3352-emif bindings Dave Gerlach
@ 2014-11-26 21:39 ` Dave Gerlach
  2014-11-26 21:39 ` [RFC PATCH 3/3] ARM: dts: am33xx: Add emif node Dave Gerlach
  8 siblings, 0 replies; 12+ messages in thread
From: Dave Gerlach @ 2014-11-26 21:39 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, linux-omap, devicetree
  Cc: Tony Lindgren, Benoit Cousson, Kevin Hilman, Greg Kroah-Hartman,
	Dave Gerlach

Certain SoCs like Texas Instruments AM335x and AM437x require parts
of the EMIF PM code to run late in the suspend sequence from SRAM,
such as saving and restoring the EMIF context and placing the memory
into self-refresh.

One requirement for these SoC's to suspend and enter it's lowest power
mode, called DeepSleep0, is that the PER power domain must be shut
off. Because the EMIF (DDR Controller) resides within this power
domain, it will lose context during a suspend operation, so we must
save it to restore once we resume. However, we cannot execute this
code from external memory, as it is not available at this point, so
the code must be executed late in the suspend path from SRAM.

This patch introduces a ti-emif-sram driver that includes several
functions written in ARM ASM that are relocatable so the PM SRAM
code can use them. It can export a table containing the absolute
addresses of the available PM functions so that other SRAM code
can branch to them. This code is required for suspend/resume on
AM335x and AM437x to work.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
---
 drivers/memory/Kconfig           |  10 ++
 drivers/memory/Makefile          |   3 +
 drivers/memory/emif.h            |   8 ++
 drivers/memory/ti-emif-sram-pm.S | 233 +++++++++++++++++++++++++++++++++++++++
 drivers/memory/ti-emif-sram.c    | 195 ++++++++++++++++++++++++++++++++
 include/linux/ti-emif-sram.h     |  26 +++++
 6 files changed, 475 insertions(+)
 create mode 100644 drivers/memory/ti-emif-sram-pm.S
 create mode 100644 drivers/memory/ti-emif-sram.c
 create mode 100644 include/linux/ti-emif-sram.h

diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
index 6d91c27..02b778e 100644
--- a/drivers/memory/Kconfig
+++ b/drivers/memory/Kconfig
@@ -41,6 +41,16 @@ config TI_EMIF
 	  parameters and other settings during frequency, voltage and
 	  temperature changes
 
+config TI_EMIF_SRAM
+	bool "Texas Instruments EMIF SRAM driver"
+	depends on SOC_AM33XX
+	help
+	  This driver is for the EMIF module available on Texas Instruments
+	  AM33XX SoCs and is required for PM. Certain parts of the EMIF PM
+	  code must run from on-chip SRAM late in the suspend sequence so
+	  this driver provides several relocatable PM functions for the SoC
+	  PM code to use.
+
 config MVEBU_DEVBUS
 	bool "Marvell EBU Device Bus Controller"
 	default y
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index c32d319..42888c2 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -13,3 +13,6 @@ obj-$(CONFIG_FSL_IFC)		+= fsl_ifc.o
 obj-$(CONFIG_MVEBU_DEVBUS)	+= mvebu-devbus.o
 obj-$(CONFIG_TEGRA20_MC)	+= tegra20-mc.o
 obj-$(CONFIG_TEGRA30_MC)	+= tegra30-mc.o
+obj-$(CONFIG_TI_EMIF_SRAM)	+= ti-emif-sram.o ti-emif-sram-pm.o
+
+AFLAGS_ti-emif-sram-pm.o	:=-Wa,-march=armv7-a
diff --git a/drivers/memory/emif.h b/drivers/memory/emif.h
index bfe08ba..8e86eb2 100644
--- a/drivers/memory/emif.h
+++ b/drivers/memory/emif.h
@@ -585,5 +585,13 @@ struct emif_regs {
 	u32 ext_phy_ctrl_3_shdw;
 	u32 ext_phy_ctrl_4_shdw;
 };
+
+struct ti_emif_pm_functions;
+
+extern unsigned int ti_emif_sram;
+extern unsigned int ti_emif_sram_sz;
+extern void __iomem *ti_emif_base_addr_virt;
+extern void __iomem *ti_emif_base_addr_phys;
+extern struct ti_emif_pm_functions ti_emif_pm;
 #endif /* __ASSEMBLY__ */
 #endif /* __EMIF_H */
diff --git a/drivers/memory/ti-emif-sram-pm.S b/drivers/memory/ti-emif-sram-pm.S
new file mode 100644
index 0000000..49b0be4
--- /dev/null
+++ b/drivers/memory/ti-emif-sram-pm.S
@@ -0,0 +1,233 @@
+/*
+ * Low level PM code for TI EMIF
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/
+ *	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 <asm/memory.h>
+#include <asm/assembler.h>
+
+#include "emif.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_SDCFG_TYPE_DDR2				0x2 << SDRAM_TYPE_SHIFT
+#define EMIF_STATUS_READY				0x4
+
+	.text
+	.align 3
+
+ENTRY(ti_emif_sram)
+
+/*
+ * void ti_emif_save_context(void)
+ *
+ * Used during suspend to save the context of all required EMIF registers
+ * to local memory if the EMIF is going to lose context during the sleep
+ * transition. Operates on the VIRTUAL address of the EMIF.
+ */
+ENTRY(ti_emif_save_context)
+	stmfd   sp!, {r4 - r11, lr}     @ save registers on stack
+
+	ldr	r0, ti_emif_base_addr_virt
+
+	/* Save EMIF configuration */
+	ldr	r1, [r0, #EMIF_SDRAM_CONFIG]
+	str	r1, emif_sdcfg_val
+	ldr	r1, [r0, #EMIF_SDRAM_REFRESH_CONTROL]
+	str	r1, emif_ref_ctrl_val
+	ldr	r1, [r0, #EMIF_SDRAM_TIMING_1]
+	str	r1, emif_timing1_val
+	ldr	r1, [r0, #EMIF_SDRAM_TIMING_2]
+	str	r1, emif_timing2_val
+	ldr	r1, [r0, #EMIF_SDRAM_TIMING_3]
+	str	r1, emif_timing3_val
+	ldr	r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
+	str	r1, emif_pmcr_val
+	ldr	r1, [r0, #EMIF_POWER_MANAGEMENT_CTRL_SHDW]
+	str	r1, emif_pmcr_shdw_val
+	ldr	r1, [r0, #EMIF_SDRAM_OUTPUT_IMPEDANCE_CALIBRATION_CONFIG]
+	str	r1, emif_zqcfg_val
+	ldr	r1, [r0, #EMIF_DDR_PHY_CTRL_1]
+	str	r1, emif_rd_lat_val
+
+	ldmfd	sp!, {r4 - r11, pc}	@ restore regs and return
+ENDPROC(ti_emif_save_context)
+
+/*
+ * void ti_emif_restore_context(void)
+ *
+ * Used during resume to restore the context of all required EMIF registers
+ * from local memory after the EMIF has lost context during a sleep transition.
+ * Operates on the PHYSICAL address of the EMIF.
+ */
+ENTRY(ti_emif_restore_context)
+	stmfd   sp!, {r4 - r11, lr}     @ save registers on stack
+
+	ldr     r0, ti_emif_base_addr_phys
+
+	/* Config EMIF Timings */
+	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]
+
+	/*
+	 * 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 sdcfg last for DDR2 only */
+	ldr	r1, emif_sdcfg_val
+	and	r2, r1, #SDRAM_TYPE_MASK
+	cmp	r2, #EMIF_SDCFG_TYPE_DDR2
+	streq	r1, [r0, #EMIF_SDRAM_CONFIG]
+
+	ldmfd	sp!, {r4 - r11, pc}	@ restore regs and return
+ENDPROC(ti_emif_restore_context)
+
+/*
+ * void ti_emif_enter_sr(void)
+ *
+ * Programs the EMIF to tell the SDRAM to enter into self-refresh
+ * mode during a sleep transition. Operates on the VIRTUAL address
+ * of the EMIF.
+ */
+ENTRY(ti_emif_enter_sr)
+	stmfd   sp!, {r4 - r11, lr}     @ save registers on stack
+
+	ldr	r0, ti_emif_base_addr_virt
+
+	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]
+
+	ldmfd	sp!, {r4 - r11, pc}	@ restore regs and return
+ENDPROC(ti_emif_enter_sr)
+
+/*
+ * void ti_emif_exit_sr(void)
+ *
+ * Programs the EMIF to tell the SDRAM to exit self-refresh mode
+ * after a sleep transition. Operates on the PHYSICAL address of
+ * the EMIF.
+ */
+ENTRY(ti_emif_exit_sr)
+	stmfd   sp!, {r4 - r11, lr}     @ save registers on stack
+
+	ldr	r0, ti_emif_base_addr_phys
+
+	/*
+	 * 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 lose 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]
+
+        /* Wait for EMIF to become ready */
+1:	ldr     r1, [r0, #EMIF_STATUS]
+	tst     r1, #EMIF_STATUS_READY
+	beq     1b
+
+	ldmfd	sp!, {r4 - r11, pc}	@ restore regs and return
+ENDPROC(ti_emif_exit_sr)
+
+/*
+ * void ti_emif_abort_sr(void)
+ *
+ * Disables self-refresh after a failed transition to a low-power
+ * state so the kernel can jump back to DDR and follow abort path.
+ * Operates on the VIRTUAL address of the EMIF.
+ */
+ENTRY(ti_emif_abort_sr)
+	stmfd   sp!, {r4 - r11, lr}     @ save registers on stack
+
+	ldr	r0, ti_emif_base_addr_virt
+
+	ldr	r1, emif_pmcr_val
+	bic	r1, r1, #EMIF_POWER_MGMT_SELF_REFRESH_MODE_MASK
+	str	r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
+
+	/* Wait for EMIF to become ready */
+1:	ldr     r1, [r0, #EMIF_STATUS]
+	tst     r1, #EMIF_STATUS_READY
+	beq     1b
+
+	ldmfd	sp!, {r4 - r11, pc}	@ restore regs and return
+ENDPROC(ti_emif_abort_sr)
+
+	.align 3
+/* DDR related defines */
+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
+
+ENTRY(ti_emif_base_addr_virt)
+        .word   0x00000000
+ENTRY(ti_emif_base_addr_phys)
+        .word   0x00000000
+ENTRY(ti_emif_pm)
+	.word	ti_emif_save_context - ti_emif_sram
+	.word	ti_emif_restore_context - ti_emif_sram
+	.word	ti_emif_enter_sr - ti_emif_sram
+	.word	ti_emif_exit_sr - ti_emif_sram
+	.word	ti_emif_abort_sr - ti_emif_sram
+ENTRY(ti_emif_sram_sz)
+        .word   . - ti_emif_save_context
diff --git a/drivers/memory/ti-emif-sram.c b/drivers/memory/ti-emif-sram.c
new file mode 100644
index 0000000..e843a3b
--- /dev/null
+++ b/drivers/memory/ti-emif-sram.c
@@ -0,0 +1,195 @@
+/*
+ * TI AM33XX SRAM EMIF 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/err.h>
+#include <linux/genalloc.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#include <asm/fncpy.h>
+
+#include "emif.h"
+
+#define EMIF_POWER_MGMT_WAIT_SELF_REFRESH_8192_CYCLES		0x00a0
+
+struct ti_emif_pm_functions {
+	u32 save_context;
+	u32 restore_context;
+	u32 enter_sr;
+	u32 exit_sr;
+	u32 abort_sr;
+} __packed;
+
+static void __iomem *ti_emif_sram_phys;
+static void __iomem *ti_emif_sram_virt;
+
+static u32 sram_suspend_address(unsigned long fn_offset)
+{
+	return (unsigned long)ti_emif_sram_virt + fn_offset;
+}
+
+static u32 sram_resume_address(unsigned long fn_offset)
+{
+	return (unsigned long)ti_emif_sram_phys + fn_offset;
+}
+
+static int ti_emif_push_sram(struct device_node *np)
+{
+	struct gen_pool *sram_pool;
+	phys_addr_t ocmcram_location;
+
+	sram_pool = of_get_named_gen_pool(np, "sram", 0);
+
+	if (!sram_pool)
+		pr_warn("PM: %s: Unable to allocate sram pool for ocmcram\n",
+			__func__);
+
+	ocmcram_location = gen_pool_alloc(sram_pool, ti_emif_sram_sz);
+	if (!ocmcram_location)
+		return -EINVAL;
+
+	/* Save physical address to calculate resume offset during pm init */
+	ti_emif_sram_phys = (void *)gen_pool_virt_to_phys(sram_pool,
+							  ocmcram_location);
+	ti_emif_sram_virt = (void *)fncpy((void *)ocmcram_location,
+					 &ti_emif_sram,
+					 ti_emif_sram_sz);
+
+	/*
+	 * These functions are called during suspend path while MMU is
+	 * still on so add virtual base to offset for absolute address
+	 */
+	ti_emif_pm.save_context = sram_suspend_address(ti_emif_pm.save_context);
+	ti_emif_pm.enter_sr = sram_suspend_address(ti_emif_pm.enter_sr);
+	ti_emif_pm.abort_sr = sram_suspend_address(ti_emif_pm.abort_sr);
+
+	/*
+	 * These are called during resume path when MMU is not enabled
+	 * so physical address is used instead
+	 */
+	ti_emif_pm.restore_context =
+		sram_resume_address(ti_emif_pm.restore_context);
+	ti_emif_pm.exit_sr = sram_resume_address(ti_emif_pm.exit_sr);
+
+	return 0;
+}
+
+/*
+ * Due to Usage Note 3.1.2 "DDR3: JEDEC Compliance for Maximum
+ * Self-Refresh Command Limit" found in AM335x Silicon Errata
+ * (Document SPRZ360F Revised November 2013) we must configure
+ * the self refresh delay timer to 0xA (8192 cycles) to avoid
+ * generating too many refresh command from the EMIF.
+ */
+static void ti_emif_configure_sr_delay(void)
+{
+	writel(EMIF_POWER_MGMT_WAIT_SELF_REFRESH_8192_CYCLES,
+	       (void *)(ti_emif_base_addr_virt +
+			EMIF_POWER_MANAGEMENT_CONTROL));
+
+	writel(EMIF_POWER_MGMT_WAIT_SELF_REFRESH_8192_CYCLES,
+	       (void *)(ti_emif_base_addr_virt +
+			EMIF_POWER_MANAGEMENT_CTRL_SHDW));
+}
+
+/**
+ * ti_emif_copy_pm_function_table - copy mapping of pm funcs in sram
+ * @dst: void * to address that table should be copied
+ *
+ * Returns 0 if success other error code if table is not available
+ */
+int ti_emif_copy_pm_function_table(void *dst)
+{
+	if (!ti_emif_sram_virt)
+		return -ENODEV;
+
+	memcpy_toio(dst, &ti_emif_pm, sizeof(ti_emif_pm));
+
+	return 0;
+}
+
+/**
+ * ti_emif_get_mem_type - return type for memory type in use
+ *
+ * Returns memory type value read from EMIF or error code if fails
+ */
+int ti_emif_get_mem_type(void)
+{
+	unsigned long temp;
+
+	if (!ti_emif_base_addr_virt || IS_ERR(ti_emif_base_addr_virt))
+		return -ENODEV;
+
+	temp = readl((void *)ti_emif_base_addr_virt + EMIF_SDRAM_CONFIG);
+
+	temp = (temp & SDRAM_TYPE_MASK) >> SDRAM_TYPE_SHIFT;
+	return temp;
+}
+
+static const struct of_device_id ti_emif_of_match[] = {
+	{ .compatible = "ti,am3352-emif", },
+	{},
+};
+
+static int ti_emif_probe(struct platform_device *pdev)
+{
+	int ret = -ENODEV;
+	struct resource *res;
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ti_emif_base_addr_virt = devm_ioremap_resource(dev, res);
+	if (IS_ERR(ti_emif_base_addr_virt)) {
+		dev_err(dev, "could not ioremap emif mem\n");
+		return PTR_ERR(ti_emif_base_addr_virt);
+	}
+
+	ti_emif_base_addr_phys = (void *)res->start;
+
+	ti_emif_configure_sr_delay();
+
+	ret = ti_emif_push_sram(np);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int ti_emif_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct platform_driver ti_emif_driver = {
+	.probe = ti_emif_probe,
+	.remove = ti_emif_remove,
+	.driver = {
+		.name = KBUILD_MODNAME,
+		.of_match_table = of_match_ptr(ti_emif_of_match),
+	},
+};
+
+module_platform_driver(ti_emif_driver);
+
+MODULE_AUTHOR("Dave Gerlach <d-gerlach@ti.com>");
+MODULE_DESCRIPTION("Texas Instruments SRAM EMIF driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" KBUILD_MODNAME);
diff --git a/include/linux/ti-emif-sram.h b/include/linux/ti-emif-sram.h
new file mode 100644
index 0000000..e0ec7fa
--- /dev/null
+++ b/include/linux/ti-emif-sram.h
@@ -0,0 +1,26 @@
+/*
+ * TI AM33XX EMIF Routines
+ *
+ * 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 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_TI_EMIF_H
+#define __LINUX_TI_EMIF_H
+
+#ifdef CONFIG_TI_EMIF_SRAM
+int ti_emif_copy_pm_function_table(void *dst);
+int ti_emif_get_mem_type(void);
+#else
+static inline int ti_emif_copy_pm_function_table(void *dst) { return -ENODEV; }
+static inline int ti_emif_get_mem_type(void) { return -ENODEV; }
+#endif /* CONFIG_TI_EMIF_SRAM */
+#endif /* __LINUX_TI_EMIF_H */
-- 
2.1.0


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

* [RFC PATCH 3/3] ARM: dts: am33xx: Add emif node
  2014-11-26 21:27 [RFC PATCH 0/3] remoteproc: Introduce wkup_m3_rproc driver Dave Gerlach
                   ` (7 preceding siblings ...)
  2014-11-26 21:39 ` [RFC PATCH 2/3] memory: ti-emif-sram: introduce relocatable suspend/resume handlers Dave Gerlach
@ 2014-11-26 21:39 ` Dave Gerlach
  8 siblings, 0 replies; 12+ messages in thread
From: Dave Gerlach @ 2014-11-26 21:39 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, linux-omap, devicetree
  Cc: Tony Lindgren, Benoit Cousson, Kevin Hilman, Greg Kroah-Hartman,
	Dave Gerlach

Add node for Texas Instruments AM335x EMIF to make use of the
ti-emif-sram driver.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
---
 arch/arm/boot/dts/am33xx.dtsi | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
index b86d8c0..b432499 100644
--- a/arch/arm/boot/dts/am33xx.dtsi
+++ b/arch/arm/boot/dts/am33xx.dtsi
@@ -153,6 +153,12 @@
 			#dma-cells = <1>;
 		};
 
+		emif: emif@4c000000 {
+			compatible = "ti,am3352-emif";
+			reg =	<0x4C000000 0x1000>;
+			sram = <&ocmcram>;
+		};
+
 		gpio0: gpio@44e07000 {
 			compatible = "ti,omap4-gpio";
 			ti,hwmods = "gpio1";
-- 
2.1.0


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

* Re: [RFC PATCH 2/3] soc: ti: Add wkup_m3_ipc driver
  2014-11-26 21:38 ` [RFC PATCH 2/3] soc: ti: Add wkup_m3_ipc driver Dave Gerlach
@ 2014-11-26 21:51   ` Arnd Bergmann
  2014-12-12 22:03     ` Dave Gerlach
  0 siblings, 1 reply; 12+ messages in thread
From: Arnd Bergmann @ 2014-11-26 21:51 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Dave Gerlach, linux-kernel, linux-omap, devicetree,
	Ohad Ben-Cohen, Paul Walmsley, Kevin Hilman, Tony Lindgren,
	Benoit Cousson

On Wednesday 26 November 2014 15:38:09 Dave Gerlach wrote:
> +
> +static const 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"},
> +};
> 

This seems awfully specific to some SoC version, and not aware of
IRQ domains. It also seems to be only used in a dev_dbg statement,
so I guess you could just kill this off entirely.

> +static struct rproc *wkup_m3_get_rproc(struct device *dev)
> +{
> +       struct device_node *node;
> +       struct rproc *rp;
> +
> +       node = of_parse_phandle(dev->of_node, "ti,rproc", 0);
> +       if (!node)
> +               return NULL;
> +
> +       dev = bus_find_device(&platform_bus_type, NULL, node, match);
> +       if (!dev)
> +               return NULL;
> +
> +       rp = dev_get_drvdata(dev);
> +       return rp;

This is wrong on a number of levels. I suspect what you really want
is an interface exported from drivers/remoteproc that looks up
a 'struct rproc' and performs the necessary reference counting.

That one can just use of_find_node_by_phandle() to get to
a device_node and use that to look up the rproc device in
a linked list it maintains.

	Arnd

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

* Re: [RFC PATCH 2/3] soc: ti: Add wkup_m3_ipc driver
  2014-11-26 21:51   ` Arnd Bergmann
@ 2014-12-12 22:03     ` Dave Gerlach
  0 siblings, 0 replies; 12+ messages in thread
From: Dave Gerlach @ 2014-12-12 22:03 UTC (permalink / raw)
  To: Arnd Bergmann, linux-arm-kernel
  Cc: linux-kernel, linux-omap, devicetree, Ohad Ben-Cohen,
	Paul Walmsley, Kevin Hilman, Tony Lindgren, Benoit Cousson,
	Ohad Ben-Cohen

On 11/26/2014 03:51 PM, Arnd Bergmann wrote:
> On Wednesday 26 November 2014 15:38:09 Dave Gerlach wrote:
>> +
>> +static const 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"},
>> +};
>>
> 
> This seems awfully specific to some SoC version, and not aware of
> IRQ domains. It also seems to be only used in a dev_dbg statement,
> so I guess you could just kill this off entirely.

This is determined by the firmware in use on the remote processor and works for
both am335x and am437x. However it is not required information and I'd be fine
with taking it out.

> 
>> +static struct rproc *wkup_m3_get_rproc(struct device *dev)
>> +{
>> +       struct device_node *node;
>> +       struct rproc *rp;
>> +
>> +       node = of_parse_phandle(dev->of_node, "ti,rproc", 0);
>> +       if (!node)
>> +               return NULL;
>> +
>> +       dev = bus_find_device(&platform_bus_type, NULL, node, match);
>> +       if (!dev)
>> +               return NULL;
>> +
>> +       rp = dev_get_drvdata(dev);
>> +       return rp;
> 
> This is wrong on a number of levels. I suspect what you really want
> is an interface exported from drivers/remoteproc that looks up
> a 'struct rproc' and performs the necessary reference counting.
> 
> That one can just use of_find_node_by_phandle() to get to
> a device_node and use that to look up the rproc device in
> a linked list it maintains.

Added Ohad as I should have cc'd him in the first place..

Yes I agree that it's not the best solution. There used to be an
rproc_get/rproc_put api but that was removed, I'll look into adding
rproc_get_by_phandle into drivers/remoteproc as that would be ideal for this
situation and a cleaner way of doing it. Thanks for the comments.

Regards,
Dave

> 
> 	Arnd
> 


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

end of thread, other threads:[~2014-12-12 22:03 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-11-26 21:27 [RFC PATCH 0/3] remoteproc: Introduce wkup_m3_rproc driver Dave Gerlach
2014-11-26 21:27 ` [RFC PATCH 1/3] ARM: OMAP2+: Use pdata-quirks for wkup_m3 deassert_hardreset Dave Gerlach
2014-11-26 21:27 ` [RFC PATCH 2/3] Documentation: dt: add ti,am3353-wkup-m3 bindings Dave Gerlach
2014-11-26 21:27 ` [RFC PATCH 3/3] remoteproc: wkup_m3: Add wkup_m3 remote proc driver Dave Gerlach
2014-11-26 21:38 ` [RFC PATCH 1/3] Documentation: dt: add ti,am3353-wkup-m3-ipc bindings Dave Gerlach
2014-11-26 21:38 ` [RFC PATCH 2/3] soc: ti: Add wkup_m3_ipc driver Dave Gerlach
2014-11-26 21:51   ` Arnd Bergmann
2014-12-12 22:03     ` Dave Gerlach
2014-11-26 21:38 ` [RFC PATCH 3/3] ARM: dts: am33xx: Add wkup_m3_ipc node Dave Gerlach
2014-11-26 21:39 ` [RFC PATCH 1/3] Documentation: dt: add ti,am3352-emif bindings Dave Gerlach
2014-11-26 21:39 ` [RFC PATCH 2/3] memory: ti-emif-sram: introduce relocatable suspend/resume handlers Dave Gerlach
2014-11-26 21:39 ` [RFC PATCH 3/3] ARM: dts: am33xx: Add emif node Dave Gerlach

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).