Skip to contentSkip to conversation

ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

PATCH Superseded Russ Dill 6 participants 42 messages Aug 13, 2013 – Sep 3, 2013

Subsystems (6)
lore
Share & tools
lore

Russ Dill

author
#0 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

This is v4 of the OPP50 (VDD CORE 0.95V during suspend) patch set.

Adjusting voltages to this lower operating point during suspend saves additional power. This operating point can only be reached when SDRAM is in self refresh and certain DPLLs have been put into bypass mode. This means that the code to change the regulator to run at the correct voltage must be run from either SRAM or the Cortex-M3 (CM3) PM co-processor must perform the action.

As different boards will power VDD CORE through different methods, the necessary steps must be contained within the device tree. In order to accommodate this, additional device tree properties have been created that can be placed in I2C bus nodes. The properties give a sequence of I2C commands that should be run at suspend and resume time.

The purpose and method of executing these sequences is left up to each platform. In the case of the am33xx, the CM3 firmware writes out the simple I2C sequences.

Each sequence is a series of I2C write commands. The first byte is the length of the write, the second byte the I2C device to address, and the following bytes are the message.

Example:

i2c0: i2c at 44e0b000 {
	pinctrl-names = "default";
	pinctrl-0 = <&i2c0_pins>;

	status = "okay";
	clock-frequency = <400000>;
 
	/* Set OPP50 (0.95V) for VDD core */
	sleep_sequence = /bits/ 8 <
		0x02 0x2d 0x25 0x1f /* Set VDD2 to 0.95V */
	>;

	/* Set OPP100 (1.10V) for VDD core */
	wake_sequence = /bits/ 8 <
		0x02 0x2d 0x25 0x2b /* Set VDD2 to 1.1V */
	>;

	tps: tps at 2d {
		reg = <0x2d>;
	};
};

In the above example, the sequence "0x25 0x1f" is written to the I2C device at address 0x2d (the TPS65910 PMIC). The PMIC interprets that as a write to a register at address 0x25.

I'd really like to get some feedback on the devicetree bindings.

The patch set depends on the latest am335x suspend resume patch set from Dave Gerlach[1], which also depends on the latest Mailbox patch set from Suman Anna[2], and the latest CM3 firmware[3].

Changes since v1:
* Rebased onto new am335x PM branch
* Changed to use 5th param register

Changes since v2:
* Pass bus speed in kHz to M3 firmware

Changes since v3:
* Rebase to Dave Gerlach's am335x suspend/resume patch set
* Add device tree documentation
* Add dts changes for evmsk and gp evm

[1] http://comments.gmane.org/gmane.linux.ports.arm.omap/102561
[2] http://comments.gmane.org/gmane.linux.ports.arm.kernel/258196
[3] http://arago-project.org/git/projects/?p=am33x-cm3.git;a=shortlog;h=refs/heads/next2

Russ Dill (4):
ARM: OMAP2+: AM33XX: I2C Sleep/wake sequence support
ARM: dts: add AM33XX vdd core opp50 suspend for Beaglebone.
ARM: dts: add AM33XX vdd core opp50 suspend for AM335X GP EVM.
ARM: dts: AM33XX vdd core opp50 suspend for EVM-SK

 .../devicetree/bindings/i2c/i2c-suspend-resume.txt | 44 +++++++++++
 arch/arm/boot/dts/am335x-bone.dts                  | 25 +++++-
 arch/arm/boot/dts/am335x-evm.dts                   | 10 +++
 arch/arm/boot/dts/am335x-evmsk.dts                 | 10 +++
 arch/arm/mach-omap2/control.c                      |  1 +
 arch/arm/mach-omap2/pm33xx.c                       | 89 ++++++++++++++++++++++
 arch/arm/mach-omap2/pm33xx.h                       |  2 +
 arch/arm/mach-omap2/wkup_m3.c                      | 57 ++++++++++++--
 8 files changed, 230 insertions(+), 8 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-suspend-resume.txt
Signature · 1 line
1.8.3.2
author
#1 lore raw

PATCH v4 1/4 ARM: OMAP2+: AM33XX: I2C Sleep/wake sequence support

Changes since v1:
* Rebased onto new am335x PM branch
* Changed to use 5th param register

Changes since v2:
* Passes I2C bus speed in kHz to M3 firmware

Changes since v3:
* Rebased to 3.11-rc3, moves some functionality to wkup_m3.c
* Additional comments
* Added device-tree binding documentation

This patch adds the ability to pass an I2C sleep sequence and wake sequence to the Cortex-M3. This is useful for adjusting voltages during sleep that cannot be lowered while SDRAM is active. A modified M3 firmware with I2C support is required.

Each sequence is a series of I2C transfers in the form:

u8 length | u8 chip address | u8 byte0/reg address | u8 byte 1 | u8 byte n ...

The length indicates the number of bytes to transfer, including the register address. The length of each transfer is limited by the I2C buffer size of 32 bytes.

The sequences are taken from the i2c1 node in the device tree. The property name for the sleep sequence is "sleep_sequence" and the property name for the wake sequence is "wake_sequence". Each property should be an array of bytes.

No actions are performed if the properties are not present in the device tree.

---
 .../devicetree/bindings/i2c/i2c-suspend-resume.txt | 44 +++++++++++
 arch/arm/mach-omap2/control.c                      |  1 +
 arch/arm/mach-omap2/pm33xx.c                       | 89 ++++++++++++++++++++++
 arch/arm/mach-omap2/pm33xx.h                       |  2 +
 arch/arm/mach-omap2/wkup_m3.c                      | 57 ++++++++++++--
 5 files changed, 186 insertions(+), 7 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-suspend-resume.txt
Documentation/devicetree/bindings/i2c/i2c-suspend-resume.txt new file +44 -0
new file mode 100644
index 0000000..af19372
@@ -0,0 +1,44 @@
+I2C suspend/resume sequences
+
+This provides the ability for a simple I2C sequence to be written at
+suspend time and resume time. This is for sequences that cannot be written
+by the I2C bus driver for reasons such as needing to be run from SRAM
+or needing to be written by firmware.
+
+The sequence is composed of messages. Each message contains a length byte,
+an address byte, and then the message.
+
+Optional properties:
+- sleep_sequence
+	I2C sequence to write during suspend
+
+- wake_sequence
+	I2C sequence to write during wake
+
+Examples :
+
+i2c0: i2c at 0 {
+	/* Set OPP50 (0.95V) for VDD core */
+	sleep_sequence = /bits/ 8 <
+		0x02 0x24 0x0b 0x6d /* Password unlock 1 */
+		0x02 0x24 0x10 0x02 /* Set DCDC3 to 0.95V */
+		0x02 0x24 0x0b 0x6d /* Password unlock 2 */
+		0x02 0x24 0x10 0x02 /* Set DCDC3 to 0.95V */
+		0x02 0x24 0x0b 0x6c /* Password unlock 1 */
+		0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+		0x02 0x24 0x0b 0x6c /* Password unlock 2 */
+		0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+	>;
+
+	/* Set OPP100 (1.10V) for VDD core */
+	wake_sequence = /bits/ 8 <
+		0x02 0x24 0x0b 0x6d /* Password unlock 1 */
+		0x02 0x24 0x10 0x08 /* Set DCDC3 to 1.1V */
+		0x02 0x24 0x0b 0x6d /* Password unlock 2 */
+		0x02 0x24 0x10 0x08 /* Set DCDC3 to 1.1V */
+		0x02 0x24 0x0b 0x6c /* Password unlock 1 */
+		0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+		0x02 0x24 0x0b 0x6c /* Password unlock 2 */
+		0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+	>;
+}
arch/arm/mach-omap2/control.c +1 -0
index 934041a..dfbd5f0 100644
@@ -639,6 +639,7 @@ void am33xx_pm_ipc_cmd(struct am33xx_ipc_data *data)
 	omap_ctrl_writel(data->param1, AM33XX_CONTROL_IPC_MSG_REG2);
 	omap_ctrl_writel(data->param2, AM33XX_CONTROL_IPC_MSG_REG3);
 	omap_ctrl_writel(data->param3, AM33XX_CONTROL_IPC_MSG_REG4);
+	omap_ctrl_writel(data->param4, AM33XX_CONTROL_IPC_MSG_REG5);
 }
 
 int am33xx_pm_status(void)
arch/arm/mach-omap2/pm33xx.c +89 -0

125 lines not on this page · raw message

arch/arm/mach-omap2/pm33xx.h +2 -0
index befdd11..d0f08a5 100644
@@ -52,6 +52,8 @@ struct forced_standby_module {
 };
 
 int wkup_m3_copy_code(const u8 *data, size_t size);
+void wkup_m3_reset_data_pos(void);
+int wkup_m3_copy_data(const u8 *data, size_t size);
 int wkup_m3_prepare(void);
 void wkup_m3_register_txev_handler(void (*txev_handler)(void));
 
arch/arm/mach-omap2/wkup_m3.c +50 -7

95 lines not on this page · raw message

Signature · 1 line
1.8.3.2
#2 lore raw

PATCH v4 1/4 ARM: OMAP2+: AM33XX: I2C Sleep/wake sequence support

Quoted 11 lines · Russ Dill

Changes since v1:
* Rebased onto new am335x PM branch
* Changed to use 5th param register

Changes since v2:
* Passes I2C bus speed in kHz to M3 firmware

Changes since v3:
* Rebased to 3.11-rc3, moves some functionality to wkup_m3.c
* Additional comments
* Added device-tree binding documentation

AFAIK, Change logs should come below scissors line (---). However there was some discussion to keep it in the commit message. Don't know what happened to it finally. But till date, all revision patch set has the change log under the scissor line.

Quoted 59 lines

This patch adds the ability to pass an I2C sleep sequence and wake sequence to the Cortex-M3. This is useful for adjusting voltages during sleep that cannot be lowered while SDRAM is active. A modified M3 firmware with I2C support is required.

Each sequence is a series of I2C transfers in the form:

u8 length | u8 chip address | u8 byte0/reg address | u8 byte 1 | u8 byte n ...

The length indicates the number of bytes to transfer, including the register address. The length of each transfer is limited by the I2C buffer size of 32 bytes.

The sequences are taken from the i2c1 node in the device tree. The property name for the sleep sequence is "sleep_sequence" and the property name for the wake sequence is "wake_sequence". Each property should be an array of bytes.

No actions are performed if the properties are not present in the device tree.

---
 .../devicetree/bindings/i2c/i2c-suspend-resume.txt | 44 +++++++++++
 arch/arm/mach-omap2/control.c                      |  1 +
 arch/arm/mach-omap2/pm33xx.c                       | 89 ++++++++++++++++++++++
 arch/arm/mach-omap2/pm33xx.h                       |  2 +
 arch/arm/mach-omap2/wkup_m3.c                      | 57 ++++++++++++--
 5 files changed, 186 insertions(+), 7 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-suspend-resume.txt
Documentation/devicetree/bindings/i2c/i2c-suspend-resume.txt new file +21 -0
new file mode 100644
index 0000000..af19372
@@ -0,0 +1,44 @@
+I2C suspend/resume sequences
+
+This provides the ability for a simple I2C sequence to be written at
+suspend time and resume time. This is for sequences that cannot be written
+by the I2C bus driver for reasons such as needing to be run from SRAM
+or needing to be written by firmware.
+
+The sequence is composed of messages. Each message contains a length byte,
+an address byte, and then the message.
+
+Optional properties:
+- sleep_sequence
+	I2C sequence to write during suspend
+
+- wake_sequence
+	I2C sequence to write during wake
+
+Examples :
+
+i2c0: i2c at 0 {
+	/* Set OPP50 (0.95V) for VDD core */

Documentation/devicetree/bindings/i2c/i2c-suspend-resume.txt line 31

Quoted 10 lines
Documentation/devicetree/bindings/i2c/i2c-suspend-resume.txt new file +10 -0
+	sleep_sequence = /bits/ 8 <
+		0x02 0x24 0x0b 0x6d /* Password unlock 1 */
+		0x02 0x24 0x10 0x02 /* Set DCDC3 to 0.95V */
+		0x02 0x24 0x0b 0x6d /* Password unlock 2 */
+		0x02 0x24 0x10 0x02 /* Set DCDC3 to 0.95V */
+		0x02 0x24 0x0b 0x6c /* Password unlock 1 */
+		0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+		0x02 0x24 0x0b 0x6c /* Password unlock 2 */
+		0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+	>;

This data is not related to i2c. Right? Then why is it under i2c dt node?

There is already a wakeup node (wkup_m3) and a pmic node (pmic node for respective boards). Can't these details go under those nodes?

Quoted 53 lines
Documentation/devicetree/bindings/i2c/i2c-suspend-resume.txt new file +13 -0
+
+	/* Set OPP100 (1.10V) for VDD core */
+	wake_sequence = /bits/ 8 <
+		0x02 0x24 0x0b 0x6d /* Password unlock 1 */
+		0x02 0x24 0x10 0x08 /* Set DCDC3 to 1.1V */
+		0x02 0x24 0x0b 0x6d /* Password unlock 2 */
+		0x02 0x24 0x10 0x08 /* Set DCDC3 to 1.1V */
+		0x02 0x24 0x0b 0x6c /* Password unlock 1 */
+		0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+		0x02 0x24 0x0b 0x6c /* Password unlock 2 */
+		0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+	>;
+}
arch/arm/mach-omap2/control.c +1 -0
index 934041a..dfbd5f0 100644
@@ -639,6 +639,7 @@ void am33xx_pm_ipc_cmd(struct am33xx_ipc_data *data)
 	omap_ctrl_writel(data->param1, AM33XX_CONTROL_IPC_MSG_REG2);
 	omap_ctrl_writel(data->param2, AM33XX_CONTROL_IPC_MSG_REG3);
 	omap_ctrl_writel(data->param3, AM33XX_CONTROL_IPC_MSG_REG4);
+	omap_ctrl_writel(data->param4, AM33XX_CONTROL_IPC_MSG_REG5);
 }
 
 int am33xx_pm_status(void)
arch/arm/mach-omap2/pm33xx.c +6 -0
index d291c76..8880da3 100644
@@ -29,6 +29,7 @@
 #include <linux/ti_emif.h>
 #include <linux/omap-mailbox.h>
 
+#include <asm/unaligned.h>
 #include <asm/suspend.h>
 #include <asm/proc-fns.h>
 #include <asm/sizes.h>
@@ -50,6 +51,10 @@
 static void __iomem *am33xx_emif_base;
 static struct powerdomain *cefuse_pwrdm, *gfx_pwrdm, *per_pwrdm, *mpu_pwrdm;
 static struct clockdomain *gfx_l4ls_clkdm;
+static char *am33xx_i2c_sleep_sequence;
+static char *am33xx_i2c_wake_sequence;
+static size_t i2c_sleep_sequence_sz;
+static size_t i2c_wake_sequence_sz;
 
 struct wakeup_src wakeups[] = {
 	{.irq_nr = 35,	.src = "USB0_PHY"},
@@ -232,12 +237,34 @@ static void am33xx_m3_state_machine_reset(void)
 static int am33xx_pm_begin(suspend_state_t state)
 {
 	int i;
+	unsigned long param4;

arch/arm/mach-omap2/pm33xx.c static void am33xx_m3_state_machine_reset(void) · line 250

Quoted 10 lines
arch/arm/mach-omap2/pm33xx.c +7 -0
+	int pos;
 
 	cpu_idle_poll_ctrl(true);
 
+	param4 = DS_IPC_DEFAULT;
+
+	wkup_m3_reset_data_pos();
+	if (am33xx_i2c_sleep_sequence) {
+		pos = wkup_m3_copy_data(am33xx_i2c_sleep_sequence,
+						i2c_sleep_sequence_sz);

Why do we need to copy this data (same constant data) on every iteration?

arch/arm/mach-omap2/pm33xx.c static void am33xx_m3_state_machine_reset(void) · line 263

Quoted 13 lines
arch/arm/mach-omap2/pm33xx.c +13 -0
+		/* Lower 16 bits stores offset to sleep sequence */
+		param4 &= ~0xffff;
+		param4 |= pos;
+	}
+
+	if (am33xx_i2c_wake_sequence) {
+		pos = wkup_m3_copy_data(am33xx_i2c_wake_sequence,
+						i2c_wake_sequence_sz);
+		/* Upper 16 bits stores offset to wake sequence */
+		param4 &= ~0xffff0000;
+		param4 |= pos << 16;
+	}
+

Seems above entire change can be done only once.

Quoted 8 lines
arch/arm/mach-omap2/pm33xx.c +1 -0
 	am33xx_pm->ipc.sleep_mode	= IPC_CMD_DS0;
 	am33xx_pm->ipc.param1		= DS_IPC_DEFAULT;
 	am33xx_pm->ipc.param2		= DS_IPC_DEFAULT;
+	am33xx_pm->ipc.param4		= param4;
 
 	am33xx_pm_ipc_cmd(&am33xx_pm->ipc);
 

arch/arm/mach-omap2/pm33xx.c static int __init am33xx_map_emif(void) · line 422

Quoted 10 lines
arch/arm/mach-omap2/pm33xx.c +7 -0
@@ -386,6 +413,62 @@ static int __init am33xx_map_emif(void)
 	return 0;
 }
 
+static int __init am33xx_setup_sleep_sequence(void)
+{
+	int ret;
+	int sz;
+	const void *prop;
+	struct device *dev;
+	u32 freq_hz = 100000;

Magic number?

arch/arm/mach-omap2/pm33xx.c static int __init am33xx_map_emif(void) · line 429

Quoted 7 lines
arch/arm/mach-omap2/pm33xx.c +7 -0
+	unsigned short freq_khz;
+
+	/*
+	 * We put the device tree node in the I2C controller that will
+	 * be sending the sequence. i2c1 is the only controller that can
+	 * be accessed by the firmware as it is the only controller in the
+	 * WKUP domain.

and on which the PMIC sits I believe?

arch/arm/mach-omap2/pm33xx.c static int __init am33xx_map_emif(void) · line 436

Quoted 7 lines
arch/arm/mach-omap2/pm33xx.c +7 -0
+	 */
+	dev = omap_device_get_by_hwmod_name("i2c1");
+	if (IS_ERR(dev))
+		return PTR_ERR(dev);
+
+	of_property_read_u32(dev->of_node, "clock-frequency", &freq_hz);
+	freq_khz = freq_hz / 1000;

Magic number?

arch/arm/mach-omap2/pm33xx.c static int __init am33xx_map_emif(void) · line 448

Quoted 12 lines
arch/arm/mach-omap2/pm33xx.c +12 -0
+
+	prop = of_get_property(dev->of_node, "sleep_sequence", &sz);
+	if (prop) {
+		/*
+		 * Length is sequence length + 2 bytes for freq_khz, and 1
+		 * byte for terminator.
+		 */
+		am33xx_i2c_sleep_sequence = kzalloc(sz + 3, GFP_KERNEL);
+		if (!am33xx_i2c_sleep_sequence)
+			return -ENOMEM;
+		put_unaligned_le16(freq_khz, am33xx_i2c_sleep_sequence);
+		memcpy(am33xx_i2c_sleep_sequence + 2, prop, sz);

so, looking at entire code, it seems there is 3 memory space for same data (or 1 original + 2 copy)

1. in DT
2. in am33xx_i2c_[sleep/wake]_sequence
3. in SRAm by call to wkup_m3_copy_data()

why not directly copy to SRAM from DT?

Quoted 107 lines
arch/arm/mach-omap2/pm33xx.c +29 -0
+		i2c_sleep_sequence_sz = sz + 3;
+	}
+
+	prop = of_get_property(dev->of_node, "wake_sequence", &sz);
+	if (prop) {
+		am33xx_i2c_wake_sequence = kzalloc(sz + 3, GFP_KERNEL);
+		if (!am33xx_i2c_wake_sequence) {
+			ret = -ENOMEM;
+			goto cleanup_sleep;
+		}
+		put_unaligned_le16(freq_khz, am33xx_i2c_sleep_sequence);
+		memcpy(am33xx_i2c_wake_sequence + 2, prop, sz);
+		i2c_wake_sequence_sz = sz + 3;
+	}
+
+	return 0;
+
+cleanup_sleep:
+	kfree(am33xx_i2c_sleep_sequence);
+	am33xx_i2c_sleep_sequence = NULL;
+	return ret;
+}
+
 int __init am33xx_pm_init(void)
 {
 	int ret;
@@ -451,6 +534,12 @@ int __init am33xx_pm_init(void)
 		}
 	}
 
