Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ff19473b authored by Linus Walleij's avatar Linus Walleij
Browse files

Merge branch 'ib-omap' into devel

parents c4c958aa 5284521a
Loading
Loading
Loading
Loading
+3 −4
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
 * published by the Free Software Foundation.
 */

#include <linux/cpu_pm.h>
#include <linux/suspend.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
@@ -29,8 +30,6 @@
#include <linux/clk-provider.h>
#include <linux/irq.h>
#include <linux/time.h>
#include <linux/gpio.h>
#include <linux/platform_data/gpio-omap.h>

#include <asm/fncpy.h>

@@ -87,7 +86,7 @@ static int omap2_enter_full_retention(void)
	l = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0) | OMAP24XX_USBSTANDBYCTRL;
	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
	 * to sleeping unnecessarily. */
@@ -100,7 +99,7 @@ static int omap2_enter_full_retention(void)
			   OMAP_SDRC_REGADDR(SDRC_POWER));

no_sleep:
	omap2_gpio_resume_after_idle();
	cpu_cluster_pm_exit();

	clk_enable(osc_ck);

+5 −9
Original line number Diff line number Diff line
@@ -18,19 +18,18 @@
 * published by the Free Software Foundation.
 */

#include <linux/cpu_pm.h>
#include <linux/pm.h>
#include <linux/suspend.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/omap-dma.h>
#include <linux/omap-gpmc.h>
#include <linux/platform_data/gpio-omap.h>

#include <trace/events/power.h>

@@ -197,7 +196,6 @@ void omap_sram_idle(void)
	int mpu_next_state = PWRDM_POWER_ON;
	int per_next_state = PWRDM_POWER_ON;
	int core_next_state = PWRDM_POWER_ON;
	int per_going_off;
	u32 sdrc_pwr = 0;

	mpu_next_state = pwrdm_read_next_pwrst(mpu_pwrdm);
@@ -227,10 +225,8 @@ void omap_sram_idle(void)
	pwrdm_pre_transition(NULL);

	/* PER */
	if (per_next_state < PWRDM_POWER_ON) {
		per_going_off = (per_next_state == PWRDM_POWER_OFF) ? 1 : 0;
		omap2_gpio_prepare_for_idle(per_going_off);
	}
	if (per_next_state == PWRDM_POWER_OFF)
		cpu_cluster_pm_enter();

	/* CORE */
	if (core_next_state < PWRDM_POWER_ON) {
@@ -295,8 +291,8 @@ void omap_sram_idle(void)
	pwrdm_post_transition(NULL);

	/* PER */
	if (per_next_state < PWRDM_POWER_ON)
		omap2_gpio_resume_after_idle();
	if (per_next_state == PWRDM_POWER_OFF)
		cpu_cluster_pm_exit();
}

static void omap3_pm_idle(void)
+227 −104
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/cpu_pm.h>
#include <linux/device.h>
#include <linux/pm_runtime.h>
#include <linux/pm.h>
@@ -28,9 +29,11 @@
#include <linux/bitops.h>
#include <linux/platform_data/gpio-omap.h>

#define OFF_MODE	1
#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);

struct gpio_regs {
@@ -48,6 +51,13 @@ 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;
@@ -55,6 +65,7 @@ struct gpio_bank {
	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;
@@ -62,6 +73,8 @@ struct gpio_bank {
	raw_spinlock_t wa_lock;
	struct gpio_chip chip;
	struct clk *dbck;
	struct notifier_block nb;
	unsigned int is_suspended:1;
	u32 mod_usage;
	u32 irq_usage;
	u32 dbck_enable_mask;
@@ -73,8 +86,8 @@ struct gpio_bank {
	int stride;
	u32 width;
	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,
@@ -368,10 +381,19 @@ static inline void omap_set_gpio_trigger(struct gpio_bank *bank, int gpio,
			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);
		/* 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} */
	if (!bank->regs->irqctrl) {
@@ -741,7 +763,9 @@ static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank)
	if (WARN_ON(!isr_reg))
		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) {
		raw_spin_lock_irqsave(&bank->lock, lock_flags);
@@ -792,7 +816,6 @@ static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank)
		}
	}
exit:
	pm_runtime_put(bank->chip.parent);
	return IRQ_HANDLED;
}

@@ -899,6 +922,82 @@ static void omap_gpio_unmask_irq(struct irq_data *d)
	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)
@@ -1218,6 +1317,38 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc)
	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 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.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;
@@ -1278,6 +1410,7 @@ static int omap_gpio_probe(struct platform_device *pdev)
#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;
@@ -1298,6 +1431,18 @@ static int omap_gpio_probe(struct platform_device *pdev)
				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->wa_lock);

