|
@@ -31,6 +31,8 @@
|
|
#define OFF_MODE 1
|
|
#define OFF_MODE 1
|
|
#define OMAP4_GPIO_DEBOUNCINGTIME_MASK 0xFF
|
|
#define OMAP4_GPIO_DEBOUNCINGTIME_MASK 0xFF
|
|
|
|
|
|
|
|
+#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 +50,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 +64,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;
|
|
@@ -75,6 +85,7 @@ struct gpio_bank {
|
|
int context_loss_count;
|
|
int context_loss_count;
|
|
int power_mode;
|
|
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 +379,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);
|
|
|
|
- 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} */
|
|
/* This part needs to be executed always for OMAP{34xx, 44xx} */
|
|
@@ -899,6 +919,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)
|
|
@@ -1270,6 +1366,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 +1375,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 +1396,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 {
|
|
|
|
+ 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);
|
|
|
|
|
|
@@ -1372,29 +1482,11 @@ static int omap_gpio_runtime_suspend(struct device *dev)
|
|
struct gpio_bank *bank = platform_get_drvdata(pdev);
|
|
struct gpio_bank *bank = platform_get_drvdata(pdev);
|
|
u32 l1 = 0, l2 = 0;
|
|
u32 l1 = 0, l2 = 0;
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
- u32 wake_low, wake_hi;
|
|
|
|
|
|
|
|
raw_spin_lock_irqsave(&bank->lock, flags);
|
|
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)
|
|
if (!bank->enabled_non_wakeup_gpios)
|
|
goto update_gpio_context_count;
|
|
goto update_gpio_context_count;
|
|
@@ -1459,16 +1551,8 @@ static int omap_gpio_runtime_resume(struct device *dev)
|
|
|
|
|
|
omap_gpio_dbck_enable(bank);
|
|
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->loses_context) {
|
|
if (!bank->get_context_loss_count) {
|
|
if (!bank->get_context_loss_count) {
|
|
@@ -1706,6 +1790,7 @@ 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_DEFERRED_WKUP_EN,
|
|
};
|
|
};
|
|
|
|
|
|
static const struct of_device_id omap_gpio_match[] = {
|
|
static const struct of_device_id omap_gpio_match[] = {
|