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

Commit e2653fbd authored by Arun KS's avatar Arun KS
Browse files

pinctrl: qcom: Save and restore TLMM registers



Save and restore TLMM registers to make hibernation work.
Uses power management notifiers for differentiating between
suspend to RAM and suspend to disk(hibernation) operations during
system suspend and restore.

Change-Id: I4042054fc04e17da07eaab3dc36dbac58d1a1b24
Signed-off-by: default avatarAnkur Saxena <ankusa@codeaurora.org>
Signed-off-by: default avatarArun KS <arunks@codeaurora.org>
parent ed316bc4
Loading
Loading
Loading
Loading
+169 −1
Original line number Diff line number Diff line
/*
 * Copyright (c) 2013, Sony Mobile Communications AB.
 * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
 * Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -40,10 +40,27 @@
#include "../pinconf.h"
#include "pinctrl-msm.h"
#include "../pinctrl-utils.h"
#include <linux/suspend.h>
#ifdef CONFIG_HIBERNATION
#include <linux/notifier.h>
#endif

#define MAX_NR_GPIO 300
#define PS_HOLD_OFFSET 0x820

#ifdef CONFIG_HIBERNATION
struct msm_gpio_regs {
	u32 ctl_reg;
	u32 io_reg;
	u32 intr_cfg_reg;
	u32 intr_status_reg;
};

struct msm_tile {
	u32 dir_con_regs[8];
};
#endif

/**
 * struct msm_pinctrl - state for a pinctrl-msm device
 * @dev:            device handle.
@@ -80,6 +97,10 @@ struct msm_pinctrl {
#endif
	phys_addr_t spi_cfg_regs;
	phys_addr_t spi_cfg_end;
#ifdef CONFIG_HIBERNATION
	struct msm_gpio_regs *gpio_regs;
	struct msm_tile *msm_tile_regs;
#endif
};

static struct msm_pinctrl *msm_pinctrl_data;
@@ -1786,8 +1807,144 @@ static void msm_pinctrl_setup_pm_reset(struct msm_pinctrl *pctrl)
}

#ifdef CONFIG_PM
#ifdef CONFIG_HIBERNATION
static bool hibernation;

static int pinctrl_hibernation_notifier(struct notifier_block *nb,
					unsigned long event, void *dummy)
{
	struct msm_pinctrl *pctrl = msm_pinctrl_data;
	const struct msm_pinctrl_soc_data *soc = pctrl->soc;

	if (event == PM_HIBERNATION_PREPARE) {
		pctrl->gpio_regs = kcalloc(soc->ngroups,
					sizeof(*pctrl->gpio_regs), GFP_KERNEL);
		if (pctrl->gpio_regs == NULL)
			return -ENOMEM;

		if (soc->tile_count) {
			pctrl->msm_tile_regs = kcalloc(soc->tile_count,
				sizeof(*pctrl->msm_tile_regs), GFP_KERNEL);
			if (pctrl->msm_tile_regs == NULL) {
				kfree(pctrl->gpio_regs);
				return -ENOMEM;
			}
		}
		hibernation = true;
	} else if (event == PM_POST_HIBERNATION) {
		kfree(pctrl->gpio_regs);
		kfree(pctrl->msm_tile_regs);
		pctrl->gpio_regs = NULL;
		pctrl->msm_tile_regs = NULL;
		hibernation = false;
	}
	return NOTIFY_OK;
}

static struct notifier_block pinctrl_notif_block = {
	.notifier_call = pinctrl_hibernation_notifier,
};

static int msm_pinctrl_hibernation_suspend(void)
{
	const struct msm_pingroup *pgroup;
	struct msm_pinctrl *pctrl = msm_pinctrl_data;
	const struct msm_pinctrl_soc_data *soc = pctrl->soc;
	void __iomem *base = NULL;
	void __iomem *tile_addr = NULL;
	u32 i, j;

	/* Save direction conn registers for hmss */
	for (i = 0; i < soc->tile_count; i++) {
		base = reassign_pctrl_reg(soc, i);
		tile_addr = base + soc->dir_conn_addr[i];
		for (j = 0; j < 8; j++)
			pctrl->msm_tile_regs[i].dir_con_regs[j] =
						readl_relaxed(tile_addr + j*4);
	}

	/* All normal gpios will have common registers, first save them */
	for (i = 0; i < soc->ngpios; i++) {
		pgroup = &soc->groups[i];
		base = reassign_pctrl_reg(soc, i);
		pctrl->gpio_regs[i].ctl_reg =
				readl_relaxed(base + pgroup->ctl_reg);
		pctrl->gpio_regs[i].io_reg =
				readl_relaxed(base + pgroup->io_reg);
		pctrl->gpio_regs[i].intr_cfg_reg =
				readl_relaxed(base + pgroup->intr_cfg_reg);
		pctrl->gpio_regs[i].intr_status_reg =
				readl_relaxed(base + pgroup->intr_status_reg);
	}

	/* Save other special gpios. Variable i in fallthrough */
	for ( ; i < soc->ngroups; i++) {
		pgroup = &soc->groups[i];
		base = reassign_pctrl_reg(soc, i);
		if (pgroup->ctl_reg)
			pctrl->gpio_regs[i].ctl_reg =
				readl_relaxed(base + pgroup->ctl_reg);
		if (pgroup->io_reg)
			pctrl->gpio_regs[i].io_reg =
				readl_relaxed(base + pgroup->io_reg);
	}
	return 0;
}

