Commit ff19473b authored by Linus Walleij's avatar Linus Walleij

Merge branch 'ib-omap' into devel

parents c4c958aa 5284521a
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <linux/cpu_pm.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
...@@ -29,8 +30,6 @@ ...@@ -29,8 +30,6 @@
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/time.h> #include <linux/time.h>
#include <linux/gpio.h>
#include <linux/platform_data/gpio-omap.h>
#include <asm/fncpy.h> #include <asm/fncpy.h>
...@@ -87,7 +86,7 @@ static int omap2_enter_full_retention(void) ...@@ -87,7 +86,7 @@ static int omap2_enter_full_retention(void)
l = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0) | OMAP24XX_USBSTANDBYCTRL; l = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0) | OMAP24XX_USBSTANDBYCTRL;
omap_ctrl_writel(l, OMAP2_CONTROL_DEVCONF0); omap_ctrl_writel(l, OMAP2_CONTROL_DEVCONF0);
omap2_gpio_prepare_for_idle(0); cpu_cluster_pm_enter();
/* One last check for pending IRQs to avoid extra latency due /* One last check for pending IRQs to avoid extra latency due
* to sleeping unnecessarily. */ * to sleeping unnecessarily. */
...@@ -100,7 +99,7 @@ static int omap2_enter_full_retention(void) ...@@ -100,7 +99,7 @@ static int omap2_enter_full_retention(void)
OMAP_SDRC_REGADDR(SDRC_POWER)); OMAP_SDRC_REGADDR(SDRC_POWER));
no_sleep: no_sleep:
omap2_gpio_resume_after_idle(); cpu_cluster_pm_exit();
clk_enable(osc_ck); clk_enable(osc_ck);
......
...@@ -18,19 +18,18 @@ ...@@ -18,19 +18,18 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <linux/cpu_pm.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/gpio.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/omap-dma.h> #include <linux/omap-dma.h>
#include <linux/omap-gpmc.h> #include <linux/omap-gpmc.h>
#include <linux/platform_data/gpio-omap.h>
#include <trace/events/power.h> #include <trace/events/power.h>
...@@ -197,7 +196,6 @@ void omap_sram_idle(void) ...@@ -197,7 +196,6 @@ void omap_sram_idle(void)
int mpu_next_state = PWRDM_POWER_ON; int mpu_next_state = PWRDM_POWER_ON;
int per_next_state = PWRDM_POWER_ON; int per_next_state = PWRDM_POWER_ON;
int core_next_state = PWRDM_POWER_ON; int core_next_state = PWRDM_POWER_ON;
int per_going_off;
u32 sdrc_pwr = 0; u32 sdrc_pwr = 0;
mpu_next_state = pwrdm_read_next_pwrst(mpu_pwrdm); mpu_next_state = pwrdm_read_next_pwrst(mpu_pwrdm);
...@@ -227,10 +225,8 @@ void omap_sram_idle(void) ...@@ -227,10 +225,8 @@ void omap_sram_idle(void)
pwrdm_pre_transition(NULL); pwrdm_pre_transition(NULL);
/* PER */ /* PER */
if (per_next_state < PWRDM_POWER_ON) { if (per_next_state == PWRDM_POWER_OFF)
per_going_off = (per_next_state == PWRDM_POWER_OFF) ? 1 : 0; cpu_cluster_pm_enter();
omap2_gpio_prepare_for_idle(per_going_off);
}
/* CORE */ /* CORE */
if (core_next_state < PWRDM_POWER_ON) { if (core_next_state < PWRDM_POWER_ON) {
...@@ -295,8 +291,8 @@ void omap_sram_idle(void) ...@@ -295,8 +291,8 @@ void omap_sram_idle(void)
pwrdm_post_transition(NULL); pwrdm_post_transition(NULL);
/* PER */ /* PER */
if (per_next_state < PWRDM_POWER_ON) if (per_next_state == PWRDM_POWER_OFF)
omap2_gpio_resume_after_idle(); cpu_cluster_pm_exit();
} }
static void omap3_pm_idle(void) static void omap3_pm_idle(void)
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/cpu_pm.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/pm.h> #include <linux/pm.h>
...@@ -28,9 +29,11 @@ ...@@ -28,9 +29,11 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/platform_data/gpio-omap.h> #include <linux/platform_data/gpio-omap.h>
#define OFF_MODE 1
#define OMAP4_GPIO_DEBOUNCINGTIME_MASK 0xFF #define OMAP4_GPIO_DEBOUNCINGTIME_MASK 0xFF
#define OMAP_GPIO_QUIRK_IDLE_REMOVE_TRIGGER BIT(2)
#define OMAP_GPIO_QUIRK_DEFERRED_WKUP_EN BIT(1)
static LIST_HEAD(omap_gpio_list); static LIST_HEAD(omap_gpio_list);
struct gpio_regs { struct gpio_regs {
...@@ -48,6 +51,13 @@ struct gpio_regs { ...@@ -48,6 +51,13 @@ struct gpio_regs {
u32 debounce_en; u32 debounce_en;
}; };
struct gpio_bank;
struct gpio_omap_funcs {
void (*idle_enable_level_quirk)(struct gpio_bank *bank);
void (*idle_disable_level_quirk)(struct gpio_bank *bank);
};
struct gpio_bank { struct gpio_bank {
struct list_head node; struct list_head node;
void __iomem *base; void __iomem *base;
...@@ -55,6 +65,7 @@ struct gpio_bank { ...@@ -55,6 +65,7 @@ struct gpio_bank {
u32 non_wakeup_gpios; u32 non_wakeup_gpios;
u32 enabled_non_wakeup_gpios; u32 enabled_non_wakeup_gpios;
struct gpio_regs context; struct gpio_regs context;
struct gpio_omap_funcs funcs;
u32 saved_datain; u32 saved_datain;
u32 level_mask; u32 level_mask;
u32 toggle_mask; u32 toggle_mask;
...@@ -62,6 +73,8 @@ struct gpio_bank { ...@@ -62,6 +73,8 @@ struct gpio_bank {
raw_spinlock_t wa_lock; raw_spinlock_t wa_lock;
struct gpio_chip chip; struct gpio_chip chip;
struct clk *dbck; struct clk *dbck;
struct notifier_block nb;
unsigned int is_suspended:1;
u32 mod_usage; u32 mod_usage;
u32 irq_usage; u32 irq_usage;
u32 dbck_enable_mask; u32 dbck_enable_mask;
...@@ -73,8 +86,8 @@ struct gpio_bank { ...@@ -73,8 +86,8 @@ struct gpio_bank {
int stride; int stride;
u32 width; u32 width;
int context_loss_count; int context_loss_count;
int power_mode;
bool workaround_enabled; bool workaround_enabled;
u32 quirks;
void (*set_dataout)(struct gpio_bank *bank, unsigned gpio, int enable); void (*set_dataout)(struct gpio_bank *bank, unsigned gpio, int enable);
void (*set_dataout_multiple)(struct gpio_bank *bank, void (*set_dataout_multiple)(struct gpio_bank *bank,
...@@ -368,9 +381,18 @@ static inline void omap_set_gpio_trigger(struct gpio_bank *bank, int gpio, ...@@ -368,9 +381,18 @@ static inline void omap_set_gpio_trigger(struct gpio_bank *bank, int gpio,
readl_relaxed(bank->base + bank->regs->fallingdetect); readl_relaxed(bank->base + bank->regs->fallingdetect);
if (likely(!(bank->non_wakeup_gpios & gpio_bit))) { if (likely(!(bank->non_wakeup_gpios & gpio_bit))) {
omap_gpio_rmw(base, bank->regs->wkup_en, gpio_bit, trigger != 0); /* Defer wkup_en register update until we idle? */
bank->context.wake_en = if (bank->quirks & OMAP_GPIO_QUIRK_DEFERRED_WKUP_EN) {
readl_relaxed(bank->base + bank->regs->wkup_en); if (trigger)
bank->context.wake_en |= gpio_bit;
else
bank->context.wake_en &= ~gpio_bit;
} else {
omap_gpio_rmw(base, bank->regs->wkup_en, gpio_bit,
trigger != 0);
bank->context.wake_en =
readl_relaxed(bank->base + bank->regs->wkup_en);
}
} }
/* This part needs to be executed always for OMAP{34xx, 44xx} */ /* This part needs to be executed always for OMAP{34xx, 44xx} */
...@@ -741,7 +763,9 @@ static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank) ...@@ -741,7 +763,9 @@ static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank)
if (WARN_ON(!isr_reg)) if (WARN_ON(!isr_reg))
goto exit; goto exit;
pm_runtime_get_sync(bank->chip.parent); if (WARN_ONCE(!pm_runtime_active(bank->chip.parent),
"gpio irq%i while runtime suspended?\n", irq))
return IRQ_NONE;
while (1) { while (1) {
raw_spin_lock_irqsave(&bank->lock, lock_flags); raw_spin_lock_irqsave(&bank->lock, lock_flags);
...@@ -792,7 +816,6 @@ static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank) ...@@ -792,7 +816,6 @@ static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank)
} }
} }
exit: exit:
pm_runtime_put(bank->chip.parent);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -899,6 +922,82 @@ static void omap_gpio_unmask_irq(struct irq_data *d) ...@@ -899,6 +922,82 @@ static void omap_gpio_unmask_irq(struct irq_data *d)
raw_spin_unlock_irqrestore(&bank->lock, flags); raw_spin_unlock_irqrestore(&bank->lock, flags);
} }
/*
* Only edges can generate a wakeup event to the PRCM.
*
* Therefore, ensure any wake-up capable GPIOs have
* edge-detection enabled before going idle to ensure a wakeup
* to the PRCM is generated on a GPIO transition. (c.f. 34xx
* NDA TRM 25.5.3.1)
*
* The normal values will be restored upon ->runtime_resume()
* by writing back the values saved in bank->context.
*/
static void __maybe_unused
omap2_gpio_enable_level_quirk(struct gpio_bank *bank)
{
u32 wake_low, wake_hi;
/* Enable additional edge detection for level gpios for idle */
wake_low = bank->context.leveldetect0 & bank->context.wake_en;
if (wake_low)
writel_relaxed(wake_low | bank->context.fallingdetect,
bank->base + bank->regs->fallingdetect);
wake_hi = bank->context.leveldetect1 & bank->context.wake_en;
if (wake_hi)
writel_relaxed(wake_hi | bank->context.risingdetect,
bank->base + bank->regs->risingdetect);
}
static void __maybe_unused
omap2_gpio_disable_level_quirk(struct gpio_bank *bank)
{
/* Disable edge detection for level gpios after idle */
writel_relaxed(bank->context.fallingdetect,
bank->base + bank->regs->fallingdetect);
writel_relaxed(bank->context.risingdetect,
bank->base + bank->regs->risingdetect);
}
/*
* On omap4 and later SoC variants a level interrupt with wkup_en
* enabled blocks the GPIO functional clock from idling until the GPIO
* instance has been reset. To avoid that, we must set wkup_en only for
* idle for level interrupts, and clear level registers for the duration
* of idle. The level interrupts will be still there on wakeup by their
* nature.
*/
static void __maybe_unused
omap4_gpio_enable_level_quirk(struct gpio_bank *bank)
{
/* Update wake register for idle, edge bits might be already set */
writel_relaxed(bank->context.wake_en,
bank->base + bank->regs->wkup_en);
/* Clear level registers for idle */
writel_relaxed(0, bank->base + bank->regs->leveldetect0);
writel_relaxed(0, bank->base + bank->regs->leveldetect1);
}
static void __maybe_unused
omap4_gpio_disable_level_quirk(struct gpio_bank *bank)
{
/* Restore level registers after idle */
writel_relaxed(bank->context.leveldetect0,
bank->base + bank->regs->leveldetect0);
writel_relaxed(bank->context.leveldetect1,
bank->base + bank->regs->leveldetect1);
/* Clear saved wkup_en for level, it will be set for next idle again */
bank->context.wake_en &= ~(bank->context.leveldetect0 |
bank->context.leveldetect1);
/* Update wake with only edge configuration */
writel_relaxed(bank->context.wake_en,
bank->base + bank->regs->wkup_en);
}
/*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/
static int omap_mpuio_suspend_noirq(struct device *dev) static int omap_mpuio_suspend_noirq(struct device *dev)
...@@ -1218,6 +1317,38 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc) ...@@ -1218,6 +1317,38 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc)
return ret; return ret;
} }
static void omap_gpio_idle(struct gpio_bank *bank, bool may_lose_context);
static void omap_gpio_unidle(struct gpio_bank *bank);
static int gpio_omap_cpu_notifier(struct notifier_block *nb,
unsigned long cmd, void *v)
{
struct gpio_bank *bank;
struct device *dev;
unsigned long flags;
bank = container_of(nb, struct gpio_bank, nb);
dev = bank->chip.parent;
raw_spin_lock_irqsave(&bank->lock, flags);
switch (cmd) {
case CPU_CLUSTER_PM_ENTER:
if (bank->is_suspended)
break;
omap_gpio_idle(bank, true);
break;
case CPU_CLUSTER_PM_ENTER_FAILED:
case CPU_CLUSTER_PM_EXIT:
if (bank->is_suspended)
break;
omap_gpio_unidle(bank);
break;
}
raw_spin_unlock_irqrestore(&bank->lock, flags);
return NOTIFY_OK;
}
static const struct of_device_id omap_gpio_match[]; static const struct of_device_id omap_gpio_match[];
static int omap_gpio_probe(struct platform_device *pdev) static int omap_gpio_probe(struct platform_device *pdev)
...@@ -1270,6 +1401,7 @@ static int omap_gpio_probe(struct platform_device *pdev) ...@@ -1270,6 +1401,7 @@ static int omap_gpio_probe(struct platform_device *pdev)
bank->chip.parent = dev; bank->chip.parent = dev;
bank->chip.owner = THIS_MODULE; bank->chip.owner = THIS_MODULE;
bank->dbck_flag = pdata->dbck_flag; bank->dbck_flag = pdata->dbck_flag;
bank->quirks = pdata->quirks;
bank->stride = pdata->bank_stride; bank->stride = pdata->bank_stride;
bank->width = pdata->bank_width; bank->width = pdata->bank_width;
bank->is_mpuio = pdata->is_mpuio; bank->is_mpuio = pdata->is_mpuio;
...@@ -1278,6 +1410,7 @@ static int omap_gpio_probe(struct platform_device *pdev) ...@@ -1278,6 +1410,7 @@ static int omap_gpio_probe(struct platform_device *pdev)
#ifdef CONFIG_OF_GPIO #ifdef CONFIG_OF_GPIO
bank->chip.of_node = of_node_get(node); bank->chip.of_node = of_node_get(node);
#endif #endif
if (node) { if (node) {
if (!of_property_read_bool(node, "ti,gpio-always-on")) if (!of_property_read_bool(node, "ti,gpio-always-on"))
bank->loses_context = true; bank->loses_context = true;
...@@ -1298,6 +1431,18 @@ static int omap_gpio_probe(struct platform_device *pdev) ...@@ -1298,6 +1431,18 @@ static int omap_gpio_probe(struct platform_device *pdev)
omap_set_gpio_dataout_mask_multiple; omap_set_gpio_dataout_mask_multiple;
} }
if (bank->quirks & OMAP_GPIO_QUIRK_DEFERRED_WKUP_EN) {
bank->funcs.idle_enable_level_quirk =
omap4_gpio_enable_level_quirk;
bank->funcs.idle_disable_level_quirk =
omap4_gpio_disable_level_quirk;
} else if (bank->quirks & OMAP_GPIO_QUIRK_IDLE_REMOVE_TRIGGER) {
bank->funcs.idle_enable_level_quirk =
omap2_gpio_enable_level_quirk;
bank->funcs.idle_disable_level_quirk =
omap2_gpio_disable_level_quirk;
}
raw_spin_lock_init(&bank->lock); raw_spin_lock_init(&bank->lock);
raw_spin_lock_init(&bank->wa_lock); raw_spin_lock_init(&bank->wa_lock);
...@@ -1322,7 +1467,6 @@ static int omap_gpio_probe(struct platform_device *pdev) ...@@ -1322,7 +1467,6 @@ static int omap_gpio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, bank); platform_set_drvdata(pdev, bank);
pm_runtime_enable(dev); pm_runtime_enable(dev);
pm_runtime_irq_safe(dev);
pm_runtime_get_sync(dev); pm_runtime_get_sync(dev);
if (bank->is_mpuio) if (bank->is_mpuio)
...@@ -1341,6 +1485,12 @@ static int omap_gpio_probe(struct platform_device *pdev) ...@@ -1341,6 +1485,12 @@ static int omap_gpio_probe(struct platform_device *pdev)
omap_gpio_show_rev(bank); omap_gpio_show_rev(bank);
if (bank->funcs.idle_enable_level_quirk &&
bank->funcs.idle_disable_level_quirk) {
bank->nb.notifier_call = gpio_omap_cpu_notifier;
cpu_pm_register_notifier(&bank->nb);
}
pm_runtime_put(dev); pm_runtime_put(dev);
list_add_tail(&bank->node, &omap_gpio_list); list_add_tail(&bank->node, &omap_gpio_list);
...@@ -1352,6 +1502,8 @@ static int omap_gpio_remove(struct platform_device *pdev) ...@@ -1352,6 +1502,8 @@ static int omap_gpio_remove(struct platform_device *pdev)
{ {
struct gpio_bank *bank = platform_get_drvdata(pdev); struct gpio_bank *bank = platform_get_drvdata(pdev);
if (bank->nb.notifier_call)
cpu_pm_unregister_notifier(&bank->nb);
list_del(&bank->node); list_del(&bank->node);
gpiochip_remove(&bank->chip); gpiochip_remove(&bank->chip);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
...@@ -1361,48 +1513,22 @@ static int omap_gpio_remove(struct platform_device *pdev) ...@@ -1361,48 +1513,22 @@ static int omap_gpio_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_ARCH_OMAP2PLUS
#if defined(CONFIG_PM)
static void omap_gpio_restore_context(struct gpio_bank *bank); static void omap_gpio_restore_context(struct gpio_bank *bank);
static int omap_gpio_runtime_suspend(struct device *dev) static void omap_gpio_idle(struct gpio_bank *bank, bool may_lose_context)
{ {
struct platform_device *pdev = to_platform_device(dev); struct device *dev = bank->chip.parent;
struct gpio_bank *bank = platform_get_drvdata(pdev);
u32 l1 = 0, l2 = 0; u32 l1 = 0, l2 = 0;
unsigned long flags;
u32 wake_low, wake_hi;
raw_spin_lock_irqsave(&bank->lock, flags);
/* if (bank->funcs.idle_enable_level_quirk)
* Only edges can generate a wakeup event to the PRCM. bank->funcs.idle_enable_level_quirk(bank);
*
* Therefore, ensure any wake-up capable GPIOs have
* edge-detection enabled before going idle to ensure a wakeup
* to the PRCM is generated on a GPIO transition. (c.f. 34xx
* NDA TRM 25.5.3.1)
*
* The normal values will be restored upon ->runtime_resume()
* by writing back the values saved in bank->context.
*/
wake_low = bank->context.leveldetect0 & bank->context.wake_en;
if (wake_low)
writel_relaxed(wake_low | bank->context.fallingdetect,
bank->base + bank->regs->fallingdetect);
wake_hi = bank->context.leveldetect1 & bank->context.wake_en;
if (wake_hi)
writel_relaxed(wake_hi | bank->context.risingdetect,
bank->base + bank->regs->risingdetect);
if (!bank->enabled_non_wakeup_gpios) if (!bank->enabled_non_wakeup_gpios)
goto update_gpio_context_count; goto update_gpio_context_count;
if (bank->power_mode != OFF_MODE) { if (!may_lose_context)
bank->power_mode = 0;
goto update_gpio_context_count; goto update_gpio_context_count;
}
/* /*
* If going to OFF, remove triggering for all * If going to OFF, remove triggering for all
* non-wakeup GPIOs. Otherwise spurious IRQs will be * non-wakeup GPIOs. Otherwise spurious IRQs will be
...@@ -1427,23 +1553,16 @@ static int omap_gpio_runtime_suspend(struct device *dev) ...@@ -1427,23 +1553,16 @@ static int omap_gpio_runtime_suspend(struct device *dev)
bank->get_context_loss_count(dev); bank->get_context_loss_count(dev);
omap_gpio_dbck_disable(bank); omap_gpio_dbck_disable(bank);
raw_spin_unlock_irqrestore(&bank->lock, flags);
return 0;
} }
static void omap_gpio_init_context(struct gpio_bank *p); static void omap_gpio_init_context(struct gpio_bank *p);
static int omap_gpio_runtime_resume(struct device *dev) static void omap_gpio_unidle(struct gpio_bank *bank)
{ {
struct platform_device *pdev = to_platform_device(dev); struct device *dev = bank->chip.parent;
struct gpio_bank *bank = platform_get_drvdata(pdev);
u32 l = 0, gen, gen0, gen1; u32 l = 0, gen, gen0, gen1;
unsigned long flags;
int c; int c;
raw_spin_lock_irqsave(&bank->lock, flags);
/* /*
* On the first resume during the probe, the context has not * On the first resume during the probe, the context has not
* been initialised and so initialise it now. Also initialise * been initialised and so initialise it now. Also initialise
...@@ -1459,16 +1578,8 @@ static int omap_gpio_runtime_resume(struct device *dev) ...@@ -1459,16 +1578,8 @@ static int omap_gpio_runtime_resume(struct device *dev)
omap_gpio_dbck_enable(bank); omap_gpio_dbck_enable(bank);
/* if (bank->funcs.idle_disable_level_quirk)
* In ->runtime_suspend(), level-triggered, wakeup-enabled bank->funcs.idle_disable_level_quirk(bank);
* GPIOs were set to edge trigger also in order to be able to
* generate a PRCM wakeup. Here we restore the
* pre-runtime_suspend() values for edge triggering.
*/
writel_relaxed(bank->context.fallingdetect,
bank->base + bank->regs->fallingdetect);
writel_relaxed(bank->context.risingdetect,
bank->base + bank->regs->risingdetect);
if (bank->loses_context) { if (bank->loses_context) {
if (!bank->get_context_loss_count) { if (!bank->get_context_loss_count) {
...@@ -1478,16 +1589,13 @@ static int omap_gpio_runtime_resume(struct device *dev) ...@@ -1478,16 +1589,13 @@ static int omap_gpio_runtime_resume(struct device *dev)
if (c != bank->context_loss_count) { if (c != bank->context_loss_count) {
omap_gpio_restore_context(bank); omap_gpio_restore_context(bank);
} else { } else {
raw_spin_unlock_irqrestore(&bank->lock, flags); return;
return 0;
} }
} }
} }
if (!bank->workaround_enabled) { if (!bank->workaround_enabled)
raw_spin_unlock_irqrestore(&bank->lock, flags); return;
return 0;
}
l = readl_relaxed(bank->base + bank->regs->datain); l = readl_relaxed(bank->base + bank->regs->datain);
...@@ -1540,41 +1648,8 @@ static int omap_gpio_runtime_resume(struct device *dev) ...@@ -1540,41 +1648,8 @@ static int omap_gpio_runtime_resume(struct device *dev)
} }
bank->workaround_enabled = false; bank->workaround_enabled = false;
raw_spin_unlock_irqrestore(&bank->lock, flags);
return 0;
}
#endif /* CONFIG_PM */
#if IS_BUILTIN(CONFIG_GPIO_OMAP)
void omap2_gpio_prepare_for_idle(int pwr_mode)
{
struct gpio_bank *bank;
list_for_each_entry(bank, &omap_gpio_list, node) {
if (!BANK_USED(bank) || !bank->loses_context)
continue;
bank->power_mode = pwr_mode;
pm_runtime_put_sync_suspend(bank->chip.parent);
}
}
void omap2_gpio_resume_after_idle(void)
{
struct gpio_bank *bank;
list_for_each_entry(bank, &omap_gpio_list, node) {
if (!BANK_USED(bank) || !bank->loses_context)
continue;
pm_runtime_get_sync(bank->chip.parent);
}
} }
#endif
#if defined(CONFIG_PM)
static void omap_gpio_init_context(struct gpio_bank *p) static void omap_gpio_init_context(struct gpio_bank *p)
{ {
struct omap_gpio_reg_offs *regs = p->regs; struct omap_gpio_reg_offs *regs = p->regs;
...@@ -1631,17 +1706,57 @@ static void omap_gpio_restore_context(struct gpio_bank *bank) ...@@ -1631,17 +1706,57 @@ static void omap_gpio_restore_context(struct gpio_bank *bank)
writel_relaxed(bank->context.irqenable2, writel_relaxed(bank->context.irqenable2,
bank->base + bank->regs->irqenable2); bank->base + bank->regs->irqenable2);
} }
#endif /* CONFIG_PM */
#else
#define omap_gpio_runtime_suspend NULL
#define omap_gpio_runtime_resume NULL
static inline void omap_gpio_init_context(struct gpio_bank *p) {}
#endif
static int __maybe_unused omap_gpio_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct gpio_bank *bank = platform_get_drvdata(pdev);
unsigned long flags;
int error = 0;
raw_spin_lock_irqsave(&bank->lock, flags);
/* Must be idled only by CPU_CLUSTER_PM_ENTER? */
if (bank->irq_usage) {
error = -EBUSY;
goto unlock;
}
omap_gpio_idle(bank, true);
bank->is_suspended = true;
unlock:
raw_spin_unlock_irqrestore(&bank->lock, flags);
return error;
}
static int __maybe_unused omap_gpio_runtime_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct gpio_bank *bank = platform_get_drvdata(pdev);
unsigned long flags;
int error = 0;
raw_spin_lock_irqsave(&bank->lock, flags);
/* Must be unidled only by CPU_CLUSTER_PM_ENTER? */
if (bank->irq_usage) {
error = -EBUSY;
goto unlock;
}
omap_gpio_unidle(bank);
bank->is_suspended = false;
unlock:
raw_spin_unlock_irqrestore(&bank->lock, flags);
return error;
}
#ifdef CONFIG_ARCH_OMAP2PLUS
static const struct dev_pm_ops gpio_pm_ops = { static const struct dev_pm_ops gpio_pm_ops = {
SET_RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume, SET_RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume,
NULL) NULL)
}; };
#else
static const struct dev_pm_ops gpio_pm_ops;
#endif /* CONFIG_ARCH_OMAP2PLUS */
#if defined(CONFIG_OF) #if defined(CONFIG_OF)
static struct omap_gpio_reg_offs omap2_gpio_regs = { static struct omap_gpio_reg_offs omap2_gpio_regs = {
...@@ -1690,6 +1805,11 @@ static struct omap_gpio_reg_offs omap4_gpio_regs = { ...@@ -1690,6 +1805,11 @@ static struct omap_gpio_reg_offs omap4_gpio_regs = {
.fallingdetect = OMAP4_GPIO_FALLINGDETECT, .fallingdetect = OMAP4_GPIO_FALLINGDETECT,
}; };
/*
* Note that omap2 does not currently support idle modes with context loss so
* no need to add OMAP_GPIO_QUIRK_IDLE_REMOVE_TRIGGER quirk flag to save
* and restore context.
*/
static const struct omap_gpio_platform_data omap2_pdata = { static const struct omap_gpio_platform_data omap2_pdata = {
.regs = &omap2_gpio_regs, .regs = &omap2_gpio_regs,
.bank_width = 32, .bank_width = 32,
...@@ -1700,12 +1820,15 @@ static const struct omap_gpio_platform_data omap3_pdata = { ...@@ -1700,12 +1820,15 @@ static const struct omap_gpio_platform_data omap3_pdata = {
.regs = &omap2_gpio_regs, .regs = &omap2_gpio_regs,
.bank_width = 32, .bank_width = 32,
.dbck_flag = true, .dbck_flag = true,
.quirks = OMAP_GPIO_QUIRK_IDLE_REMOVE_TRIGGER,
}; };
static const struct omap_gpio_platform_data omap4_pdata = { static const struct omap_gpio_platform_data omap4_pdata = {
.regs = &omap4_gpio_regs, .regs = &omap4_gpio_regs,
.bank_width = 32, .bank_width = 32,
.dbck_flag = true, .dbck_flag = true,
.quirks = OMAP_GPIO_QUIRK_IDLE_REMOVE_TRIGGER |
OMAP_GPIO_QUIRK_DEFERRED_WKUP_EN,
}; };
static const struct of_device_id omap_gpio_match[] = { static const struct of_device_id omap_gpio_match[] = {
......
...@@ -197,23 +197,12 @@ struct omap_gpio_platform_data { ...@@ -197,23 +197,12 @@ struct omap_gpio_platform_data {
bool is_mpuio; /* whether the bank is of type MPUIO */ bool is_mpuio; /* whether the bank is of type MPUIO */
u32 non_wakeup_gpios; u32 non_wakeup_gpios;
u32 quirks; /* Version specific quirks mask */
struct omap_gpio_reg_offs *regs; struct omap_gpio_reg_offs *regs;
/* Return context loss count due to PM states changing */ /* Return context loss count due to PM states changing */
int (*get_context_loss_count)(struct device *dev); int (*get_context_loss_count)(struct device *dev);
}; };
#if IS_BUILTIN(CONFIG_GPIO_OMAP)
extern void omap2_gpio_prepare_for_idle(int off_mode);
extern void omap2_gpio_resume_after_idle(void);
#else
static inline void omap2_gpio_prepare_for_idle(int off_mode)
{
}
static inline void omap2_gpio_resume_after_idle(void)
{
}
#endif
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment