root/drivers/cpufreq/s5pv210-cpufreq.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. s5pv210_set_refresh
  2. s5pv210_target
  3. check_mem_type
  4. s5pv210_cpu_init
  5. s5pv210_cpufreq_reboot_notifier_event
  6. s5pv210_cpufreq_probe

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Copyright (c) 2010 Samsung Electronics Co., Ltd.
   4  *              http://www.samsung.com
   5  *
   6  * CPU frequency scaling for S5PC110/S5PV210
   7 */
   8 
   9 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  10 
  11 #include <linux/types.h>
  12 #include <linux/kernel.h>
  13 #include <linux/init.h>
  14 #include <linux/err.h>
  15 #include <linux/clk.h>
  16 #include <linux/io.h>
  17 #include <linux/cpufreq.h>
  18 #include <linux/of.h>
  19 #include <linux/of_address.h>
  20 #include <linux/platform_device.h>
  21 #include <linux/reboot.h>
  22 #include <linux/regulator/consumer.h>
  23 
  24 static void __iomem *clk_base;
  25 static void __iomem *dmc_base[2];
  26 
  27 #define S5P_CLKREG(x)           (clk_base + (x))
  28 
  29 #define S5P_APLL_LOCK           S5P_CLKREG(0x00)
  30 #define S5P_APLL_CON            S5P_CLKREG(0x100)
  31 #define S5P_CLK_SRC0            S5P_CLKREG(0x200)
  32 #define S5P_CLK_SRC2            S5P_CLKREG(0x208)
  33 #define S5P_CLK_DIV0            S5P_CLKREG(0x300)
  34 #define S5P_CLK_DIV2            S5P_CLKREG(0x308)
  35 #define S5P_CLK_DIV6            S5P_CLKREG(0x318)
  36 #define S5P_CLKDIV_STAT0        S5P_CLKREG(0x1000)
  37 #define S5P_CLKDIV_STAT1        S5P_CLKREG(0x1004)
  38 #define S5P_CLKMUX_STAT0        S5P_CLKREG(0x1100)
  39 #define S5P_CLKMUX_STAT1        S5P_CLKREG(0x1104)
  40 
  41 #define S5P_ARM_MCS_CON         S5P_CLKREG(0x6100)
  42 
  43 /* CLKSRC0 */
  44 #define S5P_CLKSRC0_MUX200_SHIFT        (16)
  45 #define S5P_CLKSRC0_MUX200_MASK         (0x1 << S5P_CLKSRC0_MUX200_SHIFT)
  46 #define S5P_CLKSRC0_MUX166_MASK         (0x1<<20)
  47 #define S5P_CLKSRC0_MUX133_MASK         (0x1<<24)
  48 
  49 /* CLKSRC2 */
  50 #define S5P_CLKSRC2_G3D_SHIFT           (0)
  51 #define S5P_CLKSRC2_G3D_MASK            (0x3 << S5P_CLKSRC2_G3D_SHIFT)
  52 #define S5P_CLKSRC2_MFC_SHIFT           (4)
  53 #define S5P_CLKSRC2_MFC_MASK            (0x3 << S5P_CLKSRC2_MFC_SHIFT)
  54 
  55 /* CLKDIV0 */
  56 #define S5P_CLKDIV0_APLL_SHIFT          (0)
  57 #define S5P_CLKDIV0_APLL_MASK           (0x7 << S5P_CLKDIV0_APLL_SHIFT)
  58 #define S5P_CLKDIV0_A2M_SHIFT           (4)
  59 #define S5P_CLKDIV0_A2M_MASK            (0x7 << S5P_CLKDIV0_A2M_SHIFT)
  60 #define S5P_CLKDIV0_HCLK200_SHIFT       (8)
  61 #define S5P_CLKDIV0_HCLK200_MASK        (0x7 << S5P_CLKDIV0_HCLK200_SHIFT)
  62 #define S5P_CLKDIV0_PCLK100_SHIFT       (12)
  63 #define S5P_CLKDIV0_PCLK100_MASK        (0x7 << S5P_CLKDIV0_PCLK100_SHIFT)
  64 #define S5P_CLKDIV0_HCLK166_SHIFT       (16)
  65 #define S5P_CLKDIV0_HCLK166_MASK        (0xF << S5P_CLKDIV0_HCLK166_SHIFT)
  66 #define S5P_CLKDIV0_PCLK83_SHIFT        (20)
  67 #define S5P_CLKDIV0_PCLK83_MASK         (0x7 << S5P_CLKDIV0_PCLK83_SHIFT)
  68 #define S5P_CLKDIV0_HCLK133_SHIFT       (24)
  69 #define S5P_CLKDIV0_HCLK133_MASK        (0xF << S5P_CLKDIV0_HCLK133_SHIFT)
  70 #define S5P_CLKDIV0_PCLK66_SHIFT        (28)
  71 #define S5P_CLKDIV0_PCLK66_MASK         (0x7 << S5P_CLKDIV0_PCLK66_SHIFT)
  72 
  73 /* CLKDIV2 */
  74 #define S5P_CLKDIV2_G3D_SHIFT           (0)
  75 #define S5P_CLKDIV2_G3D_MASK            (0xF << S5P_CLKDIV2_G3D_SHIFT)
  76 #define S5P_CLKDIV2_MFC_SHIFT           (4)
  77 #define S5P_CLKDIV2_MFC_MASK            (0xF << S5P_CLKDIV2_MFC_SHIFT)
  78 
  79 /* CLKDIV6 */
  80 #define S5P_CLKDIV6_ONEDRAM_SHIFT       (28)
  81 #define S5P_CLKDIV6_ONEDRAM_MASK        (0xF << S5P_CLKDIV6_ONEDRAM_SHIFT)
  82 
  83 static struct clk *dmc0_clk;
  84 static struct clk *dmc1_clk;
  85 static DEFINE_MUTEX(set_freq_lock);
  86 
  87 /* APLL M,P,S values for 1G/800Mhz */
  88 #define APLL_VAL_1000   ((1 << 31) | (125 << 16) | (3 << 8) | 1)
  89 #define APLL_VAL_800    ((1 << 31) | (100 << 16) | (3 << 8) | 1)
  90 
  91 /* Use 800MHz when entering sleep mode */
  92 #define SLEEP_FREQ      (800 * 1000)
  93 
  94 /* Tracks if cpu freqency can be updated anymore */
  95 static bool no_cpufreq_access;
  96 
  97 /*
  98  * DRAM configurations to calculate refresh counter for changing
  99  * frequency of memory.
 100  */
 101 struct dram_conf {
 102         unsigned long freq;     /* HZ */
 103         unsigned long refresh;  /* DRAM refresh counter * 1000 */
 104 };
 105 
 106 /* DRAM configuration (DMC0 and DMC1) */
 107 static struct dram_conf s5pv210_dram_conf[2];
 108 
 109 enum perf_level {
 110         L0, L1, L2, L3, L4,
 111 };
 112 
 113 enum s5pv210_mem_type {
 114         LPDDR   = 0x1,
 115         LPDDR2  = 0x2,
 116         DDR2    = 0x4,
 117 };
 118 
 119 enum s5pv210_dmc_port {
 120         DMC0 = 0,
 121         DMC1,
 122 };
 123 
 124 static struct cpufreq_frequency_table s5pv210_freq_table[] = {
 125         {0, L0, 1000*1000},
 126         {0, L1, 800*1000},
 127         {0, L2, 400*1000},
 128         {0, L3, 200*1000},
 129         {0, L4, 100*1000},
 130         {0, 0, CPUFREQ_TABLE_END},
 131 };
 132 
 133 static struct regulator *arm_regulator;
 134 static struct regulator *int_regulator;
 135 
 136 struct s5pv210_dvs_conf {
 137         int arm_volt;   /* uV */
 138         int int_volt;   /* uV */
 139 };
 140 
 141 static const int arm_volt_max = 1350000;
 142 static const int int_volt_max = 1250000;
 143 
 144 static struct s5pv210_dvs_conf dvs_conf[] = {
 145         [L0] = {
 146                 .arm_volt       = 1250000,
 147                 .int_volt       = 1100000,
 148         },
 149         [L1] = {
 150                 .arm_volt       = 1200000,
 151                 .int_volt       = 1100000,
 152         },
 153         [L2] = {
 154                 .arm_volt       = 1050000,
 155                 .int_volt       = 1100000,
 156         },
 157         [L3] = {
 158                 .arm_volt       = 950000,
 159                 .int_volt       = 1100000,
 160         },
 161         [L4] = {
 162                 .arm_volt       = 950000,
 163                 .int_volt       = 1000000,
 164         },
 165 };
 166 
 167 static u32 clkdiv_val[5][11] = {
 168         /*
 169          * Clock divider value for following
 170          * { APLL, A2M, HCLK_MSYS, PCLK_MSYS,
 171          *   HCLK_DSYS, PCLK_DSYS, HCLK_PSYS, PCLK_PSYS,
 172          *   ONEDRAM, MFC, G3D }
 173          */
 174 
 175         /* L0 : [1000/200/100][166/83][133/66][200/200] */
 176         {0, 4, 4, 1, 3, 1, 4, 1, 3, 0, 0},
 177 
 178         /* L1 : [800/200/100][166/83][133/66][200/200] */
 179         {0, 3, 3, 1, 3, 1, 4, 1, 3, 0, 0},
 180 
 181         /* L2 : [400/200/100][166/83][133/66][200/200] */
 182         {1, 3, 1, 1, 3, 1, 4, 1, 3, 0, 0},
 183 
 184         /* L3 : [200/200/100][166/83][133/66][200/200] */
 185         {3, 3, 1, 1, 3, 1, 4, 1, 3, 0, 0},
 186 
 187         /* L4 : [100/100/100][83/83][66/66][100/100] */
 188         {7, 7, 0, 0, 7, 0, 9, 0, 7, 0, 0},
 189 };
 190 
 191 /*
 192  * This function set DRAM refresh counter
 193  * accoriding to operating frequency of DRAM
 194  * ch: DMC port number 0 or 1
 195  * freq: Operating frequency of DRAM(KHz)
 196  */
 197 static void s5pv210_set_refresh(enum s5pv210_dmc_port ch, unsigned long freq)
 198 {
 199         unsigned long tmp, tmp1;
 200         void __iomem *reg = NULL;
 201 
 202         if (ch == DMC0) {
 203                 reg = (dmc_base[0] + 0x30);
 204         } else if (ch == DMC1) {
 205                 reg = (dmc_base[1] + 0x30);
 206         } else {
 207                 pr_err("Cannot find DMC port\n");
 208                 return;
 209         }
 210 
 211         /* Find current DRAM frequency */
 212         tmp = s5pv210_dram_conf[ch].freq;
 213 
 214         tmp /= freq;
 215 
 216         tmp1 = s5pv210_dram_conf[ch].refresh;
 217 
 218         tmp1 /= tmp;
 219 
 220         writel_relaxed(tmp1, reg);
 221 }
 222 
 223 static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index)
 224 {
 225         unsigned long reg;
 226         unsigned int priv_index;
 227         unsigned int pll_changing = 0;
 228         unsigned int bus_speed_changing = 0;
 229         unsigned int old_freq, new_freq;
 230         int arm_volt, int_volt;
 231         int ret = 0;
 232 
 233         mutex_lock(&set_freq_lock);
 234 
 235         if (no_cpufreq_access) {
 236                 pr_err("Denied access to %s as it is disabled temporarily\n",
 237                        __func__);
 238                 ret = -EINVAL;
 239                 goto exit;
 240         }
 241 
 242         old_freq = policy->cur;
 243         new_freq = s5pv210_freq_table[index].frequency;
 244 
 245         /* Finding current running level index */
 246         priv_index = cpufreq_table_find_index_h(policy, old_freq);
 247 
 248         arm_volt = dvs_conf[index].arm_volt;
 249         int_volt = dvs_conf[index].int_volt;
 250 
 251         if (new_freq > old_freq) {
 252                 ret = regulator_set_voltage(arm_regulator,
 253                                 arm_volt, arm_volt_max);
 254                 if (ret)
 255                         goto exit;
 256 
 257                 ret = regulator_set_voltage(int_regulator,
 258                                 int_volt, int_volt_max);
 259                 if (ret)
 260                         goto exit;
 261         }
 262 
 263         /* Check if there need to change PLL */
 264         if ((index == L0) || (priv_index == L0))
 265                 pll_changing = 1;
 266 
 267         /* Check if there need to change System bus clock */
 268         if ((index == L4) || (priv_index == L4))
 269                 bus_speed_changing = 1;
 270 
 271         if (bus_speed_changing) {
 272                 /*
 273                  * Reconfigure DRAM refresh counter value for minimum
 274                  * temporary clock while changing divider.
 275                  * expected clock is 83Mhz : 7.8usec/(1/83Mhz) = 0x287
 276                  */
 277                 if (pll_changing)
 278                         s5pv210_set_refresh(DMC1, 83000);
 279                 else
 280                         s5pv210_set_refresh(DMC1, 100000);
 281 
 282                 s5pv210_set_refresh(DMC0, 83000);
 283         }
 284 
 285         /*
 286          * APLL should be changed in this level
 287          * APLL -> MPLL(for stable transition) -> APLL
 288          * Some clock source's clock API are not prepared.
 289          * Do not use clock API in below code.
 290          */
 291         if (pll_changing) {
 292                 /*
 293                  * 1. Temporary Change divider for MFC and G3D
 294                  * SCLKA2M(200/1=200)->(200/4=50)Mhz
 295                  */
 296                 reg = readl_relaxed(S5P_CLK_DIV2);
 297                 reg &= ~(S5P_CLKDIV2_G3D_MASK | S5P_CLKDIV2_MFC_MASK);
 298                 reg |= (3 << S5P_CLKDIV2_G3D_SHIFT) |
 299                         (3 << S5P_CLKDIV2_MFC_SHIFT);
 300                 writel_relaxed(reg, S5P_CLK_DIV2);
 301 
 302                 /* For MFC, G3D dividing */
 303                 do {
 304                         reg = readl_relaxed(S5P_CLKDIV_STAT0);
 305                 } while (reg & ((1 << 16) | (1 << 17)));
 306 
 307                 /*
 308                  * 2. Change SCLKA2M(200Mhz)to SCLKMPLL in MFC_MUX, G3D MUX
 309                  * (200/4=50)->(667/4=166)Mhz
 310                  */
 311                 reg = readl_relaxed(S5P_CLK_SRC2);
 312                 reg &= ~(S5P_CLKSRC2_G3D_MASK | S5P_CLKSRC2_MFC_MASK);
 313                 reg |= (1 << S5P_CLKSRC2_G3D_SHIFT) |
 314                         (1 << S5P_CLKSRC2_MFC_SHIFT);
 315                 writel_relaxed(reg, S5P_CLK_SRC2);
 316 
 317                 do {
 318                         reg = readl_relaxed(S5P_CLKMUX_STAT1);
 319                 } while (reg & ((1 << 7) | (1 << 3)));
 320 
 321                 /*
 322                  * 3. DMC1 refresh count for 133Mhz if (index == L4) is
 323                  * true refresh counter is already programed in upper
 324                  * code. 0x287@83Mhz
 325                  */
 326                 if (!bus_speed_changing)
 327                         s5pv210_set_refresh(DMC1, 133000);
 328 
 329                 /* 4. SCLKAPLL -> SCLKMPLL */
 330                 reg = readl_relaxed(S5P_CLK_SRC0);
 331                 reg &= ~(S5P_CLKSRC0_MUX200_MASK);
 332                 reg |= (0x1 << S5P_CLKSRC0_MUX200_SHIFT);
 333                 writel_relaxed(reg, S5P_CLK_SRC0);
 334 
 335                 do {
 336                         reg = readl_relaxed(S5P_CLKMUX_STAT0);
 337                 } while (reg & (0x1 << 18));
 338 
 339         }
 340 
 341         /* Change divider */
 342         reg = readl_relaxed(S5P_CLK_DIV0);
 343 
 344         reg &= ~(S5P_CLKDIV0_APLL_MASK | S5P_CLKDIV0_A2M_MASK |
 345                 S5P_CLKDIV0_HCLK200_MASK | S5P_CLKDIV0_PCLK100_MASK |
 346                 S5P_CLKDIV0_HCLK166_MASK | S5P_CLKDIV0_PCLK83_MASK |
 347                 S5P_CLKDIV0_HCLK133_MASK | S5P_CLKDIV0_PCLK66_MASK);
 348 
 349         reg |= ((clkdiv_val[index][0] << S5P_CLKDIV0_APLL_SHIFT) |
 350                 (clkdiv_val[index][1] << S5P_CLKDIV0_A2M_SHIFT) |
 351                 (clkdiv_val[index][2] << S5P_CLKDIV0_HCLK200_SHIFT) |
 352                 (clkdiv_val[index][3] << S5P_CLKDIV0_PCLK100_SHIFT) |
 353                 (clkdiv_val[index][4] << S5P_CLKDIV0_HCLK166_SHIFT) |
 354                 (clkdiv_val[index][5] << S5P_CLKDIV0_PCLK83_SHIFT) |
 355                 (clkdiv_val[index][6] << S5P_CLKDIV0_HCLK133_SHIFT) |
 356                 (clkdiv_val[index][7] << S5P_CLKDIV0_PCLK66_SHIFT));
 357 
 358         writel_relaxed(reg, S5P_CLK_DIV0);
 359 
 360         do {
 361                 reg = readl_relaxed(S5P_CLKDIV_STAT0);
 362         } while (reg & 0xff);
 363 
 364         /* ARM MCS value changed */
 365         reg = readl_relaxed(S5P_ARM_MCS_CON);
 366         reg &= ~0x3;
 367         if (index >= L3)
 368                 reg |= 0x3;
 369         else
 370                 reg |= 0x1;
 371 
 372         writel_relaxed(reg, S5P_ARM_MCS_CON);
 373 
 374         if (pll_changing) {
 375                 /* 5. Set Lock time = 30us*24Mhz = 0x2cf */
 376                 writel_relaxed(0x2cf, S5P_APLL_LOCK);
 377 
 378                 /*
 379                  * 6. Turn on APLL
 380                  * 6-1. Set PMS values
 381                  * 6-2. Wait untile the PLL is locked
 382                  */
 383                 if (index == L0)
 384                         writel_relaxed(APLL_VAL_1000, S5P_APLL_CON);
 385                 else
 386                         writel_relaxed(APLL_VAL_800, S5P_APLL_CON);
 387 
 388                 do {
 389                         reg = readl_relaxed(S5P_APLL_CON);
 390                 } while (!(reg & (0x1 << 29)));
 391 
 392                 /*
 393                  * 7. Change souce clock from SCLKMPLL(667Mhz)
 394                  * to SCLKA2M(200Mhz) in MFC_MUX and G3D MUX
 395                  * (667/4=166)->(200/4=50)Mhz
 396                  */
 397                 reg = readl_relaxed(S5P_CLK_SRC2);
 398                 reg &= ~(S5P_CLKSRC2_G3D_MASK | S5P_CLKSRC2_MFC_MASK);
 399                 reg |= (0 << S5P_CLKSRC2_G3D_SHIFT) |
 400                         (0 << S5P_CLKSRC2_MFC_SHIFT);
 401                 writel_relaxed(reg, S5P_CLK_SRC2);
 402 
 403                 do {
 404                         reg = readl_relaxed(S5P_CLKMUX_STAT1);
 405                 } while (reg & ((1 << 7) | (1 << 3)));
 406 
 407                 /*
 408                  * 8. Change divider for MFC and G3D
 409                  * (200/4=50)->(200/1=200)Mhz
 410                  */
 411                 reg = readl_relaxed(S5P_CLK_DIV2);
 412                 reg &= ~(S5P_CLKDIV2_G3D_MASK | S5P_CLKDIV2_MFC_MASK);
 413                 reg |= (clkdiv_val[index][10] << S5P_CLKDIV2_G3D_SHIFT) |
 414                         (clkdiv_val[index][9] << S5P_CLKDIV2_MFC_SHIFT);
 415                 writel_relaxed(reg, S5P_CLK_DIV2);
 416 
 417                 /* For MFC, G3D dividing */
 418                 do {
 419                         reg = readl_relaxed(S5P_CLKDIV_STAT0);
 420                 } while (reg & ((1 << 16) | (1 << 17)));
 421 
 422                 /* 9. Change MPLL to APLL in MSYS_MUX */
 423                 reg = readl_relaxed(S5P_CLK_SRC0);
 424                 reg &= ~(S5P_CLKSRC0_MUX200_MASK);
 425                 reg |= (0x0 << S5P_CLKSRC0_MUX200_SHIFT);
 426                 writel_relaxed(reg, S5P_CLK_SRC0);
 427 
 428                 do {
 429                         reg = readl_relaxed(S5P_CLKMUX_STAT0);
 430                 } while (reg & (0x1 << 18));
 431 
 432                 /*
 433                  * 10. DMC1 refresh counter
 434                  * L4 : DMC1 = 100Mhz 7.8us/(1/100) = 0x30c
 435                  * Others : DMC1 = 200Mhz 7.8us/(1/200) = 0x618
 436                  */
 437                 if (!bus_speed_changing)
 438                         s5pv210_set_refresh(DMC1, 200000);
 439         }
 440 
 441         /*
 442          * L4 level need to change memory bus speed, hence onedram clock divier
 443          * and memory refresh parameter should be changed
 444          */
 445         if (bus_speed_changing) {
 446                 reg = readl_relaxed(S5P_CLK_DIV6);
 447                 reg &= ~S5P_CLKDIV6_ONEDRAM_MASK;
 448                 reg |= (clkdiv_val[index][8] << S5P_CLKDIV6_ONEDRAM_SHIFT);
 449                 writel_relaxed(reg, S5P_CLK_DIV6);
 450 
 451                 do {
 452                         reg = readl_relaxed(S5P_CLKDIV_STAT1);
 453                 } while (reg & (1 << 15));
 454 
 455                 /* Reconfigure DRAM refresh counter value */
 456                 if (index != L4) {
 457                         /*
 458                          * DMC0 : 166Mhz
 459                          * DMC1 : 200Mhz
 460                          */
 461                         s5pv210_set_refresh(DMC0, 166000);
 462                         s5pv210_set_refresh(DMC1, 200000);
 463                 } else {
 464                         /*
 465                          * DMC0 : 83Mhz
 466                          * DMC1 : 100Mhz
 467                          */
 468                         s5pv210_set_refresh(DMC0, 83000);
 469                         s5pv210_set_refresh(DMC1, 100000);
 470                 }
 471         }
 472 
 473         if (new_freq < old_freq) {
 474                 regulator_set_voltage(int_regulator,
 475                                 int_volt, int_volt_max);
 476 
 477                 regulator_set_voltage(arm_regulator,
 478                                 arm_volt, arm_volt_max);
 479         }
 480 
 481         pr_debug("Perf changed[L%d]\n", index);
 482 
 483 exit:
 484         mutex_unlock(&set_freq_lock);
 485         return ret;
 486 }
 487 
 488 static int check_mem_type(void __iomem *dmc_reg)
 489 {
 490         unsigned long val;
 491 
 492         val = readl_relaxed(dmc_reg + 0x4);
 493         val = (val & (0xf << 8));
 494 
 495         return val >> 8;
 496 }
 497 
 498 static int s5pv210_cpu_init(struct cpufreq_policy *policy)
 499 {
 500         unsigned long mem_type;
 501         int ret;
 502 
 503         policy->clk = clk_get(NULL, "armclk");
 504         if (IS_ERR(policy->clk))
 505                 return PTR_ERR(policy->clk);
 506 
 507         dmc0_clk = clk_get(NULL, "sclk_dmc0");
 508         if (IS_ERR(dmc0_clk)) {
 509                 ret = PTR_ERR(dmc0_clk);
 510                 goto out_dmc0;
 511         }
 512 
 513         dmc1_clk = clk_get(NULL, "hclk_msys");
 514         if (IS_ERR(dmc1_clk)) {
 515                 ret = PTR_ERR(dmc1_clk);
 516                 goto out_dmc1;
 517         }
 518 
 519         if (policy->cpu != 0) {
 520                 ret = -EINVAL;
 521                 goto out_dmc1;
 522         }
 523 
 524         /*
 525          * check_mem_type : This driver only support LPDDR & LPDDR2.
 526          * other memory type is not supported.
 527          */
 528         mem_type = check_mem_type(dmc_base[0]);
 529 
 530         if ((mem_type != LPDDR) && (mem_type != LPDDR2)) {
 531                 pr_err("CPUFreq doesn't support this memory type\n");
 532                 ret = -EINVAL;
 533                 goto out_dmc1;
 534         }
 535 
 536         /* Find current refresh counter and frequency each DMC */
 537         s5pv210_dram_conf[0].refresh = (readl_relaxed(dmc_base[0] + 0x30) * 1000);
 538         s5pv210_dram_conf[0].freq = clk_get_rate(dmc0_clk);
 539 
 540         s5pv210_dram_conf[1].refresh = (readl_relaxed(dmc_base[1] + 0x30) * 1000);
 541         s5pv210_dram_conf[1].freq = clk_get_rate(dmc1_clk);
 542 
 543         policy->suspend_freq = SLEEP_FREQ;
 544         cpufreq_generic_init(policy, s5pv210_freq_table, 40000);
 545         return 0;
 546 
 547 out_dmc1:
 548         clk_put(dmc0_clk);
 549 out_dmc0:
 550         clk_put(policy->clk);
 551         return ret;
 552 }
 553 
 554 static int s5pv210_cpufreq_reboot_notifier_event(struct notifier_block *this,
 555                                                  unsigned long event, void *ptr)
 556 {
 557         int ret;
 558 
 559         ret = cpufreq_driver_target(cpufreq_cpu_get(0), SLEEP_FREQ, 0);
 560         if (ret < 0)
 561                 return NOTIFY_BAD;
 562 
 563         no_cpufreq_access = true;
 564         return NOTIFY_DONE;
 565 }
 566 
 567 static struct cpufreq_driver s5pv210_driver = {
 568         .flags          = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
 569         .verify         = cpufreq_generic_frequency_table_verify,
 570         .target_index   = s5pv210_target,
 571         .get            = cpufreq_generic_get,
 572         .init           = s5pv210_cpu_init,
 573         .name           = "s5pv210",
 574         .suspend        = cpufreq_generic_suspend,
 575         .resume         = cpufreq_generic_suspend, /* We need to set SLEEP FREQ again */
 576 };
 577 
 578 static struct notifier_block s5pv210_cpufreq_reboot_notifier = {
 579         .notifier_call = s5pv210_cpufreq_reboot_notifier_event,
 580 };
 581 
 582 static int s5pv210_cpufreq_probe(struct platform_device *pdev)
 583 {
 584         struct device_node *np;
 585         int id, result = 0;
 586 
 587         /*
 588          * HACK: This is a temporary workaround to get access to clock
 589          * and DMC controller registers directly and remove static mappings
 590          * and dependencies on platform headers. It is necessary to enable
 591          * S5PV210 multi-platform support and will be removed together with
 592          * this whole driver as soon as S5PV210 gets migrated to use
 593          * cpufreq-dt driver.
 594          */
 595         arm_regulator = regulator_get(NULL, "vddarm");
 596         if (IS_ERR(arm_regulator)) {
 597                 if (PTR_ERR(arm_regulator) == -EPROBE_DEFER)
 598                         pr_debug("vddarm regulator not ready, defer\n");
 599                 else
 600                         pr_err("failed to get regulator vddarm\n");
 601                 return PTR_ERR(arm_regulator);
 602         }
 603 
 604         int_regulator = regulator_get(NULL, "vddint");
 605         if (IS_ERR(int_regulator)) {
 606                 if (PTR_ERR(int_regulator) == -EPROBE_DEFER)
 607                         pr_debug("vddint regulator not ready, defer\n");
 608                 else
 609                         pr_err("failed to get regulator vddint\n");
 610                 result = PTR_ERR(int_regulator);
 611                 goto err_int_regulator;
 612         }
 613 
 614         np = of_find_compatible_node(NULL, NULL, "samsung,s5pv210-clock");
 615         if (!np) {
 616                 pr_err("%s: failed to find clock controller DT node\n",
 617                         __func__);
 618                 result = -ENODEV;
 619                 goto err_clock;
 620         }
 621 
 622         clk_base = of_iomap(np, 0);
 623         of_node_put(np);
 624         if (!clk_base) {
 625                 pr_err("%s: failed to map clock registers\n", __func__);
 626                 result = -EFAULT;
 627                 goto err_clock;
 628         }
 629 
 630         for_each_compatible_node(np, NULL, "samsung,s5pv210-dmc") {
 631                 id = of_alias_get_id(np, "dmc");
 632                 if (id < 0 || id >= ARRAY_SIZE(dmc_base)) {
 633                         pr_err("%s: failed to get alias of dmc node '%pOFn'\n",
 634                                 __func__, np);
 635                         of_node_put(np);
 636                         result = id;
 637                         goto err_clk_base;
 638                 }
 639 
 640                 dmc_base[id] = of_iomap(np, 0);
 641                 if (!dmc_base[id]) {
 642                         pr_err("%s: failed to map dmc%d registers\n",
 643                                 __func__, id);
 644                         of_node_put(np);
 645                         result = -EFAULT;
 646                         goto err_dmc;
 647                 }
 648         }
 649 
 650         for (id = 0; id < ARRAY_SIZE(dmc_base); ++id) {
 651                 if (!dmc_base[id]) {
 652                         pr_err("%s: failed to find dmc%d node\n", __func__, id);
 653                         result = -ENODEV;
 654                         goto err_dmc;
 655                 }
 656         }
 657 
 658         register_reboot_notifier(&s5pv210_cpufreq_reboot_notifier);
 659 
 660         return cpufreq_register_driver(&s5pv210_driver);
 661 
 662 err_dmc:
 663         for (id = 0; id < ARRAY_SIZE(dmc_base); ++id)
 664                 if (dmc_base[id]) {
 665                         iounmap(dmc_base[id]);
 666                         dmc_base[id] = NULL;
 667                 }
 668 
 669 err_clk_base:
 670         iounmap(clk_base);
 671 
 672 err_clock:
 673         regulator_put(int_regulator);
 674 
 675 err_int_regulator:
 676         regulator_put(arm_regulator);
 677 
 678         return result;
 679 }
 680 
 681 static struct platform_driver s5pv210_cpufreq_platdrv = {
 682         .driver = {
 683                 .name   = "s5pv210-cpufreq",
 684         },
 685         .probe = s5pv210_cpufreq_probe,
 686 };
 687 builtin_platform_driver(s5pv210_cpufreq_platdrv);

/* [<][>][^][v][top][bottom][index][help] */