Add kernel Hibernation code for porter board.
[AGL/meta-agl.git] / meta-agl-bsp / meta-renesas / recipes-kernel / linux / linux / hibernation / 0012-Add-rcar-gpio-hibernation-code.patch
1 From bf20be14fc1b3f7e096bdac9c5ff67362b391479 Mon Sep 17 00:00:00 2001
2 From: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
3 Date: Thu, 18 May 2017 17:46:24 +0900
4 Subject: [PATCH 12/15] Add rcar-gpio hibernation code
5
6 Signed-off-by: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
7 ---
8  drivers/pinctrl/sh-pfc/core.c | 141 +++++++++++++++++++++++++++++++++++++++---
9  drivers/pinctrl/sh-pfc/core.h |   4 ++
10  2 files changed, 138 insertions(+), 7 deletions(-)
11
12 diff --git a/drivers/pinctrl/sh-pfc/core.c b/drivers/pinctrl/sh-pfc/core.c
13 index b9e025d..c37418e 100644
14 --- a/drivers/pinctrl/sh-pfc/core.c
15 +++ b/drivers/pinctrl/sh-pfc/core.c
16 @@ -24,6 +24,7 @@
17  #include <linux/pinctrl/machine.h>
18  #include <linux/platform_device.h>
19  #include <linux/slab.h>
20 +#include <linux/cpu_pm.h>
21  
22  #include "core.h"
23  
24 @@ -201,19 +202,117 @@ static void sh_pfc_config_reg_helper(struct sh_pfc *pfc,
25         }
26  }
27  
28 +#ifdef CONFIG_CPU_PM
29 +struct reg_record {
30 +       void __iomem *reg;
31 +       unsigned long width;
32 +       unsigned long data;
33 +};
34 +
35 +struct reg_config {
36 +       bool unlock;
37 +       struct reg_record unlock_reg;
38 +       struct reg_record actual_reg;
39 +       struct list_head list;
40 +};
41 +
42 +static struct reg_config *regs_list;
43 +
44 +struct reg_range {
45 +       int start;
46 +       int end;
47 +};
48 +
49 +static int sh_pfc_cpu_pm_notify(struct notifier_block *self,
50 +                                   unsigned long action, void *hcpu)
51 +{
52 +       struct reg_config  *tmp = NULL;
53 +       struct sh_pfc *pfc = container_of(self, struct sh_pfc, pm_notify);
54 +       /* We don't setup pinmux in kernel - store all registers */
55 +       struct reg_range ranges[] = {
56 +               {0x0, 0x5c}, {0x160, 0x160}, {0x90, 0x98},
57 +               {0x100, 0x118}, {0x70, 0x70}, {0x60, 0x64},
58 +               {0x84, 0x8c}, {0x240, 0x248},
59 +       };
60 +
61 +       if (action == CPU_PM_ENTER) {
62 +               if (!regs_list) {
63 +                       /* No pinmux configuration, storing all registers */
64 +                       int store_cnt = 0;
65 +                       int i;
66 +                       for (i = 0; i < ARRAY_SIZE(ranges); i++) {
67 +                               int j;
68 +                               for (j = ranges[i].start; j <= ranges[i].end; j += sizeof(u32)) {
69 +                                       pfc->stored_regs[store_cnt] =
70 +                                               sh_pfc_read_raw_reg(sh_pfc_phys_to_virt(pfc, 0xe6060000 + j), 32);
71 +                                       pr_debug("PFC: %08x => %08x\n", 0xe6060000 + j, pfc->stored_regs[store_cnt]);
72 +                                       store_cnt++;
73 +                                       if (store_cnt >= ARRAY_SIZE(pfc->stored_regs)) {
74 +                                               pr_err("read: Register store overflow\n");
75 +                                               goto out;
76 +                                       }
77 +                               }
78 +                       }
79 +               }
80 +       } else if (action == CPU_PM_ENTER_FAILED || action == CPU_PM_EXIT) {
81 +               if (!regs_list) {
82 +                       /* No list, restoring all registers */
83 +                       int store_cnt = 0;
84 +                       int i;
85 +                       for (i = 0; i < ARRAY_SIZE(ranges); i++) {
86 +                               int j;
87 +                               for (j = ranges[i].start; j <= ranges[i].end; j += sizeof(u32)) {
88 +                                       sh_pfc_write_raw_reg(sh_pfc_phys_to_virt(pfc, 0xe6060000 + j), 32,
89 +                                               pfc->stored_regs[store_cnt]);
90 +                                       pr_debug("PFC: %08x => %08x\n", 0xe6060000 + j, pfc->stored_regs[store_cnt]);
91 +                                       store_cnt++;
92 +                                       if (store_cnt >= ARRAY_SIZE(pfc->stored_regs)) {
93 +                                               pr_err("write: Register store overflow\n");
94 +                                               goto out;
95 +                                       }
96 +                               }
97 +                       }
98 +                       goto out;
99 +               }
100 +               list_for_each_entry(tmp , &(regs_list->list), list) {
101 +               if (tmp->unlock)
102 +                       sh_pfc_write_raw_reg(tmp->unlock_reg.reg,
103 +                                            tmp->unlock_reg.width,
104 +                                            tmp->unlock_reg.data);
105 +               sh_pfc_write_raw_reg(tmp->actual_reg.reg,
106 +                                    tmp->actual_reg.width,
107 +                                    tmp->actual_reg.data);
108 +               }
109 +       }
110 +out:
111 +       return NOTIFY_OK;
112 +}
113 +
114 +static int __init sh_pfc_cpu_pm_init(struct sh_pfc *pfc)
115 +{
116 +       memset(&pfc->pm_notify, 0, sizeof(pfc->pm_notify));
117 +       pfc->pm_notify.notifier_call = sh_pfc_cpu_pm_notify;
118 +       return cpu_pm_register_notifier(&pfc->pm_notify);
119 +}
120 +#else
121 +static int __init sh_pfc_cpu_pm_init(struct sh_pfc *pfc)
122 +{
123 +       return 0;
124 +}
125 +#endif
126 +
127 +
128  static void sh_pfc_write_config_reg(struct sh_pfc *pfc,
129                                     const struct pinmux_cfg_reg *crp,
130                                     unsigned long field, unsigned long value)
131  {
132         void __iomem *mapped_reg;
133         unsigned long mask, pos, data;
134 -
135 +#ifdef CONFIG_CPU_PM
136 +       struct reg_config *tmp;
137 +#endif
138         sh_pfc_config_reg_helper(pfc, crp, field, &mapped_reg, &mask, &pos);
139  
140 -       dev_dbg(pfc->dev, "write_reg addr = %lx, value = %ld, field = %ld, "
141 -               "r_width = %ld, f_width = %ld\n",
142 -               crp->reg, value, field, crp->reg_width, crp->field_width);
143 -
144         mask = ~(mask << pos);
145         value = value << pos;
146  
147 @@ -221,14 +320,39 @@ static void sh_pfc_write_config_reg(struct sh_pfc *pfc,
148         data &= mask;
149         data |= value;
150  
151 -       if (pfc->info->unlock_reg)
152 +#ifdef CONFIG_CPU_PM
153 +       tmp = kzalloc(sizeof(struct reg_config), GFP_KERNEL);
154 +       BUG_ON(!tmp);
155 +
156 +       if (!regs_list) {
157 +               regs_list = tmp;
158 +               INIT_LIST_HEAD(&regs_list->list);
159 +       }
160 +#endif
161 +
162 +       if (pfc->info->unlock_reg) {
163 +#ifdef CONFIG_CPU_PM
164 +               tmp->unlock = true;
165 +               tmp->unlock_reg.reg = sh_pfc_phys_to_virt(pfc,
166 +                               pfc->info->unlock_reg);
167 +               tmp->unlock_reg.width = 32;
168 +               tmp->unlock_reg.data = ~data;
169 +#endif
170                 sh_pfc_write_raw_reg(
171                         sh_pfc_phys_to_virt(pfc, pfc->info->unlock_reg), 32,
172                         ~data);
173 +       }
174 +
175 +#ifdef CONFIG_CPU_PM
176 +       tmp->actual_reg.reg = mapped_reg;
177 +       tmp->actual_reg.width = crp->reg_width;
178 +       tmp->actual_reg.data = data;
179 +
180 +       list_add(&tmp->list, &regs_list->list);
181 +#endif
182  
183         sh_pfc_write_raw_reg(mapped_reg, crp->reg_width, data);
184  }
185 -
186  static int sh_pfc_get_config_reg(struct sh_pfc *pfc, u16 enum_id,
187                                  const struct pinmux_cfg_reg **crp, int *fieldp,
188                                  int *valuep)
189 @@ -574,6 +698,8 @@ static int sh_pfc_probe(struct platform_device *pdev)
190  
191         platform_set_drvdata(pdev, pfc);
192  
193 +       sh_pfc_cpu_pm_init(pfc);
194 +
195         dev_info(pfc->dev, "%s support registered\n", info->name);
196  
197         return 0;
198 @@ -596,6 +722,7 @@ static int sh_pfc_remove(struct platform_device *pdev)
199         if (pfc->info->ops && pfc->info->ops->exit)
200                 pfc->info->ops->exit(pfc);
201  
202 +
203         return 0;
204  }
205  
206 diff --git a/drivers/pinctrl/sh-pfc/core.h b/drivers/pinctrl/sh-pfc/core.h
207 index 75ecb67..5471a6c 100644
208 --- a/drivers/pinctrl/sh-pfc/core.h
209 +++ b/drivers/pinctrl/sh-pfc/core.h
210 @@ -14,6 +14,7 @@
211  #include <linux/compiler.h>
212  #include <linux/spinlock.h>
213  #include <linux/types.h>
214 +#include <linux/notifier.h>
215  
216  #include "sh_pfc.h"
217  
218 @@ -51,6 +52,9 @@ struct sh_pfc {
219         struct sh_pfc_chip *func;
220  
221         struct sh_pfc_pinctrl *pinctrl;
222 +       struct notifier_block pm_notify;
223 +#define STORE_REGS_COUNT       50
224 +       u32 stored_regs[STORE_REGS_COUNT];
225  };
226  
227  int sh_pfc_register_gpiochip(struct sh_pfc *pfc);
228 -- 
229 1.8.3.1
230