static void msm_pinctrl_hibernation_resume(void)
{
	u32 i, j;
	const struct msm_pingroup *pgroup;
	struct msm_pinctrl *pctrl = msm_pinctrl_data;
	const struct msm_pinctrl_soc_data *soc = pctrl->soc;
	void __iomem *base = NULL;
	void __iomem *tile_addr = NULL;

	if (!pctrl->gpio_regs || !pctrl->msm_tile_regs)
		return;

	for (i = 0; i < soc->tile_count; i++) {
		base = reassign_pctrl_reg(soc, i);
		tile_addr = base + soc->dir_conn_addr[i];
		for (j = 0; j < 8; j++)
			writel_relaxed(pctrl->msm_tile_regs[i].dir_con_regs[j],
							tile_addr + j*4);
	}

	/* Restore normal gpios */
	for (i = 0; i < soc->ngpios; i++) {
		pgroup = &soc->groups[i];
		base = reassign_pctrl_reg(soc, i);
		writel_relaxed(pctrl->gpio_regs[i].ctl_reg,
					base + pgroup->ctl_reg);
		writel_relaxed(pctrl->gpio_regs[i].io_reg,
					base + pgroup->io_reg);
		writel_relaxed(pctrl->gpio_regs[i].intr_cfg_reg,
					base + pgroup->intr_cfg_reg);
		writel_relaxed(pctrl->gpio_regs[i].intr_status_reg,
					base + pgroup->intr_status_reg);
	}

	/* Restore other special gpios. Variable i in fallthrough */
	for ( ; i < soc->ngroups; i++) {
		pgroup = &soc->groups[i];
		base = reassign_pctrl_reg(soc, i);
		if (pgroup->ctl_reg)
			writel_relaxed(pctrl->gpio_regs[i].ctl_reg,
						base + pgroup->ctl_reg);
		if (pgroup->io_reg)
			writel_relaxed(pctrl->gpio_regs[i].io_reg,
						base + pgroup->io_reg);
	}
}
#endif

static int msm_pinctrl_suspend(void)
{
#ifdef CONFIG_HIBERNATION
	if (unlikely(hibernation))
		msm_pinctrl_hibernation_suspend();
#endif
	return 0;
}

@@ -1800,6 +1957,12 @@ static void msm_pinctrl_resume(void)
	const struct msm_pingroup *g;
	const char *name = "null";
	struct msm_pinctrl *pctrl = msm_pinctrl_data;
#ifdef CONFIG_HIBERNATION
	if (unlikely(hibernation)) {
		msm_pinctrl_hibernation_resume();
		return;
	}
#endif

	if (!msm_show_resume_irq_mask)
		return;
@@ -1907,6 +2070,11 @@ int msm_pinctrl_probe(struct platform_device *pdev,
	platform_set_drvdata(pdev, pctrl);

	register_syscore_ops(&msm_pinctrl_pm_ops);
#ifdef CONFIG_HIBERNATION
	ret = register_pm_notifier(&pinctrl_notif_block);
	if (ret)
		return ret;
#endif
	dev_dbg(&pdev->dev, "Probed Qualcomm pinctrl driver\n");

	return 0;
+5 −1
Original line number Diff line number Diff line
/*
 * Copyright (c) 2013, Sony Mobile Communications AB.
 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
 * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -177,6 +177,10 @@ struct msm_pinctrl_soc_data {
	struct msm_gpio_mux_input *gpio_mux_in;
	unsigned int n_gpio_mux_in;
	unsigned int n_pdc_mux_offset;
#ifdef CONFIG_HIBERNATION
	u32 *dir_conn_addr;
	u32 tile_count;
#endif
#ifdef CONFIG_FRAGMENTED_GPIO_ADDRESS_SPACE
	const u32 *tile_start;
	const u32 *tile_offsets;