+	ret = am33xx_setup_sleep_sequence();
+	if (ret) {
+		pr_err("Error fetching I2C sleep/wake sequence\n");
+		goto err;
+	}
+
 	(void) clkdm_for_each(omap_pm_clkdms_setup, NULL);
 
 	/* CEFUSE domain can be turned off post bootup */
arch/arm/mach-omap2/pm33xx.h +2 -0
index befdd11..d0f08a5 100644
@@ -52,6 +52,8 @@ struct forced_standby_module {
 };
 
 int wkup_m3_copy_code(const u8 *data, size_t size);
+void wkup_m3_reset_data_pos(void);
+int wkup_m3_copy_data(const u8 *data, size_t size);
 int wkup_m3_prepare(void);
 void wkup_m3_register_txev_handler(void (*txev_handler)(void));
 
arch/arm/mach-omap2/wkup_m3.c +28 -1
index 8eaa7f3..a541de9 100644
@@ -35,6 +35,9 @@
 struct wkup_m3_context {
 	struct device	*dev;
 	void __iomem	*code;
+	void __iomem	*data;
+	void __iomem	*data_end;
+	size_t		data_size;
 	void (*txev_handler)(void);
 };
 
@@ -50,6 +53,30 @@ int wkup_m3_copy_code(const u8 *data, size_t size)
 	return 0;
 }
 
+/*
+ * This pair of functions allows data to be stuffed into the end of the
+ * CM3 data memory. This is currently used for passing the I2C sleep/wake
+ * sequences to the firmware.
+ */
+
+/* Clear out the pointer for data stored at the end of DMEM */
+void wkup_m3_reset_data_pos(void)
+{
+	wkup_m3->data_end = wkup_m3->data + wkup_m3->data_size;
+}
+
+/*
+ * Store a block of data at the end of DMEM, return the offset within DMEM
+ * that the data is stored at, or -ENOMEM if the data did not fit
+ */
+int wkup_m3_copy_data(const u8 *data, size_t size)
+{
+	if (wkup_m3->data + size > wkup_m3->data_end)
+		return -ENOMEM;
+	wkup_m3->data_end -= size;
+	memcpy_toio(wkup_m3->data_end, data, size);
+	return wkup_m3->data_end - wkup_m3->data;
+}
 
 void wkup_m3_register_txev_handler(void (*txev_handler)(void))
 {
@@ -83,7 +110,7 @@ int wkup_m3_prepare(void)
 static int wkup_m3_probe(struct platform_device *pdev)
 {
 	int irq, ret = 0;
-	struct resource *mem;
+	struct resource *umem, *dmem;
 
 	pm_runtime_enable(&pdev->dev);
 

arch/arm/mach-omap2/wkup_m3.c static int wkup_m3_probe(struct platform_device *pdev) · line 125

Quoted 5 lines
arch/arm/mach-omap2/wkup_m3.c +1 -1
@@ -95,14 +122,21 @@ static int wkup_m3_probe(struct platform_device *pdev)
 
 	irq = platform_get_irq(pdev, 0);
 	if (!irq) {
-		dev_err(wkup_m3->dev, "no irq resource\n");
+		dev_err(&pdev->dev, "no irq resource\n");

unrelated change?. Better to mention this as code cleanup in commit.

Quoted 21 lines
arch/arm/mach-omap2/wkup_m3.c +10 -3
+		ret = -ENXIO;
+		goto err;
+	}
+
+	umem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!umem) {
+		dev_err(&pdev->dev, "no UMEM resource\n");
 		ret = -ENXIO;
 		goto err;
 	}
 
-	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!mem) {
-		dev_err(wkup_m3->dev, "no memory resource\n");
+	dmem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!dmem) {
+		dev_err(&pdev->dev, "no DMEM resource\n");
 		ret = -ENXIO;
 		goto err;
 	}

arch/arm/mach-omap2/wkup_m3.c static int wkup_m3_probe(struct platform_device *pdev) · line 155

Quoted 7 lines
arch/arm/mach-omap2/wkup_m3.c +3 -1
@@ -116,12 +150,21 @@ static int wkup_m3_probe(struct platform_device *pdev)
 
 	wkup_m3->dev = &pdev->dev;
 
-	wkup_m3->code = devm_request_and_ioremap(wkup_m3->dev, mem);
+	wkup_m3->code = devm_request_and_ioremap(wkup_m3->dev, umem);
+	if (!wkup_m3->code) {
+		dev_err(wkup_m3->dev, "could not ioremap UMEM\n");

why not use "pdev->dev" here?

arch/arm/mach-omap2/wkup_m3.c static int wkup_m3_probe(struct platform_device *pdev) · line 161

Quoted 6 lines
arch/arm/mach-omap2/wkup_m3.c +5 -0
+		ret = -EADDRNOTAVAIL;
+		goto err;
+	}
+
+	wkup_m3->data = devm_request_and_ioremap(wkup_m3->dev, dmem);
 	if (!wkup_m3->code) {

I believe this is just a copy/paste error. s/code/data

arch/arm/mach-omap2/wkup_m3.c static int wkup_m3_probe(struct platform_device *pdev) · line 162

Quoted 2 lines
arch/arm/mach-omap2/wkup_m3.c +1 -1
-		dev_err(wkup_m3->dev, "could not ioremap\n");
+		dev_err(wkup_m3->dev, "could not ioremap DMEM\n");

same as above.

Quoted 12 lines
arch/arm/mach-omap2/wkup_m3.c +2 -1
 		ret = -EADDRNOTAVAIL;
 		goto err;
 	}
+	wkup_m3->data_size = resource_size(dmem);
+	wkup_m3_reset_data_pos();
 
 	ret = devm_request_irq(wkup_m3->dev, irq, wkup_m3_txev_handler,
 		  IRQF_DISABLED, "wkup_m3_txev", NULL);
-- 1.8.3.2 -- To unsubscribe from this list: send the line "unsubscribe

linux-omap" in the body of a message to majordomo at vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html

author
#3 lore raw

PATCH v4 1/4 ARM: OMAP2+: AM33XX: I2C Sleep/wake sequence support

Quoted 13 lines · Gururaja Hebbar
Quoted 11 lines · Russ Dill

Changes since v1:
* Rebased onto new am335x PM branch
* Changed to use 5th param register

Changes since v2:
* Passes I2C bus speed in kHz to M3 firmware

Changes since v3:
* Rebased to 3.11-rc3, moves some functionality to wkup_m3.c
* Additional comments
* Added device-tree binding documentation

Quoted 4 lines

AFAIK, Change logs should come below scissors line (---). However there was some discussion to keep it in the commit message. Don't know what happened to it finally. But till date, all revision patch set has the change log under the scissor line.

Will fix is v2.

Quoted 72 lines
Quoted 59 lines

This patch adds the ability to pass an I2C sleep sequence and wake sequence to the Cortex-M3. This is useful for adjusting voltages during sleep that cannot be lowered while SDRAM is active. A modified M3 firmware with I2C support is required.

Each sequence is a series of I2C transfers in the form:

u8 length | u8 chip address | u8 byte0/reg address | u8 byte 1 | u8 byte n ...

The length indicates the number of bytes to transfer, including the register address. The length of each transfer is limited by the I2C buffer size of 32 bytes.

The sequences are taken from the i2c1 node in the device tree. The property name for the sleep sequence is "sleep_sequence" and the property name for the wake sequence is "wake_sequence". Each property should be an array of bytes.

No actions are performed if the properties are not present in the device tree.

---
 .../devicetree/bindings/i2c/i2c-suspend-resume.txt | 44 +++++++++++
 arch/arm/mach-omap2/control.c                      |  1 +
 arch/arm/mach-omap2/pm33xx.c                       | 89 ++++++++++++++++++++++
 arch/arm/mach-omap2/pm33xx.h                       |  2 +
 arch/arm/mach-omap2/wkup_m3.c                      | 57 ++++++++++++--
 5 files changed, 186 insertions(+), 7 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-suspend-resume.txt
Documentation/devicetree/bindings/i2c/i2c-suspend-resume.txt new file +21 -0
new file mode 100644
index 0000000..af19372
@@ -0,0 +1,44 @@
+I2C suspend/resume sequences
+
+This provides the ability for a simple I2C sequence to be written at
+suspend time and resume time. This is for sequences that cannot be written
+by the I2C bus driver for reasons such as needing to be run from SRAM
+or needing to be written by firmware.
+
+The sequence is composed of messages. Each message contains a length byte,
+an address byte, and then the message.
+
+Optional properties:
+- sleep_sequence
+     I2C sequence to write during suspend
+
+- wake_sequence
+     I2C sequence to write during wake
+
+Examples :
+
+i2c0: i2c at 0 {
+     /* Set OPP50 (0.95V) for VDD core */

Documentation/devicetree/bindings/i2c/i2c-suspend-resume.txt line 31

Quoted 10 lines
Documentation/devicetree/bindings/i2c/i2c-suspend-resume.txt new file +10 -0
+     sleep_sequence = /bits/ 8 <
+             0x02 0x24 0x0b 0x6d /* Password unlock 1 */
+             0x02 0x24 0x10 0x02 /* Set DCDC3 to 0.95V */
+             0x02 0x24 0x0b 0x6d /* Password unlock 2 */
+             0x02 0x24 0x10 0x02 /* Set DCDC3 to 0.95V */
+             0x02 0x24 0x0b 0x6c /* Password unlock 1 */
+             0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+             0x02 0x24 0x0b 0x6c /* Password unlock 2 */
+             0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+     >;

This data is not related to i2c. Right? Then why is it under i2c dt node?

Quoted 2 lines

There is already a wakeup node (wkup_m3) and a pmic node (pmic node for respective boards). Can't these details go under those nodes?

The i2c node was chosen for several reasons. First that the bus speed is included in the message to the CM3 firmware. The bus speed and sequence can then be read from the same device tree node. Second so it's clear which I2C bus this is being sent out on. Including it under the PMIC node would mean that the code would have to search each child of the I2C bus for a sequence and if multiple children had sequences, it would not know in which order to send those sequences out.

Quoted 57 lines
Quoted 57 lines
Documentation/devicetree/bindings/i2c/i2c-suspend-resume.txt new file +13 -0
+
+     /* Set OPP100 (1.10V) for VDD core */
+     wake_sequence = /bits/ 8 <
+             0x02 0x24 0x0b 0x6d /* Password unlock 1 */
+             0x02 0x24 0x10 0x08 /* Set DCDC3 to 1.1V */
+             0x02 0x24 0x0b 0x6d /* Password unlock 2 */
+             0x02 0x24 0x10 0x08 /* Set DCDC3 to 1.1V */
+             0x02 0x24 0x0b 0x6c /* Password unlock 1 */
+             0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+             0x02 0x24 0x0b 0x6c /* Password unlock 2 */
+             0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+     >;
+}
arch/arm/mach-omap2/control.c +1 -0
index 934041a..dfbd5f0 100644
@@ -639,6 +639,7 @@ void am33xx_pm_ipc_cmd(struct am33xx_ipc_data *data)
      omap_ctrl_writel(data->param1, AM33XX_CONTROL_IPC_MSG_REG2);
      omap_ctrl_writel(data->param2, AM33XX_CONTROL_IPC_MSG_REG3);
      omap_ctrl_writel(data->param3, AM33XX_CONTROL_IPC_MSG_REG4);
+     omap_ctrl_writel(data->param4, AM33XX_CONTROL_IPC_MSG_REG5);
 }
 
 int am33xx_pm_status(void)
arch/arm/mach-omap2/pm33xx.c +7 -0
index d291c76..8880da3 100644
@@ -29,6 +29,7 @@
 #include <linux/ti_emif.h>
 #include <linux/omap-mailbox.h>
 
+#include <asm/unaligned.h>
 #include <asm/suspend.h>
 #include <asm/proc-fns.h>
 #include <asm/sizes.h>
@@ -50,6 +51,10 @@
 static void __iomem *am33xx_emif_base;
 static struct powerdomain *cefuse_pwrdm, *gfx_pwrdm, *per_pwrdm, *mpu_pwrdm;
 static struct clockdomain *gfx_l4ls_clkdm;
+static char *am33xx_i2c_sleep_sequence;
+static char *am33xx_i2c_wake_sequence;
+static size_t i2c_sleep_sequence_sz;
+static size_t i2c_wake_sequence_sz;
 
 struct wakeup_src wakeups[] = {
      {.irq_nr = 35,  .src = "USB0_PHY"},
@@ -232,12 +237,34 @@ static void am33xx_m3_state_machine_reset(void)
 static int am33xx_pm_begin(suspend_state_t state)
 {
      int i;
+     unsigned long param4;
+     int pos;
 
      cpu_idle_poll_ctrl(true);
 
Quoted 8 lines

arch/arm/mach-omap2/pm33xx.c static void am33xx_m3_state_machine_reset(void) · line 250

Quoted 6 lines
arch/arm/mach-omap2/pm33xx.c +6 -0
+     param4 = DS_IPC_DEFAULT;
+
+     wkup_m3_reset_data_pos();
+     if (am33xx_i2c_sleep_sequence) {
+             pos = wkup_m3_copy_data(am33xx_i2c_sleep_sequence,
+                                             i2c_sleep_sequence_sz);

Why do we need to copy this data (same constant data) on every iteration?

Because the CM3 firmware is reset after each suspend/resume cycle. The firmware reset handler reinitializes the DMEM.

Quoted 27 lines

arch/arm/mach-omap2/pm33xx.c static void am33xx_m3_state_machine_reset(void) · line 263

Quoted 13 lines
arch/arm/mach-omap2/pm33xx.c +13 -0
+             /* Lower 16 bits stores offset to sleep sequence */
+             param4 &= ~0xffff;
+             param4 |= pos;
+     }
+
+     if (am33xx_i2c_wake_sequence) {
+             pos = wkup_m3_copy_data(am33xx_i2c_wake_sequence,
+                                             i2c_wake_sequence_sz);
+             /* Upper 16 bits stores offset to wake sequence */
+             param4 &= ~0xffff0000;
+             param4 |= pos << 16;
+     }
+

Seems above entire change can be done only once.

Quoted 11 lines
arch/arm/mach-omap2/pm33xx.c +1 -0
      am33xx_pm->ipc.sleep_mode       = IPC_CMD_DS0;
      am33xx_pm->ipc.param1           = DS_IPC_DEFAULT;
      am33xx_pm->ipc.param2           = DS_IPC_DEFAULT;
+     am33xx_pm->ipc.param4           = param4;
 
      am33xx_pm_ipc_cmd(&am33xx_pm->ipc);
 
@@ -386,6 +413,62 @@ static int __init am33xx_map_emif(void)
      return 0;
 }
 
Quoted 9 lines

arch/arm/mach-omap2/pm33xx.c static int __init am33xx_map_emif(void) · line 422

Quoted 7 lines
arch/arm/mach-omap2/pm33xx.c +7 -0
+static int __init am33xx_setup_sleep_sequence(void)
+{
+     int ret;
+     int sz;
+     const void *prop;
+     struct device *dev;
+     u32 freq_hz = 100000;

Magic number?

It's taken from drivers/i2c/busses/i2c-omap.c

    u32 freq = 100000; /* default to 100000 Hz */

I'll add a comment to that effect.

Quoted 9 lines

arch/arm/mach-omap2/pm33xx.c static int __init am33xx_map_emif(void) · line 429

Quoted 7 lines
arch/arm/mach-omap2/pm33xx.c +7 -0
+     unsigned short freq_khz;
+
+     /*
+      * We put the device tree node in the I2C controller that will
+      * be sending the sequence. i2c1 is the only controller that can
+      * be accessed by the firmware as it is the only controller in the
+      * WKUP domain.

and on which the PMIC sits I believe?

Yes, but this code is designed not to be PMIC specific as one could chose to regulate VDD_CORE with any PMIC, or even with a standalone I2C controlled regulator.

Quoted 9 lines

arch/arm/mach-omap2/pm33xx.c static int __init am33xx_map_emif(void) · line 436

Quoted 7 lines
arch/arm/mach-omap2/pm33xx.c +7 -0
+      */
+     dev = omap_device_get_by_hwmod_name("i2c1");
+     if (IS_ERR(dev))
+             return PTR_ERR(dev);
+
+     of_property_read_u32(dev->of_node, "clock-frequency", &freq_hz);
+     freq_khz = freq_hz / 1000;

Magic number?

Nah, converting between metric prefixes this way is pretty common in the kernel.

Quoted 13 lines

arch/arm/mach-omap2/pm33xx.c static int __init am33xx_map_emif(void) · line 448

Quoted 12 lines
arch/arm/mach-omap2/pm33xx.c +12 -0
+
+     prop = of_get_property(dev->of_node, "sleep_sequence", &sz);
+     if (prop) {
+             /*
+              * Length is sequence length + 2 bytes for freq_khz, and 1
+              * byte for terminator.
+              */
+             am33xx_i2c_sleep_sequence = kzalloc(sz + 3, GFP_KERNEL);
+             if (!am33xx_i2c_sleep_sequence)
+                     return -ENOMEM;
+             put_unaligned_le16(freq_khz, am33xx_i2c_sleep_sequence);
+             memcpy(am33xx_i2c_sleep_sequence + 2, prop, sz);
Quoted 8 lines

so, looking at entire code, it seems there is 3 memory space for same data (or 1 original + 2 copy)

1. in DT
2. in am33xx_i2c_[sleep/wake]_sequence
3. in SRAm by call to wkup_m3_copy_data()

why not directly copy to SRAM from DT?

As pointed out above, the firmware reset handler would wipe it out.

Quoted 106 lines
Quoted 106 lines
arch/arm/mach-omap2/pm33xx.c +29 -0
+             i2c_sleep_sequence_sz = sz + 3;
+     }
+
+     prop = of_get_property(dev->of_node, "wake_sequence", &sz);
+     if (prop) {
+             am33xx_i2c_wake_sequence = kzalloc(sz + 3, GFP_KERNEL);
+             if (!am33xx_i2c_wake_sequence) {
+                     ret = -ENOMEM;
+                     goto cleanup_sleep;
+             }
+             put_unaligned_le16(freq_khz, am33xx_i2c_sleep_sequence);
+             memcpy(am33xx_i2c_wake_sequence + 2, prop, sz);
+             i2c_wake_sequence_sz = sz + 3;
+     }
+
+     return 0;
+
+cleanup_sleep:
+     kfree(am33xx_i2c_sleep_sequence);
+     am33xx_i2c_sleep_sequence = NULL;
+     return ret;
+}
+
 int __init am33xx_pm_init(void)
 {
      int ret;
@@ -451,6 +534,12 @@ int __init am33xx_pm_init(void)
              }
      }
 
+     ret = am33xx_setup_sleep_sequence();
+     if (ret) {
+             pr_err("Error fetching I2C sleep/wake sequence\n");
+             goto err;
+     }
+
      (void) clkdm_for_each(omap_pm_clkdms_setup, NULL);
 
      /* CEFUSE domain can be turned off post bootup */
