s5p-time.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. /* linux/arch/arm/plat-s5p/s5p-time.c
  2. *
  3. * Copyright (c) 2011 Samsung Electronics Co., Ltd.
  4. * http://www.samsung.com/
  5. *
  6. * S5P - Common hr-timer 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/sched.h>
  13. #include <linux/interrupt.h>
  14. #include <linux/irq.h>
  15. #include <linux/err.h>
  16. #include <linux/clk.h>
  17. #include <linux/clockchips.h>
  18. #include <linux/platform_device.h>
  19. #include <asm/smp_twd.h>
  20. #include <asm/mach/time.h>
  21. #include <asm/mach/arch.h>
  22. #include <asm/mach/map.h>
  23. #include <asm/sched_clock.h>
  24. #include <mach/map.h>
  25. #include <plat/devs.h>
  26. #include <plat/regs-timer.h>
  27. #include <plat/s5p-time.h>
  28. static struct clk *tin_event;
  29. static struct clk *tin_source;
  30. static struct clk *tdiv_event;
  31. static struct clk *tdiv_source;
  32. static struct clk *timerclk;
  33. static struct s5p_timer_source timer_source;
  34. static unsigned long clock_count_per_tick;
  35. static void s5p_timer_resume(void);
  36. static void s5p_time_stop(enum s5p_timer_mode mode)
  37. {
  38. unsigned long tcon;
  39. tcon = __raw_readl(S3C2410_TCON);
  40. switch (mode) {
  41. case S5P_PWM0:
  42. tcon &= ~S3C2410_TCON_T0START;
  43. break;
  44. case S5P_PWM1:
  45. tcon &= ~S3C2410_TCON_T1START;
  46. break;
  47. case S5P_PWM2:
  48. tcon &= ~S3C2410_TCON_T2START;
  49. break;
  50. case S5P_PWM3:
  51. tcon &= ~S3C2410_TCON_T3START;
  52. break;
  53. case S5P_PWM4:
  54. tcon &= ~S3C2410_TCON_T4START;
  55. break;
  56. default:
  57. printk(KERN_ERR "Invalid Timer %d\n", mode);
  58. break;
  59. }
  60. __raw_writel(tcon, S3C2410_TCON);
  61. }
  62. static void s5p_time_setup(enum s5p_timer_mode mode, unsigned long tcnt)
  63. {
  64. unsigned long tcon;
  65. tcon = __raw_readl(S3C2410_TCON);
  66. tcnt--;
  67. switch (mode) {
  68. case S5P_PWM0:
  69. tcon &= ~(0x0f << 0);
  70. tcon |= S3C2410_TCON_T0MANUALUPD;
  71. break;
  72. case S5P_PWM1:
  73. tcon &= ~(0x0f << 8);
  74. tcon |= S3C2410_TCON_T1MANUALUPD;
  75. break;
  76. case S5P_PWM2:
  77. tcon &= ~(0x0f << 12);
  78. tcon |= S3C2410_TCON_T2MANUALUPD;
  79. break;
  80. case S5P_PWM3:
  81. tcon &= ~(0x0f << 16);
  82. tcon |= S3C2410_TCON_T3MANUALUPD;
  83. break;
  84. case S5P_PWM4:
  85. tcon &= ~(0x07 << 20);
  86. tcon |= S3C2410_TCON_T4MANUALUPD;
  87. break;
  88. default:
  89. printk(KERN_ERR "Invalid Timer %d\n", mode);
  90. break;
  91. }
  92. __raw_writel(tcnt, S3C2410_TCNTB(mode));
  93. __raw_writel(tcnt, S3C2410_TCMPB(mode));
  94. __raw_writel(tcon, S3C2410_TCON);
  95. }
  96. static void s5p_time_start(enum s5p_timer_mode mode, bool periodic)
  97. {
  98. unsigned long tcon;
  99. tcon = __raw_readl(S3C2410_TCON);
  100. switch (mode) {
  101. case S5P_PWM0:
  102. tcon |= S3C2410_TCON_T0START;
  103. tcon &= ~S3C2410_TCON_T0MANUALUPD;
  104. if (periodic)
  105. tcon |= S3C2410_TCON_T0RELOAD;
  106. else
  107. tcon &= ~S3C2410_TCON_T0RELOAD;
  108. break;
  109. case S5P_PWM1:
  110. tcon |= S3C2410_TCON_T1START;
  111. tcon &= ~S3C2410_TCON_T1MANUALUPD;
  112. if (periodic)
  113. tcon |= S3C2410_TCON_T1RELOAD;
  114. else
  115. tcon &= ~S3C2410_TCON_T1RELOAD;
  116. break;
  117. case S5P_PWM2:
  118. tcon |= S3C2410_TCON_T2START;
  119. tcon &= ~S3C2410_TCON_T2MANUALUPD;
  120. if (periodic)
  121. tcon |= S3C2410_TCON_T2RELOAD;
  122. else
  123. tcon &= ~S3C2410_TCON_T2RELOAD;
  124. break;
  125. case S5P_PWM3:
  126. tcon |= S3C2410_TCON_T3START;
  127. tcon &= ~S3C2410_TCON_T3MANUALUPD;
  128. if (periodic)
  129. tcon |= S3C2410_TCON_T3RELOAD;
  130. else
  131. tcon &= ~S3C2410_TCON_T3RELOAD;
  132. break;
  133. case S5P_PWM4:
  134. tcon |= S3C2410_TCON_T4START;
  135. tcon &= ~S3C2410_TCON_T4MANUALUPD;
  136. if (periodic)
  137. tcon |= S3C2410_TCON_T4RELOAD;
  138. else
  139. tcon &= ~S3C2410_TCON_T4RELOAD;
  140. break;
  141. default:
  142. printk(KERN_ERR "Invalid Timer %d\n", mode);
  143. break;
  144. }
  145. __raw_writel(tcon, S3C2410_TCON);
  146. }
  147. static int s5p_set_next_event(unsigned long cycles,
  148. struct clock_event_device *evt)
  149. {
  150. s5p_time_setup(timer_source.event_id, cycles);
  151. s5p_time_start(timer_source.event_id, NON_PERIODIC);
  152. return 0;
  153. }
  154. static void s5p_set_mode(enum clock_event_mode mode,
  155. struct clock_event_device *evt)
  156. {
  157. s5p_time_stop(timer_source.event_id);
  158. switch (mode) {
  159. case CLOCK_EVT_MODE_PERIODIC:
  160. s5p_time_setup(timer_source.event_id, clock_count_per_tick);
  161. s5p_time_start(timer_source.event_id, PERIODIC);
  162. break;
  163. case CLOCK_EVT_MODE_ONESHOT:
  164. break;
  165. case CLOCK_EVT_MODE_UNUSED:
  166. case CLOCK_EVT_MODE_SHUTDOWN:
  167. break;
  168. case CLOCK_EVT_MODE_RESUME:
  169. s5p_timer_resume();
  170. break;
  171. }
  172. }
  173. static void s5p_timer_resume(void)
  174. {
  175. /* event timer restart */
  176. s5p_time_setup(timer_source.event_id, clock_count_per_tick);
  177. s5p_time_start(timer_source.event_id, PERIODIC);
  178. /* source timer restart */
  179. s5p_time_setup(timer_source.source_id, TCNT_MAX);
  180. s5p_time_start(timer_source.source_id, PERIODIC);
  181. }
  182. void __init s5p_set_timer_source(enum s5p_timer_mode event,
  183. enum s5p_timer_mode source)
  184. {
  185. s3c_device_timer[event].dev.bus = &platform_bus_type;
  186. s3c_device_timer[source].dev.bus = &platform_bus_type;
  187. timer_source.event_id = event;
  188. timer_source.source_id = source;
  189. }
  190. static struct clock_event_device time_event_device = {
  191. .name = "s5p_event_timer",
  192. .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
  193. .rating = 200,
  194. .set_next_event = s5p_set_next_event,
  195. .set_mode = s5p_set_mode,
  196. };
  197. static irqreturn_t s5p_clock_event_isr(int irq, void *dev_id)
  198. {
  199. struct clock_event_device *evt = dev_id;
  200. evt->event_handler(evt);
  201. return IRQ_HANDLED;
  202. }
  203. static struct irqaction s5p_clock_event_irq = {
  204. .name = "s5p_time_irq",
  205. .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
  206. .handler = s5p_clock_event_isr,
  207. .dev_id = &time_event_device,
  208. };
  209. static void __init s5p_clockevent_init(void)
  210. {
  211. unsigned long pclk;
  212. unsigned long clock_rate;
  213. unsigned int irq_number;
  214. struct clk *tscaler;
  215. pclk = clk_get_rate(timerclk);
  216. tscaler = clk_get_parent(tdiv_event);
  217. clk_set_rate(tscaler, pclk / 2);
  218. clk_set_rate(tdiv_event, pclk / 2);
  219. clk_set_parent(tin_event, tdiv_event);
  220. clock_rate = clk_get_rate(tin_event);
  221. clock_count_per_tick = clock_rate / HZ;
  222. clockevents_calc_mult_shift(&time_event_device,
  223. clock_rate, S5PTIMER_MIN_RANGE);
  224. time_event_device.max_delta_ns =
  225. clockevent_delta2ns(-1, &time_event_device);
  226. time_event_device.min_delta_ns =
  227. clockevent_delta2ns(1, &time_event_device);
  228. time_event_device.cpumask = cpumask_of(0);
  229. clockevents_register_device(&time_event_device);
  230. irq_number = timer_source.event_id + IRQ_TIMER0;
  231. setup_irq(irq_number, &s5p_clock_event_irq);
  232. }
  233. static cycle_t s5p_timer_read(struct clocksource *cs)
  234. {
  235. unsigned long offset = 0;
  236. switch (timer_source.source_id) {
  237. case S5P_PWM0:
  238. case S5P_PWM1:
  239. case S5P_PWM2:
  240. case S5P_PWM3:
  241. offset = (timer_source.source_id * 0x0c) + 0x14;
  242. break;
  243. case S5P_PWM4:
  244. offset = 0x40;
  245. break;
  246. default:
  247. printk(KERN_ERR "Invalid Timer %d\n", timer_source.source_id);
  248. return 0;
  249. }
  250. return (cycle_t) ~__raw_readl(S3C_TIMERREG(offset));
  251. }
  252. /*
  253. * Override the global weak sched_clock symbol with this
  254. * local implementation which uses the clocksource to get some
  255. * better resolution when scheduling the kernel. We accept that
  256. * this wraps around for now, since it is just a relative time
  257. * stamp. (Inspired by U300 implementation.)
  258. */
  259. static DEFINE_CLOCK_DATA(cd);
  260. unsigned long long notrace sched_clock(void)
  261. {
  262. u32 cyc;
  263. unsigned long offset = 0;
  264. switch (timer_source.source_id) {
  265. case S5P_PWM0:
  266. case S5P_PWM1:
  267. case S5P_PWM2:
  268. case S5P_PWM3:
  269. offset = (timer_source.source_id * 0x0c) + 0x14;
  270. break;
  271. case S5P_PWM4:
  272. offset = 0x40;
  273. break;
  274. default:
  275. printk(KERN_ERR "Invalid Timer %d\n", timer_source.source_id);
  276. return 0;
  277. }
  278. cyc = ~__raw_readl(S3C_TIMERREG(offset));
  279. return cyc_to_sched_clock(&cd, cyc, (u32)~0);
  280. }
  281. static void notrace s5p_update_sched_clock(void)
  282. {
  283. u32 cyc;
  284. unsigned long offset = 0;
  285. switch (timer_source.source_id) {
  286. case S5P_PWM0:
  287. case S5P_PWM1:
  288. case S5P_PWM2:
  289. case S5P_PWM3:
  290. offset = (timer_source.source_id * 0x0c) + 0x14;
  291. break;
  292. case S5P_PWM4:
  293. offset = 0x40;
  294. break;
  295. default:
  296. printk(KERN_ERR "Invalid Timer %d\n", timer_source.source_id);
  297. }
  298. cyc = ~__raw_readl(S3C_TIMERREG(offset));
  299. update_sched_clock(&cd, cyc, (u32)~0);
  300. }
  301. struct clocksource time_clocksource = {
  302. .name = "s5p_clocksource_timer",
  303. .rating = 250,
  304. .read = s5p_timer_read,
  305. .mask = CLOCKSOURCE_MASK(32),
  306. .flags = CLOCK_SOURCE_IS_CONTINUOUS,
  307. };
  308. static void __init s5p_clocksource_init(void)
  309. {
  310. unsigned long pclk;
  311. unsigned long clock_rate;
  312. pclk = clk_get_rate(timerclk);
  313. clk_set_rate(tdiv_source, pclk / 2);
  314. clk_set_parent(tin_source, tdiv_source);
  315. clock_rate = clk_get_rate(tin_source);
  316. init_sched_clock(&cd, s5p_update_sched_clock, 32, clock_rate);
  317. s5p_time_setup(timer_source.source_id, TCNT_MAX);
  318. s5p_time_start(timer_source.source_id, PERIODIC);
  319. if (clocksource_register_hz(&time_clocksource, clock_rate))
  320. panic("%s: can't register clocksource\n", time_clocksource.name);
  321. }
  322. static void __init s5p_timer_resources(void)
  323. {
  324. unsigned long event_id = timer_source.event_id;
  325. unsigned long source_id = timer_source.source_id;
  326. timerclk = clk_get(NULL, "timers");
  327. if (IS_ERR(timerclk))
  328. panic("failed to get timers clock for timer");
  329. clk_enable(timerclk);
  330. tin_event = clk_get(&s3c_device_timer[event_id].dev, "pwm-tin");
  331. if (IS_ERR(tin_event))
  332. panic("failed to get pwm-tin clock for event timer");
  333. tdiv_event = clk_get(&s3c_device_timer[event_id].dev, "pwm-tdiv");
  334. if (IS_ERR(tdiv_event))
  335. panic("failed to get pwm-tdiv clock for event timer");
  336. clk_enable(tin_event);
  337. tin_source = clk_get(&s3c_device_timer[source_id].dev, "pwm-tin");
  338. if (IS_ERR(tin_source))
  339. panic("failed to get pwm-tin clock for source timer");
  340. tdiv_source = clk_get(&s3c_device_timer[source_id].dev, "pwm-tdiv");
  341. if (IS_ERR(tdiv_source))
  342. panic("failed to get pwm-tdiv clock for source timer");
  343. clk_enable(tin_source);
  344. }
  345. static void __init s5p_timer_init(void)
  346. {
  347. s5p_timer_resources();
  348. s5p_clockevent_init();
  349. s5p_clocksource_init();
  350. }
  351. struct sys_timer s5p_timer = {
  352. .init = s5p_timer_init,
  353. };