@@ -1322,7 +1467,6 @@ static int omap_gpio_probe(struct platform_device *pdev)
	platform_set_drvdata(pdev, bank);

	pm_runtime_enable(dev);
	pm_runtime_irq_safe(dev);
	pm_runtime_get_sync(dev);

	if (bank->is_mpuio)
@@ -1341,6 +1485,12 @@ static int omap_gpio_probe(struct platform_device *pdev)

	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);

	list_add_tail(&bank->node, &omap_gpio_list);
@@ -1352,6 +1502,8 @@ static int omap_gpio_remove(struct platform_device *pdev)
{
	struct gpio_bank *bank = platform_get_drvdata(pdev);

	if (bank->nb.notifier_call)
		cpu_pm_unregister_notifier(&bank->nb);
	list_del(&bank->node);
	gpiochip_remove(&bank->chip);
	pm_runtime_disable(&pdev->dev);
@@ -1361,48 +1513,22 @@ static int omap_gpio_remove(struct platform_device *pdev)
	return 0;
}

#ifdef CONFIG_ARCH_OMAP2PLUS

#if defined(CONFIG_PM)
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 gpio_bank *bank = platform_get_drvdata(pdev);
	struct device *dev = bank->chip.parent;
	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;

	if (bank->power_mode != OFF_MODE) {
		bank->power_mode = 0;
	if (!may_lose_context)
		goto update_gpio_context_count;
	}

	/*
	 * If going to OFF, remove triggering for all
	 * non-wakeup GPIOs.  Otherwise spurious IRQs will be
@@ -1427,23 +1553,16 @@ static int omap_gpio_runtime_suspend(struct device *dev)
				bank->get_context_loss_count(dev);

	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 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 gpio_bank *bank = platform_get_drvdata(pdev);
	struct device *dev = bank->chip.parent;
	u32 l = 0, gen, gen0, gen1;
	unsigned long flags;
	int c;

	raw_spin_lock_irqsave(&bank->lock, flags);

	/*
	 * On the first resume during the probe, the context has not
	 * been initialised and so initialise it now. Also initialise
@@ -1459,16 +1578,8 @@ static int omap_gpio_runtime_resume(struct device *dev)

	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) {
@@ -1478,16 +1589,13 @@ static int omap_gpio_runtime_resume(struct device *dev)
			if (c != bank->context_loss_count) {
				omap_gpio_restore_context(bank);
			} else {
				raw_spin_unlock_irqrestore(&bank->lock, flags);
				return 0;
				return;
			}
		}
	}

	if (!bank->workaround_enabled) {
		raw_spin_unlock_irqrestore(&bank->lock, flags);
		return 0;
	}
	if (!bank->workaround_enabled)
		return;

	l = readl_relaxed(bank->base + bank->regs->datain);

@@ -1540,41 +1648,8 @@ static int omap_gpio_runtime_resume(struct device *dev)
	}

	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)
{
	struct omap_gpio_reg_offs *regs = p->regs;
@@ -1631,17 +1706,57 @@ static void omap_gpio_restore_context(struct gpio_bank *bank)
	writel_relaxed(bank->context.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 = {
	SET_RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume,
									NULL)
};
#else
static const struct dev_pm_ops gpio_pm_ops;
#endif	/* CONFIG_ARCH_OMAP2PLUS */

#if defined(CONFIG_OF)
static struct omap_gpio_reg_offs omap2_gpio_regs = {
@@ -1690,6 +1805,11 @@ static struct omap_gpio_reg_offs omap4_gpio_regs = {
	.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 = {
	.regs = &omap2_gpio_regs,
	.bank_width = 32,
@@ -1700,12 +1820,15 @@ static const struct omap_gpio_platform_data omap3_pdata = {
	.regs = &omap2_gpio_regs,
	.bank_width = 32,
	.dbck_flag = true,
	.quirks = OMAP_GPIO_QUIRK_IDLE_REMOVE_TRIGGER,
};

static const struct omap_gpio_platform_data omap4_pdata = {
	.regs = &omap4_gpio_regs,
	.bank_width = 32,
	.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[] = {
+2 −13
Original line number Diff line number Diff line
@@ -197,23 +197,12 @@ struct omap_gpio_platform_data {
	bool is_mpuio;		/* whether the bank is of type MPUIO */
	u32 non_wakeup_gpios;

	u32 quirks;		/* Version specific quirks mask */

	struct omap_gpio_reg_offs *regs;

	/* Return context loss count due to PM states changing */
	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