arch/arm/mach-omap2/pm33xx.h +2 -0
index befdd11..d0f08a5 100644
@@ -52,6 +52,8 @@ struct forced_standby_module {
 };
 
 int wkup_m3_copy_code(const u8 *data, size_t size);
+void wkup_m3_reset_data_pos(void);
+int wkup_m3_copy_data(const u8 *data, size_t size);
 int wkup_m3_prepare(void);
 void wkup_m3_register_txev_handler(void (*txev_handler)(void));
 
arch/arm/mach-omap2/wkup_m3.c +28 -1
index 8eaa7f3..a541de9 100644
@@ -35,6 +35,9 @@
 struct wkup_m3_context {
      struct device   *dev;
      void __iomem    *code;
+     void __iomem    *data;
+     void __iomem    *data_end;
+     size_t          data_size;
      void (*txev_handler)(void);
 };
 
@@ -50,6 +53,30 @@ int wkup_m3_copy_code(const u8 *data, size_t size)
      return 0;
 }
 
+/*
+ * This pair of functions allows data to be stuffed into the end of the
+ * CM3 data memory. This is currently used for passing the I2C sleep/wake
+ * sequences to the firmware.
+ */
+
+/* Clear out the pointer for data stored at the end of DMEM */
+void wkup_m3_reset_data_pos(void)
+{
+     wkup_m3->data_end = wkup_m3->data + wkup_m3->data_size;
+}
+
+/*
+ * Store a block of data at the end of DMEM, return the offset within DMEM
+ * that the data is stored at, or -ENOMEM if the data did not fit
+ */
+int wkup_m3_copy_data(const u8 *data, size_t size)
+{
+     if (wkup_m3->data + size > wkup_m3->data_end)
+             return -ENOMEM;
+     wkup_m3->data_end -= size;
+     memcpy_toio(wkup_m3->data_end, data, size);
+     return wkup_m3->data_end - wkup_m3->data;
+}
 
 void wkup_m3_register_txev_handler(void (*txev_handler)(void))
 {
@@ -83,7 +110,7 @@ int wkup_m3_prepare(void)
 static int wkup_m3_probe(struct platform_device *pdev)
 {
      int irq, ret = 0;
-     struct resource *mem;
+     struct resource *umem, *dmem;
 
      pm_runtime_enable(&pdev->dev);
 
Quoted 8 lines

arch/arm/mach-omap2/wkup_m3.c static int wkup_m3_probe(struct platform_device *pdev) · line 125

Quoted 6 lines
arch/arm/mach-omap2/wkup_m3.c +1 -1
@@ -95,14 +122,21 @@ static int wkup_m3_probe(struct platform_device *pdev)
 
      irq = platform_get_irq(pdev, 0);
      if (!irq) {
-             dev_err(wkup_m3->dev, "no irq resource\n");
+             dev_err(&pdev->dev, "no irq resource\n");

unrelated change?. Better to mention this as code cleanup in commit.

Will add a comment to that effect, the underlying error should be fixed in the next suspend/resume patch though.

Quoted 22 lines
Quoted 22 lines
arch/arm/mach-omap2/wkup_m3.c +10 -3
+             ret = -ENXIO;
+             goto err;
+     }
+
+     umem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+     if (!umem) {
+             dev_err(&pdev->dev, "no UMEM resource\n");
              ret = -ENXIO;
              goto err;
      }
 
-     mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-     if (!mem) {
-             dev_err(wkup_m3->dev, "no memory resource\n");
+     dmem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+     if (!dmem) {
+             dev_err(&pdev->dev, "no DMEM resource\n");
              ret = -ENXIO;
              goto err;
      }
@@ -116,12 +150,21 @@ static int wkup_m3_probe(struct platform_device *pdev)
 
Quoted 8 lines

arch/arm/mach-omap2/wkup_m3.c static int wkup_m3_probe(struct platform_device *pdev) · line 155

Quoted 6 lines
arch/arm/mach-omap2/wkup_m3.c +3 -1
      wkup_m3->dev = &pdev->dev;
 
-     wkup_m3->code = devm_request_and_ioremap(wkup_m3->dev, mem);
+     wkup_m3->code = devm_request_and_ioremap(wkup_m3->dev, umem);
+     if (!wkup_m3->code) {
+             dev_err(wkup_m3->dev, "could not ioremap UMEM\n");

why not use "pdev->dev" here?

Either one works

Quoted 8 lines

arch/arm/mach-omap2/wkup_m3.c static int wkup_m3_probe(struct platform_device *pdev) · line 161

Quoted 6 lines
arch/arm/mach-omap2/wkup_m3.c +5 -0
+             ret = -EADDRNOTAVAIL;
+             goto err;
+     }
+
+     wkup_m3->data = devm_request_and_ioremap(wkup_m3->dev, dmem);
      if (!wkup_m3->code) {

I believe this is just a copy/paste error. s/code/data

Doh, thanks!

Quoted 23 lines

arch/arm/mach-omap2/wkup_m3.c static int wkup_m3_probe(struct platform_device *pdev) · line 162

Quoted 2 lines
arch/arm/mach-omap2/wkup_m3.c +1 -1
-             dev_err(wkup_m3->dev, "could not ioremap\n");
+             dev_err(wkup_m3->dev, "could not ioremap DMEM\n");

same as above.

Quoted 9 lines
arch/arm/mach-omap2/wkup_m3.c +2 -1
              ret = -EADDRNOTAVAIL;
              goto err;
      }
+     wkup_m3->data_size = resource_size(dmem);
+     wkup_m3_reset_data_pos();
 
      ret = devm_request_irq(wkup_m3->dev, irq, wkup_m3_txev_handler,
                IRQF_DISABLED, "wkup_m3_txev", NULL);
-- 1.8.3.2 -- To unsubscribe from this list: send the line "unsubscribe
Quoted 3 lines

linux-omap" in the body of a message to majordomo at vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html

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

#4 lore raw

PATCH v4 1/4 ARM: OMAP2+: AM33XX: I2C Sleep/wake sequence support

Quoted 96 lines · Russ Dill
Quoted 13 lines · Gururaja Hebbar
Quoted 11 lines · Russ Dill

Changes since v1:
* Rebased onto new am335x PM branch
* Changed to use 5th param register

Changes since v2:
* Passes I2C bus speed in kHz to M3 firmware

Changes since v3:
* Rebased to 3.11-rc3, moves some functionality to wkup_m3.c
* Additional comments
* Added device-tree binding documentation

Quoted 4 lines

AFAIK, Change logs should come below scissors line (---). However there was some discussion to keep it in the commit message. Don't know what happened to it finally. But till date, all revision patch set has the change log under the scissor line.

Will fix is v2.

Quoted 72 lines
Quoted 59 lines

This patch adds the ability to pass an I2C sleep sequence and wake sequence to the Cortex-M3. This is useful for adjusting voltages during sleep that cannot be lowered while SDRAM is active. A modified M3 firmware with I2C support is required.

Each sequence is a series of I2C transfers in the form:

u8 length | u8 chip address | u8 byte0/reg address | u8 byte 1 | u8 byte n ...

The length indicates the number of bytes to transfer, including the register address. The length of each transfer is limited by the I2C buffer size of 32 bytes.

The sequences are taken from the i2c1 node in the device tree. The property name for the sleep sequence is "sleep_sequence" and the property name for the wake sequence is "wake_sequence". Each property should be an array of bytes.

No actions are performed if the properties are not present in the device tree.

---
 .../devicetree/bindings/i2c/i2c-suspend-resume.txt | 44 +++++++++++
 arch/arm/mach-omap2/control.c                      |  1 +
 arch/arm/mach-omap2/pm33xx.c                       | 89 ++++++++++++++++++++++
 arch/arm/mach-omap2/pm33xx.h                       |  2 +
 arch/arm/mach-omap2/wkup_m3.c                      | 57 ++++++++++++--
 5 files changed, 186 insertions(+), 7 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-suspend-resume.txt
Documentation/devicetree/bindings/i2c/i2c-suspend-resume.txt new file +21 -0
new file mode 100644
index 0000000..af19372
@@ -0,0 +1,44 @@
+I2C suspend/resume sequences
+
+This provides the ability for a simple I2C sequence to be written at
+suspend time and resume time. This is for sequences that cannot be written
+by the I2C bus driver for reasons such as needing to be run from SRAM
+or needing to be written by firmware.
+
+The sequence is composed of messages. Each message contains a length byte,
+an address byte, and then the message.
+
+Optional properties:
+- sleep_sequence
+     I2C sequence to write during suspend
+
+- wake_sequence
+     I2C sequence to write during wake
+
+Examples :
+
+i2c0: i2c at 0 {
+     /* Set OPP50 (0.95V) for VDD core */

Documentation/devicetree/bindings/i2c/i2c-suspend-resume.txt line 31

Quoted 10 lines
Documentation/devicetree/bindings/i2c/i2c-suspend-resume.txt new file +10 -0
+     sleep_sequence = /bits/ 8 <
+             0x02 0x24 0x0b 0x6d /* Password unlock 1 */
+             0x02 0x24 0x10 0x02 /* Set DCDC3 to 0.95V */
+             0x02 0x24 0x0b 0x6d /* Password unlock 2 */
+             0x02 0x24 0x10 0x02 /* Set DCDC3 to 0.95V */
+             0x02 0x24 0x0b 0x6c /* Password unlock 1 */
+             0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+             0x02 0x24 0x0b 0x6c /* Password unlock 2 */
+             0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+     >;

This data is not related to i2c. Right? Then why is it under i2c dt node?

Quoted 2 lines

There is already a wakeup node (wkup_m3) and a pmic node (pmic node for respective boards). Can't these details go under those nodes?

Quoted 7 lines

The i2c node was chosen for several reasons. First that the bus speed is included in the message to the CM3 firmware. The bus speed and sequence can then be read from the same device tree node. Second so it's clear which I2C bus this is being sent out on. Including it under the PMIC node would mean that the code would have to search each child of the I2C bus for a sequence and if multiple children had sequences, it would not know in which order to send those sequences out.

Agreed

Quoted 67 lines
Quoted 58 lines
Quoted 58 lines
Documentation/devicetree/bindings/i2c/i2c-suspend-resume.txt new file +13 -0
+
+     /* Set OPP100 (1.10V) for VDD core */
+     wake_sequence = /bits/ 8 <
+             0x02 0x24 0x0b 0x6d /* Password unlock 1 */
+             0x02 0x24 0x10 0x08 /* Set DCDC3 to 1.1V */
+             0x02 0x24 0x0b 0x6d /* Password unlock 2 */
+             0x02 0x24 0x10 0x08 /* Set DCDC3 to 1.1V */
+             0x02 0x24 0x0b 0x6c /* Password unlock 1 */
+             0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+             0x02 0x24 0x0b 0x6c /* Password unlock 2 */
+             0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+     >;
+}
arch/arm/mach-omap2/control.c +1 -0
index 934041a..dfbd5f0 100644
@@ -639,6 +639,7 @@ void am33xx_pm_ipc_cmd(struct am33xx_ipc_data *data)
      omap_ctrl_writel(data->param1, AM33XX_CONTROL_IPC_MSG_REG2);
      omap_ctrl_writel(data->param2, AM33XX_CONTROL_IPC_MSG_REG3);
      omap_ctrl_writel(data->param3, AM33XX_CONTROL_IPC_MSG_REG4);
+     omap_ctrl_writel(data->param4, AM33XX_CONTROL_IPC_MSG_REG5);
 }
 
 int am33xx_pm_status(void)
arch/arm/mach-omap2/pm33xx.c +7 -0
index d291c76..8880da3 100644
@@ -29,6 +29,7 @@
 #include <linux/ti_emif.h>
 #include <linux/omap-mailbox.h>
 
+#include <asm/unaligned.h>
 #include <asm/suspend.h>
 #include <asm/proc-fns.h>
 #include <asm/sizes.h>
@@ -50,6 +51,10 @@
 static void __iomem *am33xx_emif_base;
 static struct powerdomain *cefuse_pwrdm, *gfx_pwrdm, *per_pwrdm, *mpu_pwrdm;
 static struct clockdomain *gfx_l4ls_clkdm;
+static char *am33xx_i2c_sleep_sequence;
+static char *am33xx_i2c_wake_sequence;
+static size_t i2c_sleep_sequence_sz;
+static size_t i2c_wake_sequence_sz;
 
 struct wakeup_src wakeups[] = {
      {.irq_nr = 35,  .src = "USB0_PHY"},
@@ -232,12 +237,34 @@ static void am33xx_m3_state_machine_reset(void)
 static int am33xx_pm_begin(suspend_state_t state)
 {
      int i;
+     unsigned long param4;
+     int pos;
 
      cpu_idle_poll_ctrl(true);
 
Quoted 8 lines

arch/arm/mach-omap2/pm33xx.c static void am33xx_m3_state_machine_reset(void) · line 250

Quoted 6 lines
arch/arm/mach-omap2/pm33xx.c +6 -0
+     param4 = DS_IPC_DEFAULT;
+
+     wkup_m3_reset_data_pos();
+     if (am33xx_i2c_sleep_sequence) {
+             pos = wkup_m3_copy_data(am33xx_i2c_sleep_sequence,
+                                             i2c_sleep_sequence_sz);

Why do we need to copy this data (same constant data) on every iteration?

Quoted 2 lines

Because the CM3 firmware is reset after each suspend/resume cycle. The firmware reset handler reinitializes the DMEM.

Is it. Do you have any more information about the same?

Even then, you can copy from DT to CM3 and remove 2nd temp storage. Right?

Quoted 66 lines
Quoted 28 lines
Quoted 4 lines
arch/arm/mach-omap2/pm33xx.c +3 -0
+             /* Lower 16 bits stores offset to sleep sequence */
+             param4 &= ~0xffff;
+             param4 |= pos;

arch/arm/mach-omap2/pm33xx.c static void am33xx_m3_state_machine_reset(void) · line 263

Quoted 10 lines
arch/arm/mach-omap2/pm33xx.c +10 -0
+     }
+
+     if (am33xx_i2c_wake_sequence) {
+             pos = wkup_m3_copy_data(am33xx_i2c_wake_sequence,
+                                             i2c_wake_sequence_sz);
+             /* Upper 16 bits stores offset to wake sequence */
+             param4 &= ~0xffff0000;
+             param4 |= pos << 16;
+     }
+

Seems above entire change can be done only once.

Quoted 11 lines
arch/arm/mach-omap2/pm33xx.c +1 -0
      am33xx_pm->ipc.sleep_mode       = IPC_CMD_DS0;
      am33xx_pm->ipc.param1           = DS_IPC_DEFAULT;
      am33xx_pm->ipc.param2           = DS_IPC_DEFAULT;
+     am33xx_pm->ipc.param4           = param4;
 
      am33xx_pm_ipc_cmd(&am33xx_pm->ipc);
 
@@ -386,6 +413,62 @@ static int __init am33xx_map_emif(void)
      return 0;
 }
 
Quoted 9 lines

arch/arm/mach-omap2/pm33xx.c static int __init am33xx_map_emif(void) · line 422

Quoted 7 lines
arch/arm/mach-omap2/pm33xx.c +7 -0
+static int __init am33xx_setup_sleep_sequence(void)
+{
+     int ret;
+     int sz;
+     const void *prop;
+     struct device *dev;
+     u32 freq_hz = 100000;

Magic number?

It's taken from drivers/i2c/busses/i2c-omap.c

    u32 freq = 100000; /* default to 100000 Hz */

I'll add a comment to that effect.

Quoted 9 lines

arch/arm/mach-omap2/pm33xx.c static int __init am33xx_map_emif(void) · line 429

Quoted 7 lines
arch/arm/mach-omap2/pm33xx.c +7 -0
+     unsigned short freq_khz;
+
+     /*
+      * We put the device tree node in the I2C controller that will
+      * be sending the sequence. i2c1 is the only controller that can
+      * be accessed by the firmware as it is the only controller in the
+      * WKUP domain.

and on which the PMIC sits I believe?

Yes, but this code is designed not to be PMIC specific as one could chose to regulate VDD_CORE with any PMIC, or even with a standalone I2C controlled regulator.

Quoted 8 lines
Quoted 7 lines
arch/arm/mach-omap2/pm33xx.c +7 -0
+      */
+     dev = omap_device_get_by_hwmod_name("i2c1");
+     if (IS_ERR(dev))
+             return PTR_ERR(dev);
+
+     of_property_read_u32(dev->of_node, "clock-frequency", &freq_hz);
+     freq_khz = freq_hz / 1000;
Quoted 3 lines
Quoted 1 line

Magic number?

Nah, converting between metric prefixes this way is pretty common in the kernel.

oh. Correct.

Quoted 17 lines
Quoted 17 lines

arch/arm/mach-omap2/pm33xx.c static int __init am33xx_map_emif(void) · line 448

Quoted 13 lines
arch/arm/mach-omap2/pm33xx.c +12 -0
+
+     prop = of_get_property(dev->of_node, "sleep_sequence", &sz);
+     if (prop) {
+             /*
+              * Length is sequence length + 2 bytes for freq_khz, and 1
+              * byte for terminator.
+              */
+             am33xx_i2c_sleep_sequence = kzalloc(sz + 3, GFP_KERNEL);
+             if (!am33xx_i2c_sleep_sequence)
+                     return -ENOMEM;
+             put_unaligned_le16(freq_khz, am33xx_i2c_sleep_sequence);
+             memcpy(am33xx_i2c_sleep_sequence + 2, prop, sz);

so, looking at entire code, it seems there is 3 memory space for same data (or 1 original + 2 copy)

Quoted 7 lines
Quoted 5 lines

1. in DT
2. in am33xx_i2c_[sleep/wake]_sequence
3. in SRAm by call to wkup_m3_copy_data()

why not directly copy to SRAM from DT?

As pointed out above, the firmware reset handler would wipe it out.

i have update my comment above.

Quoted 143 lines
Quoted 107 lines
Quoted 107 lines
arch/arm/mach-omap2/pm33xx.c +29 -0
+             i2c_sleep_sequence_sz = sz + 3;
+     }
+
+     prop = of_get_property(dev->of_node, "wake_sequence", &sz);
+     if (prop) {
+             am33xx_i2c_wake_sequence = kzalloc(sz + 3, GFP_KERNEL);
+             if (!am33xx_i2c_wake_sequence) {
+                     ret = -ENOMEM;
+                     goto cleanup_sleep;
+             }
+             put_unaligned_le16(freq_khz, am33xx_i2c_sleep_sequence);
+             memcpy(am33xx_i2c_wake_sequence + 2, prop, sz);
+             i2c_wake_sequence_sz = sz + 3;
+     }
+
+     return 0;
+
+cleanup_sleep:
+     kfree(am33xx_i2c_sleep_sequence);
+     am33xx_i2c_sleep_sequence = NULL;
+     return ret;
+}
+
 int __init am33xx_pm_init(void)
 {
      int ret;
@@ -451,6 +534,12 @@ int __init am33xx_pm_init(void)
              }
      }
 
+     ret = am33xx_setup_sleep_sequence();
+     if (ret) {
+             pr_err("Error fetching I2C sleep/wake sequence\n");
+             goto err;
+     }
+
      (void) clkdm_for_each(omap_pm_clkdms_setup, NULL);
 
      /* CEFUSE domain can be turned off post bootup */
arch/arm/mach-omap2/pm33xx.h +2 -0
index befdd11..d0f08a5 100644
@@ -52,6 +52,8 @@ struct forced_standby_module {
 };
 
 int wkup_m3_copy_code(const u8 *data, size_t size);
+void wkup_m3_reset_data_pos(void);
+int wkup_m3_copy_data(const u8 *data, size_t size);
 int wkup_m3_prepare(void);
 void wkup_m3_register_txev_handler(void (*txev_handler)(void));
 
arch/arm/mach-omap2/wkup_m3.c +28 -1
index 8eaa7f3..a541de9 100644
@@ -35,6 +35,9 @@
 struct wkup_m3_context {
      struct device   *dev;
      void __iomem    *code;
+     void __iomem    *data;
+     void __iomem    *data_end;
+     size_t          data_size;
      void (*txev_handler)(void);
 };
 
@@ -50,6 +53,30 @@ int wkup_m3_copy_code(const u8 *data, size_t size)
      return 0;
 }
 
+/*
+ * This pair of functions allows data to be stuffed into the end of the
+ * CM3 data memory. This is currently used for passing the I2C sleep/wake
+ * sequences to the firmware.
+ */
+
+/* Clear out the pointer for data stored at the end of DMEM */
+void wkup_m3_reset_data_pos(void)
+{
+     wkup_m3->data_end = wkup_m3->data + wkup_m3->data_size;
+}
+
+/*
+ * Store a block of data at the end of DMEM, return the offset within DMEM
+ * that the data is stored at, or -ENOMEM if the data did not fit
+ */
+int wkup_m3_copy_data(const u8 *data, size_t size)
+{
+     if (wkup_m3->data + size > wkup_m3->data_end)
+             return -ENOMEM;
+     wkup_m3->data_end -= size;
+     memcpy_toio(wkup_m3->data_end, data, size);
+     return wkup_m3->data_end - wkup_m3->data;
+}
 
 void wkup_m3_register_txev_handler(void (*txev_handler)(void))
 {
@@ -83,7 +110,7 @@ int wkup_m3_prepare(void)
 static int wkup_m3_probe(struct platform_device *pdev)
 {
      int irq, ret = 0;
-     struct resource *mem;
+     struct resource *umem, *dmem;
 
      pm_runtime_enable(&pdev->dev);
 
Quoted 8 lines

arch/arm/mach-omap2/wkup_m3.c static int wkup_m3_probe(struct platform_device *pdev) · line 125

Quoted 6 lines
arch/arm/mach-omap2/wkup_m3.c +1 -1
@@ -95,14 +122,21 @@ static int wkup_m3_probe(struct platform_device *pdev)
 
      irq = platform_get_irq(pdev, 0);
      if (!irq) {
-             dev_err(wkup_m3->dev, "no irq resource\n");
+             dev_err(&pdev->dev, "no irq resource\n");

unrelated change?. Better to mention this as code cleanup in commit.

Will add a comment to that effect, the underlying error should be fixed in the next suspend/resume patch though.

Quoted 24 lines
Quoted 24 lines
arch/arm/mach-omap2/wkup_m3.c +10 -3
+             ret = -ENXIO;
+             goto err;
+     }
+
+     umem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+     if (!umem) {
+             dev_err(&pdev->dev, "no UMEM resource\n");
              ret = -ENXIO;
              goto err;
      }
 
-     mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-     if (!mem) {
-             dev_err(wkup_m3->dev, "no memory resource\n");
+     dmem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+     if (!dmem) {
+             dev_err(&pdev->dev, "no DMEM resource\n");
              ret = -ENXIO;
              goto err;
      }
@@ -116,12 +150,21 @@ static int wkup_m3_probe(struct platform_device *pdev)
 
      wkup_m3->dev = &pdev->dev;
 
Quoted 8 lines
Quoted 6 lines

arch/arm/mach-omap2/wkup_m3.c static int wkup_m3_probe(struct platform_device *pdev) · line 155

Quoted 4 lines
arch/arm/mach-omap2/wkup_m3.c +3 -1
-     wkup_m3->code = devm_request_and_ioremap(wkup_m3->dev, mem);
+     wkup_m3->code = devm_request_and_ioremap(wkup_m3->dev, umem);
+     if (!wkup_m3->code) {
+             dev_err(wkup_m3->dev, "could not ioremap UMEM\n");

why not use "pdev->dev" here?

Either one works

just for consistency.

Quoted 35 lines
Quoted 9 lines

arch/arm/mach-omap2/wkup_m3.c static int wkup_m3_probe(struct platform_device *pdev) · line 161

Quoted 7 lines
arch/arm/mach-omap2/wkup_m3.c +5 -0
+             ret = -EADDRNOTAVAIL;
+             goto err;
+     }
+
+     wkup_m3->data = devm_request_and_ioremap(wkup_m3->dev, dmem);
      if (!wkup_m3->code) {

I believe this is just a copy/paste error. s/code/data

Doh, thanks!

Quoted 23 lines

arch/arm/mach-omap2/wkup_m3.c static int wkup_m3_probe(struct platform_device *pdev) · line 162

Quoted 2 lines
arch/arm/mach-omap2/wkup_m3.c +1 -1
-             dev_err(wkup_m3->dev, "could not ioremap\n");
+             dev_err(wkup_m3->dev, "could not ioremap DMEM\n");

same as above.

Quoted 9 lines
arch/arm/mach-omap2/wkup_m3.c +2 -1
              ret = -EADDRNOTAVAIL;
              goto err;
      }
+     wkup_m3->data_size = resource_size(dmem);
+     wkup_m3_reset_data_pos();
 
      ret = devm_request_irq(wkup_m3->dev, irq, wkup_m3_txev_handler,
                IRQF_DISABLED, "wkup_m3_txev", NULL);
-- 1.8.3.2 -- To unsubscribe from this list: send the line "unsubscribe
Quoted 3 lines

linux-omap" in the body of a message to majordomo at vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html

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

#5 lore raw

PATCH v4 1/4 ARM: OMAP2+: AM33XX: I2C Sleep/wake sequence support

In reply to Russ Dill
Quoted 7 lines · Russ Dill
Quoted 6 lines · Gururaja Hebbar
Quoted 3 lines · Russ Dill

Changes since v1:
* Rebased onto new am335x PM branch
* Changed to use 5th param register

[snip]
[snip]

Quoted 10 lines
Quoted 7 lines

arch/arm/mach-omap2/pm33xx.c static void am33xx_m3_state_machine_reset(void) · line 250

Quoted 5 lines
arch/arm/mach-omap2/pm33xx.c +5 -0
+
+     wkup_m3_reset_data_pos();
+     if (am33xx_i2c_sleep_sequence) {
+             pos = wkup_m3_copy_data(am33xx_i2c_sleep_sequence,
+                                             i2c_sleep_sequence_sz);

Why do we need to copy this data (same constant data) on every iteration?

Because the CM3 firmware is reset after each suspend/resume cycle. The firmware reset handler reinitializes the DMEM.

Well in that why can't the i2c payload be copied to UMEM?

Thanks & regards
Gururaja

Quoted 279 lines
Quoted 28 lines
Quoted 4 lines
arch/arm/mach-omap2/pm33xx.c +3 -0
+             /* Lower 16 bits stores offset to sleep sequence */
+             param4 &= ~0xffff;
+             param4 |= pos;

arch/arm/mach-omap2/pm33xx.c static void am33xx_m3_state_machine_reset(void) · line 263

Quoted 10 lines
arch/arm/mach-omap2/pm33xx.c +10 -0
+     }
+
+     if (am33xx_i2c_wake_sequence) {
+             pos = wkup_m3_copy_data(am33xx_i2c_wake_sequence,
+                                             i2c_wake_sequence_sz);
+             /* Upper 16 bits stores offset to wake sequence */
+             param4 &= ~0xffff0000;
+             param4 |= pos << 16;
+     }
+

Seems above entire change can be done only once.

Quoted 11 lines
      am33xx_pm->ipc.sleep_mode       = IPC_CMD_DS0;
      am33xx_pm->ipc.param1           = DS_IPC_DEFAULT;
      am33xx_pm->ipc.param2           = DS_IPC_DEFAULT;
arch/arm/mach-omap2/pm33xx.c +1 -0
+     am33xx_pm->ipc.param4           = param4;
 
      am33xx_pm_ipc_cmd(&am33xx_pm->ipc);
@@ -386,6 +413,62 @@ static int __init am33xx_map_emif(void)
      return 0;
 }
 
Quoted 9 lines

arch/arm/mach-omap2/pm33xx.c static int __init am33xx_map_emif(void) · line 422

Quoted 7 lines
arch/arm/mach-omap2/pm33xx.c +7 -0
+static int __init am33xx_setup_sleep_sequence(void)
+{
+     int ret;
+     int sz;
+     const void *prop;
+     struct device *dev;
+     u32 freq_hz = 100000;

Magic number?

It's taken from drivers/i2c/busses/i2c-omap.c

    u32 freq = 100000; /* default to 100000 Hz */

I'll add a comment to that effect.

Quoted 9 lines

arch/arm/mach-omap2/pm33xx.c static int __init am33xx_map_emif(void) · line 429

Quoted 7 lines
arch/arm/mach-omap2/pm33xx.c +7 -0
+     unsigned short freq_khz;
+
+     /*
+      * We put the device tree node in the I2C controller that will
+      * be sending the sequence. i2c1 is the only controller that can
+      * be accessed by the firmware as it is the only controller in the
+      * WKUP domain.

and on which the PMIC sits I believe?

Yes, but this code is designed not to be PMIC specific as one could chose to regulate VDD_CORE with any PMIC, or even with a standalone I2C controlled regulator.

Quoted 9 lines

arch/arm/mach-omap2/pm33xx.c static int __init am33xx_map_emif(void) · line 436

Quoted 7 lines
arch/arm/mach-omap2/pm33xx.c +7 -0
+      */
+     dev = omap_device_get_by_hwmod_name("i2c1");
+     if (IS_ERR(dev))
+             return PTR_ERR(dev);
+
+     of_property_read_u32(dev->of_node, "clock-frequency", &freq_hz);
+     freq_khz = freq_hz / 1000;

Magic number?

Nah, converting between metric prefixes this way is pretty common in the kernel.

Quoted 13 lines

arch/arm/mach-omap2/pm33xx.c static int __init am33xx_map_emif(void) · line 448

Quoted 12 lines
arch/arm/mach-omap2/pm33xx.c +12 -0
+
+     prop = of_get_property(dev->of_node, "sleep_sequence", &sz);
+     if (prop) {
+             /*
+              * Length is sequence length + 2 bytes for freq_khz, and 1
+              * byte for terminator.
+              */
+             am33xx_i2c_sleep_sequence = kzalloc(sz + 3, GFP_KERNEL);
+             if (!am33xx_i2c_sleep_sequence)
+                     return -ENOMEM;
+             put_unaligned_le16(freq_khz, am33xx_i2c_sleep_sequence);
+             memcpy(am33xx_i2c_sleep_sequence + 2, prop, sz);
Quoted 8 lines

so, looking at entire code, it seems there is 3 memory space for same data (or 1 original + 2 copy)

1. in DT
2. in am33xx_i2c_[sleep/wake]_sequence
3. in SRAm by call to wkup_m3_copy_data()

why not directly copy to SRAM from DT?

As pointed out above, the firmware reset handler would wipe it out.

Quoted 106 lines
Quoted 106 lines
arch/arm/mach-omap2/pm33xx.c +29 -0
+             i2c_sleep_sequence_sz = sz + 3;
+     }
+
+     prop = of_get_property(dev->of_node, "wake_sequence", &sz);
+     if (prop) {
+             am33xx_i2c_wake_sequence = kzalloc(sz + 3, GFP_KERNEL);
+             if (!am33xx_i2c_wake_sequence) {
+                     ret = -ENOMEM;
+                     goto cleanup_sleep;
+             }
+             put_unaligned_le16(freq_khz, am33xx_i2c_sleep_sequence);
+             memcpy(am33xx_i2c_wake_sequence + 2, prop, sz);
+             i2c_wake_sequence_sz = sz + 3;
+     }
+
+     return 0;
+
+cleanup_sleep:
+     kfree(am33xx_i2c_sleep_sequence);
+     am33xx_i2c_sleep_sequence = NULL;
+     return ret;
+}
+
 int __init am33xx_pm_init(void)
 {
      int ret;
@@ -451,6 +534,12 @@ int __init am33xx_pm_init(void)
              }
      }
 
+     ret = am33xx_setup_sleep_sequence();
+     if (ret) {
+             pr_err("Error fetching I2C sleep/wake sequence\n");
+             goto err;
+     }
+
      (void) clkdm_for_each(omap_pm_clkdms_setup, NULL);
 
      /* CEFUSE domain can be turned off post bootup */
arch/arm/mach-omap2/pm33xx.h +2 -0
index befdd11..d0f08a5 100644
@@ -52,6 +52,8 @@ struct forced_standby_module {
 };
 
 int wkup_m3_copy_code(const u8 *data, size_t size);
+void wkup_m3_reset_data_pos(void);
+int wkup_m3_copy_data(const u8 *data, size_t size);
 int wkup_m3_prepare(void);
 void wkup_m3_register_txev_handler(void (*txev_handler)(void));
 
arch/arm/mach-omap2/wkup_m3.c +28 -1
index 8eaa7f3..a541de9 100644
@@ -35,6 +35,9 @@
 struct wkup_m3_context {
      struct device   *dev;
      void __iomem    *code;
+     void __iomem    *data;
+     void __iomem    *data_end;
+     size_t          data_size;
      void (*txev_handler)(void);
 };
 
@@ -50,6 +53,30 @@ int wkup_m3_copy_code(const u8 *data, size_t size)
      return 0;
 }
 
+/*
+ * This pair of functions allows data to be stuffed into the end of the
+ * CM3 data memory. This is currently used for passing the I2C sleep/wake
+ * sequences to the firmware.
+ */
+
+/* Clear out the pointer for data stored at the end of DMEM */
+void wkup_m3_reset_data_pos(void)
+{
+     wkup_m3->data_end = wkup_m3->data + wkup_m3->data_size;
+}
+
+/*
+ * Store a block of data at the end of DMEM, return the offset within DMEM
+ * that the data is stored at, or -ENOMEM if the data did not fit
+ */
+int wkup_m3_copy_data(const u8 *data, size_t size)
+{
+     if (wkup_m3->data + size > wkup_m3->data_end)
+             return -ENOMEM;
+     wkup_m3->data_end -= size;
+     memcpy_toio(wkup_m3->data_end, data, size);
+     return wkup_m3->data_end - wkup_m3->data;
+}
 
 void wkup_m3_register_txev_handler(void (*txev_handler)(void))
 {
@@ -83,7 +110,7 @@ int wkup_m3_prepare(void)
 static int wkup_m3_probe(struct platform_device *pdev)
 {
      int irq, ret = 0;
-     struct resource *mem;
+     struct resource *umem, *dmem;
 
      pm_runtime_enable(&pdev->dev);
 
Quoted 8 lines

arch/arm/mach-omap2/wkup_m3.c static int wkup_m3_probe(struct platform_device *pdev) · line 125

Quoted 6 lines
arch/arm/mach-omap2/wkup_m3.c +1 -1
@@ -95,14 +122,21 @@ static int wkup_m3_probe(struct platform_device *pdev)
 
      irq = platform_get_irq(pdev, 0);
      if (!irq) {
-             dev_err(wkup_m3->dev, "no irq resource\n");
+             dev_err(&pdev->dev, "no irq resource\n");

unrelated change?. Better to mention this as code cleanup in commit.

Will add a comment to that effect, the underlying error should be fixed in the next suspend/resume patch though.

Quoted 22 lines
Quoted 22 lines
arch/arm/mach-omap2/wkup_m3.c +10 -3
+             ret = -ENXIO;
+             goto err;
+     }
+
+     umem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+     if (!umem) {
+             dev_err(&pdev->dev, "no UMEM resource\n");
              ret = -ENXIO;
              goto err;
      }
 
-     mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-     if (!mem) {
-             dev_err(wkup_m3->dev, "no memory resource\n");
+     dmem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+     if (!dmem) {
+             dev_err(&pdev->dev, "no DMEM resource\n");
              ret = -ENXIO;
              goto err;
      }
@@ -116,12 +150,21 @@ static int wkup_m3_probe(struct platform_device *pdev)
 
Quoted 8 lines

arch/arm/mach-omap2/wkup_m3.c static int wkup_m3_probe(struct platform_device *pdev) · line 155

Quoted 6 lines
arch/arm/mach-omap2/wkup_m3.c +3 -1
      wkup_m3->dev = &pdev->dev;
 
-     wkup_m3->code = devm_request_and_ioremap(wkup_m3->dev, mem);
+     wkup_m3->code = devm_request_and_ioremap(wkup_m3->dev, umem);
+     if (!wkup_m3->code) {
+             dev_err(wkup_m3->dev, "could not ioremap UMEM\n");

why not use "pdev->dev" here?

Either one works

Quoted 8 lines

arch/arm/mach-omap2/wkup_m3.c static int wkup_m3_probe(struct platform_device *pdev) · line 161

Quoted 6 lines
arch/arm/mach-omap2/wkup_m3.c +5 -0
+             ret = -EADDRNOTAVAIL;
+             goto err;
+     }
+
+     wkup_m3->data = devm_request_and_ioremap(wkup_m3->dev, dmem);
      if (!wkup_m3->code) {

I believe this is just a copy/paste error. s/code/data

Doh, thanks!

Quoted 23 lines

arch/arm/mach-omap2/wkup_m3.c static int wkup_m3_probe(struct platform_device *pdev) · line 162

Quoted 2 lines
arch/arm/mach-omap2/wkup_m3.c +1 -1
-             dev_err(wkup_m3->dev, "could not ioremap\n");
+             dev_err(wkup_m3->dev, "could not ioremap DMEM\n");

same as above.

Quoted 9 lines
arch/arm/mach-omap2/wkup_m3.c +2 -1
              ret = -EADDRNOTAVAIL;
              goto err;
      }
+     wkup_m3->data_size = resource_size(dmem);
+     wkup_m3_reset_data_pos();
 
      ret = devm_request_irq(wkup_m3->dev, irq, wkup_m3_txev_handler,
                IRQF_DISABLED, "wkup_m3_txev", NULL);
-- 1.8.3.2 -- To unsubscribe from this list: send the line "unsubscribe
Quoted 3 lines

linux-omap" in the body of a message to majordomo at vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html

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

author
#6 lore raw

PATCH v4 1/4 ARM: OMAP2+: AM33XX: I2C Sleep/wake sequence support

Quoted 21 lines · Gururaja Hebbar
Quoted 7 lines · Russ Dill
Quoted 6 lines · Gururaja Hebbar
Quoted 3 lines · Russ Dill

Changes since v1:
* Rebased onto new am335x PM branch
* Changed to use 5th param register

[snip]
[snip]

Quoted 8 lines
Quoted 7 lines

arch/arm/mach-omap2/pm33xx.c static void am33xx_m3_state_machine_reset(void) · line 250

Quoted 5 lines
arch/arm/mach-omap2/pm33xx.c +5 -0
+
+     wkup_m3_reset_data_pos();
+     if (am33xx_i2c_sleep_sequence) {
+             pos = wkup_m3_copy_data(am33xx_i2c_sleep_sequence,
+                                             i2c_sleep_sequence_sz);

Why do we need to copy this data (same constant data) on every iteration?

Quoted 8 lines
Quoted 2 lines

Because the CM3 firmware is reset after each suspend/resume cycle. The firmware reset handler reinitializes the DMEM.

Well in that why can't the i2c payload be copied to UMEM?

Thanks & regards
Gururaja

I've given this some thought, and gone back and forth a bit. UMEM is a bit more complicated because the linker decides what should go where. Also, it may be that in the future, either the PM for am335x or am43xx will be writing different sequences depending on the suspend mode/options.

Is there a specific aspect of copying the data each suspend cycle that you are trying to fix? Is it just that the data could only be copied once? Or is it the latency of the copy?

Thanks!

#7 lore raw

PATCH v4 1/4 ARM: OMAP2+: AM33XX: I2C Sleep/wake sequence support

Quoted 26 lines · Russ Dill
Quoted 24 lines · Gururaja Hebbar
Quoted 7 lines · Russ Dill
Quoted 6 lines · Gururaja Hebbar
Quoted 3 lines · Russ Dill

Changes since v1:
* Rebased onto new am335x PM branch
* Changed to use 5th param register

[snip]
[snip]

Quoted 10 lines
Quoted 7 lines

arch/arm/mach-omap2/pm33xx.c static void am33xx_m3_state_machine_reset(void) · line 250

Quoted 5 lines
arch/arm/mach-omap2/pm33xx.c +5 -0
+
+     wkup_m3_reset_data_pos();
+     if (am33xx_i2c_sleep_sequence) {
+             pos = wkup_m3_copy_data(am33xx_i2c_sleep_sequence,
+                                             i2c_sleep_sequence_sz);

Why do we need to copy this data (same constant data) on every iteration?

Because the CM3 firmware is reset after each suspend/resume cycle. The firmware reset handler reinitializes the DMEM.

Quoted 8 lines
Quoted 5 lines

Well in that why can't the i2c payload be copied to UMEM?

Thanks & regards
Gururaja

I've given this some thought, and gone back and forth a bit. UMEM is a bit more complicated because the linker decides what should go where.

but linker doesn't know about the copy and the location we are doing at runtime.

Quoted 3 lines

Also, it may be that in the future, either the PM for am335x or am43xx will be writing different sequences depending on the suspend mode/options.

still these info will be coming from DT and can be copied to UMEM. only issue could be space.

Quoted 4 lines

Is there a specific aspect of copying the data each suspend cycle that you are trying to fix? Is it just that the data could only be copied once? Or is it the latency of the copy?

Copy latency on every iterations is what worries me. it will surely add a delay for suspend.

Quoted 3 lines

Thanks!

author
#8 lore raw

PATCH v4 2/4 ARM: dts: add AM33XX vdd core opp50 suspend for Beaglebone.

In reply to Russ Dill

Changes since v1:
* Rebased onto new am335x PM branch

This adds a sleep and wake sequence to set the VDD core voltage to the OPP50 level, 0.950V. This saves power during suspend. The sequences are specific to the Beaglebone layout and PMIC, the TPS65217. The sequences are written out by the Cortex-M3.

---
 arch/arm/boot/dts/am335x-bone.dts | 25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)
arch/arm/boot/dts/am335x-bone.dts +24 -1
index 444b4ed..3f6528d 100644
@@ -127,10 +127,33 @@
 			status = "okay";
 			clock-frequency = <400000>;
 
+			/* Set OPP50 (0.95V) for VDD core */
+			sleep_sequence = /bits/ 8 <
+				0x02 0x24 0x0b 0x6d /* Password unlock 1 */
+				0x02 0x24 0x10 0x02 /* Set DCDC3 to 0.95V */
+				0x02 0x24 0x0b 0x6d /* Password unlock 2 */
+				0x02 0x24 0x10 0x02 /* Set DCDC3 to 0.95V */
+				0x02 0x24 0x0b 0x6c /* Password unlock 1 */
+				0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+				0x02 0x24 0x0b 0x6c /* Password unlock 2 */
+				0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+			>;
+
+			/* Set OPP100 (1.10V) for VDD core */
+			wake_sequence = /bits/ 8 <
+				0x02 0x24 0x0b 0x6d /* Password unlock 1 */
+				0x02 0x24 0x10 0x08 /* Set DCDC3 to 1.1V */
+				0x02 0x24 0x0b 0x6d /* Password unlock 2 */
+				0x02 0x24 0x10 0x08 /* Set DCDC3 to 1.1V */
+				0x02 0x24 0x0b 0x6c /* Password unlock 1 */
+				0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+				0x02 0x24 0x0b 0x6c /* Password unlock 2 */
+				0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+			>;
+
 			tps: tps at 24 {
 				reg = <0x24>;
 			};
-
 		};
 	};
 
Signature · 1 line
1.8.3.2
#9 lore raw

PATCH v4 2/4 ARM: dts: add AM33XX vdd core opp50 suspend for Beaglebone.

Quoted 18 lines · Russ Dill

Changes since v1:
* Rebased onto new am335x PM branch

This adds a sleep and wake sequence to set the VDD core voltage to the OPP50 level, 0.950V. This saves power during suspend. The sequences are specific to the Beaglebone layout and PMIC, the TPS65217. The sequences are written out by the Cortex-M3.

---
 arch/arm/boot/dts/am335x-bone.dts | 25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)
arch/arm/boot/dts/am335x-bone.dts +0 -0
index 444b4ed..3f6528d 100644

arch/arm/boot/dts/am335x-bone.dts line 131

Quoted 5 lines
arch/arm/boot/dts/am335x-bone.dts +2 -0
@@ -127,10 +127,33 @@
 			status = "okay";
 			clock-frequency = <400000>;
 
+			/* Set OPP50 (0.95V) for VDD core */
+			sleep_sequence = /bits/ 8 <

For user readability, can you mention the PMIC used here as a comment?

Quoted 30 lines
arch/arm/boot/dts/am335x-bone.dts +22 -1
+				0x02 0x24 0x0b 0x6d /* Password unlock 1 */
+				0x02 0x24 0x10 0x02 /* Set DCDC3 to 0.95V */
+				0x02 0x24 0x0b 0x6d /* Password unlock 2 */
+				0x02 0x24 0x10 0x02 /* Set DCDC3 to 0.95V */
+				0x02 0x24 0x0b 0x6c /* Password unlock 1 */
+				0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+				0x02 0x24 0x0b 0x6c /* Password unlock 2 */
+				0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+			>;
+
+			/* Set OPP100 (1.10V) for VDD core */
+			wake_sequence = /bits/ 8 <
+				0x02 0x24 0x0b 0x6d /* Password unlock 1 */
+				0x02 0x24 0x10 0x08 /* Set DCDC3 to 1.1V */
+				0x02 0x24 0x0b 0x6d /* Password unlock 2 */
+				0x02 0x24 0x10 0x08 /* Set DCDC3 to 1.1V */
+				0x02 0x24 0x0b 0x6c /* Password unlock 1 */
+				0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+				0x02 0x24 0x0b 0x6c /* Password unlock 2 */
+				0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+			>;
+
 			tps: tps at 24 {
 				reg = <0x24>;
 			};
-
 		};
 	};
 
