s5pv210-cpufreq.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. /*
  2. * Copyright (c) 2010 Samsung Electronics Co., Ltd.
  3. * http://www.samsung.com
  4. *
  5. * CPU frequency scaling for S5PC110/S5PV210
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License version 2 as
  9. * published by the Free Software Foundation.
  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/reboot.h>
  19. #include <linux/regulator/consumer.h>
  20. #include <linux/suspend.h>
  21. #include <mach/map.h>
  22. #include <mach/regs-clock.h>
  23. static struct clk *cpu_clk;
  24. static struct clk *dmc0_clk;
  25. static struct clk *dmc1_clk;
  26. static struct cpufreq_freqs freqs;
  27. static DEFINE_MUTEX(set_freq_lock);
  28. /* APLL M,P,S values for 1G/800Mhz */
  29. #define APLL_VAL_1000 ((1 << 31) | (125 << 16) | (3 << 8) | 1)
  30. #define APLL_VAL_800 ((1 << 31) | (100 << 16) | (3 << 8) | 1)
  31. /* Use 800MHz when entering sleep mode */
  32. #define SLEEP_FREQ (800 * 1000)
  33. /*
  34. * relation has an additional symantics other than the standard of cpufreq
  35. * DISALBE_FURTHER_CPUFREQ: disable further access to target
  36. * ENABLE_FURTUER_CPUFREQ: enable access to target
  37. */
  38. enum cpufreq_access {
  39. DISABLE_FURTHER_CPUFREQ = 0x10,
  40. ENABLE_FURTHER_CPUFREQ = 0x20,
  41. };
  42. static bool no_cpufreq_access;
  43. /*
  44. * DRAM configurations to calculate refresh counter for changing
  45. * frequency of memory.
  46. */
  47. struct dram_conf {
  48. unsigned long freq; /* HZ */
  49. unsigned long refresh; /* DRAM refresh counter * 1000 */
  50. };
  51. /* DRAM configuration (DMC0 and DMC1) */
  52. static struct dram_conf s5pv210_dram_conf[2];
  53. enum perf_level {
  54. L0, L1, L2, L3, L4,
  55. };
  56. enum s5pv210_mem_type {
  57. LPDDR = 0x1,
  58. LPDDR2 = 0x2,
  59. DDR2 = 0x4,
  60. };
  61. enum s5pv210_dmc_port {
  62. DMC0 = 0,
  63. DMC1,
  64. };
  65. static struct cpufreq_frequency_table s5pv210_freq_table[] = {
  66. {L0, 1000*1000},
  67. {L1, 800*1000},
  68. {L2, 400*1000},
  69. {L3, 200*1000},
  70. {L4, 100*1000},
  71. {0, CPUFREQ_TABLE_END},
  72. };
  73. static struct regulator *arm_regulator;
  74. static struct regulator *int_regulator;
  75. struct s5pv210_dvs_conf {
  76. int arm_volt; /* uV */
  77. int int_volt; /* uV */
  78. };
  79. static const int arm_volt_max = 1350000;
  80. static const int int_volt_max = 1250000;
  81. static struct s5pv210_dvs_conf dvs_conf[] = {
  82. [L0] = {
  83. .arm_volt = 1250000,
  84. .int_volt = 1100000,
  85. },
  86. [L1] = {
  87. .arm_volt = 1200000,
  88. .int_volt = 1100000,
  89. },
  90. [L2] = {
  91. .arm_volt = 1050000,
  92. .int_volt = 1100000,
  93. },
  94. [L3] = {
  95. .arm_volt = 950000,
  96. .int_volt = 1100000,
  97. },
  98. [L4] = {
  99. .arm_volt = 950000,
  100. .int_volt = 1000000,
  101. },
  102. };
  103. static u32 clkdiv_val[5][11] = {
  104. /*
  105. * Clock divider value for following
  106. * { APLL, A2M, HCLK_MSYS, PCLK_MSYS,
  107. * HCLK_DSYS, PCLK_DSYS, HCLK_PSYS, PCLK_PSYS,
  108. * ONEDRAM, MFC, G3D }
  109. */
  110. /* L0 : [1000/200/100][166/83][133/66][200/200] */
  111. {0, 4, 4, 1, 3, 1, 4, 1, 3, 0, 0},
  112. /* L1 : [800/200/100][166/83][133/66][200/200] */
  113. {0, 3, 3, 1, 3, 1, 4, 1, 3, 0, 0},
  114. /* L2 : [400/200/100][166/83][133/66][200/200] */
  115. {1, 3, 1, 1, 3, 1, 4, 1, 3, 0, 0},
  116. /* L3 : [200/200/100][166/83][133/66][200/200] */
  117. {3, 3, 1, 1, 3, 1, 4, 1, 3, 0, 0},
  118. /* L4 : [100/100/100][83/83][66/66][100/100] */
  119. {7, 7, 0, 0, 7, 0, 9, 0, 7, 0, 0},
  120. };
  121. /*
  122. * This function set DRAM refresh counter
  123. * accoriding to operating frequency of DRAM
  124. * ch: DMC port number 0 or 1
  125. * freq: Operating frequency of DRAM(KHz)
  126. */
  127. static void s5pv210_set_refresh(enum s5pv210_dmc_port ch, unsigned long freq)
  128. {
  129. unsigned long tmp, tmp1;
  130. void __iomem *reg = NULL;
  131. if (ch == DMC0) {
  132. reg = (S5P_VA_DMC0 + 0x30);
  133. } else if (ch == DMC1) {
  134. reg = (S5P_VA_DMC1 + 0x30);
  135. } else {
  136. printk(KERN_ERR "Cannot find DMC port\n");
  137. return;
  138. }
  139. /* Find current DRAM frequency */
  140. tmp = s5pv210_dram_conf[ch].freq;
  141. do_div(tmp, freq);
  142. tmp1 = s5pv210_dram_conf[ch].refresh;
  143. do_div(tmp1, tmp);
  144. __raw_writel(tmp1, reg);
  145. }
  146. static int s5pv210_verify_speed(struct cpufreq_policy *policy)
  147. {
  148. if (policy->cpu)
  149. return -EINVAL;
  150. return cpufreq_frequency_table_verify(policy, s5pv210_freq_table);
  151. }
  152. static unsigned int s5pv210_getspeed(unsigned int cpu)
  153. {
  154. if (cpu)
  155. return 0;
  156. return clk_get_rate(cpu_clk) / 1000;
  157. }
  158. static int s5pv210_target(struct cpufreq_policy *policy,
  159. unsigned int target_freq,
  160. unsigned int relation)
  161. {
  162. unsigned long reg;
  163. unsigned int index, priv_index;
  164. unsigned int pll_changing = 0;
  165. unsigned int bus_speed_changing = 0;
  166. int arm_volt, int_volt;
  167. int ret = 0;
  168. mutex_lock(&set_freq_lock);
  169. if (relation & ENABLE_FURTHER_CPUFREQ)
  170. no_cpufreq_access = false;
  171. if (no_cpufreq_access) {
  172. #ifdef CONFIG_PM_VERBOSE
  173. pr_err("%s:%d denied access to %s as it is disabled"
  174. "temporarily\n", __FILE__, __LINE__, __func__);
  175. #endif
  176. ret = -EINVAL;
  177. goto exit;
  178. }
  179. if (relation & DISABLE_FURTHER_CPUFREQ)
  180. no_cpufreq_access = true;
  181. relation &= ~(ENABLE_FURTHER_CPUFREQ | DISABLE_FURTHER_CPUFREQ);
  182. freqs.old = s5pv210_getspeed(0);
  183. if (cpufreq_frequency_table_target(policy, s5pv210_freq_table,
  184. target_freq, relation, &index)) {
  185. ret = -EINVAL;
  186. goto exit;
  187. }
  188. freqs.new = s5pv210_freq_table[index].frequency;
  189. if (freqs.new == freqs.old)
  190. goto exit;
  191. /* Finding current running level index */
  192. if (cpufreq_frequency_table_target(policy, s5pv210_freq_table,
  193. freqs.old, relation, &priv_index)) {
  194. ret = -EINVAL;
  195. goto exit;
  196. }
  197. arm_volt = dvs_conf[index].arm_volt;
  198. int_volt = dvs_conf[index].int_volt;
  199. if (freqs.new > freqs.old) {
  200. ret = regulator_set_voltage(arm_regulator,
  201. arm_volt, arm_volt_max);
  202. if (ret)
  203. goto exit;
  204. ret = regulator_set_voltage(int_regulator,
  205. int_volt, int_volt_max);
  206. if (ret)
  207. goto exit;
  208. }
  209. cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
  210. /* Check if there need to change PLL */
  211. if ((index == L0) || (priv_index == L0))
  212. pll_changing = 1;
  213. /* Check if there need to change System bus clock */
  214. if ((index == L4) || (priv_index == L4))
  215. bus_speed_changing = 1;
  216. if (bus_speed_changing) {
  217. /*
  218. * Reconfigure DRAM refresh counter value for minimum
  219. * temporary clock while changing divider.
  220. * expected clock is 83Mhz : 7.8usec/(1/83Mhz) = 0x287
  221. */
  222. if (pll_changing)
  223. s5pv210_set_refresh(DMC1, 83000);
  224. else
  225. s5pv210_set_refresh(DMC1, 100000);
  226. s5pv210_set_refresh(DMC0, 83000);
  227. }
  228. /*
  229. * APLL should be changed in this level
  230. * APLL -> MPLL(for stable transition) -> APLL
  231. * Some clock source's clock API are not prepared.
  232. * Do not use clock API in below code.
  233. */
  234. if (pll_changing) {
  235. /*
  236. * 1. Temporary Change divider for MFC and G3D
  237. * SCLKA2M(200/1=200)->(200/4=50)Mhz
  238. */
  239. reg = __raw_readl(S5P_CLK_DIV2);
  240. reg &= ~(S5P_CLKDIV2_G3D_MASK | S5P_CLKDIV2_MFC_MASK);
  241. reg |= (3 << S5P_CLKDIV2_G3D_SHIFT) |
  242. (3 << S5P_CLKDIV2_MFC_SHIFT);
  243. __raw_writel(reg, S5P_CLK_DIV2);
  244. /* For MFC, G3D dividing */
  245. do {
  246. reg = __raw_readl(S5P_CLKDIV_STAT0);
  247. } while (reg & ((1 << 16) | (1 << 17)));
  248. /*
  249. * 2. Change SCLKA2M(200Mhz)to SCLKMPLL in MFC_MUX, G3D MUX
  250. * (200/4=50)->(667/4=166)Mhz
  251. */
  252. reg = __raw_readl(S5P_CLK_SRC2);
  253. reg &= ~(S5P_CLKSRC2_G3D_MASK | S5P_CLKSRC2_MFC_MASK);
  254. reg |= (1 << S5P_CLKSRC2_G3D_SHIFT) |
  255. (1 << S5P_CLKSRC2_MFC_SHIFT);
  256. __raw_writel(reg, S5P_CLK_SRC2);
  257. do {
  258. reg = __raw_readl(S5P_CLKMUX_STAT1);
  259. } while (reg & ((1 << 7) | (1 << 3)));
  260. /*
  261. * 3. DMC1 refresh count for 133Mhz if (index == L4) is
  262. * true refresh counter is already programed in upper
  263. * code. 0x287@83Mhz
  264. */
  265. if (!bus_speed_changing)
  266. s5pv210_set_refresh(DMC1, 133000);
  267. /* 4. SCLKAPLL -> SCLKMPLL */
  268. reg = __raw_readl(S5P_CLK_SRC0);
  269. reg &= ~(S5P_CLKSRC0_MUX200_MASK);
  270. reg |= (0x1 << S5P_CLKSRC0_MUX200_SHIFT);
  271. __raw_writel(reg, S5P_CLK_SRC0);
  272. do {
  273. reg = __raw_readl(S5P_CLKMUX_STAT0);
  274. } while (reg & (0x1 << 18));
  275. }
  276. /* Change divider */
  277. reg = __raw_readl(S5P_CLK_DIV0);
  278. reg &= ~(S5P_CLKDIV0_APLL_MASK | S5P_CLKDIV0_A2M_MASK |
  279. S5P_CLKDIV0_HCLK200_MASK | S5P_CLKDIV0_PCLK100_MASK |
  280. S5P_CLKDIV0_HCLK166_MASK | S5P_CLKDIV0_PCLK83_MASK |
  281. S5P_CLKDIV0_HCLK133_MASK | S5P_CLKDIV0_PCLK66_MASK);
  282. reg |= ((clkdiv_val[index][0] << S5P_CLKDIV0_APLL_SHIFT) |
  283. (clkdiv_val[index][1] << S5P_CLKDIV0_A2M_SHIFT) |
  284. (clkdiv_val[index][2] << S5P_CLKDIV0_HCLK200_SHIFT) |
  285. (clkdiv_val[index][3] << S5P_CLKDIV0_PCLK100_SHIFT) |
  286. (clkdiv_val[index][4] << S5P_CLKDIV0_HCLK166_SHIFT) |
  287. (clkdiv_val[index][5] << S5P_CLKDIV0_PCLK83_SHIFT) |
  288. (clkdiv_val[index][6] << S5P_CLKDIV0_HCLK133_SHIFT) |
  289. (clkdiv_val[index][7] << S5P_CLKDIV0_PCLK66_SHIFT));
  290. __raw_writel(reg, S5P_CLK_DIV0);
  291. do {
  292. reg = __raw_readl(S5P_CLKDIV_STAT0);
  293. } while (reg & 0xff);
  294. /* ARM MCS value changed */
  295. reg = __raw_readl(S5P_ARM_MCS_CON);
  296. reg &= ~0x3;
  297. if (index >= L3)
  298. reg |= 0x3;
  299. else
  300. reg |= 0x1;
  301. __raw_writel(reg, S5P_ARM_MCS_CON);
  302. if (pll_changing) {
  303. /* 5. Set Lock time = 30us*24Mhz = 0x2cf */
  304. __raw_writel(0x2cf, S5P_APLL_LOCK);
  305. /*
  306. * 6. Turn on APLL
  307. * 6-1. Set PMS values
  308. * 6-2. Wait untile the PLL is locked
  309. */
  310. if (index == L0)
  311. __raw_writel(APLL_VAL_1000, S5P_APLL_CON);
  312. else
  313. __raw_writel(APLL_VAL_800, S5P_APLL_CON);
  314. do {
  315. reg = __raw_readl(S5P_APLL_CON);
  316. } while (!(reg & (0x1 << 29)));
  317. /*
  318. * 7. Change souce clock from SCLKMPLL(667Mhz)
  319. * to SCLKA2M(200Mhz) in MFC_MUX and G3D MUX
  320. * (667/4=166)->(200/4=50)Mhz
  321. */
  322. reg = __raw_readl(S5P_CLK_SRC2);
  323. reg &= ~(S5P_CLKSRC2_G3D_MASK | S5P_CLKSRC2_MFC_MASK);
  324. reg |= (0 << S5P_CLKSRC2_G3D_SHIFT) |
  325. (0 << S5P_CLKSRC2_MFC_SHIFT);
  326. __raw_writel(reg, S5P_CLK_SRC2);
  327. do {
  328. reg = __raw_readl(S5P_CLKMUX_STAT1);
  329. } while (reg & ((1 << 7) | (1 << 3)));
  330. /*
  331. * 8. Change divider for MFC and G3D
  332. * (200/4=50)->(200/1=200)Mhz
  333. */
  334. reg = __raw_readl(S5P_CLK_DIV2);
  335. reg &= ~(S5P_CLKDIV2_G3D_MASK | S5P_CLKDIV2_MFC_MASK);
  336. reg |= (clkdiv_val[index][10] << S5P_CLKDIV2_G3D_SHIFT) |
  337. (clkdiv_val[index][9] << S5P_CLKDIV2_MFC_SHIFT);
  338. __raw_writel(reg, S5P_CLK_DIV2);
  339. /* For MFC, G3D dividing */
  340. do {
  341. reg = __raw_readl(S5P_CLKDIV_STAT0);
  342. } while (reg & ((1 << 16) | (1 << 17)));
  343. /* 9. Change MPLL to APLL in MSYS_MUX */
  344. reg = __raw_readl(S5P_CLK_SRC0);
  345. reg &= ~(S5P_CLKSRC0_MUX200_MASK);
  346. reg |= (0x0 << S5P_CLKSRC0_MUX200_SHIFT);
  347. __raw_writel(reg, S5P_CLK_SRC0);
  348. do {
  349. reg = __raw_readl(S5P_CLKMUX_STAT0);
  350. } while (reg & (0x1 << 18));
  351. /*
  352. * 10. DMC1 refresh counter
  353. * L4 : DMC1 = 100Mhz 7.8us/(1/100) = 0x30c
  354. * Others : DMC1 = 200Mhz 7.8us/(1/200) = 0x618
  355. */
  356. if (!bus_speed_changing)
  357. s5pv210_set_refresh(DMC1, 200000);
  358. }
  359. /*
  360. * L4 level need to change memory bus speed, hence onedram clock divier
  361. * and memory refresh parameter should be changed
  362. */
  363. if (bus_speed_changing) {
  364. reg = __raw_readl(S5P_CLK_DIV6);
  365. reg &= ~S5P_CLKDIV6_ONEDRAM_MASK;
  366. reg |= (clkdiv_val[index][8] << S5P_CLKDIV6_ONEDRAM_SHIFT);
  367. __raw_writel(reg, S5P_CLK_DIV6);
  368. do {
  369. reg = __raw_readl(S5P_CLKDIV_STAT1);
  370. } while (reg & (1 << 15));
  371. /* Reconfigure DRAM refresh counter value */
  372. if (index != L4) {
  373. /*
  374. * DMC0 : 166Mhz
  375. * DMC1 : 200Mhz
  376. */
  377. s5pv210_set_refresh(DMC0, 166000);
  378. s5pv210_set_refresh(DMC1, 200000);
  379. } else {
  380. /*
  381. * DMC0 : 83Mhz
  382. * DMC1 : 100Mhz
  383. */
  384. s5pv210_set_refresh(DMC0, 83000);
  385. s5pv210_set_refresh(DMC1, 100000);
  386. }
  387. }
  388. cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
  389. if (freqs.new < freqs.old) {
  390. regulator_set_voltage(int_regulator,
  391. int_volt, int_volt_max);
  392. regulator_set_voltage(arm_regulator,
  393. arm_volt, arm_volt_max);
  394. }
  395. printk(KERN_DEBUG "Perf changed[L%d]\n", index);
  396. exit:
  397. mutex_unlock(&set_freq_lock);
  398. return ret;
  399. }
  400. #ifdef CONFIG_PM
  401. static int s5pv210_cpufreq_suspend(struct cpufreq_policy *policy)
  402. {
  403. return 0;
  404. }
  405. static int s5pv210_cpufreq_resume(struct cpufreq_policy *policy)
  406. {
  407. return 0;
  408. }
  409. #endif
  410. static int check_mem_type(void __iomem *dmc_reg)
  411. {
  412. unsigned long val;
  413. val = __raw_readl(dmc_reg + 0x4);
  414. val = (val & (0xf << 8));
  415. return val >> 8;
  416. }
  417. static int __init s5pv210_cpu_init(struct cpufreq_policy *policy)
  418. {
  419. unsigned long mem_type;
  420. int ret;
  421. cpu_clk = clk_get(NULL, "armclk");
  422. if (IS_ERR(cpu_clk))
  423. return PTR_ERR(cpu_clk);
  424. dmc0_clk = clk_get(NULL, "sclk_dmc0");
  425. if (IS_ERR(dmc0_clk)) {
  426. ret = PTR_ERR(dmc0_clk);
  427. goto out_dmc0;
  428. }
  429. dmc1_clk = clk_get(NULL, "hclk_msys");
  430. if (IS_ERR(dmc1_clk)) {
  431. ret = PTR_ERR(dmc1_clk);
  432. goto out_dmc1;
  433. }
  434. if (policy->cpu != 0) {
  435. ret = -EINVAL;
  436. goto out_dmc1;
  437. }
  438. /*
  439. * check_mem_type : This driver only support LPDDR & LPDDR2.
  440. * other memory type is not supported.
  441. */
  442. mem_type = check_mem_type(S5P_VA_DMC0);
  443. if ((mem_type != LPDDR) && (mem_type != LPDDR2)) {
  444. printk(KERN_ERR "CPUFreq doesn't support this memory type\n");
  445. ret = -EINVAL;
  446. goto out_dmc1;
  447. }
  448. /* Find current refresh counter and frequency each DMC */
  449. s5pv210_dram_conf[0].refresh = (__raw_readl(S5P_VA_DMC0 + 0x30) * 1000);
  450. s5pv210_dram_conf[0].freq = clk_get_rate(dmc0_clk);
  451. s5pv210_dram_conf[1].refresh = (__raw_readl(S5P_VA_DMC1 + 0x30) * 1000);
  452. s5pv210_dram_conf[1].freq = clk_get_rate(dmc1_clk);
  453. policy->cur = policy->min = policy->max = s5pv210_getspeed(0);
  454. cpufreq_frequency_table_get_attr(s5pv210_freq_table, policy->cpu);
  455. policy->cpuinfo.transition_latency = 40000;
  456. return cpufreq_frequency_table_cpuinfo(policy, s5pv210_freq_table);
  457. out_dmc1:
  458. clk_put(dmc0_clk);
  459. out_dmc0:
  460. clk_put(cpu_clk);
  461. return ret;
  462. }
  463. static int s5pv210_cpufreq_notifier_event(struct notifier_block *this,
  464. unsigned long event, void *ptr)
  465. {
  466. int ret;
  467. switch (event) {
  468. case PM_SUSPEND_PREPARE:
  469. ret = cpufreq_driver_target(cpufreq_cpu_get(0), SLEEP_FREQ,
  470. DISABLE_FURTHER_CPUFREQ);
  471. if (ret < 0)
  472. return NOTIFY_BAD;
  473. return NOTIFY_OK;
  474. case PM_POST_RESTORE:
  475. case PM_POST_SUSPEND:
  476. cpufreq_driver_target(cpufreq_cpu_get(0), SLEEP_FREQ,
  477. ENABLE_FURTHER_CPUFREQ);
  478. return NOTIFY_OK;
  479. }
  480. return NOTIFY_DONE;
  481. }
  482. static int s5pv210_cpufreq_reboot_notifier_event(struct notifier_block *this,
  483. unsigned long event, void *ptr)
  484. {
  485. int ret;
  486. ret = cpufreq_driver_target(cpufreq_cpu_get(0), SLEEP_FREQ,
  487. DISABLE_FURTHER_CPUFREQ);
  488. if (ret < 0)
  489. return NOTIFY_BAD;
  490. return NOTIFY_DONE;
  491. }
  492. static struct cpufreq_driver s5pv210_driver = {
  493. .flags = CPUFREQ_STICKY,
  494. .verify = s5pv210_verify_speed,
  495. .target = s5pv210_target,
  496. .get = s5pv210_getspeed,
  497. .init = s5pv210_cpu_init,
  498. .name = "s5pv210",
  499. #ifdef CONFIG_PM
  500. .suspend = s5pv210_cpufreq_suspend,
  501. .resume = s5pv210_cpufreq_resume,
  502. #endif
  503. };
  504. static struct notifier_block s5pv210_cpufreq_notifier = {
  505. .notifier_call = s5pv210_cpufreq_notifier_event,
  506. };
  507. static struct notifier_block s5pv210_cpufreq_reboot_notifier = {
  508. .notifier_call = s5pv210_cpufreq_reboot_notifier_event,
  509. };
  510. static int __init s5pv210_cpufreq_init(void)
  511. {
  512. arm_regulator = regulator_get(NULL, "vddarm");
  513. if (IS_ERR(arm_regulator)) {
  514. pr_err("failed to get regulator vddarm");
  515. return PTR_ERR(arm_regulator);
  516. }
  517. int_regulator = regulator_get(NULL, "vddint");
  518. if (IS_ERR(int_regulator)) {
  519. pr_err("failed to get regulator vddint");
  520. regulator_put(arm_regulator);
  521. return PTR_ERR(int_regulator);
  522. }
  523. register_pm_notifier(&s5pv210_cpufreq_notifier);
  524. register_reboot_notifier(&s5pv210_cpufreq_reboot_notifier);
  525. return cpufreq_register_driver(&s5pv210_driver);
  526. }
  527. late_initcall(s5pv210_cpufreq_init);