Loading drivers/pinctrl/qcom/pinctrl-msm.c +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 Loading Loading @@ -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. Loading Loading @@ -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; Loading Loading @@ -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; } Loading @@ -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; Loading Loading @@ -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; Loading drivers/pinctrl/qcom/pinctrl-msm.h +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 Loading Loading @@ -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; Loading drivers/pinctrl/qcom/pinctrl-sm8150.c +22 −3 Original line number Diff line number Diff line Loading @@ -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) \ { \ Loading @@ -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, \ Loading Loading @@ -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), Loading @@ -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) Loading Loading
drivers/pinctrl/qcom/pinctrl-msm.c +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 Loading Loading @@ -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. Loading Loading @@ -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; Loading Loading @@ -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; } Loading @@ -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; Loading Loading @@ -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; Loading
drivers/pinctrl/qcom/pinctrl-msm.h +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 Loading Loading @@ -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; Loading
drivers/pinctrl/qcom/pinctrl-sm8150.c +22 −3 Original line number Diff line number Diff line Loading @@ -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) \ { \ Loading @@ -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, \ Loading Loading @@ -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), Loading @@ -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) Loading