author
#10 lore raw

PATCH v4 2/4 ARM: dts: add AM33XX vdd core opp50 suspend for Beaglebone.

Quoted 22 lines · Gururaja Hebbar
Quoted 21 lines · Russ Dill

Changes since v1:
* Rebased onto new am335x PM branch

This adds a sleep and wake sequence to set the VDD core voltage to the OPP50 level, 0.950V. This saves power during suspend. The sequences are specific to the Beaglebone layout and PMIC, the TPS65217. The sequences are written out by the Cortex-M3.

---
 arch/arm/boot/dts/am335x-bone.dts | 25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)
arch/arm/boot/dts/am335x-bone.dts +0 -0
index 444b4ed..3f6528d 100644
@@ -127,10 +127,33 @@
                      status = "okay";
                      clock-frequency = <400000>;
 
Quoted 4 lines

arch/arm/boot/dts/am335x-bone.dts line 131

Quoted 2 lines
arch/arm/boot/dts/am335x-bone.dts +2 -0
+                     /* Set OPP50 (0.95V) for VDD core */
+                     sleep_sequence = /bits/ 8 <

For user readability, can you mention the PMIC used here as a comment?

Sounds like a good modification, will do.

Quoted 36 lines
Quoted 20 lines
arch/arm/boot/dts/am335x-bone.dts +19 -0
+                             0x02 0x24 0x0b 0x6d /* Password unlock 1 */
+                             0x02 0x24 0x10 0x02 /* Set DCDC3 to 0.95V */
+                             0x02 0x24 0x0b 0x6d /* Password unlock 2 */
+                             0x02 0x24 0x10 0x02 /* Set DCDC3 to 0.95V */
+                             0x02 0x24 0x0b 0x6c /* Password unlock 1 */
+                             0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+                             0x02 0x24 0x0b 0x6c /* Password unlock 2 */
+                             0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+                     >;
+
+                     /* Set OPP100 (1.10V) for VDD core */
+                     wake_sequence = /bits/ 8 <
+                             0x02 0x24 0x0b 0x6d /* Password unlock 1 */
+                             0x02 0x24 0x10 0x08 /* Set DCDC3 to 1.1V */
+                             0x02 0x24 0x0b 0x6d /* Password unlock 2 */
+                             0x02 0x24 0x10 0x08 /* Set DCDC3 to 1.1V */
+                             0x02 0x24 0x0b 0x6c /* Password unlock 1 */
+                             0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+                             0x02 0x24 0x0b 0x6c /* Password unlock 2 */

