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

Commit 9dc7b424 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "pinctrl: qcom: Add hibernation support for sm8150"

parents d127170b 74ce5307
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;
+22 −3
Original line number Diff line number Diff line
@@ -29,6 +29,11 @@
#define SOUTH	0x00D00000
#define WEST	0x00100000
#define EAST	0x00500000
#define NORTH_PDC_OFFSET    0xbc000
#define SOUTH_PDC_OFFSET    0xbe000
#define WEST_PDC_OFFSET     0xbb000
#define EAST_PDC_OFFSET     0xb7000
#define NUM_TILES 4
#define REG_SIZE 0x1000
#define PINGROUP(id, base, f1, f2, f3, f4, f5, f6, f7, f8, f9)	\
	{						\
@@ -53,9 +58,10 @@
		.intr_cfg_reg = base + 0x8 + REG_SIZE * id,	\
		.intr_status_reg = base + 0xc + REG_SIZE * id,	\
		.intr_target_reg = base + 0x8 + REG_SIZE * id,	\
		.dir_conn_reg = (base == EAST) ? base + 0xb7000 : \
			((base == WEST) ? base + 0xbb000 : \
			((base == NORTH) ? base + 0xbc000 : base + 0xbe000)), \
		.dir_conn_reg = (base == EAST) ? base + EAST_PDC_OFFSET : \
			((base == WEST) ? base + WEST_PDC_OFFSET : \
			((base == NORTH) ? base + NORTH_PDC_OFFSET : \
			  base + SOUTH_PDC_OFFSET)), \
		.mux_bit = 2,			\
		.pull_bit = 0,			\
		.drv_bit = 6,			\
@@ -1939,6 +1945,15 @@ static struct msm_dir_conn sm8150_dir_conn[] = {
	{-1, 209},
};

#ifdef CONFIG_HIBERNATION
static u32 tile_dir_conn_addr[NUM_TILES] = {
	[0] =	NORTH + NORTH_PDC_OFFSET,
	[1] =	SOUTH + SOUTH_PDC_OFFSET,
	[2] =	WEST + WEST_PDC_OFFSET,
	[3] =	EAST + EAST_PDC_OFFSET
};
#endif

static struct msm_pinctrl_soc_data sm8150_pinctrl = {
	.pins = sm8150_pins,
	.npins = ARRAY_SIZE(sm8150_pins),
@@ -1950,6 +1965,10 @@ static struct msm_pinctrl_soc_data sm8150_pinctrl = {
	.dir_conn = sm8150_dir_conn,
	.n_dir_conns = ARRAY_SIZE(sm8150_dir_conn),
	.dir_conn_irq_base = 216,
#ifdef CONFIG_HIBERNATION
	.dir_conn_addr = tile_dir_conn_addr,
	.tile_count = ARRAY_SIZE(tile_dir_conn_addr),
#endif
};

static int sm8150_pinctrl_dir_conn_probe(struct platform_device *pdev)