summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-omap2/pm34xx.c5
-rw-r--r--arch/arm/plat-omap/dma.c43
-rw-r--r--drivers/dma/ti/omap-dma.c150
-rw-r--r--include/linux/omap-dma.h2
4 files changed, 138 insertions, 62 deletions
diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c
index 54254fc92c2e..e66e9948636c 100644
--- a/arch/arm/mach-omap2/pm34xx.c
+++ b/arch/arm/mach-omap2/pm34xx.c
@@ -25,7 +25,6 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/slab.h>
-#include <linux/omap-dma.h>
#include <linux/omap-gpmc.h>
#include <trace/events/power.h>
@@ -85,7 +84,6 @@ static void omap3_core_save_context(void)
omap3_gpmc_save_context();
/* Save the system control module context, padconf already save above*/
omap3_control_save_context();
- omap_dma_global_context_save();
}
static void omap3_core_restore_context(void)
@@ -96,7 +94,6 @@ static void omap3_core_restore_context(void)
omap3_gpmc_restore_context();
/* Restore the interrupt controller context */
omap_intc_restore_context();
- omap_dma_global_context_restore();
}
/*
@@ -547,9 +544,7 @@ int __init omap3_pm_init(void)
local_irq_disable();
- omap_dma_global_context_save();
omap3_save_secure_ram_context();
- omap_dma_global_context_restore();
local_irq_enable();
}
diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c
index 8aea155b10a8..0f68c6faecad 100644
--- a/arch/arm/plat-omap/dma.c
+++ b/arch/arm/plat-omap/dma.c
@@ -68,13 +68,6 @@ static void omap_clear_dma(int lch);
static int enable_1510_mode;
static u32 errata;
-static struct omap_dma_global_context_registers {
- u32 dma_irqenable_l0;
- u32 dma_irqenable_l1;
- u32 dma_ocp_sysconfig;
- u32 dma_gcr;
-} omap_dma_global_context;
-
struct dma_link_info {
int *linked_dmach_q;
int no_of_lchs_linked;
@@ -905,42 +898,6 @@ static irqreturn_t omap1_dma_irq_handler(int irq, void *dev_id)
#define omap1_dma_irq_handler NULL
#endif
-/*
- * Note that we are currently using only IRQENABLE_L0 and L1.
- * As the DSP may be using IRQENABLE_L2 and L3, let's not
- * touch those for now.
- */
-void omap_dma_global_context_save(void)
-{
- omap_dma_global_context.dma_irqenable_l0 =
- p->dma_read(IRQENABLE_L0, 0);
- omap_dma_global_context.dma_irqenable_l1 =
- p->dma_read(IRQENABLE_L1, 0);
- omap_dma_global_context.dma_ocp_sysconfig =
- p->dma_read(OCP_SYSCONFIG, 0);
- omap_dma_global_context.dma_gcr = p->dma_read(GCR, 0);
-}
-
-void omap_dma_global_context_restore(void)
-{
- int ch;
-
- p->dma_write(omap_dma_global_context.dma_gcr, GCR, 0);
- p->dma_write(omap_dma_global_context.dma_ocp_sysconfig,
- OCP_SYSCONFIG, 0);
- p->dma_write(omap_dma_global_context.dma_irqenable_l0,
- IRQENABLE_L0, 0);
- p->dma_write(omap_dma_global_context.dma_irqenable_l1,
- IRQENABLE_L1, 0);
-
- if (IS_DMA_ERRATA(DMA_ROMCODE_BUG))
- p->dma_write(0x3 , IRQSTATUS_L0, 0);
-
- for (ch = 0; ch < dma_chan_count; ch++)
- if (dma_chan[ch].dev_id != -1)
- omap_clear_dma(ch);
-}
-
struct omap_system_dma_plat_info *omap_get_plat_info(void)
{
return p;
diff --git a/drivers/dma/ti/omap-dma.c b/drivers/dma/ti/omap-dma.c
index 6b6ba238b81a..5423cb7b5256 100644
--- a/drivers/dma/ti/omap-dma.c
+++ b/drivers/dma/ti/omap-dma.c
@@ -2,6 +2,7 @@
/*
* OMAP DMAengine support
*/
+#include <linux/cpu_pm.h>
#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
@@ -23,12 +24,28 @@
#define OMAP_SDMA_REQUESTS 127
#define OMAP_SDMA_CHANNELS 32
+struct omap_dma_config {
+ int lch_end;
+ unsigned int may_lose_context:1;
+};
+
+struct omap_dma_context {
+ u32 irqenable_l0;
+ u32 irqenable_l1;
+ u32 ocp_sysconfig;
+ u32 gcr;
+};
+
struct omap_dmadev {
struct dma_device ddev;
spinlock_t lock;
void __iomem *base;
const struct omap_dma_reg *reg_map;
struct omap_system_dma_plat_info *plat;
+ const struct omap_dma_config *cfg;
+ struct notifier_block nb;
+ struct omap_dma_context context;
+ int lch_count;
bool legacy;
bool ll123_supported;
struct dma_pool *desc_pool;
@@ -376,6 +393,19 @@ static unsigned omap_dma_get_csr(struct omap_chan *c)
return val;
}
+static void omap_dma_clear_lch(struct omap_dmadev *od, int lch)
+{
+ struct omap_chan *c;
+ int i;
+
+ c = od->lch_map[lch];
+ if (!c)
+ return;
+
+ for (i = CSDP; i <= od->cfg->lch_end; i++)
+ omap_dma_chan_write(c, i, 0);
+}
+
static void omap_dma_assign(struct omap_dmadev *od, struct omap_chan *c,
unsigned lch)
{
@@ -652,6 +682,7 @@ static int omap_dma_alloc_chan_resources(struct dma_chan *chan)
if (ret >= 0) {
omap_dma_assign(od, c, c->dma_ch);
+ pr_info("XXX %s: assigned lch: %i\n", __func__, c->dma_ch);
if (!od->legacy) {
unsigned val;
@@ -1453,16 +1484,74 @@ static void omap_dma_free(struct omap_dmadev *od)
}
}
+/*
+ * We are using IRQENABLE_L1, and legacy DMA code was using IRQENABLE_L0.
+ * As the DSP may be using IRQENABLE_L2 and L3, let's not touch those for
+ * now. Context save seems to be only currently needed on omap3.
+ */
+static void omap_dma_context_save(struct omap_dmadev *od)
+{
+ od->context.irqenable_l0 = omap_dma_glbl_read(od, IRQENABLE_L0);
+ od->context.irqenable_l1 = omap_dma_glbl_read(od, IRQENABLE_L1);
+ od->context.ocp_sysconfig = omap_dma_glbl_read(od, OCP_SYSCONFIG);
+ od->context.gcr = omap_dma_glbl_read(od, GCR);
+}
+
+static void omap_dma_context_restore(struct omap_dmadev *od)
+{
+ int i;
+
+ omap_dma_glbl_write(od, GCR, od->context.gcr);
+ omap_dma_glbl_write(od, OCP_SYSCONFIG, od->context.ocp_sysconfig);
+ omap_dma_glbl_write(od, IRQENABLE_L0, od->context.irqenable_l0);
+ omap_dma_glbl_write(od, IRQENABLE_L1, od->context.irqenable_l1);
+
+ /* Clear IRQSTATUS_L0 as legacy DMA code is no longer doing it */
+ if (od->plat->errata & DMA_ROMCODE_BUG)
+ omap_dma_glbl_write(od, IRQSTATUS_L0, 0);
+
+ /* Clear dma channels */
+ for (i = 0; i < od->lch_count; i++)
+ omap_dma_clear_lch(od, i);
+}
+
+/* Currently only used for omap3 */
+static int omap_dma_context_notifier(struct notifier_block *nb,
+ unsigned long cmd, void *v)
+{
+ struct omap_dmadev *od;
+
+ od = container_of(nb, struct omap_dmadev, nb);
+
+ switch (cmd) {
+ case CPU_CLUSTER_PM_ENTER:
+ omap_dma_context_save(od);
+ break;
+ case CPU_CLUSTER_PM_ENTER_FAILED:
+ case CPU_CLUSTER_PM_EXIT:
+ omap_dma_context_restore(od);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
#define OMAP_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES))
+/*
+ * No flags currently set for default configuration as omap1 is still
+ * using platform data.
+ */
+static const struct omap_dma_config default_cfg;
+
static int omap_dma_probe(struct platform_device *pdev)
{
+ const struct omap_dma_config *conf;
struct omap_dmadev *od;
struct resource *res;
int rc, i, irq;
- u32 lch_count;
od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL);
if (!od)
@@ -1473,6 +1562,12 @@ static int omap_dma_probe(struct platform_device *pdev)
if (IS_ERR(od->base))
return PTR_ERR(od->base);
+ conf = of_device_get_match_data(&pdev->dev);
+ if (conf)
+ od->cfg = conf;
+ else
+ od->cfg = &default_cfg;
+
od->plat = omap_get_plat_info();
if (!od->plat)
return -EPROBE_DEFER;
@@ -1522,18 +1617,19 @@ static int omap_dma_probe(struct platform_device *pdev)
/* Number of available logical channels */
if (!pdev->dev.of_node) {
- lch_count = od->plat->dma_attr->lch_count;
- if (unlikely(!lch_count))
- lch_count = OMAP_SDMA_CHANNELS;
+ od->lch_count = od->plat->dma_attr->lch_count;
+ if (unlikely(!od->lch_count))
+ od->lch_count = OMAP_SDMA_CHANNELS;
} else if (of_property_read_u32(pdev->dev.of_node, "dma-channels",
- &lch_count)) {
+ &od->lch_count)) {
dev_info(&pdev->dev,
"Missing dma-channels property, using %u.\n",
OMAP_SDMA_CHANNELS);
- lch_count = OMAP_SDMA_CHANNELS;
+ od->lch_count = OMAP_SDMA_CHANNELS;
}
- od->lch_map = devm_kcalloc(&pdev->dev, lch_count, sizeof(*od->lch_map),
+ od->lch_map = devm_kcalloc(&pdev->dev, od->lch_count,
+ sizeof(*od->lch_map),
GFP_KERNEL);
if (!od->lch_map)
return -ENOMEM;
@@ -1605,6 +1701,11 @@ static int omap_dma_probe(struct platform_device *pdev)
}
}
+ if (od->cfg->may_lose_context) {
+ od->nb.notifier_call = omap_dma_context_notifier;
+ cpu_pm_register_notifier(&od->nb);
+ }
+
dev_info(&pdev->dev, "OMAP DMA engine driver%s\n",
od->ll123_supported ? " (LinkedList1/2/3 supported)" : "");
@@ -1616,6 +1717,9 @@ static int omap_dma_remove(struct platform_device *pdev)
struct omap_dmadev *od = platform_get_drvdata(pdev);
int irq;
+ if (od->cfg->may_lose_context)
+ cpu_pm_unregister_notifier(&od->nb);
+
if (pdev->dev.of_node)
of_dma_controller_free(pdev->dev.of_node);
@@ -1637,12 +1741,34 @@ static int omap_dma_remove(struct platform_device *pdev)
return 0;
}
+static const struct omap_dma_config omap2420_data = {
+ .lch_end = CCFN,
+};
+
+static const struct omap_dma_config omap2430_data = {
+ .lch_end = CCFN,
+};
+
+static const struct omap_dma_config omap3430_data = {
+ .lch_end = CCFN,
+ .may_lose_context = true,
+};
+
+static const struct omap_dma_config omap3630_data = {
+ .lch_end = CCDN,
+ .may_lose_context = true,
+};
+
+static const struct omap_dma_config omap4_data = {
+ .lch_end = CCDN,
+};
+
static const struct of_device_id omap_dma_match[] = {
- { .compatible = "ti,omap2420-sdma", },
- { .compatible = "ti,omap2430-sdma", },
- { .compatible = "ti,omap3430-sdma", },
- { .compatible = "ti,omap3630-sdma", },
- { .compatible = "ti,omap4430-sdma", },
+ { .compatible = "ti,omap2420-sdma", .data = &omap2420_data, },
+ { .compatible = "ti,omap2430-sdma", .data = &omap2430_data, },
+ { .compatible = "ti,omap3430-sdma", .data = &omap3430_data, },
+ { .compatible = "ti,omap3630-sdma", .data = &omap3630_data, },
+ { .compatible = "ti,omap4430-sdma", .data = &omap4_data, },
{},
};
MODULE_DEVICE_TABLE(of, omap_dma_match);
diff --git a/include/linux/omap-dma.h b/include/linux/omap-dma.h
index ee0de8443274..5c5c93ad6b50 100644
--- a/include/linux/omap-dma.h
+++ b/include/linux/omap-dma.h
@@ -327,8 +327,6 @@ extern dma_addr_t omap_get_dma_src_pos(int lch);
extern dma_addr_t omap_get_dma_dst_pos(int lch);
extern int omap_get_dma_active_status(int lch);
extern int omap_dma_running(void);
-void omap_dma_global_context_save(void);
-void omap_dma_global_context_restore(void);
#if defined(CONFIG_ARCH_OMAP1) && IS_ENABLED(CONFIG_FB_OMAP)
#include <mach/lcd_dma.h>
OpenPOWER on IntegriCloud