s5pv210-cpufreq.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  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. freqs.cpu = 0;
  190. if (freqs.new == freqs.old)
  191. goto exit;
  192. /* Finding current running level index */
  193. if (cpufreq_frequency_table_target(policy, s5pv210_freq_table,
  194. freqs.old, relation, &priv_index)) {
  195. ret = -EINVAL;
  196. goto exit;
  197. }
  198. arm_volt = dvs_conf[index].arm_volt;
  199. int_volt = dvs_conf[index].int_volt;
  200. if (freqs.new > freqs.old) {
  201. ret = regulator_set_voltage(arm_regulator,
  202. arm_volt, arm_volt_max);
  203. if (ret)
  204. goto exit;
  205. ret = regulator_set_voltage(int_regulator,
  206. int_volt, int_volt_max);
  207. if (ret)
  208. goto exit;
  209. }
  210. cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
  211. /* Check if there need to change PLL */
  212. if ((index == L0) || (priv_index == L0))
  213. pll_changing = 1;
  214. /* Check if there need to change System bus clock */
  215. if ((index == L4) || (priv_index == L4))
  216. bus_speed_changing = 1;
  217. if (bus_speed_changing) {
  218. /*
  219. * Reconfigure DRAM refresh counter value for minimum
  220. * temporary clock while changing divider.
  221. * expected clock is 83Mhz : 7.8usec/(1/83Mhz) = 0x287
  222. */
  223. if (pll_changing)
  224. s5pv210_set_refresh(DMC1, 83000);
  225. else
  226. s5pv210_set_refresh(DMC1, 100000);
  227. s5pv210_set_refresh(DMC0, 83000);
  228. }
  229. /*
  230. * APLL should be changed in this level
  231. * APLL -> MPLL(for stable transition) -> APLL
  232. * Some clock source's clock API are not prepared.
  233. * Do not use clock API in below code.
  234. */
  235. if (pll_changing) {
  236. /*
  237. * 1. Temporary Change divider for MFC and G3D
  238. * SCLKA2M(200/1=200)->(200/4=50)Mhz
  239. */
  240. reg = __raw_readl(S5P_CLK_DIV2);
  241. reg &= ~(S5P_CLKDIV2_G3D_MASK | S5P_CLKDIV2_MFC_MASK);
  242. reg |= (3 << S5P_CLKDIV2_G3D_SHIFT) |
  243. (3 << S5P_CLKDIV2_MFC_SHIFT);
  244. __raw_writel(reg, S5P_CLK_DIV2);
  245. /* For MFC, G3D dividing */
  246. do {
  247. reg = __raw_readl(S5P_CLKDIV_STAT0);
  248. } while (reg & ((1 << 16) | (1 << 17)));
  249. /*
  250. * 2. Change SCLKA2M(200Mhz)to SCLKMPLL in MFC_MUX, G3D MUX
  251. * (200/4=50)->(667/4=166)Mhz
  252. */
  253. reg = __raw_readl(S5P_CLK_SRC2);
  254. reg &= ~(S5P_CLKSRC2_G3D_MASK | S5P_CLKSRC2_MFC_MASK);
  255. reg |= (1 << S5P_CLKSRC2_G3D_SHIFT) |
  256. (1 << S5P_CLKSRC2_MFC_SHIFT);
  257. __raw_writel(reg, S5P_CLK_SRC2);
  258. do {
  259. reg = __raw_readl(S5P_CLKMUX_STAT1);
  260. } while (reg & ((1 << 7) | (1 << 3)));
  261. /*
  262. * 3. DMC1 refresh count for 133Mhz if (index == L4) is
  263. * true refresh counter is already programed in upper
  264. * code. 0x287@83Mhz
  265. */
  266. if (!bus_speed_changing)
  267. s5pv210_set_refresh(DMC1, 133000);
  268. /* 4. SCLKAPLL -> SCLKMPLL */
  269. reg = __raw_readl(S5P_CLK_SRC0);
  270. reg &= ~(S5P_CLKSRC0_MUX200_MASK);
  271. reg |= (0x1 << S5P_CLKSRC0_MUX200_SHIFT);
  272. __raw_writel(reg, S5P_CLK_SRC0);
  273. do {
  274. reg = __raw_readl(S5P_CLKMUX_STAT0);
  275. } while (reg & (0x1 << 18));
  276. }
  277. /* Change divider */
  278. reg = __raw_readl(S5P_CLK_DIV0);
  279. reg &= ~(S5P_CLKDIV0_APLL_MASK | S5P_CLKDIV0_A2M_MASK |
  280. S5P_CLKDIV0_HCLK200_MASK | S5P_CLKDIV0_PCLK100_MASK |
  281. S5P_CLKDIV0_HCLK166_MASK | S5P_CLKDIV0_PCLK83_MASK |
  282. S5P_CLKDIV0_HCLK133_MASK | S5P_CLKDIV0_PCLK66_MASK);
  283. reg |= ((clkdiv_val[index][0] << S5P_CLKDIV0_APLL_SHIFT) |
  284. (clkdiv_val[index][1] << S5P_CLKDIV0_A2M_SHIFT) |
  285. (clkdiv_val[index][2] << S5P_CLKDIV0_HCLK200_SHIFT) |
  286. (clkdiv_val[index][3] << S5P_CLKDIV0_PCLK100_SHIFT) |
  287. (clkdiv_val[index][4] << S5P_CLKDIV0_HCLK166_SHIFT) |
  288. (clkdiv_val[index][5] << S5P_CLKDIV0_PCLK83_SHIFT) |
  289. (clkdiv_val[index][6] << S5P_CLKDIV0_HCLK133_SHIFT) |
  290. (clkdiv_val[index][7] << S5P_CLKDIV0_PCLK66_SHIFT));
  291. __raw_writel(reg, S5P_CLK_DIV0);
  292. do {
  293. reg = __raw_readl(S5P_CLKDIV_STAT0);
  294. } while (reg & 0xff);
  295. /* ARM MCS value changed */
  296. reg = __raw_readl(S5P_ARM_MCS_CON);
  297. reg &= ~0x3;
  298. if (index >= L3)
  299. reg |= 0x3;
  300. else
  301. reg |= 0x1;
  302. __raw_writel(reg, S5P_ARM_MCS_CON);
  303. if (pll_changing) {
  304. /* 5. Set Lock time = 30us*24Mhz = 0x2cf */
  305. __raw_writel(0x2cf, S5P_APLL_LOCK);
  306. /*
  307. * 6. Turn on APLL
  308. * 6-1. Set PMS values
  309. * 6-2. Wait untile the PLL is locked
  310. */
  311. if (index == L0)
  312. __raw_writel(APLL_VAL_1000, S5P_APLL_CON);
  313. else
  314. __raw_writel(APLL_VAL_800, S5P_APLL_CON);
  315. do {
  316. reg = __raw_readl(S5P_APLL_CON);
  317. } while (!(reg & (0x1 << 29)));
  318. /*
  319. * 7. Change souce clock from SCLKMPLL(667Mhz)
  320. * to SCLKA2M(200Mhz) in MFC_MUX and G3D MUX
  321. * (667/4=166)->(200/4=50)Mhz
  322. */
  323. reg = __raw_readl(S5P_CLK_SRC2);
  324. reg &= ~(S5P_CLKSRC2_G3D_MASK | S5P_CLKSRC2_MFC_MASK);
  325. reg |= (0 << S5P_CLKSRC2_G3D_SHIFT) |
  326. (0 << S5P_CLKSRC2_MFC_SHIFT);
  327. __raw_writel(reg, S5P_CLK_SRC2);
  328. do {
  329. reg = __raw_readl(S5P_CLKMUX_STAT1);
  330. } while (reg & ((1 << 7) | (1 << 3)));
  331. /*
  332. * 8. Change divider for MFC and G3D
  333. * (200/4=50)->(200/1=200)Mhz
  334. */
  335. reg = __raw_readl(S5P_CLK_DIV2);
  336. reg &= ~(S5P_CLKDIV2_G3D_MASK | S5P_CLKDIV2_MFC_MASK);
  337. reg |= (clkdiv_val[index][10] << S5P_CLKDIV2_G3D_SHIFT) |
  338. (clkdiv_val[index][9] << S5P_CLKDIV2_MFC_SHIFT);
  339. __raw_writel(reg, S5P_CLK_DIV2);
  340. /* For MFC, G3D dividing */
  341. do {
  342. reg = __raw_readl(S5P_CLKDIV_STAT0);
  343. } while (reg & ((1 << 16) | (1 << 17)));
  344. /* 9. Change MPLL to APLL in MSYS_MUX */
  345. reg = __raw_readl(S5P_CLK_SRC0);
  346. reg &= ~(S5P_CLKSRC0_MUX200_MASK);
  347. reg |= (0x0 << S5P_CLKSRC0_MUX200_SHIFT);
  348. __raw_writel(reg, S5P_CLK_SRC0);
  349. do {
  350. reg = __raw_readl(S5P_CLKMUX_STAT0);
  351. } while (reg & (0x1 << 18));
  352. /*
  353. * 10. DMC1 refresh counter
  354. * L4 : DMC1 = 100Mhz 7.8us/(1/100) = 0x30c
  355. * Others : DMC1 = 200Mhz 7.8us/(1/200) = 0x618
  356. */
  357. if (!bus_speed_changing)
  358. s5pv210_set_refresh(DMC1, 200000);
  359. }
  360. /*
  361. * L4 level need to change memory bus speed, hence onedram clock divier
  362. * and memory refresh parameter should be changed
  363. */
  364. if (bus_speed_changing) {
  365. reg = __raw_readl(S5P_CLK_DIV6);
  366. reg &= ~S5P_CLKDIV6_ONEDRAM_MASK;
  367. reg |= (clkdiv_val[index][8] << S5P_CLKDIV6_ONEDRAM_SHIFT);
  368. __raw_writel(reg, S5P_CLK_DIV6);
  369. do {
  370. reg = __raw_readl(S5P_CLKDIV_STAT1);
  371. } while (reg & (1 << 15));
  372. /* Reconfigure DRAM refresh counter value */
  373. if (index != L4) {
  374. /*
  375. * DMC0 : 166Mhz
  376. * DMC1 : 200Mhz
  377. */
  378. s5pv210_set_refresh(DMC0, 166000);
  379. s5pv210_set_refresh(DMC1, 200000);
  380. } else {
  381. /*
  382. * DMC0 : 83Mhz
  383. * DMC1 : 100Mhz
  384. */
  385. s5pv210_set_refresh(DMC0, 83000);
  386. s5pv210_set_refresh(DMC1, 100000);
  387. }
  388. }
  389. cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
  390. if (freqs.new < freqs.old) {
  391. regulator_set_voltage(int_regulator,
  392. int_volt, int_volt_max);
  393. regulator_set_voltage(arm_regulator,
  394. arm_volt, arm_volt_max);
  395. }
  396. printk(KERN_DEBUG "Perf changed[L%d]\n", index);
  397. exit:
  398. mutex_unlock(&set_freq_lock);
  399. return ret;
  400. }
  401. #ifdef CONFIG_PM
  402. static int s5pv210_cpufreq_suspend(struct cpufreq_policy *policy)
  403. {
  404. return 0;
  405. }
  406. static int s5pv210_cpufreq_resume(struct cpufreq_policy *policy)
  407. {
  408. return 0;
  409. }
  410. #endif
  411. static int check_mem_type(void __iomem *dmc_reg)
  412. {
  413. unsigned long val;
  414. val = __raw_readl(dmc_reg + 0x4);
  415. val = (val & (0xf << 8));
  416. return val >> 8;
  417. }
  418. static int __init s5pv210_cpu_init(struct cpufreq_policy *policy)
  419. {
  420. unsigned long mem_type;
  421. int ret;
  422. cpu_clk = clk_get(NULL, "armclk");
  423. if (IS_ERR(cpu_clk))
  424. return PTR_ERR(cpu_clk);
  425. dmc0_clk = clk_get(NULL, "sclk_dmc0");
  426. if (IS_ERR(dmc0_clk)) {
  427. ret = PTR_ERR(dmc0_clk);
  428. goto out_dmc0;
  429. }
  430. dmc1_clk = clk_get(NULL, "hclk_msys");
  431. if (IS_ERR(dmc1_clk)) {
  432. ret = PTR_ERR(dmc1_clk);
  433. goto out_dmc1;
  434. }
  435. if (policy->cpu != 0) {
  436. ret = -EINVAL;
  437. goto out_dmc1;
  438. }
  439. /*
  440. * check_mem_type : This driver only support LPDDR & LPDDR2.
  441. * other memory type is not supported.
  442. */
  443. mem_type = check_mem_type(S5P_VA_DMC0);
  444. if ((mem_type != LPDDR) && (mem_type != LPDDR2)) {
  445. printk(KERN_ERR "CPUFreq doesn't support this memory type\n");
  446. ret = -EINVAL;
  447. goto out_dmc1;
  448. }
  449. /* Find current refresh counter and frequency each DMC */
  450. s5pv210_dram_conf[0].refresh = (__raw_readl(S5P_VA_DMC0 + 0x30) * 1000);
  451. s5pv210_dram_conf[0].freq = clk_get_rate(dmc0_clk);
  452. s5pv210_dram_conf[1].refresh = (__raw_readl(S5P_VA_DMC1 + 0x30) * 1000);
  453. s5pv210_dram_conf[1].freq = clk_get_rate(dmc1_clk);
  454. policy->cur = policy->min = policy->max = s5pv210_getspeed(0);
  455. cpufreq_frequency_table_get_attr(s5pv210_freq_table, policy->cpu);
  456. policy->cpuinfo.transition_latency = 40000;
  457. return cpufreq_frequency_table_cpuinfo(policy, s5pv210_freq_table);
  458. out_dmc1:
  459. clk_put(dmc0_clk);
  460. out_dmc0:
  461. clk_put(cpu_clk);
  462. return ret;
  463. }
  464. static int s5pv210_cpufreq_notifier_event(struct notifier_block *this,
  465. unsigned long event, void *ptr)
  466. {
  467. int ret;
  468. switch (event) {
  469. case PM_SUSPEND_PREPARE:
  470. ret = cpufreq_driver_target(cpufreq_cpu_get(0), SLEEP_FREQ,
  471. DISABLE_FURTHER_CPUFREQ);
  472. if (ret < 0)
  473. return NOTIFY_BAD;
  474. return NOTIFY_OK;
  475. case PM_POST_RESTORE:
  476. case PM_POST_SUSPEND:
  477. cpufreq_driver_target(cpufreq_cpu_get(0), SLEEP_FREQ,
  478. ENABLE_FURTHER_CPUFREQ);
  479. return NOTIFY_OK;
  480. }
  481. return NOTIFY_DONE;
  482. }
  483. static int s5pv210_cpufreq_reboot_notifier_event(struct notifier_block *this,
  484. unsigned long event, void *ptr)
  485. {
  486. int ret;
  487. ret = cpufreq_driver_target(cpufreq_cpu_get(0), SLEEP_FREQ,
  488. DISABLE_FURTHER_CPUFREQ);
  489. if (ret < 0)
  490. return NOTIFY_BAD;
  491. return NOTIFY_DONE;
  492. }
  493. static struct cpufreq_driver s5pv210_driver = {
  494. .flags = CPUFREQ_STICKY,
  495. .verify = s5pv210_verify_speed,
  496. .target = s5pv210_target,
  497. .get = s5pv210_getspeed,
  498. .init = s5pv210_cpu_init,
  499. .name = "s5pv210",
  500. #ifdef CONFIG_PM
  501. .suspend = s5pv210_cpufreq_suspend,
  502. .resume = s5pv210_cpufreq_resume,
  503. #endif
  504. };
  505. static struct notifier_block s5pv210_cpufreq_notifier = {
  506. .notifier_call = s5pv210_cpufreq_notifier_event,
  507. };
  508. static struct notifier_block s5pv210_cpufreq_reboot_notifier = {
  509. .notifier_call = s5pv210_cpufreq_reboot_notifier_event,
  510. };
  511. static int __init s5pv210_cpufreq_init(void)
  512. {
  513. arm_regulator = regulator_get(NULL, "vddarm");
  514. if (IS_ERR(arm_regulator)) {
  515. pr_err("failed to get regulator vddarm");
  516. return PTR_ERR(arm_regulator);
  517. }
  518. int_regulator = regulator_get(NULL, "vddint");
  519. if (IS_ERR(int_regulator)) {
  520. pr_err("failed to get regulator vddint");
  521. regulator_put(arm_regulator);
  522. return PTR_ERR(int_regulator);
  523. }
  524. register_pm_notifier(&s5pv210_cpufreq_notifier);
  525. register_reboot_notifier(&s5pv210_cpufreq_reboot_notifier);
  526. return cpufreq_register_driver(&s5pv210_driver);
  527. }
  528. late_initcall(s5pv210_cpufreq_init);