|
|
|
|
@@ -9,6 +9,7 @@
|
|
|
|
|
* Copyright (C) 2009 Texas Instruments
|
|
|
|
|
* Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com>
|
|
|
|
|
*/
|
|
|
|
|
#include <linux/cpu_pm.h>
|
|
|
|
|
#include <linux/irq.h>
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
|
#include <linux/init.h>
|
|
|
|
|
@@ -232,7 +233,10 @@ struct gpmc_device {
|
|
|
|
|
int irq;
|
|
|
|
|
struct irq_chip irq_chip;
|
|
|
|
|
struct gpio_chip gpio_chip;
|
|
|
|
|
struct notifier_block nb;
|
|
|
|
|
struct omap3_gpmc_regs context;
|
|
|
|
|
int nirqs;
|
|
|
|
|
unsigned int is_suspended:1;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct irq_domain *gpmc_irq_domain;
|
|
|
|
|
@@ -2384,6 +2388,106 @@ static int gpmc_gpio_init(struct gpmc_device *gpmc)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void omap3_gpmc_save_context(struct gpmc_device *gpmc)
|
|
|
|
|
{
|
|
|
|
|
struct omap3_gpmc_regs *gpmc_context;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (!gpmc || !gpmc_base)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
gpmc_context = &gpmc->context;
|
|
|
|
|
|
|
|
|
|
gpmc_context->sysconfig = gpmc_read_reg(GPMC_SYSCONFIG);
|
|
|
|
|
gpmc_context->irqenable = gpmc_read_reg(GPMC_IRQENABLE);
|
|
|
|
|
gpmc_context->timeout_ctrl = gpmc_read_reg(GPMC_TIMEOUT_CONTROL);
|
|
|
|
|
gpmc_context->config = gpmc_read_reg(GPMC_CONFIG);
|
|
|
|
|
gpmc_context->prefetch_config1 = gpmc_read_reg(GPMC_PREFETCH_CONFIG1);
|
|
|
|
|
gpmc_context->prefetch_config2 = gpmc_read_reg(GPMC_PREFETCH_CONFIG2);
|
|
|
|
|
gpmc_context->prefetch_control = gpmc_read_reg(GPMC_PREFETCH_CONTROL);
|
|
|
|
|
for (i = 0; i < gpmc_cs_num; i++) {
|
|
|
|
|
gpmc_context->cs_context[i].is_valid = gpmc_cs_mem_enabled(i);
|
|
|
|
|
if (gpmc_context->cs_context[i].is_valid) {
|
|
|
|
|
gpmc_context->cs_context[i].config1 =
|
|
|
|
|
gpmc_cs_read_reg(i, GPMC_CS_CONFIG1);
|
|
|
|
|
gpmc_context->cs_context[i].config2 =
|
|
|
|
|
gpmc_cs_read_reg(i, GPMC_CS_CONFIG2);
|
|
|
|
|
gpmc_context->cs_context[i].config3 =
|
|
|
|
|
gpmc_cs_read_reg(i, GPMC_CS_CONFIG3);
|
|
|
|
|
gpmc_context->cs_context[i].config4 =
|
|
|
|
|
gpmc_cs_read_reg(i, GPMC_CS_CONFIG4);
|
|
|
|
|
gpmc_context->cs_context[i].config5 =
|
|
|
|
|
gpmc_cs_read_reg(i, GPMC_CS_CONFIG5);
|
|
|
|
|
gpmc_context->cs_context[i].config6 =
|
|
|
|
|
gpmc_cs_read_reg(i, GPMC_CS_CONFIG6);
|
|
|
|
|
gpmc_context->cs_context[i].config7 =
|
|
|
|
|
gpmc_cs_read_reg(i, GPMC_CS_CONFIG7);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void omap3_gpmc_restore_context(struct gpmc_device *gpmc)
|
|
|
|
|
{
|
|
|
|
|
struct omap3_gpmc_regs *gpmc_context;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (!gpmc || !gpmc_base)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
gpmc_context = &gpmc->context;
|
|
|
|
|
|
|
|
|
|
gpmc_write_reg(GPMC_SYSCONFIG, gpmc_context->sysconfig);
|
|
|
|
|
gpmc_write_reg(GPMC_IRQENABLE, gpmc_context->irqenable);
|
|
|
|
|
gpmc_write_reg(GPMC_TIMEOUT_CONTROL, gpmc_context->timeout_ctrl);
|
|
|
|
|
gpmc_write_reg(GPMC_CONFIG, gpmc_context->config);
|
|
|
|
|
gpmc_write_reg(GPMC_PREFETCH_CONFIG1, gpmc_context->prefetch_config1);
|
|
|
|
|
gpmc_write_reg(GPMC_PREFETCH_CONFIG2, gpmc_context->prefetch_config2);
|
|
|
|
|
gpmc_write_reg(GPMC_PREFETCH_CONTROL, gpmc_context->prefetch_control);
|
|
|
|
|
for (i = 0; i < gpmc_cs_num; i++) {
|
|
|
|
|
if (gpmc_context->cs_context[i].is_valid) {
|
|
|
|
|
gpmc_cs_write_reg(i, GPMC_CS_CONFIG1,
|
|
|
|
|
gpmc_context->cs_context[i].config1);
|
|
|
|
|
gpmc_cs_write_reg(i, GPMC_CS_CONFIG2,
|
|
|
|
|
gpmc_context->cs_context[i].config2);
|
|
|
|
|
gpmc_cs_write_reg(i, GPMC_CS_CONFIG3,
|
|
|
|
|
gpmc_context->cs_context[i].config3);
|
|
|
|
|
gpmc_cs_write_reg(i, GPMC_CS_CONFIG4,
|
|
|
|
|
gpmc_context->cs_context[i].config4);
|
|
|
|
|
gpmc_cs_write_reg(i, GPMC_CS_CONFIG5,
|
|
|
|
|
gpmc_context->cs_context[i].config5);
|
|
|
|
|
gpmc_cs_write_reg(i, GPMC_CS_CONFIG6,
|
|
|
|
|
gpmc_context->cs_context[i].config6);
|
|
|
|
|
gpmc_cs_write_reg(i, GPMC_CS_CONFIG7,
|
|
|
|
|
gpmc_context->cs_context[i].config7);
|
|
|
|
|
} else {
|
|
|
|
|
gpmc_cs_write_reg(i, GPMC_CS_CONFIG7, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int omap_gpmc_context_notifier(struct notifier_block *nb,
|
|
|
|
|
unsigned long cmd, void *v)
|
|
|
|
|
{
|
|
|
|
|
struct gpmc_device *gpmc;
|
|
|
|
|
|
|
|
|
|
gpmc = container_of(nb, struct gpmc_device, nb);
|
|
|
|
|
if (gpmc->is_suspended || pm_runtime_suspended(gpmc->dev))
|
|
|
|
|
return NOTIFY_OK;
|
|
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
|
case CPU_CLUSTER_PM_ENTER:
|
|
|
|
|
omap3_gpmc_save_context(gpmc);
|
|
|
|
|
break;
|
|
|
|
|
case CPU_CLUSTER_PM_ENTER_FAILED: /* No need to restore context */
|
|
|
|
|
break;
|
|
|
|
|
case CPU_CLUSTER_PM_EXIT:
|
|
|
|
|
omap3_gpmc_restore_context(gpmc);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NOTIFY_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int gpmc_probe(struct platform_device *pdev)
|
|
|
|
|
{
|
|
|
|
|
int rc;
|
|
|
|
|
@@ -2472,6 +2576,9 @@ static int gpmc_probe(struct platform_device *pdev)
|
|
|
|
|
|
|
|
|
|
gpmc_probe_dt_children(pdev);
|
|
|
|
|
|
|
|
|
|
gpmc->nb.notifier_call = omap_gpmc_context_notifier;
|
|
|
|
|
cpu_pm_register_notifier(&gpmc->nb);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
gpio_init_failed:
|
|
|
|
|
@@ -2486,6 +2593,7 @@ static int gpmc_remove(struct platform_device *pdev)
|
|
|
|
|
{
|
|
|
|
|
struct gpmc_device *gpmc = platform_get_drvdata(pdev);
|
|
|
|
|
|
|
|
|
|
cpu_pm_unregister_notifier(&gpmc->nb);
|
|
|
|
|
gpmc_free_irq(gpmc);
|
|
|
|
|
gpmc_mem_exit();
|
|
|
|
|
pm_runtime_put_sync(&pdev->dev);
|
|
|
|
|
@@ -2497,15 +2605,23 @@ static int gpmc_remove(struct platform_device *pdev)
|
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
|
|
|
static int gpmc_suspend(struct device *dev)
|
|
|
|
|
{
|
|
|
|
|
omap3_gpmc_save_context();
|
|
|
|
|
struct gpmc_device *gpmc = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
|
|
omap3_gpmc_save_context(gpmc);
|
|
|
|
|
pm_runtime_put_sync(dev);
|
|
|
|
|
gpmc->is_suspended = 1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int gpmc_resume(struct device *dev)
|
|
|
|
|
{
|
|
|
|
|
struct gpmc_device *gpmc = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
|
|
pm_runtime_get_sync(dev);
|
|
|
|
|
omap3_gpmc_restore_context();
|
|
|
|
|
omap3_gpmc_restore_context(gpmc);
|
|
|
|
|
gpmc->is_suspended = 0;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
@@ -2527,74 +2643,3 @@ static __init int gpmc_init(void)
|
|
|
|
|
return platform_driver_register(&gpmc_driver);
|
|
|
|
|
}
|
|
|
|
|
postcore_initcall(gpmc_init);
|
|
|
|
|
|
|
|
|
|
static struct omap3_gpmc_regs gpmc_context;
|
|
|
|
|
|
|
|
|
|
void omap3_gpmc_save_context(void)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (!gpmc_base)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
gpmc_context.sysconfig = gpmc_read_reg(GPMC_SYSCONFIG);
|
|
|
|
|
gpmc_context.irqenable = gpmc_read_reg(GPMC_IRQENABLE);
|
|
|
|
|
gpmc_context.timeout_ctrl = gpmc_read_reg(GPMC_TIMEOUT_CONTROL);
|
|
|
|
|
gpmc_context.config = gpmc_read_reg(GPMC_CONFIG);
|
|
|
|
|
gpmc_context.prefetch_config1 = gpmc_read_reg(GPMC_PREFETCH_CONFIG1);
|
|
|
|
|
gpmc_context.prefetch_config2 = gpmc_read_reg(GPMC_PREFETCH_CONFIG2);
|
|
|
|
|
gpmc_context.prefetch_control = gpmc_read_reg(GPMC_PREFETCH_CONTROL);
|
|
|
|
|
for (i = 0; i < gpmc_cs_num; i++) {
|
|
|
|
|
gpmc_context.cs_context[i].is_valid = gpmc_cs_mem_enabled(i);
|
|
|
|
|
if (gpmc_context.cs_context[i].is_valid) {
|
|
|
|
|
gpmc_context.cs_context[i].config1 =
|
|
|
|
|
gpmc_cs_read_reg(i, GPMC_CS_CONFIG1);
|
|
|
|
|
gpmc_context.cs_context[i].config2 =
|
|
|
|
|
gpmc_cs_read_reg(i, GPMC_CS_CONFIG2);
|
|
|
|
|
gpmc_context.cs_context[i].config3 =
|
|
|
|
|
gpmc_cs_read_reg(i, GPMC_CS_CONFIG3);
|
|
|
|
|
gpmc_context.cs_context[i].config4 =
|
|
|
|
|
gpmc_cs_read_reg(i, GPMC_CS_CONFIG4);
|
|
|
|
|
gpmc_context.cs_context[i].config5 =
|
|
|
|
|
gpmc_cs_read_reg(i, GPMC_CS_CONFIG5);
|
|
|
|
|
gpmc_context.cs_context[i].config6 =
|
|
|
|
|
gpmc_cs_read_reg(i, GPMC_CS_CONFIG6);
|
|
|
|
|
gpmc_context.cs_context[i].config7 =
|
|
|
|
|
gpmc_cs_read_reg(i, GPMC_CS_CONFIG7);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void omap3_gpmc_restore_context(void)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (!gpmc_base)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
gpmc_write_reg(GPMC_SYSCONFIG, gpmc_context.sysconfig);
|
|
|
|
|
gpmc_write_reg(GPMC_IRQENABLE, gpmc_context.irqenable);
|
|
|
|
|
gpmc_write_reg(GPMC_TIMEOUT_CONTROL, gpmc_context.timeout_ctrl);
|
|
|
|
|
gpmc_write_reg(GPMC_CONFIG, gpmc_context.config);
|
|
|
|
|
gpmc_write_reg(GPMC_PREFETCH_CONFIG1, gpmc_context.prefetch_config1);
|
|
|
|
|
gpmc_write_reg(GPMC_PREFETCH_CONFIG2, gpmc_context.prefetch_config2);
|
|
|
|
|
gpmc_write_reg(GPMC_PREFETCH_CONTROL, gpmc_context.prefetch_control);
|
|
|
|
|
for (i = 0; i < gpmc_cs_num; i++) {
|
|
|
|
|
if (gpmc_context.cs_context[i].is_valid) {
|
|
|
|
|
gpmc_cs_write_reg(i, GPMC_CS_CONFIG1,
|
|
|
|
|
gpmc_context.cs_context[i].config1);
|
|
|
|
|
gpmc_cs_write_reg(i, GPMC_CS_CONFIG2,
|
|
|
|
|
gpmc_context.cs_context[i].config2);
|
|
|
|
|
gpmc_cs_write_reg(i, GPMC_CS_CONFIG3,
|
|
|
|
|
gpmc_context.cs_context[i].config3);
|
|
|
|
|
gpmc_cs_write_reg(i, GPMC_CS_CONFIG4,
|
|
|
|
|
gpmc_context.cs_context[i].config4);
|
|
|
|
|
gpmc_cs_write_reg(i, GPMC_CS_CONFIG5,
|
|
|
|
|
gpmc_context.cs_context[i].config5);
|
|
|
|
|
gpmc_cs_write_reg(i, GPMC_CS_CONFIG6,
|
|
|
|
|
gpmc_context.cs_context[i].config6);
|
|
|
|
|
gpmc_cs_write_reg(i, GPMC_CS_CONFIG7,
|
|
|
|
|
gpmc_context.cs_context[i].config7);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|