diff options
| -rw-r--r-- | arch/arm/mach-exynos/pm.c | 148 | ||||
| -rw-r--r-- | drivers/clk/samsung/clk-exynos4.c | 172 | ||||
| -rw-r--r-- | drivers/clk/samsung/clk-exynos5250.c | 49 | ||||
| -rw-r--r-- | drivers/clk/samsung/clk-exynos5420.c | 49 | ||||
| -rw-r--r-- | drivers/clk/samsung/clk-exynos5440.c | 2 | ||||
| -rw-r--r-- | drivers/clk/samsung/clk-s3c64xx.c | 79 | ||||
| -rw-r--r-- | drivers/clk/samsung/clk.c | 71 | ||||
| -rw-r--r-- | drivers/clk/samsung/clk.h | 14 |
8 files changed, 348 insertions, 236 deletions
diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c index e00025bbbe89..ba18214c9aca 100644 --- a/arch/arm/mach-exynos/pm.c +++ b/arch/arm/mach-exynos/pm.c | |||
| @@ -35,56 +35,6 @@ | |||
| 35 | #include "common.h" | 35 | #include "common.h" |
| 36 | #include "regs-pmu.h" | 36 | #include "regs-pmu.h" |
| 37 | 37 | ||
| 38 | #define EXYNOS4_EPLL_LOCK (S5P_VA_CMU + 0x0C010) | ||
| 39 | #define EXYNOS4_VPLL_LOCK (S5P_VA_CMU + 0x0C020) | ||
| 40 | |||
| 41 | #define EXYNOS4_EPLL_CON0 (S5P_VA_CMU + 0x0C110) | ||
| 42 | #define EXYNOS4_EPLL_CON1 (S5P_VA_CMU + 0x0C114) | ||
| 43 | #define EXYNOS4_VPLL_CON0 (S5P_VA_CMU + 0x0C120) | ||
| 44 | #define EXYNOS4_VPLL_CON1 (S5P_VA_CMU + 0x0C124) | ||
| 45 | |||
| 46 | #define EXYNOS4_CLKSRC_MASK_TOP (S5P_VA_CMU + 0x0C310) | ||
| 47 | #define EXYNOS4_CLKSRC_MASK_CAM (S5P_VA_CMU + 0x0C320) | ||
| 48 | #define EXYNOS4_CLKSRC_MASK_TV (S5P_VA_CMU + 0x0C324) | ||
| 49 | #define EXYNOS4_CLKSRC_MASK_LCD0 (S5P_VA_CMU + 0x0C334) | ||
| 50 | #define EXYNOS4_CLKSRC_MASK_MAUDIO (S5P_VA_CMU + 0x0C33C) | ||
| 51 | #define EXYNOS4_CLKSRC_MASK_FSYS (S5P_VA_CMU + 0x0C340) | ||
| 52 | #define EXYNOS4_CLKSRC_MASK_PERIL0 (S5P_VA_CMU + 0x0C350) | ||
| 53 | #define EXYNOS4_CLKSRC_MASK_PERIL1 (S5P_VA_CMU + 0x0C354) | ||
| 54 | |||
| 55 | #define EXYNOS4_CLKSRC_MASK_DMC (S5P_VA_CMU + 0x10300) | ||
| 56 | |||
| 57 | #define EXYNOS4_EPLLCON0_LOCKED_SHIFT (29) | ||
| 58 | #define EXYNOS4_VPLLCON0_LOCKED_SHIFT (29) | ||
| 59 | |||
| 60 | #define EXYNOS4210_CLKSRC_MASK_LCD1 (S5P_VA_CMU + 0x0C338) | ||
| 61 | |||
| 62 | static const struct sleep_save exynos4_set_clksrc[] = { | ||
| 63 | { .reg = EXYNOS4_CLKSRC_MASK_TOP , .val = 0x00000001, }, | ||
| 64 | { .reg = EXYNOS4_CLKSRC_MASK_CAM , .val = 0x11111111, }, | ||
| 65 | { .reg = EXYNOS4_CLKSRC_MASK_TV , .val = 0x00000111, }, | ||
| 66 | { .reg = EXYNOS4_CLKSRC_MASK_LCD0 , .val = 0x00001111, }, | ||
| 67 | { .reg = EXYNOS4_CLKSRC_MASK_MAUDIO , .val = 0x00000001, }, | ||
| 68 | { .reg = EXYNOS4_CLKSRC_MASK_FSYS , .val = 0x01011111, }, | ||
| 69 | { .reg = EXYNOS4_CLKSRC_MASK_PERIL0 , .val = 0x01111111, }, | ||
| 70 | { .reg = EXYNOS4_CLKSRC_MASK_PERIL1 , .val = 0x01110111, }, | ||
| 71 | { .reg = EXYNOS4_CLKSRC_MASK_DMC , .val = 0x00010000, }, | ||
| 72 | }; | ||
| 73 | |||
| 74 | static const struct sleep_save exynos4210_set_clksrc[] = { | ||
| 75 | { .reg = EXYNOS4210_CLKSRC_MASK_LCD1 , .val = 0x00001111, }, | ||
| 76 | }; | ||
| 77 | |||
| 78 | static struct sleep_save exynos4_epll_save[] = { | ||
| 79 | SAVE_ITEM(EXYNOS4_EPLL_CON0), | ||
| 80 | SAVE_ITEM(EXYNOS4_EPLL_CON1), | ||
| 81 | }; | ||
| 82 | |||
| 83 | static struct sleep_save exynos4_vpll_save[] = { | ||
| 84 | SAVE_ITEM(EXYNOS4_VPLL_CON0), | ||
| 85 | SAVE_ITEM(EXYNOS4_VPLL_CON1), | ||
| 86 | }; | ||
| 87 | |||
| 88 | static struct sleep_save exynos5_sys_save[] = { | 38 | static struct sleep_save exynos5_sys_save[] = { |
| 89 | SAVE_ITEM(EXYNOS5_SYS_I2C_CFG), | 39 | SAVE_ITEM(EXYNOS5_SYS_I2C_CFG), |
| 90 | }; | 40 | }; |
| @@ -124,10 +74,7 @@ static void exynos_pm_prepare(void) | |||
| 124 | 74 | ||
| 125 | s3c_pm_do_save(exynos_core_save, ARRAY_SIZE(exynos_core_save)); | 75 | s3c_pm_do_save(exynos_core_save, ARRAY_SIZE(exynos_core_save)); |
| 126 | 76 | ||
| 127 | if (!soc_is_exynos5250()) { | 77 | if (soc_is_exynos5250()) { |
| 128 | s3c_pm_do_save(exynos4_epll_save, ARRAY_SIZE(exynos4_epll_save)); | ||
| 129 | s3c_pm_do_save(exynos4_vpll_save, ARRAY_SIZE(exynos4_vpll_save)); | ||
| 130 | } else { | ||
| 131 | s3c_pm_do_save(exynos5_sys_save, ARRAY_SIZE(exynos5_sys_save)); | 78 | s3c_pm_do_save(exynos5_sys_save, ARRAY_SIZE(exynos5_sys_save)); |
| 132 | /* Disable USE_RETENTION of JPEG_MEM_OPTION */ | 79 | /* Disable USE_RETENTION of JPEG_MEM_OPTION */ |
| 133 | tmp = __raw_readl(EXYNOS5_JPEG_MEM_OPTION); | 80 | tmp = __raw_readl(EXYNOS5_JPEG_MEM_OPTION); |
| @@ -143,15 +90,6 @@ static void exynos_pm_prepare(void) | |||
| 143 | /* ensure at least INFORM0 has the resume address */ | 90 | /* ensure at least INFORM0 has the resume address */ |
| 144 | 91 | ||
| 145 | __raw_writel(virt_to_phys(s3c_cpu_resume), S5P_INFORM0); | 92 | __raw_writel(virt_to_phys(s3c_cpu_resume), S5P_INFORM0); |
| 146 | |||
| 147 | /* Before enter central sequence mode, clock src register have to set */ | ||
| 148 | |||
| 149 | if (!soc_is_exynos5250()) | ||
| 150 | s3c_pm_do_restore_core(exynos4_set_clksrc, ARRAY_SIZE(exynos4_set_clksrc)); | ||
| 151 | |||
| 152 | if (soc_is_exynos4210()) | ||
| 153 | s3c_pm_do_restore_core(exynos4210_set_clksrc, ARRAY_SIZE(exynos4210_set_clksrc)); | ||
| 154 | |||
| 155 | } | 93 | } |
| 156 | 94 | ||
| 157 | static int exynos_pm_add(struct device *dev, struct subsys_interface *sif) | 95 | static int exynos_pm_add(struct device *dev, struct subsys_interface *sif) |
| @@ -162,73 +100,6 @@ static int exynos_pm_add(struct device *dev, struct subsys_interface *sif) | |||
| 162 | return 0; | 100 | return 0; |
| 163 | } | 101 | } |
| 164 | 102 | ||
| 165 | static unsigned long pll_base_rate; | ||
| 166 | |||
| 167 | static void exynos4_restore_pll(void) | ||
| 168 | { | ||
| 169 | unsigned long pll_con, locktime, lockcnt; | ||
| 170 | unsigned long pll_in_rate; | ||
| 171 | unsigned int p_div, epll_wait = 0, vpll_wait = 0; | ||
| 172 | |||
| 173 | if (pll_base_rate == 0) | ||
| 174 | return; | ||
| 175 | |||
| 176 | pll_in_rate = pll_base_rate; | ||
| 177 | |||
| 178 | /* EPLL */ | ||
| 179 | pll_con = exynos4_epll_save[0].val; | ||
| 180 | |||
| 181 | if (pll_con & (1 << 31)) { | ||
| 182 | pll_con &= (PLL46XX_PDIV_MASK << PLL46XX_PDIV_SHIFT); | ||
| 183 | p_div = (pll_con >> PLL46XX_PDIV_SHIFT); | ||
| 184 | |||
| 185 | pll_in_rate /= 1000000; | ||
| 186 | |||
| 187 | locktime = (3000 / pll_in_rate) * p_div; | ||
| 188 | lockcnt = locktime * 10000 / (10000 / pll_in_rate); | ||
| 189 | |||
| 190 | __raw_writel(lockcnt, EXYNOS4_EPLL_LOCK); | ||
| 191 | |||
| 192 | s3c_pm_do_restore_core(exynos4_epll_save, | ||
| 193 | ARRAY_SIZE(exynos4_epll_save)); | ||
| 194 | epll_wait = 1; | ||
| 195 | } | ||
| 196 | |||
| 197 | pll_in_rate = pll_base_rate; | ||
| 198 | |||
| 199 | /* VPLL */ | ||
| 200 | pll_con = exynos4_vpll_save[0].val; | ||
| 201 | |||
| 202 | if (pll_con & (1 << 31)) { | ||
| 203 | pll_in_rate /= 1000000; | ||
| 204 | /* 750us */ | ||
| 205 | locktime = 750; | ||
| 206 | lockcnt = locktime * 10000 / (10000 / pll_in_rate); | ||
| 207 | |||
| 208 | __raw_writel(lockcnt, EXYNOS4_VPLL_LOCK); | ||
| 209 | |||
| 210 | s3c_pm_do_restore_core(exynos4_vpll_save, | ||
| 211 | ARRAY_SIZE(exynos4_vpll_save)); | ||
| 212 | vpll_wait = 1; | ||
| 213 | } | ||
| 214 | |||
| 215 | /* Wait PLL locking */ | ||
| 216 | |||
| 217 | do { | ||
| 218 | if (epll_wait) { | ||
| 219 | pll_con = __raw_readl(EXYNOS4_EPLL_CON0); | ||
| 220 | if (pll_con & (1 << EXYNOS4_EPLLCON0_LOCKED_SHIFT)) | ||
| 221 | epll_wait = 0; | ||
| 222 | } | ||
| 223 | |||
| 224 | if (vpll_wait) { | ||
| 225 | pll_con = __raw_readl(EXYNOS4_VPLL_CON0); | ||
| 226 | if (pll_con & (1 << EXYNOS4_VPLLCON0_LOCKED_SHIFT)) | ||
| 227 | vpll_wait = 0; | ||
| 228 | } | ||
| 229 | } while (epll_wait || vpll_wait); | ||
| 230 | } | ||
| 231 | |||
| 232 | static struct subsys_interface exynos_pm_interface = { | 103 | static struct subsys_interface exynos_pm_interface = { |
| 233 | .name = "exynos_pm", | 104 | .name = "exynos_pm", |
| 234 | .subsys = &exynos_subsys, | 105 | .subsys = &exynos_subsys, |
| @@ -237,7 +108,6 @@ static struct subsys_interface exynos_pm_interface = { | |||
| 237 | 108 | ||
| 238 | static __init int exynos_pm_drvinit(void) | 109 | static __init int exynos_pm_drvinit(void) |
| 239 | { | 110 | { |
| 240 | struct clk *pll_base; | ||
| 241 | unsigned int tmp; | 111 | unsigned int tmp; |
| 242 | 112 | ||
| 243 | if (soc_is_exynos5440()) | 113 | if (soc_is_exynos5440()) |
| @@ -251,15 +121,6 @@ static __init int exynos_pm_drvinit(void) | |||
| 251 | tmp |= ((0xFF << 8) | (0x1F << 1)); | 121 | tmp |= ((0xFF << 8) | (0x1F << 1)); |
| 252 | __raw_writel(tmp, S5P_WAKEUP_MASK); | 122 | __raw_writel(tmp, S5P_WAKEUP_MASK); |
| 253 | 123 | ||
| 254 | if (!soc_is_exynos5250()) { | ||
| 255 | pll_base = clk_get(NULL, "xtal"); | ||
| 256 | |||
| 257 | if (!IS_ERR(pll_base)) { | ||
| 258 | pll_base_rate = clk_get_rate(pll_base); | ||
| 259 | clk_put(pll_base); | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 263 | return subsys_interface_register(&exynos_pm_interface); | 124 | return subsys_interface_register(&exynos_pm_interface); |
| 264 | } | 125 | } |
| 265 | arch_initcall(exynos_pm_drvinit); | 126 | arch_initcall(exynos_pm_drvinit); |
| @@ -343,13 +204,8 @@ static void exynos_pm_resume(void) | |||
| 343 | 204 | ||
| 344 | s3c_pm_do_restore_core(exynos_core_save, ARRAY_SIZE(exynos_core_save)); | 205 | s3c_pm_do_restore_core(exynos_core_save, ARRAY_SIZE(exynos_core_save)); |
| 345 | 206 | ||
| 346 | if (!soc_is_exynos5250()) { | 207 | if (IS_ENABLED(CONFIG_SMP) && !soc_is_exynos5250()) |
| 347 | exynos4_restore_pll(); | ||
| 348 | |||
| 349 | #ifdef CONFIG_SMP | ||
| 350 | scu_enable(S5P_VA_SCU); | 208 | scu_enable(S5P_VA_SCU); |
| 351 | #endif | ||
| 352 | } | ||
| 353 | 209 | ||
| 354 | early_wakeup: | 210 | early_wakeup: |
| 355 | 211 | ||
diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 010f071af883..b4f967210175 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | #include <linux/clk-provider.h> | 16 | #include <linux/clk-provider.h> |
| 17 | #include <linux/of.h> | 17 | #include <linux/of.h> |
| 18 | #include <linux/of_address.h> | 18 | #include <linux/of_address.h> |
| 19 | #include <linux/syscore_ops.h> | ||
| 19 | 20 | ||
| 20 | #include "clk.h" | 21 | #include "clk.h" |
| 21 | 22 | ||
| @@ -130,6 +131,17 @@ enum exynos4_plls { | |||
| 130 | nr_plls /* number of PLLs */ | 131 | nr_plls /* number of PLLs */ |
| 131 | }; | 132 | }; |
| 132 | 133 | ||
| 134 | static void __iomem *reg_base; | ||
| 135 | static enum exynos4_soc exynos4_soc; | ||
| 136 | |||
| 137 | /* | ||
| 138 | * Support for CMU save/restore across system suspends | ||
| 139 | */ | ||
| 140 | #ifdef CONFIG_PM_SLEEP | ||
| 141 | static struct samsung_clk_reg_dump *exynos4_save_common; | ||
| 142 | static struct samsung_clk_reg_dump *exynos4_save_soc; | ||
| 143 | static struct samsung_clk_reg_dump *exynos4_save_pll; | ||
| 144 | |||
| 133 | /* | 145 | /* |
| 134 | * list of controller registers to be saved and restored during a | 146 | * list of controller registers to be saved and restored during a |
| 135 | * suspend/resume cycle. | 147 | * suspend/resume cycle. |
| @@ -154,6 +166,17 @@ static unsigned long exynos4x12_clk_save[] __initdata = { | |||
| 154 | E4X12_MPLL_CON0, | 166 | E4X12_MPLL_CON0, |
| 155 | }; | 167 | }; |
| 156 | 168 | ||
| 169 | static unsigned long exynos4_clk_pll_regs[] __initdata = { | ||
| 170 | EPLL_LOCK, | ||
| 171 | VPLL_LOCK, | ||
| 172 | EPLL_CON0, | ||
| 173 | EPLL_CON1, | ||
| 174 | EPLL_CON2, | ||
| 175 | VPLL_CON0, | ||
| 176 | VPLL_CON1, | ||
| 177 | VPLL_CON2, | ||
| 178 | }; | ||
| 179 | |||
| 157 | static unsigned long exynos4_clk_regs[] __initdata = { | 180 | static unsigned long exynos4_clk_regs[] __initdata = { |
| 158 | SRC_LEFTBUS, | 181 | SRC_LEFTBUS, |
| 159 | DIV_LEFTBUS, | 182 | DIV_LEFTBUS, |
| @@ -161,12 +184,6 @@ static unsigned long exynos4_clk_regs[] __initdata = { | |||
| 161 | SRC_RIGHTBUS, | 184 | SRC_RIGHTBUS, |
| 162 | DIV_RIGHTBUS, | 185 | DIV_RIGHTBUS, |
| 163 | GATE_IP_RIGHTBUS, | 186 | GATE_IP_RIGHTBUS, |
| 164 | EPLL_CON0, | ||
| 165 | EPLL_CON1, | ||
| 166 | EPLL_CON2, | ||
| 167 | VPLL_CON0, | ||
| 168 | VPLL_CON1, | ||
| 169 | VPLL_CON2, | ||
| 170 | SRC_TOP0, | 187 | SRC_TOP0, |
| 171 | SRC_TOP1, | 188 | SRC_TOP1, |
| 172 | SRC_CAM, | 189 | SRC_CAM, |
| @@ -227,6 +244,124 @@ static unsigned long exynos4_clk_regs[] __initdata = { | |||
| 227 | GATE_IP_CPU, | 244 | GATE_IP_CPU, |
| 228 | }; | 245 | }; |
| 229 | 246 | ||
| 247 | static const struct samsung_clk_reg_dump src_mask_suspend[] = { | ||
| 248 | { .offset = SRC_MASK_TOP, .value = 0x00000001, }, | ||
| 249 | { .offset = SRC_MASK_CAM, .value = 0x11111111, }, | ||
| 250 | { .offset = SRC_MASK_TV, .value = 0x00000111, }, | ||
| 251 | { .offset = SRC_MASK_LCD0, .value = 0x00001111, }, | ||
| 252 | { .offset = SRC_MASK_MAUDIO, .value = 0x00000001, }, | ||
| 253 | { .offset = SRC_MASK_FSYS, .value = 0x01011111, }, | ||
| 254 | { .offset = SRC_MASK_PERIL0, .value = 0x01111111, }, | ||
| 255 | { .offset = SRC_MASK_PERIL1, .value = 0x01110111, }, | ||
| 256 | { .offset = SRC_MASK_DMC, .value = 0x00010000, }, | ||
| 257 | }; | ||
| 258 | |||
| 259 | static const struct samsung_clk_reg_dump src_mask_suspend_e4210[] = { | ||
| 260 | { .offset = E4210_SRC_MASK_LCD1, .value = 0x00001111, }, | ||
| 261 | }; | ||
| 262 | |||
| 263 | #define PLL_ENABLED (1 << 31) | ||
| 264 | #define PLL_LOCKED (1 << 29) | ||
| 265 | |||
| 266 | static void exynos4_clk_wait_for_pll(u32 reg) | ||
| 267 | { | ||
| 268 | u32 pll_con; | ||
| 269 | |||
| 270 | pll_con = readl(reg_base + reg); | ||
| 271 | if (!(pll_con & PLL_ENABLED)) | ||
| 272 | return; | ||
| 273 | |||
| 274 | while (!(pll_con & PLL_LOCKED)) { | ||
| 275 | cpu_relax(); | ||
| 276 | pll_con = readl(reg_base + reg); | ||
| 277 | } | ||
| 278 | } | ||
| 279 | |||
| 280 | static int exynos4_clk_suspend(void) | ||
| 281 | { | ||
| 282 | samsung_clk_save(reg_base, exynos4_save_common, | ||
| 283 | ARRAY_SIZE(exynos4_clk_regs)); | ||
| 284 | samsung_clk_save(reg_base, exynos4_save_pll, | ||
| 285 | ARRAY_SIZE(exynos4_clk_pll_regs)); | ||
| 286 | |||
| 287 | if (exynos4_soc == EXYNOS4210) { | ||
| 288 | samsung_clk_save(reg_base, exynos4_save_soc, | ||
| 289 | ARRAY_SIZE(exynos4210_clk_save)); | ||
| 290 | samsung_clk_restore(reg_base, src_mask_suspend_e4210, | ||
| 291 | ARRAY_SIZE(src_mask_suspend_e4210)); | ||
| 292 | } else { | ||
| 293 | samsung_clk_save(reg_base, exynos4_save_soc, | ||
| 294 | ARRAY_SIZE(exynos4x12_clk_save)); | ||
| 295 | } | ||
| 296 | |||
| 297 | samsung_clk_restore(reg_base, src_mask_suspend, | ||
| 298 | ARRAY_SIZE(src_mask_suspend)); | ||
| 299 | |||
| 300 | return 0; | ||
| 301 | } | ||
| 302 | |||
| 303 | static void exynos4_clk_resume(void) | ||
| 304 | { | ||
| 305 | samsung_clk_restore(reg_base, exynos4_save_pll, | ||
| 306 | ARRAY_SIZE(exynos4_clk_pll_regs)); | ||
| 307 | |||
| 308 | exynos4_clk_wait_for_pll(EPLL_CON0); | ||
| 309 | exynos4_clk_wait_for_pll(VPLL_CON0); | ||
| 310 | |||
| 311 | samsung_clk_restore(reg_base, exynos4_save_common, | ||
| 312 | ARRAY_SIZE(exynos4_clk_regs)); | ||
| 313 | |||
| 314 | if (exynos4_soc == EXYNOS4210) | ||
| 315 | samsung_clk_restore(reg_base, exynos4_save_soc, | ||
| 316 | ARRAY_SIZE(exynos4210_clk_save)); | ||
| 317 | else | ||
| 318 | samsung_clk_restore(reg_base, exynos4_save_soc, | ||
| 319 | ARRAY_SIZE(exynos4x12_clk_save)); | ||
| 320 | } | ||
| 321 | |||
| 322 | static struct syscore_ops exynos4_clk_syscore_ops = { | ||
| 323 | .suspend = exynos4_clk_suspend, | ||
| 324 | .resume = exynos4_clk_resume, | ||
| 325 | }; | ||
| 326 | |||
| 327 | static void exynos4_clk_sleep_init(void) | ||
| 328 | { | ||
| 329 | exynos4_save_common = samsung_clk_alloc_reg_dump(exynos4_clk_regs, | ||
| 330 | ARRAY_SIZE(exynos4_clk_regs)); | ||
| 331 | if (!exynos4_save_common) | ||
| 332 | goto err_warn; | ||
| 333 | |||
| 334 | if (exynos4_soc == EXYNOS4210) | ||
| 335 | exynos4_save_soc = samsung_clk_alloc_reg_dump( | ||
| 336 | exynos4210_clk_save, | ||
| 337 | ARRAY_SIZE(exynos4210_clk_save)); | ||
| 338 | else | ||
| 339 | exynos4_save_soc = samsung_clk_alloc_reg_dump( | ||
| 340 | exynos4x12_clk_save, | ||
| 341 | ARRAY_SIZE(exynos4x12_clk_save)); | ||
| 342 | if (!exynos4_save_soc) | ||
| 343 | goto err_common; | ||
| 344 | |||
| 345 | exynos4_save_pll = samsung_clk_alloc_reg_dump(exynos4_clk_pll_regs, | ||
| 346 | ARRAY_SIZE(exynos4_clk_pll_regs)); | ||
| 347 | if (!exynos4_save_pll) | ||
| 348 | goto err_soc; | ||
| 349 | |||
| 350 | register_syscore_ops(&exynos4_clk_syscore_ops); | ||
| 351 | return; | ||
| 352 | |||
| 353 | err_soc: | ||
| 354 | kfree(exynos4_save_soc); | ||
| 355 | err_common: | ||
| 356 | kfree(exynos4_save_common); | ||
| 357 | err_warn: | ||
| 358 | pr_warn("%s: failed to allocate sleep save data, no sleep support!\n", | ||
| 359 | __func__); | ||
| 360 | } | ||
| 361 | #else | ||
| 362 | static void exynos4_clk_sleep_init(void) {} | ||
| 363 | #endif | ||
| 364 | |||
| 230 | /* list of all parent clock list */ | 365 | /* list of all parent clock list */ |
| 231 | PNAME(mout_apll_p) = { "fin_pll", "fout_apll", }; | 366 | PNAME(mout_apll_p) = { "fin_pll", "fout_apll", }; |
| 232 | PNAME(mout_mpll_p) = { "fin_pll", "fout_mpll", }; | 367 | PNAME(mout_mpll_p) = { "fin_pll", "fout_mpll", }; |
| @@ -908,12 +1043,13 @@ static unsigned long exynos4_get_xom(void) | |||
| 908 | return xom; | 1043 | return xom; |
| 909 | } | 1044 | } |
| 910 | 1045 | ||
| 911 | static void __init exynos4_clk_register_finpll(unsigned long xom) | 1046 | static void __init exynos4_clk_register_finpll(void) |
| 912 | { | 1047 | { |
| 913 | struct samsung_fixed_rate_clock fclk; | 1048 | struct samsung_fixed_rate_clock fclk; |
| 914 | struct clk *clk; | 1049 | struct clk *clk; |
| 915 | unsigned long finpll_f = 24000000; | 1050 | unsigned long finpll_f = 24000000; |
| 916 | char *parent_name; | 1051 | char *parent_name; |
| 1052 | unsigned int xom = exynos4_get_xom(); | ||
| 917 | 1053 | ||
| 918 | parent_name = xom & 1 ? "xusbxti" : "xxti"; | 1054 | parent_name = xom & 1 ? "xusbxti" : "xxti"; |
| 919 | clk = clk_get(NULL, parent_name); | 1055 | clk = clk_get(NULL, parent_name); |
| @@ -1038,27 +1174,21 @@ static struct samsung_pll_clock exynos4x12_plls[nr_plls] __initdata = { | |||
| 1038 | 1174 | ||
| 1039 | /* register exynos4 clocks */ | 1175 | /* register exynos4 clocks */ |
| 1040 | static void __init exynos4_clk_init(struct device_node *np, | 1176 | static void __init exynos4_clk_init(struct device_node *np, |
| 1041 | enum exynos4_soc exynos4_soc, | 1177 | enum exynos4_soc soc) |
| 1042 | void __iomem *reg_base, unsigned long xom) | ||
| 1043 | { | 1178 | { |
| 1179 | exynos4_soc = soc; | ||
| 1180 | |||
| 1044 | reg_base = of_iomap(np, 0); | 1181 | reg_base = of_iomap(np, 0); |
| 1045 | if (!reg_base) | 1182 | if (!reg_base) |
| 1046 | panic("%s: failed to map registers\n", __func__); | 1183 | panic("%s: failed to map registers\n", __func__); |
| 1047 | 1184 | ||
| 1048 | if (exynos4_soc == EXYNOS4210) | 1185 | samsung_clk_init(np, reg_base, CLK_NR_CLKS); |
| 1049 | samsung_clk_init(np, reg_base, CLK_NR_CLKS, | ||
| 1050 | exynos4_clk_regs, ARRAY_SIZE(exynos4_clk_regs), | ||
| 1051 | exynos4210_clk_save, ARRAY_SIZE(exynos4210_clk_save)); | ||
| 1052 | else | ||
| 1053 | samsung_clk_init(np, reg_base, CLK_NR_CLKS, | ||
| 1054 | exynos4_clk_regs, ARRAY_SIZE(exynos4_clk_regs), | ||
| 1055 | exynos4x12_clk_save, ARRAY_SIZE(exynos4x12_clk_save)); | ||
| 1056 | 1186 | ||
| 1057 | samsung_clk_of_register_fixed_ext(exynos4_fixed_rate_ext_clks, | 1187 | samsung_clk_of_register_fixed_ext(exynos4_fixed_rate_ext_clks, |
| 1058 | ARRAY_SIZE(exynos4_fixed_rate_ext_clks), | 1188 | ARRAY_SIZE(exynos4_fixed_rate_ext_clks), |
| 1059 | ext_clk_match); | 1189 | ext_clk_match); |
| 1060 | 1190 | ||
| 1061 | exynos4_clk_register_finpll(xom); | 1191 | exynos4_clk_register_finpll(); |
| 1062 | 1192 | ||
| 1063 | if (exynos4_soc == EXYNOS4210) { | 1193 | if (exynos4_soc == EXYNOS4210) { |
| 1064 | samsung_clk_register_mux(exynos4210_mux_early, | 1194 | samsung_clk_register_mux(exynos4210_mux_early, |
| @@ -1125,6 +1255,8 @@ static void __init exynos4_clk_init(struct device_node *np, | |||
| 1125 | samsung_clk_register_alias(exynos4_aliases, | 1255 | samsung_clk_register_alias(exynos4_aliases, |
| 1126 | ARRAY_SIZE(exynos4_aliases)); | 1256 | ARRAY_SIZE(exynos4_aliases)); |
| 1127 | 1257 | ||
| 1258 | exynos4_clk_sleep_init(); | ||
| 1259 | |||
| 1128 | pr_info("%s clocks: sclk_apll = %ld, sclk_mpll = %ld\n" | 1260 | pr_info("%s clocks: sclk_apll = %ld, sclk_mpll = %ld\n" |
| 1129 | "\tsclk_epll = %ld, sclk_vpll = %ld, arm_clk = %ld\n", | 1261 | "\tsclk_epll = %ld, sclk_vpll = %ld, arm_clk = %ld\n", |
| 1130 | exynos4_soc == EXYNOS4210 ? "Exynos4210" : "Exynos4x12", | 1262 | exynos4_soc == EXYNOS4210 ? "Exynos4210" : "Exynos4x12", |
| @@ -1136,12 +1268,12 @@ static void __init exynos4_clk_init(struct device_node *np, | |||
| 1136 | 1268 | ||
| 1137 | static void __init exynos4210_clk_init(struct device_node *np) | 1269 | static void __init exynos4210_clk_init(struct device_node *np) |
| 1138 | { | 1270 | { |
| 1139 | exynos4_clk_init(np, EXYNOS4210, NULL, exynos4_get_xom()); | 1271 | exynos4_clk_init(np, EXYNOS4210); |
| 1140 | } | 1272 | } |
| 1141 | CLK_OF_DECLARE(exynos4210_clk, "samsung,exynos4210-clock", exynos4210_clk_init); | 1273 | CLK_OF_DECLARE(exynos4210_clk, "samsung,exynos4210-clock", exynos4210_clk_init); |
| 1142 | 1274 | ||
| 1143 | static void __init exynos4412_clk_init(struct device_node *np) | 1275 | static void __init exynos4412_clk_init(struct device_node *np) |
| 1144 | { | 1276 | { |
| 1145 | exynos4_clk_init(np, EXYNOS4X12, NULL, exynos4_get_xom()); | 1277 | exynos4_clk_init(np, EXYNOS4X12); |
| 1146 | } | 1278 | } |
| 1147 | CLK_OF_DECLARE(exynos4412_clk, "samsung,exynos4412-clock", exynos4412_clk_init); | 1279 | CLK_OF_DECLARE(exynos4412_clk, "samsung,exynos4412-clock", exynos4412_clk_init); |
diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c index ff4beebe1f0b..e7ee4420da81 100644 --- a/drivers/clk/samsung/clk-exynos5250.c +++ b/drivers/clk/samsung/clk-exynos5250.c | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | #include <linux/clk-provider.h> | 16 | #include <linux/clk-provider.h> |
| 17 | #include <linux/of.h> | 17 | #include <linux/of.h> |
| 18 | #include <linux/of_address.h> | 18 | #include <linux/of_address.h> |
| 19 | #include <linux/syscore_ops.h> | ||
| 19 | 20 | ||
| 20 | #include "clk.h" | 21 | #include "clk.h" |
| 21 | 22 | ||
| @@ -85,6 +86,11 @@ enum exynos5250_plls { | |||
| 85 | nr_plls /* number of PLLs */ | 86 | nr_plls /* number of PLLs */ |
| 86 | }; | 87 | }; |
| 87 | 88 | ||
| 89 | static void __iomem *reg_base; | ||
| 90 | |||
| 91 | #ifdef CONFIG_PM_SLEEP | ||
| 92 | static struct samsung_clk_reg_dump *exynos5250_save; | ||
| 93 | |||
| 88 | /* | 94 | /* |
| 89 | * list of controller registers to be saved and restored during a | 95 | * list of controller registers to be saved and restored during a |
| 90 | * suspend/resume cycle. | 96 | * suspend/resume cycle. |
| @@ -137,6 +143,41 @@ static unsigned long exynos5250_clk_regs[] __initdata = { | |||
| 137 | GATE_IP_ACP, | 143 | GATE_IP_ACP, |
| 138 | }; | 144 | }; |
| 139 | 145 | ||
| 146 | static int exynos5250_clk_suspend(void) | ||
| 147 | { | ||
| 148 | samsung_clk_save(reg_base, exynos5250_save, | ||
| 149 | ARRAY_SIZE(exynos5250_clk_regs)); | ||
| 150 | |||
| 151 | return 0; | ||
| 152 | } | ||
| 153 | |||
| 154 | static void exynos5250_clk_resume(void) | ||
| 155 | { | ||
| 156 | samsung_clk_restore(reg_base, exynos5250_save, | ||
| 157 | ARRAY_SIZE(exynos5250_clk_regs)); | ||
| 158 | } | ||
| 159 | |||
| 160 | static struct syscore_ops exynos5250_clk_syscore_ops = { | ||
| 161 | .suspend = exynos5250_clk_suspend, | ||
| 162 | .resume = exynos5250_clk_resume, | ||
| 163 | }; | ||
| 164 | |||
| 165 | static void exynos5250_clk_sleep_init(void) | ||
| 166 | { | ||
| 167 | exynos5250_save = samsung_clk_alloc_reg_dump(exynos5250_clk_regs, | ||
| 168 | ARRAY_SIZE(exynos5250_clk_regs)); | ||
| 169 | if (!exynos5250_save) { | ||
| 170 | pr_warn("%s: failed to allocate sleep save data, no sleep support!\n", | ||
| 171 | __func__); | ||
| 172 | return; | ||
| 173 | } | ||
| 174 | |||
| 175 | register_syscore_ops(&exynos5250_clk_syscore_ops); | ||
| 176 | } | ||
| 177 | #else | ||
| 178 | static void exynos5250_clk_sleep_init(void) {} | ||
| 179 | #endif | ||
| 180 | |||
| 140 | /* list of all parent clock list */ | 181 | /* list of all parent clock list */ |
| 141 | PNAME(mout_apll_p) = { "fin_pll", "fout_apll", }; | 182 | PNAME(mout_apll_p) = { "fin_pll", "fout_apll", }; |
| 142 | PNAME(mout_cpu_p) = { "mout_apll", "mout_mpll", }; | 183 | PNAME(mout_cpu_p) = { "mout_apll", "mout_mpll", }; |
| @@ -645,8 +686,6 @@ static struct of_device_id ext_clk_match[] __initdata = { | |||
| 645 | /* register exynox5250 clocks */ | 686 | /* register exynox5250 clocks */ |
| 646 | static void __init exynos5250_clk_init(struct device_node *np) | 687 | static void __init exynos5250_clk_init(struct device_node *np) |
| 647 | { | 688 | { |
| 648 | void __iomem *reg_base; | ||
| 649 | |||
| 650 | if (np) { | 689 | if (np) { |
| 651 | reg_base = of_iomap(np, 0); | 690 | reg_base = of_iomap(np, 0); |
| 652 | if (!reg_base) | 691 | if (!reg_base) |
| @@ -655,9 +694,7 @@ static void __init exynos5250_clk_init(struct device_node *np) | |||
| 655 | panic("%s: unable to determine soc\n", __func__); | 694 | panic("%s: unable to determine soc\n", __func__); |
| 656 | } | 695 | } |
| 657 | 696 | ||
| 658 | samsung_clk_init(np, reg_base, CLK_NR_CLKS, | 697 | samsung_clk_init(np, reg_base, CLK_NR_CLKS); |
| 659 | exynos5250_clk_regs, ARRAY_SIZE(exynos5250_clk_regs), | ||
| 660 | NULL, 0); | ||
| 661 | samsung_clk_of_register_fixed_ext(exynos5250_fixed_rate_ext_clks, | 698 | samsung_clk_of_register_fixed_ext(exynos5250_fixed_rate_ext_clks, |
| 662 | ARRAY_SIZE(exynos5250_fixed_rate_ext_clks), | 699 | ARRAY_SIZE(exynos5250_fixed_rate_ext_clks), |
| 663 | ext_clk_match); | 700 | ext_clk_match); |
| @@ -685,6 +722,8 @@ static void __init exynos5250_clk_init(struct device_node *np) | |||
| 685 | samsung_clk_register_gate(exynos5250_gate_clks, | 722 | samsung_clk_register_gate(exynos5250_gate_clks, |
| 686 | ARRAY_SIZE(exynos5250_gate_clks)); | 723 | ARRAY_SIZE(exynos5250_gate_clks)); |
| 687 | 724 | ||
| 725 | exynos5250_clk_sleep_init(); | ||
| 726 | |||
| 688 | pr_info("Exynos5250: clock setup completed, armclk=%ld\n", | 727 | pr_info("Exynos5250: clock setup completed, armclk=%ld\n", |
| 689 | _get_rate("div_arm2")); | 728 | _get_rate("div_arm2")); |
| 690 | } | 729 | } |
diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index ab4f2f7d88ef..60b26819bed5 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | #include <linux/clk-provider.h> | 16 | #include <linux/clk-provider.h> |
| 17 | #include <linux/of.h> | 17 | #include <linux/of.h> |
| 18 | #include <linux/of_address.h> | 18 | #include <linux/of_address.h> |
| 19 | #include <linux/syscore_ops.h> | ||
| 19 | 20 | ||
| 20 | #include "clk.h" | 21 | #include "clk.h" |
| 21 | 22 | ||
| @@ -108,6 +109,11 @@ enum exynos5420_plls { | |||
| 108 | nr_plls /* number of PLLs */ | 109 | nr_plls /* number of PLLs */ |
| 109 | }; | 110 | }; |
| 110 | 111 | ||
| 112 | static void __iomem *reg_base; | ||
| 113 | |||
| 114 | #ifdef CONFIG_PM_SLEEP | ||
| 115 | static struct samsung_clk_reg_dump *exynos5420_save; | ||
| 116 | |||
| 111 | /* | 117 | /* |
| 112 | * list of controller registers to be saved and restored during a | 118 | * list of controller registers to be saved and restored during a |
| 113 | * suspend/resume cycle. | 119 | * suspend/resume cycle. |
| @@ -174,6 +180,41 @@ static unsigned long exynos5420_clk_regs[] __initdata = { | |||
| 174 | DIV_KFC0, | 180 | DIV_KFC0, |
| 175 | }; | 181 | }; |
| 176 | 182 | ||
| 183 | static int exynos5420_clk_suspend(void) | ||
| 184 | { | ||
| 185 | samsung_clk_save(reg_base, exynos5420_save, | ||
| 186 | ARRAY_SIZE(exynos5420_clk_regs)); | ||
| 187 | |||
| 188 | return 0; | ||
| 189 | } | ||
| 190 | |||
| 191 | static void exynos5420_clk_resume(void) | ||
| 192 | { | ||
| 193 | samsung_clk_restore(reg_base, exynos5420_save, | ||
| 194 | ARRAY_SIZE(exynos5420_clk_regs)); | ||
| 195 | } | ||
| 196 | |||
| 197 | static struct syscore_ops exynos5420_clk_syscore_ops = { | ||
| 198 | .suspend = exynos5420_clk_suspend, | ||
| 199 | .resume = exynos5420_clk_resume, | ||
| 200 | }; | ||
| 201 | |||
| 202 | static void exynos5420_clk_sleep_init(void) | ||
| 203 | { | ||
| 204 | exynos5420_save = samsung_clk_alloc_reg_dump(exynos5420_clk_regs, | ||
| 205 | ARRAY_SIZE(exynos5420_clk_regs)); | ||
| 206 | if (!exynos5420_save) { | ||
| 207 | pr_warn("%s: failed to allocate sleep save data, no sleep support!\n", | ||
| 208 | __func__); | ||
| 209 | return; | ||
| 210 | } | ||
| 211 | |||
| 212 | register_syscore_ops(&exynos5420_clk_syscore_ops); | ||
| 213 | } | ||
| 214 | #else | ||
| 215 | static void exynos5420_clk_sleep_init(void) {} | ||
| 216 | #endif | ||
| 217 | |||
| 177 | /* list of all parent clocks */ | 218 | /* list of all parent clocks */ |
| 178 | PNAME(mspll_cpu_p) = { "sclk_cpll", "sclk_dpll", | 219 | PNAME(mspll_cpu_p) = { "sclk_cpll", "sclk_dpll", |
| 179 | "sclk_mpll", "sclk_spll" }; | 220 | "sclk_mpll", "sclk_spll" }; |
| @@ -737,8 +778,6 @@ static struct of_device_id ext_clk_match[] __initdata = { | |||
| 737 | /* register exynos5420 clocks */ | 778 | /* register exynos5420 clocks */ |
| 738 | static void __init exynos5420_clk_init(struct device_node *np) | 779 | static void __init exynos5420_clk_init(struct device_node *np) |
| 739 | { | 780 | { |
| 740 | void __iomem *reg_base; | ||
| 741 | |||
| 742 | if (np) { | 781 | if (np) { |
| 743 | reg_base = of_iomap(np, 0); | 782 | reg_base = of_iomap(np, 0); |
| 744 | if (!reg_base) | 783 | if (!reg_base) |
| @@ -747,9 +786,7 @@ static void __init exynos5420_clk_init(struct device_node *np) | |||
| 747 | panic("%s: unable to determine soc\n", __func__); | 786 | panic("%s: unable to determine soc\n", __func__); |
| 748 | } | 787 | } |
| 749 | 788 | ||
| 750 | samsung_clk_init(np, reg_base, CLK_NR_CLKS, | 789 | samsung_clk_init(np, reg_base, CLK_NR_CLKS); |
| 751 | exynos5420_clk_regs, ARRAY_SIZE(exynos5420_clk_regs), | ||
| 752 | NULL, 0); | ||
| 753 | samsung_clk_of_register_fixed_ext(exynos5420_fixed_rate_ext_clks, | 790 | samsung_clk_of_register_fixed_ext(exynos5420_fixed_rate_ext_clks, |
| 754 | ARRAY_SIZE(exynos5420_fixed_rate_ext_clks), | 791 | ARRAY_SIZE(exynos5420_fixed_rate_ext_clks), |
| 755 | ext_clk_match); | 792 | ext_clk_match); |
| @@ -765,5 +802,7 @@ static void __init exynos5420_clk_init(struct device_node *np) | |||
| 765 | ARRAY_SIZE(exynos5420_div_clks)); | 802 | ARRAY_SIZE(exynos5420_div_clks)); |
| 766 | samsung_clk_register_gate(exynos5420_gate_clks, | 803 | samsung_clk_register_gate(exynos5420_gate_clks, |
| 767 | ARRAY_SIZE(exynos5420_gate_clks)); | 804 | ARRAY_SIZE(exynos5420_gate_clks)); |
| 805 | |||
| 806 | exynos5420_clk_sleep_init(); | ||
| 768 | } | 807 | } |
| 769 | CLK_OF_DECLARE(exynos5420_clk, "samsung,exynos5420-clock", exynos5420_clk_init); | 808 | CLK_OF_DECLARE(exynos5420_clk, "samsung,exynos5420-clock", exynos5420_clk_init); |
diff --git a/drivers/clk/samsung/clk-exynos5440.c b/drivers/clk/samsung/clk-exynos5440.c index cbc15b56891d..2bfad5a993d0 100644 --- a/drivers/clk/samsung/clk-exynos5440.c +++ b/drivers/clk/samsung/clk-exynos5440.c | |||
| @@ -101,7 +101,7 @@ static void __init exynos5440_clk_init(struct device_node *np) | |||
| 101 | return; | 101 | return; |
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | samsung_clk_init(np, reg_base, CLK_NR_CLKS, NULL, 0, NULL, 0); | 104 | samsung_clk_init(np, reg_base, CLK_NR_CLKS); |
| 105 | samsung_clk_of_register_fixed_ext(exynos5440_fixed_rate_ext_clks, | 105 | samsung_clk_of_register_fixed_ext(exynos5440_fixed_rate_ext_clks, |
| 106 | ARRAY_SIZE(exynos5440_fixed_rate_ext_clks), ext_clk_match); | 106 | ARRAY_SIZE(exynos5440_fixed_rate_ext_clks), ext_clk_match); |
| 107 | 107 | ||
diff --git a/drivers/clk/samsung/clk-s3c64xx.c b/drivers/clk/samsung/clk-s3c64xx.c index 8e27aee6887e..8bda658137a8 100644 --- a/drivers/clk/samsung/clk-s3c64xx.c +++ b/drivers/clk/samsung/clk-s3c64xx.c | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include <linux/clk-provider.h> | 13 | #include <linux/clk-provider.h> |
| 14 | #include <linux/of.h> | 14 | #include <linux/of.h> |
| 15 | #include <linux/of_address.h> | 15 | #include <linux/of_address.h> |
| 16 | #include <linux/syscore_ops.h> | ||
| 16 | 17 | ||
| 17 | #include <dt-bindings/clock/samsung,s3c64xx-clock.h> | 18 | #include <dt-bindings/clock/samsung,s3c64xx-clock.h> |
| 18 | 19 | ||
| @@ -61,6 +62,13 @@ enum s3c64xx_plls { | |||
| 61 | apll, mpll, epll, | 62 | apll, mpll, epll, |
| 62 | }; | 63 | }; |
| 63 | 64 | ||
| 65 | static void __iomem *reg_base; | ||
| 66 | static bool is_s3c6400; | ||
| 67 | |||
| 68 | #ifdef CONFIG_PM_SLEEP | ||
| 69 | static struct samsung_clk_reg_dump *s3c64xx_save_common; | ||
| 70 | static struct samsung_clk_reg_dump *s3c64xx_save_soc; | ||
| 71 | |||
| 64 | /* | 72 | /* |
| 65 | * List of controller registers to be saved and restored during | 73 | * List of controller registers to be saved and restored during |
| 66 | * a suspend/resume cycle. | 74 | * a suspend/resume cycle. |
| @@ -87,6 +95,60 @@ static unsigned long s3c6410_clk_regs[] __initdata = { | |||
| 87 | MEM0_GATE, | 95 | MEM0_GATE, |
| 88 | }; | 96 | }; |
| 89 | 97 | ||
| 98 | static int s3c64xx_clk_suspend(void) | ||
| 99 | { | ||
| 100 | samsung_clk_save(reg_base, s3c64xx_save_common, | ||
| 101 | ARRAY_SIZE(s3c64xx_clk_regs)); | ||
| 102 | |||
| 103 | if (!is_s3c6400) | ||
| 104 | samsung_clk_save(reg_base, s3c64xx_save_soc, | ||
| 105 | ARRAY_SIZE(s3c6410_clk_regs)); | ||
| 106 | |||
| 107 | return 0; | ||
| 108 | } | ||
| 109 | |||
| 110 | static void s3c64xx_clk_resume(void) | ||
| 111 | { | ||
| 112 | samsung_clk_restore(reg_base, s3c64xx_save_common, | ||
| 113 | ARRAY_SIZE(s3c64xx_clk_regs)); | ||
| 114 | |||
| 115 | if (!is_s3c6400) | ||
| 116 | samsung_clk_restore(reg_base, s3c64xx_save_soc, | ||
| 117 | ARRAY_SIZE(s3c6410_clk_regs)); | ||
| 118 | } | ||
| 119 | |||
| 120 | static struct syscore_ops s3c64xx_clk_syscore_ops = { | ||
| 121 | .suspend = s3c64xx_clk_suspend, | ||
| 122 | .resume = s3c64xx_clk_resume, | ||
| 123 | }; | ||
| 124 | |||
| 125 | static void s3c64xx_clk_sleep_init(void) | ||
| 126 | { | ||
| 127 | s3c64xx_save_common = samsung_clk_alloc_reg_dump(s3c64xx_clk_regs, | ||
| 128 | ARRAY_SIZE(s3c64xx_clk_regs)); | ||
| 129 | if (!s3c64xx_save_common) | ||
| 130 | goto err_warn; | ||
| 131 | |||
| 132 | if (!is_s3c6400) { | ||
| 133 | s3c64xx_save_soc = samsung_clk_alloc_reg_dump(s3c6410_clk_regs, | ||
| 134 | ARRAY_SIZE(s3c6410_clk_regs)); | ||
| 135 | if (!s3c64xx_save_soc) | ||
| 136 | goto err_soc; | ||
| 137 | } | ||
| 138 | |||
| 139 | register_syscore_ops(&s3c64xx_clk_syscore_ops); | ||
| 140 | return; | ||
| 141 | |||
| 142 | err_soc: | ||
| 143 | kfree(s3c64xx_save_common); | ||
| 144 | err_warn: | ||
| 145 | pr_warn("%s: failed to allocate sleep save data, no sleep support!\n", | ||
| 146 | __func__); | ||
| 147 | } | ||
| 148 | #else | ||
| 149 | static void s3c64xx_clk_sleep_init(void) {} | ||
| 150 | #endif | ||
| 151 | |||
| 90 | /* List of parent clocks common for all S3C64xx SoCs. */ | 152 | /* List of parent clocks common for all S3C64xx SoCs. */ |
| 91 | PNAME(spi_mmc_p) = { "mout_epll", "dout_mpll", "fin_pll", "clk27m" }; | 153 | PNAME(spi_mmc_p) = { "mout_epll", "dout_mpll", "fin_pll", "clk27m" }; |
| 92 | PNAME(uart_p) = { "mout_epll", "dout_mpll" }; | 154 | PNAME(uart_p) = { "mout_epll", "dout_mpll" }; |
| @@ -391,11 +453,11 @@ static void __init s3c64xx_clk_register_fixed_ext(unsigned long fin_pll_f, | |||
| 391 | 453 | ||
| 392 | /* Register s3c64xx clocks. */ | 454 | /* Register s3c64xx clocks. */ |
| 393 | void __init s3c64xx_clk_init(struct device_node *np, unsigned long xtal_f, | 455 | void __init s3c64xx_clk_init(struct device_node *np, unsigned long xtal_f, |
| 394 | unsigned long xusbxti_f, bool is_s3c6400, | 456 | unsigned long xusbxti_f, bool s3c6400, |
| 395 | void __iomem *reg_base) | 457 | void __iomem *base) |
| 396 | { | 458 | { |
| 397 | unsigned long *soc_regs = NULL; | 459 | reg_base = base; |
| 398 | unsigned long nr_soc_regs = 0; | 460 | is_s3c6400 = s3c6400; |
| 399 | 461 | ||
| 400 | if (np) { | 462 | if (np) { |
| 401 | reg_base = of_iomap(np, 0); | 463 | reg_base = of_iomap(np, 0); |
| @@ -403,13 +465,7 @@ void __init s3c64xx_clk_init(struct device_node *np, unsigned long xtal_f, | |||
| 403 | panic("%s: failed to map registers\n", __func__); | 465 | panic("%s: failed to map registers\n", __func__); |
| 404 | } | 466 | } |
| 405 | 467 | ||
| 406 | if (!is_s3c6400) { | 468 | samsung_clk_init(np, reg_base, NR_CLKS); |
| 407 | soc_regs = s3c6410_clk_regs; | ||
| 408 | nr_soc_regs = ARRAY_SIZE(s3c6410_clk_regs); | ||
| 409 | } | ||
| 410 | |||
| 411 | samsung_clk_init(np, reg_base, NR_CLKS, s3c64xx_clk_regs, | ||
| 412 | ARRAY_SIZE(s3c64xx_clk_regs), soc_regs, nr_soc_regs); | ||
| 413 | 469 | ||
| 414 | /* Register external clocks. */ | 470 | /* Register external clocks. */ |
| 415 | if (!np) | 471 | if (!np) |
| @@ -452,6 +508,7 @@ void __init s3c64xx_clk_init(struct device_node *np, unsigned long xtal_f, | |||
| 452 | 508 | ||
| 453 | samsung_clk_register_alias(s3c64xx_clock_aliases, | 509 | samsung_clk_register_alias(s3c64xx_clock_aliases, |
| 454 | ARRAY_SIZE(s3c64xx_clock_aliases)); | 510 | ARRAY_SIZE(s3c64xx_clock_aliases)); |
| 511 | s3c64xx_clk_sleep_init(); | ||
| 455 | 512 | ||
| 456 | pr_info("%s clocks: apll = %lu, mpll = %lu\n" | 513 | pr_info("%s clocks: apll = %lu, mpll = %lu\n" |
| 457 | "\tepll = %lu, arm_clk = %lu\n", | 514 | "\tepll = %lu, arm_clk = %lu\n", |
diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c index f503f32e2f80..91bec3ebdc8f 100644 --- a/drivers/clk/samsung/clk.c +++ b/drivers/clk/samsung/clk.c | |||
| @@ -21,64 +21,45 @@ static void __iomem *reg_base; | |||
| 21 | static struct clk_onecell_data clk_data; | 21 | static struct clk_onecell_data clk_data; |
| 22 | #endif | 22 | #endif |
| 23 | 23 | ||
| 24 | #ifdef CONFIG_PM_SLEEP | 24 | void samsung_clk_save(void __iomem *base, |
| 25 | static struct samsung_clk_reg_dump *reg_dump; | 25 | struct samsung_clk_reg_dump *rd, |
| 26 | static unsigned long nr_reg_dump; | 26 | unsigned int num_regs) |
| 27 | |||
| 28 | static int samsung_clk_suspend(void) | ||
| 29 | { | 27 | { |
| 30 | struct samsung_clk_reg_dump *rd = reg_dump; | 28 | for (; num_regs > 0; --num_regs, ++rd) |
| 31 | unsigned long i; | 29 | rd->value = readl(base + rd->offset); |
| 32 | 30 | } | |
| 33 | for (i = 0; i < nr_reg_dump; i++, rd++) | ||
| 34 | rd->value = __raw_readl(reg_base + rd->offset); | ||
| 35 | 31 | ||
| 36 | return 0; | 32 | void samsung_clk_restore(void __iomem *base, |
| 33 | const struct samsung_clk_reg_dump *rd, | ||
| 34 | unsigned int num_regs) | ||
| 35 | { | ||
| 36 | for (; num_regs > 0; --num_regs, ++rd) | ||
| 37 | writel(rd->value, base + rd->offset); | ||
| 37 | } | 38 | } |
| 38 | 39 | ||
| 39 | static void samsung_clk_resume(void) | 40 | struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump( |
| 41 | const unsigned long *rdump, | ||
| 42 | unsigned long nr_rdump) | ||
| 40 | { | 43 | { |
| 41 | struct samsung_clk_reg_dump *rd = reg_dump; | 44 | struct samsung_clk_reg_dump *rd; |
| 42 | unsigned long i; | 45 | unsigned int i; |
| 43 | 46 | ||
| 44 | for (i = 0; i < nr_reg_dump; i++, rd++) | 47 | rd = kcalloc(nr_rdump, sizeof(*rd), GFP_KERNEL); |
| 45 | __raw_writel(rd->value, reg_base + rd->offset); | 48 | if (!rd) |
| 46 | } | 49 | return NULL; |
| 50 | |||
| 51 | for (i = 0; i < nr_rdump; ++i) | ||
| 52 | rd[i].offset = rdump[i]; | ||
| 47 | 53 | ||
| 48 | static struct syscore_ops samsung_clk_syscore_ops = { | 54 | return rd; |
| 49 | .suspend = samsung_clk_suspend, | 55 | } |
| 50 | .resume = samsung_clk_resume, | ||
| 51 | }; | ||
| 52 | #endif /* CONFIG_PM_SLEEP */ | ||
| 53 | 56 | ||
| 54 | /* setup the essentials required to support clock lookup using ccf */ | 57 | /* setup the essentials required to support clock lookup using ccf */ |
| 55 | void __init samsung_clk_init(struct device_node *np, void __iomem *base, | 58 | void __init samsung_clk_init(struct device_node *np, void __iomem *base, |
| 56 | unsigned long nr_clks, unsigned long *rdump, | 59 | unsigned long nr_clks) |
| 57 | unsigned long nr_rdump, unsigned long *soc_rdump, | ||
| 58 | unsigned long nr_soc_rdump) | ||
| 59 | { | 60 | { |
| 60 | reg_base = base; | 61 | reg_base = base; |
| 61 | 62 | ||
| 62 | #ifdef CONFIG_PM_SLEEP | ||
| 63 | if (rdump && nr_rdump) { | ||
| 64 | unsigned int idx; | ||
| 65 | reg_dump = kzalloc(sizeof(struct samsung_clk_reg_dump) | ||
| 66 | * (nr_rdump + nr_soc_rdump), GFP_KERNEL); | ||
| 67 | if (!reg_dump) { | ||
| 68 | pr_err("%s: memory alloc for register dump failed\n", | ||
| 69 | __func__); | ||
| 70 | return; | ||
| 71 | } | ||
| 72 | |||
| 73 | for (idx = 0; idx < nr_rdump; idx++) | ||
| 74 | reg_dump[idx].offset = rdump[idx]; | ||
| 75 | for (idx = 0; idx < nr_soc_rdump; idx++) | ||
| 76 | reg_dump[nr_rdump + idx].offset = soc_rdump[idx]; | ||
| 77 | nr_reg_dump = nr_rdump + nr_soc_rdump; | ||
| 78 | register_syscore_ops(&samsung_clk_syscore_ops); | ||
| 79 | } | ||
| 80 | #endif | ||
| 81 | |||
| 82 | clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL); | 63 | clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL); |
| 83 | if (!clk_table) | 64 | if (!clk_table) |
| 84 | panic("could not allocate clock lookup table\n"); | 65 | panic("could not allocate clock lookup table\n"); |
diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h index 31b4174e7a5b..c7141ba826e0 100644 --- a/drivers/clk/samsung/clk.h +++ b/drivers/clk/samsung/clk.h | |||
| @@ -313,9 +313,7 @@ struct samsung_pll_clock { | |||
| 313 | _lock, _con, _rtable, _alias) | 313 | _lock, _con, _rtable, _alias) |
| 314 | 314 | ||
| 315 | extern void __init samsung_clk_init(struct device_node *np, void __iomem *base, | 315 | extern void __init samsung_clk_init(struct device_node *np, void __iomem *base, |
| 316 | unsigned long nr_clks, unsigned long *rdump, | 316 | unsigned long nr_clks); |
| 317 | unsigned long nr_rdump, unsigned long *soc_rdump, | ||
| 318 | unsigned long nr_soc_rdump); | ||
| 319 | extern void __init samsung_clk_of_register_fixed_ext( | 317 | extern void __init samsung_clk_of_register_fixed_ext( |
| 320 | struct samsung_fixed_rate_clock *fixed_rate_clk, | 318 | struct samsung_fixed_rate_clock *fixed_rate_clk, |
| 321 | unsigned int nr_fixed_rate_clk, | 319 | unsigned int nr_fixed_rate_clk, |
| @@ -340,4 +338,14 @@ extern void __init samsung_clk_register_pll(struct samsung_pll_clock *pll_list, | |||
| 340 | 338 | ||
| 341 | extern unsigned long _get_rate(const char *clk_name); | 339 | extern unsigned long _get_rate(const char *clk_name); |
| 342 | 340 | ||
| 341 | extern void samsung_clk_save(void __iomem *base, | ||
| 342 | struct samsung_clk_reg_dump *rd, | ||
| 343 | unsigned int num_regs); | ||
| 344 | extern void samsung_clk_restore(void __iomem *base, | ||
| 345 | const struct samsung_clk_reg_dump *rd, | ||
| 346 | unsigned int num_regs); | ||
| 347 | extern struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump( | ||
| 348 | const unsigned long *rdump, | ||
| 349 | unsigned long nr_rdump); | ||
| 350 | |||
| 343 | #endif /* __SAMSUNG_CLK_H */ | 351 | #endif /* __SAMSUNG_CLK_H */ |