arch/arm/boot/dts/am335x-bone.dts line 159

Quoted 10 lines
arch/arm/boot/dts/am335x-bone.dts +3 -1
+                             0x02 0x24 0x11 0x86 /* Apply DCDC changes */
+                     >;
+
                      tps: tps at 24 {
                              reg = <0x24>;
                      };
-
              };
      };
 

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

author
#11 lore raw

PATCH v4 3/4 ARM: dts: add AM33XX vdd core opp50 suspend for AM335X GP EVM.

In reply to Russ Dill

From: Hebbar, Gururaja <[email protected]>

This adds a sleep and wake sequence to set the VDD core voltage to the OPP50 level, 0.950V. This saves power during suspend. The sequences are specific to the EVM layout and PMIC, the TPS65910. The sequences are written out by the Cortex-M3.

---
 arch/arm/boot/dts/am335x-evm.dts | 10 ++++++++++
 1 file changed, 10 insertions(+)
arch/arm/boot/dts/am335x-evm.dts +10 -0
index 3aee1a4..81ce169 100644
@@ -166,6 +166,16 @@
 			status = "okay";
 			clock-frequency = <400000>;
 
+			/* Set OPP50 (0.95V) for VDD core */
+			sleep_sequence = /bits/ 8 <
+				0x02 0x2d 0x25 0x1f /* Set VDD2 to 0.95V */
+			>;
+
+			/* Set OPP100 (1.10V) for VDD core */
+			wake_sequence = /bits/ 8 <
+				0x02 0x2d 0x25 0x2b /* Set VDD2 to 1.1V */
+			>;
+
 			tps: tps at 2d {
 				reg = <0x2d>;
 			};
Signature · 1 line
1.8.3.2
author
#12 lore raw

PATCH v4 4/4 ARM: dts: AM33XX vdd core opp50 suspend for EVM-SK

In reply to Russ Dill

From: Hebbar, Gururaja <[email protected]>

This adds a sleep and wake sequence to set the VDD core voltage to the OPP50 level, 0.950V. This saves power during suspend. The sequences are specific to the EVM-SK layout and PMIC, the TPS65910. The sequences are written out by the Cortex-M3.

---
 arch/arm/boot/dts/am335x-evmsk.dts | 10 ++++++++++
 1 file changed, 10 insertions(+)
arch/arm/boot/dts/am335x-evmsk.dts +10 -0
index 0c8ad17..704bbb3 100644
@@ -174,6 +174,16 @@
 			status = "okay";
 			clock-frequency = <400000>;
 
+			/* Set OPP50 (0.95V) for VDD core */
+			sleep_sequence = /bits/ 8 <
+				0x02 0x2d 0x25 0x1f /* Set VDD2 to 0.95V */
+			>;
+
+			/* Set OPP100 (1.10V) for VDD core */
+			wake_sequence = /bits/ 8 <
+				0x02 0x2d 0x25 0x2b /* Set VDD2 to 1.1V */
+			>;
+
 			tps: tps at 2d {
 				reg = <0x2d>;
 			};
Signature · 1 line
1.8.3.2
#13 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

In reply to Russ Dill
Quoted 17 lines · Russ Dill

The purpose and method of executing these sequences is left up to each platform. In the case of the am33xx, the CM3 firmware writes out the simple I2C sequences.

Each sequence is a series of I2C write commands. The first byte is the length of the write, the second byte the I2C device to address, and the following bytes are the message.

        /* Set OPP100 (1.10V) for VDD core */
        wake_sequence = /bits/ 8 <
                0x02 0x2d 0x25 0x2b /* Set VDD2 to 1.1V */
        >;

        tps: tps at 2d {
                reg = <0x2d>;
        };
Quoted 5 lines

In the above example, the sequence "0x25 0x1f" is written to the I2C device at address 0x2d (the TPS65910 PMIC). The PMIC interprets that as a write to a register at address 0x25.

I'd really like to get some feedback on the devicetree bindings.

Shouldn't the TPS driver know how to generate this sequence? It seems fragile to do voltage adjustments behind the back of the regulator framework and the TPS driver. The wake-sequence values should match the (in-memory) regulator configuration on resume (which may have been changed by DVFS).

The CM3 driver needs to figure out where the core regulator is connected using using either DT or the regulator framework and ask the TPS (via a new interface) for register writes for sleep/wake sequences. Then those sequences will actually match the correct voltages configured using DT/DVFS.

Regards,
Jan

Signature · 4 lines
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
author
#14 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

Quoted 22 lines · Jan L?bbe
Quoted 20 lines · Russ Dill

The purpose and method of executing these sequences is left up to each platform. In the case of the am33xx, the CM3 firmware writes out the simple I2C sequences.

Each sequence is a series of I2C write commands. The first byte is the length of the write, the second byte the I2C device to address, and the following bytes are the message.

        /* Set OPP100 (1.10V) for VDD core */
        wake_sequence = /bits/ 8 <
                0x02 0x2d 0x25 0x2b /* Set VDD2 to 1.1V */
        >;

        tps: tps at 2d {
                reg = <0x2d>;
        };

In the above example, the sequence "0x25 0x1f" is written to the I2C device at address 0x2d (the TPS65910 PMIC). The PMIC interprets that as a write to a register at address 0x25.

Quoted 7 lines
Quoted 1 line

I'd really like to get some feedback on the devicetree bindings.

Shouldn't the TPS driver know how to generate this sequence? It seems fragile to do voltage adjustments behind the back of the regulator framework and the TPS driver. The wake-sequence values should match the (in-memory) regulator configuration on resume (which may have been changed by DVFS).

The sequence is both PMIC specific and board specific. Additionally, the PMICs used aren't am335x specific. It would be nice to have the regulator framework and the driver write all this out, but the sequence is written out by the Cortex-M3 processor running some PM firmware. Even if the code was changed to run on the A8, it'd have to run from a small piece of SRAM.

As far as DVFS, I'm not aware of any DVFS implementations that muck with VDD CORE.

Quoted 5 lines

The CM3 driver needs to figure out where the core regulator is connected using using either DT or the regulator framework and ask the TPS (via a new interface) for register writes for sleep/wake sequences. Then those sequences will actually match the correct voltages configured using DT/DVFS.

That seems like it'd add a lot of complexity to the regulator subsystem, as well as all the PMIC and other I2C regulators that any users of these device tree properties may end up using for not a lot of gain. There would be two separate code paths for any used I2C regulator or PMIC for setting voltages.

Quoted 13 lines

Regards,
Jan
--

Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

#15 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

Quoted 11 lines · Russ Dill
Quoted 5 lines

The CM3 driver needs to figure out where the core regulator is connected using using either DT or the regulator framework and ask the TPS (via a new interface) for register writes for sleep/wake sequences. Then those sequences will actually match the correct voltages configured using DT/DVFS.

That seems like it'd add a lot of complexity to the regulator subsystem, as well as all the PMIC and other I2C regulators that any users of these device tree properties may end up using for not a lot of gain. There would be two separate code paths for any used I2C regulator or PMIC for setting voltages.

Yes, it would be additional complexity, but it would avoid duplicating information which is already in the DT (location&address of the PMIC, type of pmic) and in the kernel (how to program voltages for a given regulator). As DT is supposed to describe the hardware, duplicate information should be unnecessary.

One different way to look at this is that the CM3 firmware is a separate consumer of the DT. DT being OS-indepentent, the CM3 firmware should be able to get all the information it needs from the DT (maybe with help from the CM3 driver).

Regards,
Jan

Signature · 4 lines
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
#16 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

In reply to Russ Dill

[+Mark Brown for regulator suspend sequence ideas]

Quoted 19 lines · Russ Dill
Quoted 18 lines · Jan L?bbe
Quoted 16 lines · Russ Dill

The purpose and method of executing these sequences is left up to each platform. In the case of the am33xx, the CM3 firmware writes out the simple I2C sequences.

Each sequence is a series of I2C write commands. The first byte is the length of the write, the second byte the I2C device to address, and the following bytes are the message.

        /* Set OPP100 (1.10V) for VDD core */
        wake_sequence = /bits/ 8 <
                0x02 0x2d 0x25 0x2b /* Set VDD2 to 1.1V */
        >;

        tps: tps at 2d {
                reg = <0x2d>;
        };
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

In the above example, the sequence "0x25 0x1f" is written to the I2C device at address 0x2d (the TPS65910 PMIC). The PMIC interprets that as a write to a register at address 0x25.

I'd really like to get some feedback on the devicetree bindings.

Well the first comment (also made by others) is that the DT should be describing the hardware, and by that rule, the wake/sleep sequences are not properties of the i2c node but rather properties of the pmic. You've pointed out the inconveniences caused by that, but I'm not sure that those are enough to break the basic DT rules there. I'll leave it to the DT reviewers to make that decision.

Quoted 6 lines
Quoted 5 lines

Shouldn't the TPS driver know how to generate this sequence? It seems fragile to do voltage adjustments behind the back of the regulator framework and the TPS driver. The wake-sequence values should match the (in-memory) regulator configuration on resume (which may have been changed by DVFS).

Quoted 6 lines

The sequence is both PMIC specific and board specific. Additionally, the PMICs used aren't am335x specific. It would be nice to have the regulator framework and the driver write all this out, but the sequence is written out by the Cortex-M3 processor running some PM firmware. Even if the code was changed to run on the A8, it'd have to run from a small piece of SRAM.

So, why/how was the decision made to use the M3 instead of the MPU running from SRAM?

As a firmware minimalist, I obviously prefer to do this from the MPU side. But also, because the M3 is reset every suspend sequence, this becomes rather heavy to do from the M3.

Currently voltage scaling is only being proposed for suspend in this series, but in theory it's possible from idle as well. Doing this from the MPU/SRAM seems much better suited for idle.

Quoted 9 lines

As far as DVFS, I'm not aware of any DVFS implementations that muck with VDD CORE.

Quoted 5 lines

The CM3 driver needs to figure out where the core regulator is connected using using either DT or the regulator framework and ask the TPS (via a new interface) for register writes for sleep/wake sequences. Then those sequences will actually match the correct voltages configured using DT/DVFS.

Quoted 5 lines

That seems like it'd add a lot of complexity to the regulator subsystem, as well as all the PMIC and other I2C regulators that any users of these device tree properties may end up using for not a lot of gain. There would be two separate code paths for any used I2C regulator or PMIC for setting voltages.

Added Mark for his thoughts on this, but seems like complexity that the regulator framework might need to grow anyways.

The framework already has a concept of suspend voltage, suspend mode etc. Maybe it needs some generalizing so low-level platform code could query the framework for the sequence so it can be done late in platform idle/suspend paths. Especially for regmap drivers, this seems feasible.

Kevin

author
#17 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

Quoted 6 lines · Kevin Hilman

[+Mark Brown for regulator suspend sequence ideas]

Quoted 2 lines · Russ Dill
Quoted 1 line · Jan L?bbe

On Tue, 2013-08-13 at 15:20 -0700, Russ Dill wrote:

[snip]

Quoted 13 lines
Quoted 6 lines
Quoted 5 lines

Shouldn't the TPS driver know how to generate this sequence? It seems fragile to do voltage adjustments behind the back of the regulator framework and the TPS driver. The wake-sequence values should match the (in-memory) regulator configuration on resume (which may have been changed by DVFS).

