Skip to contentSkip to conversation

ARM: omap: edma: add suspend suspend/resume hooks

PATCH RESEND Superseded Dave Gerlach 3 participants 4 messages Aug 22, 2014 – Aug 26, 2014 Arm (32-bit)

v7 of 9 Superseded Latest is v9 →

lore
Share & tools
lore
#0 lore raw

PATCH RESEND v7 1/1 ARM: omap: edma: add suspend suspend/resume hooks

From: Daniel Mack <[email protected]>

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

All information can be reconstructed by already known runtime information.

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

[nm at ti.com: added error handling for runtime + suspend_late/early_resume]
[d-gerlach at ti.com: updated to remove queue_tc_mapping use]

---

Needed for am335x suspend, but never got picked up. Previously sent here: http://marc.info/?l=linux-arm-kernel&m=138556067416051&w=2

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

PATCH RESEND v7 1/1 ARM: omap: edma: add suspend suspend/resume hooks

Quoted 67 lines · Dave Gerlach

From: Daniel Mack <[email protected]>

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

All information can be reconstructed by already known runtime information.

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

[nm at ti.com: added error handling for runtime + suspend_late/early_resume]
[d-gerlach at ti.com: updated to remove queue_tc_mapping use]

---

Needed for am335x suspend, but never got picked up. Previously sent here: http://marc.info/?l=linux-arm-kernel&m=138556067416051&w=2

 arch/arm/common/edma.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 84 insertions(+), 2 deletions(-)
arch/arm/common/edma.c +5 -2
index 8809917..ece1e3d 100644
@@ -244,6 +244,8 @@ struct edma {
 	/* list of channels with no even trigger; terminated by "-1" */
 	const s8	*noevent;
 
+	struct edma_soc_info *info;
+
 	/* The edma_inuse bit for each PaRAM slot is clear unless the
 	 * channel is in use ... by ARM or DSP, for QDMA, or whatever.
 	 */
@@ -295,7 +297,7 @@ static void map_dmach_queue(unsigned ctlr, unsigned ch_no,
 			~(0x7 << bit), queue_no << bit);
 }
 
