#define OFF_MODE 1
#define OMAP4_GPIO_DEBOUNCINGTIME_MASK 0xFF
+#define OMAP_GPIO_QUIRK_DEFERRED_WKUP_EN BIT(1)
+
static LIST_HEAD(omap_gpio_list);
struct gpio_regs {
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 list_head node;
void __iomem *base;
u32 non_wakeup_gpios;
u32 enabled_non_wakeup_gpios;
struct gpio_regs context;
+ struct gpio_omap_funcs funcs;
u32 saved_datain;
u32 level_mask;
u32 toggle_mask;
int context_loss_count;
int power_mode;
bool workaround_enabled;
+ u32 quirks;
void (*set_dataout)(struct gpio_bank *bank, unsigned gpio, int enable);
void (*set_dataout_multiple)(struct gpio_bank *bank,
readl_relaxed(bank->base + bank->regs->fallingdetect);
if (likely(!(bank->non_wakeup_gpios & gpio_bit))) {
- omap_gpio_rmw(base, bank->regs->wkup_en, gpio_bit, trigger != 0);
- bank->context.wake_en =
- readl_relaxed(bank->base + bank->regs->wkup_en);
+ /* Defer wkup_en register update until we idle? */
+ if (bank->quirks & OMAP_GPIO_QUIRK_DEFERRED_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} */
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)
bank->chip.parent = dev;
bank->chip.owner = THIS_MODULE;
bank->dbck_flag = pdata->dbck_flag;
+ bank->quirks = pdata->quirks;
bank->stride = pdata->bank_stride;
bank->width = pdata->bank_width;
bank->is_mpuio = pdata->is_mpuio;
#ifdef CONFIG_OF_GPIO
bank->chip.of_node = of_node_get(node);
#endif
+
if (node) {
if (!of_property_read_bool(node, "ti,gpio-always-on"))
bank->loses_context = true;
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 {
+ 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->wa_lock);
struct gpio_bank *bank = platform_get_drvdata(pdev);
u32 l1 = 0, l2 = 0;
unsigned long flags;
- u32 wake_low, wake_hi;
raw_spin_lock_irqsave(&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.
- */
- 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->funcs.idle_enable_level_quirk)
+ bank->funcs.idle_enable_level_quirk(bank);
if (!bank->enabled_non_wakeup_gpios)
goto update_gpio_context_count;
omap_gpio_dbck_enable(bank);
- /*
- * In ->runtime_suspend(), level-triggered, wakeup-enabled
- * 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->funcs.idle_disable_level_quirk)
+ bank->funcs.idle_disable_level_quirk(bank);
if (bank->loses_context) {
if (!bank->get_context_loss_count) {
.regs = &omap4_gpio_regs,
.bank_width = 32,
.dbck_flag = true,
+ .quirks = OMAP_GPIO_QUIRK_DEFERRED_WKUP_EN,
};
static const struct of_device_id omap_gpio_match[] = {