Quoted 6 lines

The sequence is both PMIC specific and board specific. Additionally, the PMICs used aren't am335x specific. It would be nice to have the regulator framework and the driver write all this out, but the sequence is written out by the Cortex-M3 processor running some PM firmware. Even if the code was changed to run on the A8, it'd have to run from a small piece of SRAM.

Quoted 6 lines

So, why/how was the decision made to use the M3 instead of the MPU running from SRAM?

As a firmware minimalist, I obviously prefer to do this from the MPU side. But also, because the M3 is reset every suspend sequence, this becomes rather heavy to do from the M3.

After the feedback Vaibhav Bedia received on v2 of his suspend/resume patchset for am335x, he decided to move many of the operations from sleep33xx.S into the M3 firmware.

See the commit message here:
http://arago-project.org/git/projects/?p=am33x-cm3.git;a=commit;h=a972ce2f6

I need to wait until after the PLLs are put into bypass, which is now done in the M3 firmware. It also ends up being a lot easier to write the I2C writer code there in C rather than in assembly in sleep33xx.S.

Quoted 3 lines

Currently voltage scaling is only being proposed for suspend in this series, but in theory it's possible from idle as well. Doing this from the MPU/SRAM seems much better suited for idle.

The M3 firmware will also handle any cpuidle modes deeper than just putting SDRAM into self refresh. This is actually the only way of doing things like turning the MPU domain off on am335x.

Quoted 15 lines
Quoted 9 lines

As far as DVFS, I'm not aware of any DVFS implementations that muck with VDD CORE.

Quoted 5 lines

The CM3 driver needs to figure out where the core regulator is connected using using either DT or the regulator framework and ask the TPS (via a new interface) for register writes for sleep/wake sequences. Then those sequences will actually match the correct voltages configured using DT/DVFS.

Quoted 5 lines

That seems like it'd add a lot of complexity to the regulator subsystem, as well as all the PMIC and other I2C regulators that any users of these device tree properties may end up using for not a lot of gain. There would be two separate code paths for any used I2C regulator or PMIC for setting voltages.

Quoted 7 lines

Added Mark for his thoughts on this, but seems like complexity that the regulator framework might need to grow anyways.

The framework already has a concept of suspend voltage, suspend mode etc. Maybe it needs some generalizing so low-level platform code could query the framework for the sequence so it can be done late in platform idle/suspend paths. Especially for regmap drivers, this seems feasible.

Yes, my main hesitation for going down this path is possible lack of support for such an interface upstream.

Quoted 5 lines

Kevin
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html

#18 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

Quoted 9 lines · Russ Dill
Quoted 4 lines · Kevin Hilman

The framework already has a concept of suspend voltage, suspend mode etc. Maybe it needs some generalizing so low-level platform code could query the framework for the sequence so it can be done late in platform idle/suspend paths. Especially for regmap drivers, this seems feasible.

Yes, my main hesitation for going down this path is possible lack of support for such an interface upstream.

Someone is going to have to walk me through the context for me to fully understand what this is all about - what's the problem?

Generally for suspend if the sequencing is being done by the processor
it's been handled by the drivers for the components in question. For
sequencing beyond that you're into hardware features which aren't all
that regular sadly and often aren't controllable either. It sounds like
this is the latter case but the thing that manages some of the late
power down stages does so using a firmware we can rewrite?
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130829/03182f12/attachment.sig>

#19 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

Quoted 8 lines · Mark Brown
Quoted 6 lines · Russ Dill
Quoted 4 lines · Kevin Hilman

The framework already has a concept of suspend voltage, suspend mode etc. Maybe it needs some generalizing so low-level platform code could query the framework for the sequence so it can be done late in platform idle/suspend paths. Especially for regmap drivers, this seems feasible.

Quoted 5 lines
Quoted 2 lines

Yes, my main hesitation for going down this path is possible lack of support for such an interface upstream.

Someone is going to have to walk me through the context for me to fully understand what this is all about - what's the problem?

The basic problem is how to have low-level platform code (or possibly firmware) send commands to a regulator to scale voltage. This has to happen *very* late in the suspend process, so having the drivers do it themselves is not feasible.

The proposal in this series is to define the i2c commands sequence to be sent to the regulator in the i2c node of the DT. The platform-specific code then reads the sequence from the DT and sends the commands (or in in the case of the current series, passes the sequence to some firmware on a microcontroller which does the sequence.)

Quoted 6 lines

Generally for suspend if the sequencing is being done by the processor it's been handled by the drivers for the components in question. For sequencing beyond that you're into hardware features which aren't all that regular sadly and often aren't controllable either. It sounds like this is the latter case but the thing that manages some of the late power down stages does so using a firmware we can rewrite?

For the TI AM335x, the source for M3 firmware is available and configurable[1]. However, I'd still rather see this done by the low-level platform code in linux, not offloaded to the firmware, but the linux vs. firmware decision orthogonal to how to define/probe the sequences that need to be sent to the regulator.

Kevin

[1] git://arago-project.org/git/projects/am33x-cm3.git

#20 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

Quoted 9 lines · Kevin Hilman
Quoted 2 lines · Mark Brown

Someone is going to have to walk me through the context for me to fully understand what this is all about - what's the problem?

The basic problem is how to have low-level platform code (or possibly firmware) send commands to a regulator to scale voltage. This has to happen *very* late in the suspend process, so having the drivers do it themselves is not feasible.

Why does it have to happen this late and are the sequences definitely fixed ones not ones that could depend on the system state at the time we suspend? It'd help to know what exactly is being controlled here...

Quoted 6 lines

The proposal in this series is to define the i2c commands sequence to be sent to the regulator in the i2c node of the DT. The platform-specific code then reads the sequence from the DT and sends the commands (or in in the case of the current series, passes the sequence to some firmware on a microcontroller which does the sequence.)

Surely specifying things in terms of the actual sequence would be better
than trying to specify the I2C commands if you want this to be done from
Linux? If the firmware has to cope directly then this would require the
firmware to understand what it's doing of course.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130829/36460f8a/attachment.sig>

author
#21 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

Quoted 6 lines · Mark Brown
Quoted 4 lines · Kevin Hilman
Quoted 2 lines · Mark Brown

Someone is going to have to walk me through the context for me to fully understand what this is all about - what's the problem?

Quoted 8 lines
Quoted 4 lines

The basic problem is how to have low-level platform code (or possibly firmware) send commands to a regulator to scale voltage. This has to happen *very* late in the suspend process, so having the drivers do it themselves is not feasible.

Why does it have to happen this late and are the sequences definitely fixed ones not ones that could depend on the system state at the time we suspend? It'd help to know what exactly is being controlled here...

On all am335x platforms, the lower operating point for core voltage cannot be reached without first disabling the DDR controller, and programming it into a lower power mode. For DDR3 platforms, no such lower power mode is available and the lower operating point for core voltage can only be reached with the memory controller disabled.

It certainly is possible that some bizarre I2C regulator may mix in regulator voltage and some other state into one I2C register. In the case of such a platform, setting the lower operating point would not be supported.

Quoted 11 lines
Quoted 6 lines

The proposal in this series is to define the i2c commands sequence to be sent to the regulator in the i2c node of the DT. The platform-specific code then reads the sequence from the DT and sends the commands (or in in the case of the current series, passes the sequence to some firmware on a microcontroller which does the sequence.)

Surely specifying things in terms of the actual sequence would be better than trying to specify the I2C commands if you want this to be done from Linux? If the firmware has to cope directly then this would require the firmware to understand what it's doing of course.

I'm not sure what you mean by "actual sequence". Maybe if I show you a couple examples, it will be more clear:

This is for the AM335X-EVM which has a TPS65910. The first byte is the number of bytes to write, the second byte is the I2C device to write those bytes to, the third byte happens to be a register address on the TPS65910, and the 4th byte happens to be the value for 0.95V and 1.1V respectively.

/* Set OPP50 (0.95V) for VDD core on TPS65910 */
sleep_sequence = /bits/ 8 <

        0x02 0x2d 0x25 0x1f /* Set VDD2 to 0.95V */
Quoted 1 line

;

/* Set OPP100 (1.10V) for VDD core on TPS65910 */
wake_sequence = /bits/ 8 <

        0x02 0x2d 0x25 0x2b /* Set VDD2 to 1.1V */
Quoted 1 line

;

These next sequences are for the Beaglebone which has a different PMIC. The format is the same, but the PMIC requires an unlock sequence to be performed for certain registers and it also requires an additional step to apply DCDC changes:

/* Set OPP50 (0.95V) for VDD core on TPS65217 */
sleep_sequence = /bits/ 8 <

        0x02 0x24 0x0b 0x6d /* Password unlock 1 */
        0x02 0x24 0x10 0x02 /* Set DCDC3 to 0.95V */
        0x02 0x24 0x0b 0x6d /* Password unlock 2 */
        0x02 0x24 0x10 0x02 /* Set DCDC3 to 0.95V */
        0x02 0x24 0x0b 0x6c /* Password unlock 1 */
        0x02 0x24 0x11 0x86 /* Apply DCDC changes */
        0x02 0x24 0x0b 0x6c /* Password unlock 2 */
        0x02 0x24 0x11 0x86 /* Apply DCDC changes */
Quoted 1 line

;

/* Set OPP100 (1.10V) for VDD core on TPS65217 */
wake_sequence = /bits/ 8 <

        0x02 0x24 0x0b 0x6d /* Password unlock 1 */
        0x02 0x24 0x10 0x08 /* Set DCDC3 to 1.1V */
        0x02 0x24 0x0b 0x6d /* Password unlock 2 */
        0x02 0x24 0x10 0x08 /* Set DCDC3 to 1.1V */
        0x02 0x24 0x0b 0x6c /* Password unlock 1 */
        0x02 0x24 0x11 0x86 /* Apply DCDC changes */
        0x02 0x24 0x0b 0x6c /* Password unlock 2 */
        0x02 0x24 0x11 0x86 /* Apply DCDC changes */
Quoted 1 line

;

#22 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

Quoted 11 lines · Russ Dill
Quoted 3 lines · Mark Brown

Why does it have to happen this late and are the sequences definitely fixed ones not ones that could depend on the system state at the time we suspend? It'd help to know what exactly is being controlled here...

On all am335x platforms, the lower operating point for core voltage cannot be reached without first disabling the DDR controller, and programming it into a lower power mode. For DDR3 platforms, no such lower power mode is available and the lower operating point for core voltage can only be reached with the memory controller disabled.

So this is done from cpuidle rather than system suspend.

Quoted 4 lines

It certainly is possible that some bizarre I2C regulator may mix in regulator voltage and some other state into one I2C register. In the case of such a platform, setting the lower operating point would not be supported.

This isn't particularly bizzare, there is no technical reason why a register in the PMIC has to be given over completely to setting the voltage and indeed there are PMICs in mainline right now which don't do that - if you've got 16 bit registers it's probably not going to take the entire 16 bits to encode the voltage range.

Quoted 7 lines
Quoted 4 lines

Surely specifying things in terms of the actual sequence would be better than trying to specify the I2C commands if you want this to be done from Linux? If the firmware has to cope directly then this would require the firmware to understand what it's doing of course.

I'm not sure what you mean by "actual sequence". Maybe if I show you a couple examples, it will be more clear:

I mean describe the intended sequence of events in the system rather
than the raw register commands to accomplish them.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130829/10787a1f/attachment.sig>

author
#23 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

Quoted 7 lines · Mark Brown
Quoted 5 lines · Russ Dill
Quoted 3 lines · Mark Brown

Why does it have to happen this late and are the sequences definitely fixed ones not ones that could depend on the system state at the time we suspend? It'd help to know what exactly is being controlled here...

Quoted 7 lines
Quoted 5 lines

On all am335x platforms, the lower operating point for core voltage cannot be reached without first disabling the DDR controller, and programming it into a lower power mode. For DDR3 platforms, no such lower power mode is available and the lower operating point for core voltage can only be reached with the memory controller disabled.

So this is done from cpuidle rather than system suspend.

It is done for system suspend. It isn't possible to do this in a cpuidle use case as the memory controller would need to come out of idle automatically to service DMA requests.

Quoted 16 lines
Quoted 4 lines

It certainly is possible that some bizarre I2C regulator may mix in regulator voltage and some other state into one I2C register. In the case of such a platform, setting the lower operating point would not be supported.

This isn't particularly bizzare, there is no technical reason why a register in the PMIC has to be given over completely to setting the voltage and indeed there are PMICs in mainline right now which don't do that - if you've got 16 bit registers it's probably not going to take the entire 16 bits to encode the voltage range.

Quoted 4 lines
Quoted 4 lines

Surely specifying things in terms of the actual sequence would be better than trying to specify the I2C commands if you want this to be done from Linux? If the firmware has to cope directly then this would require the firmware to understand what it's doing of course.

Quoted 5 lines
Quoted 2 lines

I'm not sure what you mean by "actual sequence". Maybe if I show you a couple examples, it will be more clear:

I mean describe the intended sequence of events in the system rather than the raw register commands to accomplish them.

I'm still not sure what you mean.

Quoted 5 lines

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

#24 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

Quoted 7 lines · Russ Dill
Quoted 1 line · Mark Brown

So this is done from cpuidle rather than system suspend.

It is done for system suspend. It isn't possible to do this in a cpuidle use case as the memory controller would need to come out of idle automatically to service DMA requests.

OK, so it's all part of the core power down sequence for the SoC? I'm not sure that makes a big difference but it at least means that there are fewer runtime interactions to worry about.

Quoted 4 lines
Quoted 2 lines

I mean describe the intended sequence of events in the system rather than the raw register commands to accomplish them.

I'm still not sure what you mean.

Say "set voltage X" or even "regulator X supplies Y" not "send this
bytestream to the device".
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130829/047e1eca/attachment.sig>

author
#25 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

Quoted 5 lines · Mark Brown
Quoted 3 lines · Russ Dill
Quoted 1 line · Mark Brown

So this is done from cpuidle rather than system suspend.

Quoted 7 lines
Quoted 3 lines

It is done for system suspend. It isn't possible to do this in a cpuidle use case as the memory controller would need to come out of idle automatically to service DMA requests.

OK, so it's all part of the core power down sequence for the SoC? I'm not sure that makes a big difference but it at least means that there are fewer runtime interactions to worry about.

Well, core suspend sequence for the SoC.

Quoted 7 lines
Quoted 4 lines
Quoted 2 lines

I mean describe the intended sequence of events in the system rather than the raw register commands to accomplish them.

I'm still not sure what you mean.

Say "set voltage X" or even "regulator X supplies Y" not "send this bytestream to the device".

That's already known at the SoC level, "set the vdd core regulator to 0.95V". What isn't known is how to do that for each board.

Quoted 5 lines

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

author
#26 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

In reply to Mark Brown
Quoted 14 lines · Mark Brown
Quoted 9 lines · Russ Dill
Quoted 4 lines · Kevin Hilman

The framework already has a concept of suspend voltage, suspend mode etc. Maybe it needs some generalizing so low-level platform code could query the framework for the sequence so it can be done late in platform idle/suspend paths. Especially for regmap drivers, this seems feasible.

Yes, my main hesitation for going down this path is possible lack of support for such an interface upstream.

Someone is going to have to walk me through the context for me to fully understand what this is all about - what's the problem?

Quoted 6 lines

Generally for suspend if the sequencing is being done by the processor it's been handled by the drivers for the components in question. For sequencing beyond that you're into hardware features which aren't all that regular sadly and often aren't controllable either. It sounds like this is the latter case but the thing that manages some of the late power down stages does so using a firmware we can rewrite?

So the AM335X processor has a small M3 coprocessor attached to it. The M3 coprocessor is in charge of performing many of the sleep and resume transition steps. It has a 16k code space for firmware. The CM3 coprocessor has access to most of the same registers the primary AM335X processor has access to (PM registers, clock registers, I2C controller, etc).

For the deepest sleep modes, the core power supply can have its voltage reduced, but only after several steps by the CM3 coprocessor have been performed. The core power supply voltage must be reduced by the CM3 coprocessor. The core regulator is controlled by I2C on all the AM335X platforms that I am aware of, but different platforms have different I2C regulators.

The path I'm taking in this patchset is to just put the board specific I2C sequences necessary for the CM3 coprocessor to write out into the devicetree. I've made it as generic as I can so that other platforms with similar issues can reuse the bindings. Because of the limitations of the firmware size and the desire not to have some sort of byte code, this is a write only sequence.

Kevin Hilman is suggesting some way to query the regulator framework for this sequence. There would be some call to the regulator framework that would return a series of I2C commands that would be necessary to program a regulator to a specific voltage. It would be called twice, once for the suspend voltage, once for the resume voltage. The regulator framework would then call into the driver that sets that voltage, and by some method query the driver for the necessary I2C sequence, either by some special new call, or some regmap magic.

#27 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

Quoted 6 lines · Russ Dill

The path I'm taking in this patchset is to just put the board specific I2C sequences necessary for the CM3 coprocessor to write out into the devicetree. I've made it as generic as I can so that other platforms with similar issues can reuse the bindings. Because of the limitations of the firmware size and the desire not to have some sort of byte code, this is a write only sequence.

Making it write only seems to be a mistake - like I said in my other mail I'd expect you'd want bitfield updates here. The read and modify could be done earlier by Linux though.

Quoted 8 lines

Kevin Hilman is suggesting some way to query the regulator framework for this sequence. There would be some call to the regulator framework that would return a series of I2C commands that would be necessary to program a regulator to a specific voltage. It would be called twice, once for the suspend voltage, once for the resume voltage. The regulator framework would then call into the driver that sets that voltage, and by some method query the driver for the necessary I2C sequence, either by some special new call, or some regmap magic.

If it's just setting a single voltage then extracting the bitfield to update shouldn't be too hard for regmap based regulators. Off the top of my head I'd expect either the driver for the M3 or the cpuidle driver that talks to it to be a consumer of the regulator and then go from there.

Just putting the sequence directly into the device tree doesn't feel
like the right abstraction.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130829/c308dfaa/attachment.sig>

author
#28 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

Quoted 9 lines · Mark Brown
Quoted 6 lines · Russ Dill

The path I'm taking in this patchset is to just put the board specific I2C sequences necessary for the CM3 coprocessor to write out into the devicetree. I've made it as generic as I can so that other platforms with similar issues can reuse the bindings. Because of the limitations of the firmware size and the desire not to have some sort of byte code, this is a write only sequence.

Quoted 3 lines

Making it write only seems to be a mistake - like I said in my other mail I'd expect you'd want bitfield updates here. The read and modify could be done earlier by Linux though.

Updating bitfields in memory is pretty basic, but with I2C, each device has its own register sizes and methods of accessing registers. The first byte of an I2C sequence being a register address is pretty common though. You'd need a method of defining in the device tree piece how to modify bitfields.

Quoted 9 lines
Quoted 8 lines

Kevin Hilman is suggesting some way to query the regulator framework for this sequence. There would be some call to the regulator framework that would return a series of I2C commands that would be necessary to program a regulator to a specific voltage. It would be called twice, once for the suspend voltage, once for the resume voltage. The regulator framework would then call into the driver that sets that voltage, and by some method query the driver for the necessary I2C sequence, either by some special new call, or some regmap magic.

Quoted 5 lines

If it's just setting a single voltage then extracting the bitfield to update shouldn't be too hard for regmap based regulators. Off the top of my head I'd expect either the driver for the M3 or the cpuidle driver that talks to it to be a consumer of the regulator and then go from there.

It'd be a list of bitfields. So are you suggesting a regulator_ops call that would return a list of bitfield updates along with an I2C controller, an I2C device address, I2C register addressing scheme (8 bit, 16 bit, or other) and the info for each bitfield (register size)? And then each regulator driver would be updated as needed with this code. Would this be a way forward? Would regmap actually tie into this at all?