-static void __init assign_priority_to_queue(unsigned ctlr, int queue_no,
+static void assign_priority_to_queue(unsigned ctlr, int queue_no,
 		int priority)
 {
 	int bit = queue_no * 4;
@@ -314,7 +316,7 @@ static void __init assign_priority_to_queue(unsigned ctlr, int queue_no,
  * included in that particular EDMA variant (Eg : dm646x)
  *
  */
-static void __init map_dmach_param(unsigned ctlr)
+static void map_dmach_param(unsigned ctlr)
 {
 	int i;
 	for (i = 0; i < EDMA_MAX_DMACH; i++)
@@ -1791,15 +1793,95 @@ static int edma_probe(struct platform_device *pdev)
 			edma_write_array2(j, EDMA_DRAE, i, 1, 0x0);
 			edma_write_array(j, EDMA_QRAE, i, 0x0);
 		}
+		edma_cc[j]->info = info[j];
 		arch_num_cc++;
 	}
 
 	return 0;
 }

arch/arm/common/edma.c static int edma_probe(struct platform_device *pdev) · line 1811

Quoted 10 lines
arch/arm/common/edma.c +9 -0
 
+static int edma_pm_suspend(struct device *dev)
+{
+	int j, r;
+
+	r = pm_runtime_get_sync(dev);
+	if (r < 0) {
+		dev_err(dev, "%s: get_sync returned %d\n", __func__, r);
+		return r;
+	}

The driver currently does a pm_runtime_get_sync() once during probe. And does not do a put(). So this should actually be not required. In fact looks like this additional get() call will prevent the clock from getting disabled which is probably not what you intend.

arch/arm/common/edma.c static int edma_probe(struct platform_device *pdev) · line 1817

Quoted 6 lines
arch/arm/common/edma.c +6 -0
+
+	for (j = 0; j < arch_num_cc; j++) {
+		struct edma *ecc = edma_cc[j];
+
+		disable_irq(ecc->irq_res_start);
+		disable_irq(ecc->irq_res_end);

Do we really need to disable these irqs?

Quoted 52 lines
arch/arm/common/edma.c +52 -0
+	}
+
+	pm_runtime_put_sync(dev);
+
+	return 0;
+}
+
+static int edma_pm_resume(struct device *dev)
+{
+	int i, j, r;
+
+	r = pm_runtime_get_sync(dev);
+	if (r < 0) {
+		dev_err(dev, "%s: get_sync returned %d\n", __func__, r);
+		return r;
+	}
+
+	for (j = 0; j < arch_num_cc; j++) {
+		struct edma *cc = edma_cc[j];
+
+		s8 (*queue_priority_mapping)[2];
+
+		queue_priority_mapping = cc->info->queue_priority_mapping;
+
+		/* Event queue priority mapping */
+		for (i = 0; queue_priority_mapping[i][0] != -1; i++)
+			assign_priority_to_queue(j,
+						 queue_priority_mapping[i][0],
+						 queue_priority_mapping[i][1]);
+
+		/*
+		 * Map the channel to param entry if channel mapping logic
+		 * exist
+		 */
+		if (edma_read(j, EDMA_CCCFG) & CHMAP_EXIST)
+			map_dmach_param(j);
+
+		for (i = 0; i < cc->num_channels; i++) {
+			if (test_bit(i, cc->edma_inuse)) {
+				/* ensure access through shadow region 0 */
+				edma_or_array2(j, EDMA_DRAE, 0, i >> 5,
+					       BIT(i & 0x1f));
+
+				setup_dma_interrupt(i,
+						    cc->intr_data[i].callback,
+						    cc->intr_data[i].data);
+			}
+		}
+
+		enable_irq(cc->irq_res_start);
+		enable_irq(cc->irq_res_end);
+	}

arch/arm/common/edma.c static int edma_probe(struct platform_device *pdev) · line 1879

Quoted 10 lines
arch/arm/common/edma.c +10 -0
+
+	pm_runtime_put_sync(dev);
+
+	return 0;
+}
+
+static const struct dev_pm_ops edma_pm_ops = {
+	.suspend_late	= edma_pm_suspend,
+	.resume_early	= edma_pm_resume,
+};

You can use SET_LATE_SYSTEM_SLEEP_PM_OPS() as some other DMA drivers are doing too.

Thanks,
Sekhar

#2 lore raw

PATCH RESEND v7 1/1 ARM: omap: edma: add suspend suspend/resume hooks

Hi,

Quoted 1 line · Sekhar Nori

On Friday 22 August 2014 01:16 PM, Dave Gerlach wrote:

Thanks for pushing that forward!

Quoted 6 lines
Quoted 6 lines
arch/arm/common/edma.c +6 -0
+static int edma_pm_suspend(struct device *dev)
+{
+	int j, r;
+
+	r = pm_runtime_get_sync(dev);
+	if (r < 0) {
Quoted 8 lines

arch/arm/common/edma.c static int edma_probe(struct platform_device *pdev) · line 1811

Quoted 3 lines
arch/arm/common/edma.c +3 -0
+		dev_err(dev, "%s: get_sync returned %d\n", __func__, r);
+		return r;
+	}

The driver currently does a pm_runtime_get_sync() once during probe. And does not do a put(). So this should actually be not required. In fact looks like this additional get() call will prevent the clock from getting disabled which is probably not what you intend.

Well, the pm runtime is put again ...

Quoted 9 lines

arch/arm/common/edma.c static int edma_probe(struct platform_device *pdev) · line 1817

Quoted 6 lines
arch/arm/common/edma.c +6 -0
+
+	for (j = 0; j < arch_num_cc; j++) {
+		struct edma *ecc = edma_cc[j];
+
+		disable_irq(ecc->irq_res_start);
+		disable_irq(ecc->irq_res_end);

Do we really need to disable these irqs?

Quoted 3 lines

arch/arm/common/edma.c static int edma_probe(struct platform_device *pdev) · line 1820

Quoted 3 lines
arch/arm/common/edma.c +3 -0
+	}
+
+	pm_runtime_put_sync(dev);

... here, so it's in sync and should be fine.

I was also sure than when I wrote the code, disabling the interrupts during suspend was necessary, and even the only thing that has to be done at suspend time. Now that I address this again, my tests show that in can in fact be omitted.

So I'll send a v9 now that has no edma_pm_suspend() at all anymore.

Quoted 7 lines

arch/arm/common/edma.c static int edma_probe(struct platform_device *pdev) · line 1879

Quoted 4 lines
arch/arm/common/edma.c +4 -0
+static const struct dev_pm_ops edma_pm_ops = {
+	.suspend_late	= edma_pm_suspend,
+	.resume_early	= edma_pm_resume,
+};

You can use SET_LATE_SYSTEM_SLEEP_PM_OPS() as some other DMA drivers are doing too.

Sure, why not.

Thanks,
Daniel

#3 lore raw

PATCH RESEND v7 1/1 ARM: omap: edma: add suspend suspend/resume hooks

Quoted 33 lines · Daniel Mack

Hi,

Quoted 1 line · Sekhar Nori

On Friday 22 August 2014 01:16 PM, Dave Gerlach wrote:

Thanks for pushing that forward!

Quoted 6 lines
Quoted 6 lines
arch/arm/common/edma.c +6 -0
+static int edma_pm_suspend(struct device *dev)
+{
+	int j, r;
+
+	r = pm_runtime_get_sync(dev);
+	if (r < 0) {
Quoted 8 lines

arch/arm/common/edma.c static int edma_probe(struct platform_device *pdev) · line 1811

Quoted 3 lines
arch/arm/common/edma.c +3 -0
+		dev_err(dev, "%s: get_sync returned %d\n", __func__, r);
+		return r;
+	}

The driver currently does a pm_runtime_get_sync() once during probe. And does not do a put(). So this should actually be not required. In fact looks like this additional get() call will prevent the clock from getting disabled which is probably not what you intend.

Well, the pm runtime is put again ...

Quoted 9 lines

arch/arm/common/edma.c static int edma_probe(struct platform_device *pdev) · line 1817

Quoted 6 lines
arch/arm/common/edma.c +6 -0
+
+	for (j = 0; j < arch_num_cc; j++) {
+		struct edma *ecc = edma_cc[j];
+
+		disable_irq(ecc->irq_res_start);
+		disable_irq(ecc->irq_res_end);

Do we really need to disable these irqs?

Quoted 5 lines
Quoted 3 lines

arch/arm/common/edma.c static int edma_probe(struct platform_device *pdev) · line 1820

Quoted 3 lines
arch/arm/common/edma.c +3 -0
+	}
+
+	pm_runtime_put_sync(dev);

... here, so it's in sync and should be fine.

May be I am missing something but because of the pm_runtime_get_sync() in probe() usage count is already 1 when suspend() is called. The pm_runtime_get_sync() in this function makes it 2 and therefore pm_runtime_put_sync() returns immediately because the usage count is greater that 0 after decrementing by 1. That means the module's clocks is not disabled after suspend() is finished.

Quoted 5 lines

I was also sure than when I wrote the code, disabling the interrupts during suspend was necessary, and even the only thing that has to be done at suspend time. Now that I address this again, my tests show that in can in fact be omitted.

Thanks!

Regards,
Sekhar