cpufreq.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. /* linux/arch/arm/mach-s5pv310/cpufreq.c
  2. *
  3. * Copyright (c) 2010 Samsung Electronics Co., Ltd.
  4. * http://www.samsung.com
  5. *
  6. * S5PV310 - CPU frequency scaling support
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License version 2 as
  10. * published by the Free Software Foundation.
  11. */
  12. #include <linux/types.h>
  13. #include <linux/kernel.h>
  14. #include <linux/err.h>
  15. #include <linux/clk.h>
  16. #include <linux/io.h>
  17. #include <linux/slab.h>
  18. #include <linux/regulator/consumer.h>
  19. #include <linux/cpufreq.h>
  20. #include <mach/map.h>
  21. #include <mach/regs-clock.h>
  22. #include <mach/regs-mem.h>
  23. #include <plat/clock.h>
  24. #include <plat/pm.h>
  25. static struct clk *cpu_clk;
  26. static struct clk *moutcore;
  27. static struct clk *mout_mpll;
  28. static struct clk *mout_apll;
  29. #ifdef CONFIG_REGULATOR
  30. static struct regulator *arm_regulator;
  31. static struct regulator *int_regulator;
  32. #endif
  33. static struct cpufreq_freqs freqs;
  34. static unsigned int memtype;
  35. enum s5pv310_memory_type {
  36. DDR2 = 4,
  37. LPDDR2,
  38. DDR3,
  39. };
  40. enum cpufreq_level_index {
  41. L0, L1, L2, L3, CPUFREQ_LEVEL_END,
  42. };
  43. static struct cpufreq_frequency_table s5pv310_freq_table[] = {
  44. {L0, 1000*1000},
  45. {L1, 800*1000},
  46. {L2, 400*1000},
  47. {L3, 100*1000},
  48. {0, CPUFREQ_TABLE_END},
  49. };
  50. static unsigned int clkdiv_cpu0[CPUFREQ_LEVEL_END][7] = {
  51. /*
  52. * Clock divider value for following
  53. * { DIVCORE, DIVCOREM0, DIVCOREM1, DIVPERIPH,
  54. * DIVATB, DIVPCLK_DBG, DIVAPLL }
  55. */
  56. /* ARM L0: 1000MHz */
  57. { 0, 3, 7, 3, 3, 0, 1 },
  58. /* ARM L1: 800MHz */
  59. { 0, 3, 7, 3, 3, 0, 1 },
  60. /* ARM L2: 400MHz */
  61. { 0, 1, 3, 1, 3, 0, 1 },
  62. /* ARM L3: 100MHz */
  63. { 0, 0, 1, 0, 3, 1, 1 },
  64. };
  65. static unsigned int clkdiv_cpu1[CPUFREQ_LEVEL_END][2] = {
  66. /*
  67. * Clock divider value for following
  68. * { DIVCOPY, DIVHPM }
  69. */
  70. /* ARM L0: 1000MHz */
  71. { 3, 0 },
  72. /* ARM L1: 800MHz */
  73. { 3, 0 },
  74. /* ARM L2: 400MHz */
  75. { 3, 0 },
  76. /* ARM L3: 100MHz */
  77. { 3, 0 },
  78. };
  79. static unsigned int clkdiv_dmc0[CPUFREQ_LEVEL_END][8] = {
  80. /*
  81. * Clock divider value for following
  82. * { DIVACP, DIVACP_PCLK, DIVDPHY, DIVDMC, DIVDMCD
  83. * DIVDMCP, DIVCOPY2, DIVCORE_TIMERS }
  84. */
  85. /* DMC L0: 400MHz */
  86. { 3, 1, 1, 1, 1, 1, 3, 1 },
  87. /* DMC L1: 400MHz */
  88. { 3, 1, 1, 1, 1, 1, 3, 1 },
  89. /* DMC L2: 266.7MHz */
  90. { 7, 1, 1, 2, 1, 1, 3, 1 },
  91. /* DMC L3: 200MHz */
  92. { 7, 1, 1, 3, 1, 1, 3, 1 },
  93. };
  94. static unsigned int clkdiv_top[CPUFREQ_LEVEL_END][5] = {
  95. /*
  96. * Clock divider value for following
  97. * { DIVACLK200, DIVACLK100, DIVACLK160, DIVACLK133, DIVONENAND }
  98. */
  99. /* ACLK200 L0: 200MHz */
  100. { 3, 7, 4, 5, 1 },
  101. /* ACLK200 L1: 200MHz */
  102. { 3, 7, 4, 5, 1 },
  103. /* ACLK200 L2: 160MHz */
  104. { 4, 7, 5, 7, 1 },
  105. /* ACLK200 L3: 133.3MHz */
  106. { 5, 7, 7, 7, 1 },
  107. };
  108. static unsigned int clkdiv_lr_bus[CPUFREQ_LEVEL_END][2] = {
  109. /*
  110. * Clock divider value for following
  111. * { DIVGDL/R, DIVGPL/R }
  112. */
  113. /* ACLK_GDL/R L0: 200MHz */
  114. { 3, 1 },
  115. /* ACLK_GDL/R L1: 200MHz */
  116. { 3, 1 },
  117. /* ACLK_GDL/R L2: 160MHz */
  118. { 4, 1 },
  119. /* ACLK_GDL/R L3: 133.3MHz */
  120. { 5, 1 },
  121. };
  122. struct cpufreq_voltage_table {
  123. unsigned int index; /* any */
  124. unsigned int arm_volt; /* uV */
  125. unsigned int int_volt;
  126. };
  127. static struct cpufreq_voltage_table s5pv310_volt_table[CPUFREQ_LEVEL_END] = {
  128. {
  129. .index = L0,
  130. .arm_volt = 1200000,
  131. .int_volt = 1100000,
  132. }, {
  133. .index = L1,
  134. .arm_volt = 1100000,
  135. .int_volt = 1100000,
  136. }, {
  137. .index = L2,
  138. .arm_volt = 1000000,
  139. .int_volt = 1000000,
  140. }, {
  141. .index = L3,
  142. .arm_volt = 900000,
  143. .int_volt = 1000000,
  144. },
  145. };
  146. static unsigned int s5pv310_apll_pms_table[CPUFREQ_LEVEL_END] = {
  147. /* APLL FOUT L0: 1000MHz */
  148. ((250 << 16) | (6 << 8) | 1),
  149. /* APLL FOUT L1: 800MHz */
  150. ((200 << 16) | (6 << 8) | 1),
  151. /* APLL FOUT L2 : 400MHz */
  152. ((200 << 16) | (6 << 8) | 2),
  153. /* APLL FOUT L3: 100MHz */
  154. ((200 << 16) | (6 << 8) | 4),
  155. };
  156. int s5pv310_verify_speed(struct cpufreq_policy *policy)
  157. {
  158. return cpufreq_frequency_table_verify(policy, s5pv310_freq_table);
  159. }
  160. unsigned int s5pv310_getspeed(unsigned int cpu)
  161. {
  162. return clk_get_rate(cpu_clk) / 1000;
  163. }
  164. void s5pv310_set_clkdiv(unsigned int div_index)
  165. {
  166. unsigned int tmp;
  167. /* Change Divider - CPU0 */
  168. tmp = __raw_readl(S5P_CLKDIV_CPU);
  169. tmp &= ~(S5P_CLKDIV_CPU0_CORE_MASK | S5P_CLKDIV_CPU0_COREM0_MASK |
  170. S5P_CLKDIV_CPU0_COREM1_MASK | S5P_CLKDIV_CPU0_PERIPH_MASK |
  171. S5P_CLKDIV_CPU0_ATB_MASK | S5P_CLKDIV_CPU0_PCLKDBG_MASK |
  172. S5P_CLKDIV_CPU0_APLL_MASK);
  173. tmp |= ((clkdiv_cpu0[div_index][0] << S5P_CLKDIV_CPU0_CORE_SHIFT) |
  174. (clkdiv_cpu0[div_index][1] << S5P_CLKDIV_CPU0_COREM0_SHIFT) |
  175. (clkdiv_cpu0[div_index][2] << S5P_CLKDIV_CPU0_COREM1_SHIFT) |
  176. (clkdiv_cpu0[div_index][3] << S5P_CLKDIV_CPU0_PERIPH_SHIFT) |
  177. (clkdiv_cpu0[div_index][4] << S5P_CLKDIV_CPU0_ATB_SHIFT) |
  178. (clkdiv_cpu0[div_index][5] << S5P_CLKDIV_CPU0_PCLKDBG_SHIFT) |
  179. (clkdiv_cpu0[div_index][6] << S5P_CLKDIV_CPU0_APLL_SHIFT));
  180. __raw_writel(tmp, S5P_CLKDIV_CPU);
  181. do {
  182. tmp = __raw_readl(S5P_CLKDIV_STATCPU);
  183. } while (tmp & 0x1111111);
  184. /* Change Divider - CPU1 */
  185. tmp = __raw_readl(S5P_CLKDIV_CPU1);
  186. tmp &= ~((0x7 << 4) | 0x7);
  187. tmp |= ((clkdiv_cpu1[div_index][0] << 4) |
  188. (clkdiv_cpu1[div_index][1] << 0));
  189. __raw_writel(tmp, S5P_CLKDIV_CPU1);
  190. do {
  191. tmp = __raw_readl(S5P_CLKDIV_STATCPU1);
  192. } while (tmp & 0x11);
  193. /* Change Divider - DMC0 */
  194. tmp = __raw_readl(S5P_CLKDIV_DMC0);
  195. tmp &= ~(S5P_CLKDIV_DMC0_ACP_MASK | S5P_CLKDIV_DMC0_ACPPCLK_MASK |
  196. S5P_CLKDIV_DMC0_DPHY_MASK | S5P_CLKDIV_DMC0_DMC_MASK |
  197. S5P_CLKDIV_DMC0_DMCD_MASK | S5P_CLKDIV_DMC0_DMCP_MASK |
  198. S5P_CLKDIV_DMC0_COPY2_MASK | S5P_CLKDIV_DMC0_CORETI_MASK);
  199. tmp |= ((clkdiv_dmc0[div_index][0] << S5P_CLKDIV_DMC0_ACP_SHIFT) |
  200. (clkdiv_dmc0[div_index][1] << S5P_CLKDIV_DMC0_ACPPCLK_SHIFT) |
  201. (clkdiv_dmc0[div_index][2] << S5P_CLKDIV_DMC0_DPHY_SHIFT) |
  202. (clkdiv_dmc0[div_index][3] << S5P_CLKDIV_DMC0_DMC_SHIFT) |
  203. (clkdiv_dmc0[div_index][4] << S5P_CLKDIV_DMC0_DMCD_SHIFT) |
  204. (clkdiv_dmc0[div_index][5] << S5P_CLKDIV_DMC0_DMCP_SHIFT) |
  205. (clkdiv_dmc0[div_index][6] << S5P_CLKDIV_DMC0_COPY2_SHIFT) |
  206. (clkdiv_dmc0[div_index][7] << S5P_CLKDIV_DMC0_CORETI_SHIFT));
  207. __raw_writel(tmp, S5P_CLKDIV_DMC0);
  208. do {
  209. tmp = __raw_readl(S5P_CLKDIV_STAT_DMC0);
  210. } while (tmp & 0x11111111);
  211. /* Change Divider - TOP */
  212. tmp = __raw_readl(S5P_CLKDIV_TOP);
  213. tmp &= ~(S5P_CLKDIV_TOP_ACLK200_MASK | S5P_CLKDIV_TOP_ACLK100_MASK |
  214. S5P_CLKDIV_TOP_ACLK160_MASK | S5P_CLKDIV_TOP_ACLK133_MASK |
  215. S5P_CLKDIV_TOP_ONENAND_MASK);
  216. tmp |= ((clkdiv_top[div_index][0] << S5P_CLKDIV_TOP_ACLK200_SHIFT) |
  217. (clkdiv_top[div_index][1] << S5P_CLKDIV_TOP_ACLK100_SHIFT) |
  218. (clkdiv_top[div_index][2] << S5P_CLKDIV_TOP_ACLK160_SHIFT) |
  219. (clkdiv_top[div_index][3] << S5P_CLKDIV_TOP_ACLK133_SHIFT) |
  220. (clkdiv_top[div_index][4] << S5P_CLKDIV_TOP_ONENAND_SHIFT));
  221. __raw_writel(tmp, S5P_CLKDIV_TOP);
  222. do {
  223. tmp = __raw_readl(S5P_CLKDIV_STAT_TOP);
  224. } while (tmp & 0x11111);
  225. /* Change Divider - LEFTBUS */
  226. tmp = __raw_readl(S5P_CLKDIV_LEFTBUS);
  227. tmp &= ~(S5P_CLKDIV_BUS_GDLR_MASK | S5P_CLKDIV_BUS_GPLR_MASK);
  228. tmp |= ((clkdiv_lr_bus[div_index][0] << S5P_CLKDIV_BUS_GDLR_SHIFT) |
  229. (clkdiv_lr_bus[div_index][1] << S5P_CLKDIV_BUS_GPLR_SHIFT));
  230. __raw_writel(tmp, S5P_CLKDIV_LEFTBUS);
  231. do {
  232. tmp = __raw_readl(S5P_CLKDIV_STAT_LEFTBUS);
  233. } while (tmp & 0x11);
  234. /* Change Divider - RIGHTBUS */
  235. tmp = __raw_readl(S5P_CLKDIV_RIGHTBUS);
  236. tmp &= ~(S5P_CLKDIV_BUS_GDLR_MASK | S5P_CLKDIV_BUS_GPLR_MASK);
  237. tmp |= ((clkdiv_lr_bus[div_index][0] << S5P_CLKDIV_BUS_GDLR_SHIFT) |
  238. (clkdiv_lr_bus[div_index][1] << S5P_CLKDIV_BUS_GPLR_SHIFT));
  239. __raw_writel(tmp, S5P_CLKDIV_RIGHTBUS);
  240. do {
  241. tmp = __raw_readl(S5P_CLKDIV_STAT_RIGHTBUS);
  242. } while (tmp & 0x11);
  243. }
  244. static void s5pv310_set_apll(unsigned int index)
  245. {
  246. unsigned int tmp;
  247. /* 1. MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */
  248. clk_set_parent(moutcore, mout_mpll);
  249. do {
  250. tmp = (__raw_readl(S5P_CLKMUX_STATCPU)
  251. >> S5P_CLKSRC_CPU_MUXCORE_SHIFT);
  252. tmp &= 0x7;
  253. } while (tmp != 0x2);
  254. /* 2. Set APLL Lock time */
  255. __raw_writel(S5P_APLL_LOCKTIME, S5P_APLL_LOCK);
  256. /* 3. Change PLL PMS values */
  257. tmp = __raw_readl(S5P_APLL_CON0);
  258. tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0));
  259. tmp |= s5pv310_apll_pms_table[index];
  260. __raw_writel(tmp, S5P_APLL_CON0);
  261. /* 4. wait_lock_time */
  262. do {
  263. tmp = __raw_readl(S5P_APLL_CON0);
  264. } while (!(tmp & (0x1 << S5P_APLLCON0_LOCKED_SHIFT)));
  265. /* 5. MUX_CORE_SEL = APLL */
  266. clk_set_parent(moutcore, mout_apll);
  267. do {
  268. tmp = __raw_readl(S5P_CLKMUX_STATCPU);
  269. tmp &= S5P_CLKMUX_STATCPU_MUXCORE_MASK;
  270. } while (tmp != (0x1 << S5P_CLKSRC_CPU_MUXCORE_SHIFT));
  271. }
  272. static void s5pv310_set_frequency(unsigned int old_index, unsigned int new_index)
  273. {
  274. unsigned int tmp;
  275. if (old_index > new_index) {
  276. /* The frequency changing to L0 needs to change apll */
  277. if (freqs.new == s5pv310_freq_table[L0].frequency) {
  278. /* 1. Change the system clock divider values */
  279. s5pv310_set_clkdiv(new_index);
  280. /* 2. Change the apll m,p,s value */
  281. s5pv310_set_apll(new_index);
  282. } else {
  283. /* 1. Change the system clock divider values */
  284. s5pv310_set_clkdiv(new_index);
  285. /* 2. Change just s value in apll m,p,s value */
  286. tmp = __raw_readl(S5P_APLL_CON0);
  287. tmp &= ~(0x7 << 0);
  288. tmp |= (s5pv310_apll_pms_table[new_index] & 0x7);
  289. __raw_writel(tmp, S5P_APLL_CON0);
  290. }
  291. }
  292. else if (old_index < new_index) {
  293. /* The frequency changing from L0 needs to change apll */
  294. if (freqs.old == s5pv310_freq_table[L0].frequency) {
  295. /* 1. Change the apll m,p,s value */
  296. s5pv310_set_apll(new_index);
  297. /* 2. Change the system clock divider values */
  298. s5pv310_set_clkdiv(new_index);
  299. } else {
  300. /* 1. Change just s value in apll m,p,s value */
  301. tmp = __raw_readl(S5P_APLL_CON0);
  302. tmp &= ~(0x7 << 0);
  303. tmp |= (s5pv310_apll_pms_table[new_index] & 0x7);
  304. __raw_writel(tmp, S5P_APLL_CON0);
  305. /* 2. Change the system clock divider values */
  306. s5pv310_set_clkdiv(new_index);
  307. }
  308. }
  309. }
  310. static int s5pv310_target(struct cpufreq_policy *policy,
  311. unsigned int target_freq,
  312. unsigned int relation)
  313. {
  314. unsigned int index, old_index;
  315. unsigned int arm_volt, int_volt;
  316. freqs.old = s5pv310_getspeed(policy->cpu);
  317. if (cpufreq_frequency_table_target(policy, s5pv310_freq_table,
  318. freqs.old, relation, &old_index))
  319. return -EINVAL;
  320. if (cpufreq_frequency_table_target(policy, s5pv310_freq_table,
  321. target_freq, relation, &index))
  322. return -EINVAL;
  323. freqs.new = s5pv310_freq_table[index].frequency;
  324. freqs.cpu = policy->cpu;
  325. if (freqs.new == freqs.old)
  326. return 0;
  327. /* get the voltage value */
  328. arm_volt = s5pv310_volt_table[index].arm_volt;
  329. int_volt = s5pv310_volt_table[index].int_volt;
  330. cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
  331. /* control regulator */
  332. if (freqs.new > freqs.old) {
  333. /* Voltage up */
  334. #ifdef CONFIG_REGULATOR
  335. regulator_set_voltage(arm_regulator, arm_volt, arm_volt);
  336. regulator_set_voltage(int_regulator, int_volt, int_volt);
  337. #endif
  338. }
  339. /* Clock Configuration Procedure */
  340. s5pv310_set_frequency(old_index, index);
  341. /* control regulator */
  342. if (freqs.new < freqs.old) {
  343. /* Voltage down */
  344. #ifdef CONFIG_REGULATOR
  345. regulator_set_voltage(arm_regulator, arm_volt, arm_volt);
  346. regulator_set_voltage(int_regulator, int_volt, int_volt);
  347. #endif
  348. }
  349. cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
  350. return 0;
  351. }
  352. #ifdef CONFIG_PM
  353. static int s5pv310_cpufreq_suspend(struct cpufreq_policy *policy,
  354. pm_message_t pmsg)
  355. {
  356. return 0;
  357. }
  358. static int s5pv310_cpufreq_resume(struct cpufreq_policy *policy)
  359. {
  360. return 0;
  361. }
  362. #endif
  363. static int s5pv310_cpufreq_cpu_init(struct cpufreq_policy *policy)
  364. {
  365. policy->cur = policy->min = policy->max = s5pv310_getspeed(policy->cpu);
  366. cpufreq_frequency_table_get_attr(s5pv310_freq_table, policy->cpu);
  367. /* set the transition latency value */
  368. policy->cpuinfo.transition_latency = 100000;
  369. /*
  370. * S5PV310 multi-core processors has 2 cores
  371. * that the frequency cannot be set independently.
  372. * Each cpu is bound to the same speed.
  373. * So the affected cpu is all of the cpus.
  374. */
  375. cpumask_setall(policy->cpus);
  376. return cpufreq_frequency_table_cpuinfo(policy, s5pv310_freq_table);
  377. }
  378. static struct cpufreq_driver s5pv310_driver = {
  379. .flags = CPUFREQ_STICKY,
  380. .verify = s5pv310_verify_speed,
  381. .target = s5pv310_target,
  382. .get = s5pv310_getspeed,
  383. .init = s5pv310_cpufreq_cpu_init,
  384. .name = "s5pv310_cpufreq",
  385. #ifdef CONFIG_PM
  386. .suspend = s5pv310_cpufreq_suspend,
  387. .resume = s5pv310_cpufreq_resume,
  388. #endif
  389. };
  390. static int __init s5pv310_cpufreq_init(void)
  391. {
  392. cpu_clk = clk_get(NULL, "armclk");
  393. if (IS_ERR(cpu_clk))
  394. return PTR_ERR(cpu_clk);
  395. moutcore = clk_get(NULL, "moutcore");
  396. if (IS_ERR(moutcore))
  397. goto out;
  398. mout_mpll = clk_get(NULL, "mout_mpll");
  399. if (IS_ERR(mout_mpll))
  400. goto out;
  401. mout_apll = clk_get(NULL, "mout_apll");
  402. if (IS_ERR(mout_apll))
  403. goto out;
  404. #ifdef CONFIG_REGULATOR
  405. arm_regulator = regulator_get(NULL, "vdd_arm");
  406. if (IS_ERR(arm_regulator)) {
  407. printk(KERN_ERR "failed to get resource %s\n", "vdd_arm");
  408. goto out;
  409. }
  410. int_regulator = regulator_get(NULL, "vdd_int");
  411. if (IS_ERR(int_regulator)) {
  412. printk(KERN_ERR "failed to get resource %s\n", "vdd_int");
  413. goto out;
  414. }
  415. #endif
  416. /*
  417. * Check DRAM type.
  418. * Because DVFS level is different according to DRAM type.
  419. */
  420. memtype = __raw_readl(S5P_VA_DMC0 + S5P_DMC0_MEMCON_OFFSET);
  421. memtype = (memtype >> S5P_DMC0_MEMTYPE_SHIFT);
  422. memtype &= S5P_DMC0_MEMTYPE_MASK;
  423. if ((memtype < DDR2) && (memtype > DDR3)) {
  424. printk(KERN_ERR "%s: wrong memtype= 0x%x\n", __func__, memtype);
  425. goto out;
  426. } else {
  427. printk(KERN_DEBUG "%s: memtype= 0x%x\n", __func__, memtype);
  428. }
  429. return cpufreq_register_driver(&s5pv310_driver);
  430. out:
  431. if (!IS_ERR(cpu_clk))
  432. clk_put(cpu_clk);
  433. if (!IS_ERR(moutcore))
  434. clk_put(moutcore);
  435. if (!IS_ERR(mout_mpll))
  436. clk_put(mout_mpll);
  437. if (!IS_ERR(mout_apll))
  438. clk_put(mout_apll);
  439. #ifdef CONFIG_REGULATOR
  440. if (!IS_ERR(arm_regulator))
  441. regulator_put(arm_regulator);
  442. if (!IS_ERR(int_regulator))
  443. regulator_put(int_regulator);
  444. #endif
  445. printk(KERN_ERR "%s: failed initialization\n", __func__);
  446. return -EINVAL;
  447. }
  448. late_initcall(s5pv310_cpufreq_init);