This is VDD core, so its not related to cpuidle or the M3.

Quoted 8 lines

Just putting the sequence directly into the device tree doesn't feel like the right abstraction.

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

#29 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

Quoted 11 lines · Russ Dill
Quoted 3 lines · Mark Brown

Making it write only seems to be a mistake - like I said in my other mail I'd expect you'd want bitfield updates here. The read and modify could be done earlier by Linux though.

Updating bitfields in memory is pretty basic, but with I2C, each device has its own register sizes and methods of accessing registers. The first byte of an I2C sequence being a register address is pretty common though. You'd need a method of defining in the device tree piece how to modify bitfields.

I'd not assume byte based addressing for a PMIC, it's very common but not universal. The difficulties here assume putting this in the device tree as explicit register I/O - I do tend to agree with Kevin that this doesn't seem like the right abstraction.

Quoted 6 lines
Quoted 5 lines

If it's just setting a single voltage then extracting the bitfield to update shouldn't be too hard for regmap based regulators. Off the top of my head I'd expect either the driver for the M3 or the cpuidle driver that talks to it to be a consumer of the regulator and then go from there.

Quoted 7 lines

It'd be a list of bitfields. So are you suggesting a regulator_ops call that would return a list of bitfield updates along with an I2C controller, an I2C device address, I2C register addressing scheme (8 bit, 16 bit, or other) and the info for each bitfield (register size)? And then each regulator driver would be updated as needed with this code. Would this be a way forward? Would regmap actually tie into this at all?

I was thinking about the majority of regulators where setting a voltage would be a single bitfield update (plus ramp delay, which is going to need to be accounted for in the power on case). If we need to do longer sequences things get a bit more tricky in terms of interface but it seems doable.

regmap would tie in in that it already has a way of describing the format for interactions with the device so we could just reuse the same description, and with some work we can probably reuse the code that generates the bitstreams for sending to the devices too (though I don't immediately see a nice way of doing that).

Not sure if this is worth the effort or not though, but then there's the whole DT as ABI thing to worry about...

Quoted 1 line

This is VDD core, so its not related to cpuidle or the M3.

It's related to the M3 if the M3 is managing it; you can have multiple
consumers for a regulator so it doesn't need to be one or the other.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130829/606d058c/attachment.sig>

author
#30 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

Quoted 32 lines · Mark Brown
Quoted 11 lines · Russ Dill
Quoted 3 lines · Mark Brown

Making it write only seems to be a mistake - like I said in my other mail I'd expect you'd want bitfield updates here. The read and modify could be done earlier by Linux though.

Updating bitfields in memory is pretty basic, but with I2C, each device has its own register sizes and methods of accessing registers. The first byte of an I2C sequence being a register address is pretty common though. You'd need a method of defining in the device tree piece how to modify bitfields.

I'd not assume byte based addressing for a PMIC, it's very common but not universal. The difficulties here assume putting this in the device tree as explicit register I/O - I do tend to agree with Kevin that this doesn't seem like the right abstraction.

Quoted 6 lines
Quoted 5 lines

If it's just setting a single voltage then extracting the bitfield to update shouldn't be too hard for regmap based regulators. Off the top of my head I'd expect either the driver for the M3 or the cpuidle driver that talks to it to be a consumer of the regulator and then go from there.

Quoted 7 lines

It'd be a list of bitfields. So are you suggesting a regulator_ops call that would return a list of bitfield updates along with an I2C controller, an I2C device address, I2C register addressing scheme (8 bit, 16 bit, or other) and the info for each bitfield (register size)? And then each regulator driver would be updated as needed with this code. Would this be a way forward? Would regmap actually tie into this at all?

Quoted 5 lines

I was thinking about the majority of regulators where setting a voltage would be a single bitfield update (plus ramp delay, which is going to need to be accounted for in the power on case). If we need to do longer sequences things get a bit more tricky in terms of interface but it seems doable.

The sequence for TPS65217 on beaglebone is rather long, it's 8 register writes. It has to write the new value to the DCDC3 register and the apply bit to the DCDC register, but both registers are password locked registers. The password sequence is to write the password register, then the register, then the password register again, then the register again.

Quoted 6 lines

regmap would tie in in that it already has a way of describing the format for interactions with the device so we could just reuse the same description, and with some work we can probably reuse the code that generates the bitstreams for sending to the devices too (though I don't immediately see a nice way of doing that).

Quoted 7 lines

Not sure if this is worth the effort or not though, but then there's the whole DT as ABI thing to worry about...

Quoted 1 line

This is VDD core, so its not related to cpuidle or the M3.

It's related to the M3 if the M3 is managing it; you can have multiple consumers for a regulator so it doesn't need to be one or the other.

Ah.

Quoted 5 lines

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

#31 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

Quoted 8 lines · Russ Dill
Quoted 5 lines · Mark Brown

I was thinking about the majority of regulators where setting a voltage would be a single bitfield update (plus ramp delay, which is going to need to be accounted for in the power on case). If we need to do longer sequences things get a bit more tricky in terms of interface but it seems doable.

Quoted 6 lines

The sequence for TPS65217 on beaglebone is rather long, it's 8 register writes. It has to write the new value to the DCDC3 register and the apply bit to the DCDC register, but both registers are password locked registers. The password sequence is to write the password register, then the register, then the password register again, then the register again.

Oh dear, that's a bit annoying. It's doable obviously if people decide
to go down that route but it's work and this is a very common system.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130903/76f33bcf/attachment-0001.sig>

#32 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

In reply to Russ Dill
Quoted 28 lines · Russ Dill
Quoted 6 lines · Kevin Hilman

[+Mark Brown for regulator suspend sequence ideas]

Quoted 2 lines · Russ Dill
Quoted 1 line · Jan L?bbe

On Tue, 2013-08-13 at 15:20 -0700, Russ Dill wrote:

[snip]

Quoted 13 lines
Quoted 6 lines
Quoted 5 lines

Shouldn't the TPS driver know how to generate this sequence? It seems fragile to do voltage adjustments behind the back of the regulator framework and the TPS driver. The wake-sequence values should match the (in-memory) regulator configuration on resume (which may have been changed by DVFS).

Quoted 6 lines

The sequence is both PMIC specific and board specific. Additionally, the PMICs used aren't am335x specific. It would be nice to have the regulator framework and the driver write all this out, but the sequence is written out by the Cortex-M3 processor running some PM firmware. Even if the code was changed to run on the A8, it'd have to run from a small piece of SRAM.

Quoted 6 lines

So, why/how was the decision made to use the M3 instead of the MPU running from SRAM?

As a firmware minimalist, I obviously prefer to do this from the MPU side. But also, because the M3 is reset every suspend sequence, this becomes rather heavy to do from the M3.

Quoted 6 lines

After the feedback Vaibhav Bedia received on v2 of his suspend/resume patchset for am335x, he decided to move many of the operations from sleep33xx.S into the M3 firmware.

See the commit message here:
http://arago-project.org/git/projects/?p=am33x-cm3.git;a=commit;h=a972ce2f6

Was this feedback on the public lists? That patch has never been posted to linux-omap AFAICT.

Quoted 3 lines

I need to wait until after the PLLs are put into bypass, which is now done in the M3 firmware. It also ends up being a lot easier to write the I2C writer code there in C rather than in assembly in sleep33xx.S.

hmm, (carefully) written functions in C can still be copied to SRAM. I dont' see that as an obstacle.

Quoted 7 lines
Quoted 3 lines

Currently voltage scaling is only being proposed for suspend in this series, but in theory it's possible from idle as well. Doing this from the MPU/SRAM seems much better suited for idle.

The M3 firmware will also handle any cpuidle modes deeper than just putting SDRAM into self refresh. This is actually the only way of doing things like turning the MPU domain off on am335x.

Yes, it will have to handle the MPU/interconnect off parts but other than that, that's the only thing it *has* to do (well, and handle wakeup from the deep state.) The rest of the stuff being piled into the M3 is a result of software/firmware design decisions AFAICT. As I predicted when I first saw this SoC design, the firmware is getting bigger and bigger. Initially it was argued that it would be tiny, and only handle the things it had to do. Now it's growing due to "convenience". IMO, this is a bad trend, and one that will make this code more and more difficult to maintain upstream (assuming that's a goal.)

Kevin

author
#33 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

Quoted 55 lines · Kevin Hilman
Quoted 28 lines · Russ Dill
Quoted 6 lines · Kevin Hilman

[+Mark Brown for regulator suspend sequence ideas]

Quoted 2 lines · Russ Dill
Quoted 1 line · Jan L?bbe

On Tue, 2013-08-13 at 15:20 -0700, Russ Dill wrote:

[snip]

Quoted 13 lines
Quoted 6 lines
Quoted 5 lines

Shouldn't the TPS driver know how to generate this sequence? It seems fragile to do voltage adjustments behind the back of the regulator framework and the TPS driver. The wake-sequence values should match the (in-memory) regulator configuration on resume (which may have been changed by DVFS).

Quoted 6 lines

The sequence is both PMIC specific and board specific. Additionally, the PMICs used aren't am335x specific. It would be nice to have the regulator framework and the driver write all this out, but the sequence is written out by the Cortex-M3 processor running some PM firmware. Even if the code was changed to run on the A8, it'd have to run from a small piece of SRAM.

Quoted 6 lines

So, why/how was the decision made to use the M3 instead of the MPU running from SRAM?

As a firmware minimalist, I obviously prefer to do this from the MPU side. But also, because the M3 is reset every suspend sequence, this becomes rather heavy to do from the M3.

Quoted 6 lines

After the feedback Vaibhav Bedia received on v2 of his suspend/resume patchset for am335x, he decided to move many of the operations from sleep33xx.S into the M3 firmware.

See the commit message here:
http://arago-project.org/git/projects/?p=am33x-cm3.git;a=commit;h=a972ce2f6

Was this feedback on the public lists? That patch has never been posted to linux-omap AFAICT.

Quoted 3 lines

I need to wait until after the PLLs are put into bypass, which is now done in the M3 firmware. It also ends up being a lot easier to write the I2C writer code there in C rather than in assembly in sleep33xx.S.

hmm, (carefully) written functions in C can still be copied to SRAM. I dont' see that as an obstacle.

Quoted 7 lines
Quoted 3 lines

Currently voltage scaling is only being proposed for suspend in this series, but in theory it's possible from idle as well. Doing this from the MPU/SRAM seems much better suited for idle.

The M3 firmware will also handle any cpuidle modes deeper than just putting SDRAM into self refresh. This is actually the only way of doing things like turning the MPU domain off on am335x.

Quoted 9 lines

Yes, it will have to handle the MPU/interconnect off parts but other than that, that's the only thing it *has* to do (well, and handle wakeup from the deep state.) The rest of the stuff being piled into the M3 is a result of software/firmware design decisions AFAICT. As I predicted when I first saw this SoC design, the firmware is getting bigger and bigger. Initially it was argued that it would be tiny, and only handle the things it had to do. Now it's growing due to "convenience". IMO, this is a bad trend, and one that will make this code more and more difficult to maintain upstream (assuming that's a goal.)

Do you mean upstream as in the firmware upstream, or upstream as in the kernel upstream? Upstream kernel is really easy to maintain because sleep33xx.S just puts the SDRAM into self-refresh.

The code to perform these transitions is going to exist, either in kernel or in firmware. If you are looking for this to be maintainable C code that is copied into SRAM, it will need to be built much like a firmware, with it's elf sections being copied into SRAM properly. I'm not sure how much it complicates things, but the code needs to be able to run with the MMU on and MMU off. The C code would be a minimalized duplication of much of what is already in mach-omap2. Because you are proposing to split this up between A8 and M3, much of that code would then be duplicated again within the M3 firmware.

And don't forget that am335x is just the first platform with such a PM scheme.

Because the M3 firmware already has to manage power domains, hwmods, plls, and clockdomains, adding or removing which ones it handles doesn't really change the size or complexity of the firmware. In fact, because so much of the code is common code, moving this into kernel would just mean making two copies of the firmware with different steps to be run, one for the A8 SRAM, one for the M3.

#34 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

Quoted 66 lines · Russ Dill
Quoted 55 lines · Kevin Hilman
Quoted 28 lines · Russ Dill
Quoted 6 lines · Kevin Hilman

[+Mark Brown for regulator suspend sequence ideas]

Quoted 2 lines · Russ Dill
Quoted 1 line · Jan L?bbe

On Tue, 2013-08-13 at 15:20 -0700, Russ Dill wrote:

[snip]

Quoted 13 lines
Quoted 6 lines
Quoted 5 lines

Shouldn't the TPS driver know how to generate this sequence? It seems fragile to do voltage adjustments behind the back of the regulator framework and the TPS driver. The wake-sequence values should match the (in-memory) regulator configuration on resume (which may have been changed by DVFS).

Quoted 6 lines

The sequence is both PMIC specific and board specific. Additionally, the PMICs used aren't am335x specific. It would be nice to have the regulator framework and the driver write all this out, but the sequence is written out by the Cortex-M3 processor running some PM firmware. Even if the code was changed to run on the A8, it'd have to run from a small piece of SRAM.

Quoted 6 lines

So, why/how was the decision made to use the M3 instead of the MPU running from SRAM?

As a firmware minimalist, I obviously prefer to do this from the MPU side. But also, because the M3 is reset every suspend sequence, this becomes rather heavy to do from the M3.

Quoted 6 lines

After the feedback Vaibhav Bedia received on v2 of his suspend/resume patchset for am335x, he decided to move many of the operations from sleep33xx.S into the M3 firmware.

See the commit message here:
http://arago-project.org/git/projects/?p=am33x-cm3.git;a=commit;h=a972ce2f6

Was this feedback on the public lists? That patch has never been posted to linux-omap AFAICT.

Quoted 3 lines

I need to wait until after the PLLs are put into bypass, which is now done in the M3 firmware. It also ends up being a lot easier to write the I2C writer code there in C rather than in assembly in sleep33xx.S.

hmm, (carefully) written functions in C can still be copied to SRAM. I dont' see that as an obstacle.

Quoted 7 lines
Quoted 3 lines

Currently voltage scaling is only being proposed for suspend in this series, but in theory it's possible from idle as well. Doing this from the MPU/SRAM seems much better suited for idle.

The M3 firmware will also handle any cpuidle modes deeper than just putting SDRAM into self refresh. This is actually the only way of doing things like turning the MPU domain off on am335x.

Quoted 9 lines

Yes, it will have to handle the MPU/interconnect off parts but other than that, that's the only thing it *has* to do (well, and handle wakeup from the deep state.) The rest of the stuff being piled into the M3 is a result of software/firmware design decisions AFAICT. As I predicted when I first saw this SoC design, the firmware is getting bigger and bigger. Initially it was argued that it would be tiny, and only handle the things it had to do. Now it's growing due to "convenience". IMO, this is a bad trend, and one that will make this code more and more difficult to maintain upstream (assuming that's a goal.)

Quoted 3 lines

Do you mean upstream as in the firmware upstream, or upstream as in the kernel upstream? Upstream kernel is really easy to maintain because sleep33xx.S just puts the SDRAM into self-refresh.

I mean making the kernel "simple" at the price of more complicated (and much less reviewed) firmware code is a bad trend. I understand the desire to move this stuff into firmware and bypass the kernel review process, but I predict it will backfire.

Quoted 4 lines

The code to perform these transitions is going to exist, either in kernel or in firmware. If you are looking for this to be maintainable C code that is copied into SRAM, it will need to be built much like a firmware, with it's elf sections being copied into SRAM properly.

You're making it too complicated. No need for ELF, etc.

So the argument for moving much of the stuff from the kernel (in the patch you referenced above) was that the "assembly code is quite big and folks have found it hard to review and fixup issues in this dense piece of code." (interestingly, it was moved to firmware where it will see even less review, but that's off topic.)

My idea was that if reviewing assembly is the issue, why not carefully write a self-contained C function that can be copied to SRAM. It might need a little stub in SRAM to setup the stack etc., but it should be quite doable. That eliminates the problem cited as the reason for the move to M3.

Quoted 8 lines

I'm not sure how much it complicates things, but the code needs to be able to run with the MMU on and MMU off. The C code would be a minimalized duplication of much of what is already in mach-omap2. Because you are proposing to split this up between A8 and M3, much of that code would then be duplicated again within the M3 firmware.

And don't forget that am335x is just the first platform with such a PM scheme.

IMO, all the more reason to handle it in Linux, not in platform-specific firmware.

Quoted 3 lines

Because the M3 firmware already has to manage power domains, hwmods, plls, and clockdomains, adding or removing which ones it handles doesn't really change the size or complexity of the firmware.

No, but it significantly complicates its interaction with Linux, which also has to know about all these things already. IMO, as I've stated from the very beginning of this SoC, there should be a very clean split. M3 should only handle the minimum, what the MPU simply cannot do (MPU/interconnect off, wakeup). MPU/Linux should handle the rest.

Quoted 3 lines

In fact, because so much of the code is common code, moving this into kernel would just mean making two copies of the firmware with different steps to be run, one for the A8 SRAM, one for the M3.

Maybe I'm getting confused, but the more you talk about the linux and the firmware doing the same code, the more I think the firmware is (trying) to do too much. If this is going to be understandable (and maintainable), there needs to be a clean split of roles between the MPU and the M3.

Kevin

#35 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

On Thu, Aug 29, 2013 at 3:11 PM, Kevin Hilman <[email protected]> wrote:
[...]

First of all, apologies for jumping in here. Now that i am not actively involved in
AM335x/AM437x stuff i wasn't actively following all the discussion here.

Looking at the lists now i just wanted to mention a few things so that we can agree on the approach to be taken here.

Quoted 96 lines
Quoted 43 lines
Quoted 33 lines
Quoted 8 lines
Quoted 7 lines

So, why/how was the decision made to use the M3 instead of the MPU running from SRAM?

As a firmware minimalist, I obviously prefer to do this from the MPU side. But also, because the M3 is reset every suspend sequence, this becomes rather heavy to do from the M3.

Quoted 6 lines

After the feedback Vaibhav Bedia received on v2 of his suspend/resume patchset for am335x, he decided to move many of the operations from sleep33xx.S into the M3 firmware.

See the commit message here:
http://arago-project.org/git/projects/?p=am33x-cm3.git;a=commit;h=a972ce2f6

Was this feedback on the public lists? That patch has never been posted to linux-omap AFAICT.

Quoted 3 lines

I need to wait until after the PLLs are put into bypass, which is now done in the M3 firmware. It also ends up being a lot easier to write the I2C writer code there in C rather than in assembly in sleep33xx.S.

hmm, (carefully) written functions in C can still be copied to SRAM. I dont' see that as an obstacle.

Quoted 7 lines
Quoted 3 lines

Currently voltage scaling is only being proposed for suspend in this series, but in theory it's possible from idle as well. Doing this from the MPU/SRAM seems much better suited for idle.

The M3 firmware will also handle any cpuidle modes deeper than just putting SDRAM into self refresh. This is actually the only way of doing things like turning the MPU domain off on am335x.

Quoted 9 lines

Yes, it will have to handle the MPU/interconnect off parts but other than that, that's the only thing it *has* to do (well, and handle wakeup from the deep state.) The rest of the stuff being piled into the M3 is a result of software/firmware design decisions AFAICT. As I predicted when I first saw this SoC design, the firmware is getting bigger and bigger. Initially it was argued that it would be tiny, and only handle the things it had to do. Now it's growing due to "convenience". IMO, this is a bad trend, and one that will make this code more and more difficult to maintain upstream (assuming that's a goal.)

Quoted 3 lines

Do you mean upstream as in the firmware upstream, or upstream as in the kernel upstream? Upstream kernel is really easy to maintain because sleep33xx.S just puts the SDRAM into self-refresh.

I mean making the kernel "simple" at the price of more complicated (and much less reviewed) firmware code is a bad trend. I understand the desire to move this stuff into firmware and bypass the kernel review process, but I predict it will backfire.

Quoted 4 lines

The code to perform these transitions is going to exist, either in kernel or in firmware. If you are looking for this to be maintainable C code that is copied into SRAM, it will need to be built much like a firmware, with it's elf sections being copied into SRAM properly.

You're making it too complicated. No need for ELF, etc.

So the argument for moving much of the stuff from the kernel (in the patch you referenced above) was that the "assembly code is quite big and folks have found it hard to review and fixup issues in this dense piece of code." (interestingly, it was moved to firmware where it will see even less review, but that's off topic.)

My idea was that if reviewing assembly is the issue, why not carefully write a self-contained C function that can be copied to SRAM. It might need a little stub in SRAM to setup the stack etc., but it should be quite doable. That eliminates the problem cited as the reason for the move to M3.

Quoted 8 lines

I'm not sure how much it complicates things, but the code needs to be able to run with the MMU on and MMU off. The C code would be a minimalized duplication of much of what is already in mach-omap2. Because you are proposing to split this up between A8 and M3, much of that code would then be duplicated again within the M3 firmware.

And don't forget that am335x is just the first platform with such a PM scheme.

IMO, all the more reason to handle it in Linux, not in platform-specific firmware.

Quoted 3 lines

Because the M3 firmware already has to manage power domains, hwmods, plls, and clockdomains, adding or removing which ones it handles doesn't really change the size or complexity of the firmware.

No, but it significantly complicates its interaction with Linux, which also has to know about all these things already. IMO, as I've stated from the very beginning of this SoC, there should be a very clean split. M3 should only handle the minimum, what the MPU simply cannot do (MPU/interconnect off, wakeup). MPU/Linux should handle the rest.

Quoted 3 lines

In fact, because so much of the code is common code, moving this into kernel would just mean making two copies of the firmware with different steps to be run, one for the A8 SRAM, one for the M3.

Quoted 6 lines

Maybe I'm getting confused, but the more you talk about the linux and the firmware doing the same code, the more I think the firmware is (trying) to do too much. If this is going to be understandable (and maintainable), there needs to be a clean split of roles between the MPU and the M3.

In the past i tried to keep the firmware as minimal as possible and pushed back all efforts to push unnecessary stuff in there. Like Kevin, i too was of the opinion that we need to do whatever's possible in the kernel.

Sadly, things just got a point where it made much more sense (technically as well as non-technically) to just put the low level stuff in the M3.

1. In addition to the Linux support, we have non-OS based code for AM335x (and AM437x) which in the past just had to duplicate whatever was done in the Linux. Being a different codebase the non-OS guys have their own set of challenges and we had to at times sit through and debug issues in one codebase while the other was working fine. Ideally these things should never come up but sadly they do and we need to solve them.

We also have people trying to implement this thing on different sort of OS environments and they also end up debugging the same set of issues.

With only a handful of folks able to spend time debugging issues 'do everything in Linux and expect others to copy it' model doesn't scale up. Keeping things in the
firmware with the code available online helps do away with the 'developer scalability'
problem and if one looks at things differently enables code-reuse at the same time.

Moreover, when the code was finally moved to M3, AFAICT we had validated all supported combinations (DDR2, DDR3, DDR3 with VTT, DDR3 without VTT control) that TI hardware guys could throw at us.Yes there could be stupid bugs lurking in the code since it wasn't reviewed as much as i would have liked but we
have something which proves the functionality to be used as the base.

2. As has been already been pointed out by Russ, on AM335x this step needs to be done very late in the suspend process (the last stage actually). On the next SoC there are other complications related to security which enforce this step must be done from the M3. So trying to keep it in M3 for both AM335x and AM437x helps avoid the code churn that will happen if we were to do this differently on two SoCs which have similar PM architectures.

Morevoer, all the suggestions on how to keep the code in Linux working around the
complications due to the main memory not being accessible will need to be replicated
on the non-Linux s/w stacks and that's just make it more difficult for them.

3. Moving this to M3 leaves the option open to try out some crazy stuff for power optimizations wherein the CORE voltage is lowered even below OPP50. This is yet to be proved in h/w so one can ignore this for now but yes it's a possibility.

Regards,
Vaibhav

[1] http://www.ti.com/tool/starterware-sitara

#36 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

Quoted 6 lines · Vaibhav Bedia

On Thu, Aug 29, 2013 at 3:11 PM, Kevin Hilman <[email protected]> wrote:
[...]

First of all, apologies for jumping in here. Now that i am not actively involved in AM335x/AM437x stuff i wasn't actively following all the discussion here.

No apologies needed, I was hoping you would jump in here. Thanks for sharing your experience and insight.

Quoted 123 lines

Looking at the lists now i just wanted to mention a few things so that we can agree on the approach to be taken here.

Quoted 96 lines
Quoted 43 lines
Quoted 33 lines
Quoted 8 lines
Quoted 7 lines

So, why/how was the decision made to use the M3 instead of the MPU running from SRAM?

As a firmware minimalist, I obviously prefer to do this from the MPU side. But also, because the M3 is reset every suspend sequence, this becomes rather heavy to do from the M3.

Quoted 6 lines

After the feedback Vaibhav Bedia received on v2 of his suspend/resume patchset for am335x, he decided to move many of the operations from sleep33xx.S into the M3 firmware.

See the commit message here:
http://arago-project.org/git/projects/?p=am33x-cm3.git;a=commit;h=a972ce2f6

Was this feedback on the public lists? That patch has never been posted to linux-omap AFAICT.

Quoted 3 lines

I need to wait until after the PLLs are put into bypass, which is now done in the M3 firmware. It also ends up being a lot easier to write the I2C writer code there in C rather than in assembly in sleep33xx.S.

hmm, (carefully) written functions in C can still be copied to SRAM. I dont' see that as an obstacle.

Quoted 7 lines
Quoted 3 lines

Currently voltage scaling is only being proposed for suspend in this series, but in theory it's possible from idle as well. Doing this from the MPU/SRAM seems much better suited for idle.

The M3 firmware will also handle any cpuidle modes deeper than just putting SDRAM into self refresh. This is actually the only way of doing things like turning the MPU domain off on am335x.

Quoted 9 lines

Yes, it will have to handle the MPU/interconnect off parts but other than that, that's the only thing it *has* to do (well, and handle wakeup from the deep state.) The rest of the stuff being piled into the M3 is a result of software/firmware design decisions AFAICT. As I predicted when I first saw this SoC design, the firmware is getting bigger and bigger. Initially it was argued that it would be tiny, and only handle the things it had to do. Now it's growing due to "convenience". IMO, this is a bad trend, and one that will make this code more and more difficult to maintain upstream (assuming that's a goal.)

Quoted 3 lines

Do you mean upstream as in the firmware upstream, or upstream as in the kernel upstream? Upstream kernel is really easy to maintain because sleep33xx.S just puts the SDRAM into self-refresh.

I mean making the kernel "simple" at the price of more complicated (and much less reviewed) firmware code is a bad trend. I understand the desire to move this stuff into firmware and bypass the kernel review process, but I predict it will backfire.

Quoted 4 lines

The code to perform these transitions is going to exist, either in kernel or in firmware. If you are looking for this to be maintainable C code that is copied into SRAM, it will need to be built much like a firmware, with it's elf sections being copied into SRAM properly.

You're making it too complicated. No need for ELF, etc.

So the argument for moving much of the stuff from the kernel (in the patch you referenced above) was that the "assembly code is quite big and folks have found it hard to review and fixup issues in this dense piece of code." (interestingly, it was moved to firmware where it will see even less review, but that's off topic.)

My idea was that if reviewing assembly is the issue, why not carefully write a self-contained C function that can be copied to SRAM. It might need a little stub in SRAM to setup the stack etc., but it should be quite doable. That eliminates the problem cited as the reason for the move to M3.

Quoted 8 lines

I'm not sure how much it complicates things, but the code needs to be able to run with the MMU on and MMU off. The C code would be a minimalized duplication of much of what is already in mach-omap2. Because you are proposing to split this up between A8 and M3, much of that code would then be duplicated again within the M3 firmware.

And don't forget that am335x is just the first platform with such a PM scheme.

IMO, all the more reason to handle it in Linux, not in platform-specific firmware.

Quoted 3 lines

Because the M3 firmware already has to manage power domains, hwmods, plls, and clockdomains, adding or removing which ones it handles doesn't really change the size or complexity of the firmware.

No, but it significantly complicates its interaction with Linux, which also has to know about all these things already. IMO, as I've stated from the very beginning of this SoC, there should be a very clean split. M3 should only handle the minimum, what the MPU simply cannot do (MPU/interconnect off, wakeup). MPU/Linux should handle the rest.

Quoted 3 lines

In fact, because so much of the code is common code, moving this into kernel would just mean making two copies of the firmware with different steps to be run, one for the A8 SRAM, one for the M3.

Quoted 6 lines

Maybe I'm getting confused, but the more you talk about the linux and the firmware doing the same code, the more I think the firmware is (trying) to do too much. If this is going to be understandable (and maintainable), there needs to be a clean split of roles between the MPU and the M3.

In the past i tried to keep the firmware as minimal as possible and pushed back all efforts to push unnecessary stuff in there. Like Kevin, i too was of the opinion that we need to do whatever's possible in the kernel.

Sadly, things just got a point where it made much more sense (technically as well as non-technically) to just put the low level stuff in the M3.

1. In addition to the Linux support, we have non-OS based code for AM335x (and AM437x) which in the past just had to duplicate whatever was done in the Linux. Being a different codebase the non-OS guys have their own set of challenges and we had to at times sit through and debug issues in one codebase while the other was working fine. Ideally these things should never come up but sadly they do and we need to solve them.

We also have people trying to implement this thing on different sort of OS environments and they also end up debugging the same set of issues.

Quoted 7 lines

With only a handful of folks able to spend time debugging issues 'do everything in Linux and expect others to copy it' model doesn't scale up. Keeping things in the
firmware with the code available online helps do away with the 'developer scalability'
problem and if one looks at things differently enables code-reuse at the same time.

Yes, yes, this is the classic argument for a HAL, and making that case has always gone over well in the kernel. ;)

Quoted 6 lines

Moreover, when the code was finally moved to M3, AFAICT we had validated all supported combinations (DDR2, DDR3, DDR3 with VTT, DDR3 without VTT control) that TI hardware guys could throw at us.Yes there could be stupid bugs lurking in the code since it wasn't reviewed as much as i would have liked but we
have something which proves the functionality to be used as the base.

I don't follow the argument here. You mean it was validated *before* moving it to the M3, or after? Either way, I'm how that makes a case for moving it to the M3. Surely if it was on the MPU, it would also have been validated, no?

Quoted 3 lines

2. As has been already been pointed out by Russ, on AM335x this step needs to be done very late in the suspend process (the last stage actually).

late, yes. But *technically*, it can still be done from the MPU.

Quoted 2 lines

On the next SoC there are other complications related to security which enforce this step must be done from the M3.

Aha! So far, the first technical reason I've seen. Can you elaborate? Exactly which step has to be done from the M3, and why can't the MPU do it? And could it be done more simply by putting *only* the parts needed on the M3 and not letting it snowball into a pile of stuff?

Quoted 3 lines

So trying to keep it in M3 for both AM335x and AM437x helps avoid the code churn that will happen if we were to do this differently on two SoCs which have similar PM architectures.

I'm not conviced it has to be done so differently between the two. Let's have some more details about the security differences before making that decision.

Quoted 4 lines

Morevoer, all the suggestions on how to keep the code in Linux working around the complications due to the main memory not being accessible will need to be replicated on the non-Linux s/w stacks and that's just make it more difficult for them.

The linux code has to be very self-contained (in assembler or C), so I'm not sure how it's difficult to replicated in any other OS (or non-OS.)

Quoted 4 lines

3. Moving this to M3 leaves the option open to try out some crazy stuff for power optimizations wherein the CORE voltage is lowered even below OPP50. This is yet to be proved in h/w so one can ignore this for now but yes it's a possibility.

All interesting reasons, but other than the security one, all boil down to non-technical arguments about what's easier. But as you well know, "the easy way" usually isn't the same thing as "the right way" when it comes to getting things into the upstream linux kernel.

Kevin

author
#37 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

Quoted 1 line · Kevin Hilman

Vaibhav Bedia <[email protected]> writes:

[snip]

Quoted 7 lines
Quoted 4 lines

Morevoer, all the suggestions on how to keep the code in Linux working around the complications due to the main memory not being accessible will need to be replicated on the non-Linux s/w stacks and that's just make it more difficult for them.

The linux code has to be very self-contained (in assembler or C), so I'm not sure how it's difficult to replicated in any other OS (or non-OS.)

Let me pull on this thread a little bit. Would this involve defining a bunch of different sections for each arch so that I could do:

void __sram_am33xx am33xx_some_pm_function(args....) {
}

static struct am33xx_foo_dyn *blargity __sramdata_am33xx = { ...};

static const struct am33xx_foo_const *blarg __sramconst_am33xx = { ... };

The sram push code could then push these sections into sram. There would then be a set of inlines or macros for converting addresses within these sections to SRAM addresses, maybe also inlines or macros for calling code that has been pushed into sram by wrapping the original function pointer. The macros or inlines that call SRAM code could perform the trampoline as well.

Would there be much general interest in a scheme like this? Is there already I scheme like this I haven't noticed. Would such an explosion of sections be a problem if all platforms started using this for SRAM code?

#38 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

Quoted 20 lines · Russ Dill
Quoted 1 line · Kevin Hilman

Vaibhav Bedia <[email protected]> writes:

[snip]

Quoted 7 lines
Quoted 4 lines

Morevoer, all the suggestions on how to keep the code in Linux working around the complications due to the main memory not being accessible will need to be replicated on the non-Linux s/w stacks and that's just make it more difficult for them.

The linux code has to be very self-contained (in assembler or C), so I'm not sure how it's difficult to replicated in any other OS (or non-OS.)

Let me pull on this thread a little bit. Would this involve defining a bunch of different sections for each arch so that I could do:

void __sram_am33xx am33xx_some_pm_function(args....) {
}

static struct am33xx_foo_dyn *blargity __sramdata_am33xx = { ...};

Quoted 8 lines

static const struct am33xx_foo_const *blarg __sramconst_am33xx = { ... };

The sram push code could then push these sections into sram. There would then be a set of inlines or macros for converting addresses within these sections to SRAM addresses, maybe also inlines or macros for calling code that has been pushed into sram by wrapping the original function pointer. The macros or inlines that call SRAM code could perform the trampoline as well.

Well, I was thinking of something much dumber.

I was thinking about just _carefully_ writing a single, self-contained C function, with all of its data on the stack (and consts as #defines). Think of it is a step up in readability from straight assembly (which was the stated reason for moving the code to the M3 in the first place.)

Kevin

author
#39 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

Quoted 8 lines · Kevin Hilman

Well, I was thinking of something much dumber.

I was thinking about just _carefully_ writing a single, self-contained C function, with all of its data on the stack (and consts as #defines). Think of it is a step up in readability from straight assembly (which was the stated reason for moving the code to the M3 in the first place.)

Kevin

That can only give you your suspend function. You need a resume function to undo everything before calling cpu_resume, and the resume function needs access to the data that was in the suspend function.

#40 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

Quoted 8 lines · Russ Dill
Quoted 7 lines · Kevin Hilman

Well, I was thinking of something much dumber.

I was thinking about just _carefully_ writing a single, self-contained C function, with all of its data on the stack (and consts as #defines). Think of it is a step up in readability from straight assembly (which was the stated reason for moving the code to the M3 in the first place.)

Quoted 5 lines
Quoted 1 line

Kevin

That can only give you your suspend function. You need a resume function to undo everything before calling cpu_resume, and the resume function needs access to the data that was in the suspend function.

Well, it can be done (and I've seen it done) with various hacks, but I agree it's not pretty, but it is at least slightly prettier than piles of asm. That being said, I saw you posted a more generic solution that looks like a real solution that can scale. I'll have a closer look at that. Thanks for working on it.

Kevin

#41 lore raw

v4 cover ARM: OMAP2+: AM33XX: VDD CORE OPP50 support

In reply to Kevin Hilman

On Thu, Aug 29, 2013 at 5:33 PM, Kevin Hilman <[email protected]> wrote:
[...]

Quoted 33 lines
Quoted 25 lines
Quoted 7 lines

Maybe I'm getting confused, but the more you talk about the linux and the firmware doing the same code, the more I think the firmware is (trying) to do too much. If this is going to be understandable (and maintainable), there needs to be a clean split of roles between the MPU and the M3.

In the past i tried to keep the firmware as minimal as possible and pushed back all efforts to push unnecessary stuff in there. Like Kevin, i too was of the opinion that we need to do whatever's possible in the kernel.

Sadly, things just got a point where it made much more sense (technically as well as non-technically) to just put the low level stuff in the M3.

1. In addition to the Linux support, we have non-OS based code for AM335x (and AM437x) which in the past just had to duplicate whatever was done in the Linux. Being a different codebase the non-OS guys have their own set of challenges and we had to at times sit through and debug issues in one codebase while the other was working fine. Ideally these things should never come up but sadly they do and we need to solve them.

We also have people trying to implement this thing on different sort of OS environments and they also end up debugging the same set of issues.

Quoted 7 lines

With only a handful of folks able to spend time debugging issues 'do everything in Linux and expect others to copy it' model doesn't scale up. Keeping things in the
firmware with the code available online helps do away with the 'developer scalability'
problem and if one looks at things differently enables code-reuse at the same time.

Quoted 3 lines

Yes, yes, this is the classic argument for a HAL, and making that case has always gone over well in the kernel. ;)

If the carefully crafted C code with a bit of linker magic thrown in works, that's great
but for anyone without the generic Linux code in general and the Linux community to go to with queries will have a tough time getting it done. TI non-OS code has been
implemented from scratch and one of the purposes is to keep things as simple as possible for folks. I know it's all non-technical stuff and most likely i am a bit
biased but just a different POV from someone who spent hours debugging 'this works
and that doesn't' kind of issues. Trying to wrap your head around random s/w stacks
and figuring out what needs to be changed is not pretty IMHO :\

Quoted 7 lines
Quoted 6 lines

Moreover, when the code was finally moved to M3, AFAICT we had validated all supported combinations (DDR2, DDR3, DDR3 with VTT, DDR3 without VTT control) that TI hardware guys could throw at us.Yes there could be stupid bugs lurking in the code since it wasn't reviewed as much as i would have liked but we
have something which proves the functionality to be used as the base.

Quoted 5 lines

I don't follow the argument here. You mean it was validated *before* moving it to the M3, or after? Either way, I'm how that makes a case for moving it to the M3. Surely if it was on the MPU, it would also have been validated, no?

At least i did all the validation without the VDD_CORE changes. The point i was trying to make is that we are not trying to hide bugs in there, if that's one of the
concerns. Just trying to make it simple (subjective, i know) for others to use.

Quoted 6 lines
Quoted 3 lines

2. As has been already been pointed out by Russ, on AM335x this step needs to be done very late in the suspend process (the last stage actually).

late, yes. But *technically*, it can still be done from the MPU.

Quoted 8 lines
Quoted 2 lines

On the next SoC there are other complications related to security which enforce this step must be done from the M3.

Aha! So far, the first technical reason I've seen. Can you elaborate? Exactly which step has to be done from the M3, and why can't the MPU do it? And could it be done more simply by putting *only* the parts needed on the M3 and not letting it snowball into a pile of stuff?

Sent out another mail for this.

Regards,
Vaibhav