mfgpt_32.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. /*
  2. * Driver/API for AMD Geode Multi-Function General Purpose Timers (MFGPT)
  3. *
  4. * Copyright (C) 2006, Advanced Micro Devices, Inc.
  5. * Copyright (C) 2007, Andres Salomon <dilinger@debian.org>
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of version 2 of the GNU General Public License
  9. * as published by the Free Software Foundation.
  10. *
  11. * The MFGPTs are documented in AMD Geode CS5536 Companion Device Data Book.
  12. */
  13. /*
  14. * We are using the 32Khz input clock - its the only one that has the
  15. * ranges we find desirable. The following table lists the suitable
  16. * divisors and the associated hz, minimum interval
  17. * and the maximum interval:
  18. *
  19. * Divisor Hz Min Delta (S) Max Delta (S)
  20. * 1 32000 .0005 2.048
  21. * 2 16000 .001 4.096
  22. * 4 8000 .002 8.192
  23. * 8 4000 .004 16.384
  24. * 16 2000 .008 32.768
  25. * 32 1000 .016 65.536
  26. * 64 500 .032 131.072
  27. * 128 250 .064 262.144
  28. * 256 125 .128 524.288
  29. */
  30. #include <linux/kernel.h>
  31. #include <linux/interrupt.h>
  32. #include <linux/module.h>
  33. #include <asm/geode.h>
  34. #define F_AVAIL 0x01
  35. static struct mfgpt_timer_t {
  36. int flags;
  37. struct module *owner;
  38. } mfgpt_timers[MFGPT_MAX_TIMERS];
  39. /* Selected from the table above */
  40. #define MFGPT_DIVISOR 16
  41. #define MFGPT_SCALE 4 /* divisor = 2^(scale) */
  42. #define MFGPT_HZ (32000 / MFGPT_DIVISOR)
  43. #define MFGPT_PERIODIC (MFGPT_HZ / HZ)
  44. #ifdef CONFIG_GEODE_MFGPT_TIMER
  45. static int __init mfgpt_timer_setup(void);
  46. #else
  47. #define mfgpt_timer_setup() (0)
  48. #endif
  49. /* Allow for disabling of MFGPTs */
  50. static int disable;
  51. static int __init mfgpt_disable(char *s)
  52. {
  53. disable = 1;
  54. return 1;
  55. }
  56. __setup("nomfgpt", mfgpt_disable);
  57. /*
  58. * Check whether any MFGPTs are available for the kernel to use. In most
  59. * cases, firmware that uses AMD's VSA code will claim all timers during
  60. * bootup; we certainly don't want to take them if they're already in use.
  61. * In other cases (such as with VSAless OpenFirmware), the system firmware
  62. * leaves timers available for us to use.
  63. */
  64. int __init geode_mfgpt_detect(void)
  65. {
  66. int count = 0, i;
  67. u16 val;
  68. if (disable) {
  69. printk(KERN_INFO "geode-mfgpt: Skipping MFGPT setup\n");
  70. return 0;
  71. }
  72. for (i = 0; i < MFGPT_MAX_TIMERS; i++) {
  73. val = geode_mfgpt_read(i, MFGPT_REG_SETUP);
  74. if (!(val & MFGPT_SETUP_SETUP)) {
  75. mfgpt_timers[i].flags = F_AVAIL;
  76. count++;
  77. }
  78. }
  79. /* set up clock event device, if desired */
  80. i = mfgpt_timer_setup();
  81. return count;
  82. }
  83. int geode_mfgpt_toggle_event(int timer, int cmp, int event, int enable)
  84. {
  85. u32 msr, mask, value, dummy;
  86. int shift = (cmp == MFGPT_CMP1) ? 0 : 8;
  87. if (timer < 0 || timer >= MFGPT_MAX_TIMERS)
  88. return -EIO;
  89. /*
  90. * The register maps for these are described in sections 6.17.1.x of
  91. * the AMD Geode CS5536 Companion Device Data Book.
  92. */
  93. switch (event) {
  94. case MFGPT_EVENT_RESET:
  95. /*
  96. * XXX: According to the docs, we cannot reset timers above
  97. * 6; that is, resets for 7 and 8 will be ignored. Is this
  98. * a problem? -dilinger
  99. */
  100. msr = MFGPT_NR_MSR;
  101. mask = 1 << (timer + 24);
  102. break;
  103. case MFGPT_EVENT_NMI:
  104. msr = MFGPT_NR_MSR;
  105. mask = 1 << (timer + shift);
  106. break;
  107. case MFGPT_EVENT_IRQ:
  108. msr = MFGPT_IRQ_MSR;
  109. mask = 1 << (timer + shift);
  110. break;
  111. default:
  112. return -EIO;
  113. }
  114. rdmsr(msr, value, dummy);
  115. if (enable)
  116. value |= mask;
  117. else
  118. value &= ~mask;
  119. wrmsr(msr, value, dummy);
  120. return 0;
  121. }
  122. int geode_mfgpt_set_irq(int timer, int cmp, int irq, int enable)
  123. {
  124. u32 val, dummy;
  125. int offset;
  126. if (timer < 0 || timer >= MFGPT_MAX_TIMERS)
  127. return -EIO;
  128. if (geode_mfgpt_toggle_event(timer, cmp, MFGPT_EVENT_IRQ, enable))
  129. return -EIO;
  130. rdmsr(MSR_PIC_ZSEL_LOW, val, dummy);
  131. offset = (timer % 4) * 4;
  132. val &= ~((0xF << offset) | (0xF << (offset + 16)));
  133. if (enable) {
  134. val |= (irq & 0x0F) << (offset);
  135. val |= (irq & 0x0F) << (offset + 16);
  136. }
  137. wrmsr(MSR_PIC_ZSEL_LOW, val, dummy);
  138. return 0;
  139. }
  140. static int mfgpt_get(int timer, struct module *owner)
  141. {
  142. mfgpt_timers[timer].flags &= ~F_AVAIL;
  143. mfgpt_timers[timer].owner = owner;
  144. printk(KERN_INFO "geode-mfgpt: Registered timer %d\n", timer);
  145. return timer;
  146. }
  147. int geode_mfgpt_alloc_timer(int timer, int domain, struct module *owner)
  148. {
  149. int i;
  150. if (!geode_get_dev_base(GEODE_DEV_MFGPT))
  151. return -ENODEV;
  152. if (timer >= MFGPT_MAX_TIMERS)
  153. return -EIO;
  154. if (timer < 0) {
  155. /* Try to find an available timer */
  156. for (i = 0; i < MFGPT_MAX_TIMERS; i++) {
  157. if (mfgpt_timers[i].flags & F_AVAIL)
  158. return mfgpt_get(i, owner);
  159. if (i == 5 && domain == MFGPT_DOMAIN_WORKING)
  160. break;
  161. }
  162. } else {
  163. /* If they requested a specific timer, try to honor that */
  164. if (mfgpt_timers[timer].flags & F_AVAIL)
  165. return mfgpt_get(timer, owner);
  166. }
  167. /* No timers available - too bad */
  168. return -1;
  169. }
  170. #ifdef CONFIG_GEODE_MFGPT_TIMER
  171. /*
  172. * The MFPGT timers on the CS5536 provide us with suitable timers to use
  173. * as clock event sources - not as good as a HPET or APIC, but certainly
  174. * better then the PIT. This isn't a general purpose MFGPT driver, but
  175. * a simplified one designed specifically to act as a clock event source.
  176. * For full details about the MFGPT, please consult the CS5536 data sheet.
  177. */
  178. #include <linux/clocksource.h>
  179. #include <linux/clockchips.h>
  180. static unsigned int mfgpt_tick_mode = CLOCK_EVT_MODE_SHUTDOWN;
  181. static u16 mfgpt_event_clock;
  182. static int irq = 7;
  183. static int __init mfgpt_setup(char *str)
  184. {
  185. get_option(&str, &irq);
  186. return 1;
  187. }
  188. __setup("mfgpt_irq=", mfgpt_setup);
  189. static inline void mfgpt_disable_timer(u16 clock)
  190. {
  191. u16 val = geode_mfgpt_read(clock, MFGPT_REG_SETUP);
  192. geode_mfgpt_write(clock, MFGPT_REG_SETUP, val & ~MFGPT_SETUP_CNTEN);
  193. }
  194. static int mfgpt_next_event(unsigned long, struct clock_event_device *);
  195. static void mfgpt_set_mode(enum clock_event_mode, struct clock_event_device *);
  196. static struct clock_event_device mfgpt_clockevent = {
  197. .name = "mfgpt-timer",
  198. .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
  199. .set_mode = mfgpt_set_mode,
  200. .set_next_event = mfgpt_next_event,
  201. .rating = 250,
  202. .cpumask = CPU_MASK_ALL,
  203. .shift = 32
  204. };
  205. static inline void mfgpt_start_timer(u16 clock, u16 delta)
  206. {
  207. geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_CMP2, (u16) delta);
  208. geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_COUNTER, 0);
  209. geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_SETUP,
  210. MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP2);
  211. }
  212. static void mfgpt_set_mode(enum clock_event_mode mode,
  213. struct clock_event_device *evt)
  214. {
  215. mfgpt_disable_timer(mfgpt_event_clock);
  216. if (mode == CLOCK_EVT_MODE_PERIODIC)
  217. mfgpt_start_timer(mfgpt_event_clock, MFGPT_PERIODIC);
  218. mfgpt_tick_mode = mode;
  219. }
  220. static int mfgpt_next_event(unsigned long delta, struct clock_event_device *evt)
  221. {
  222. mfgpt_start_timer(mfgpt_event_clock, delta);
  223. return 0;
  224. }
  225. /* Assume (foolishly?), that this interrupt was due to our tick */
  226. static irqreturn_t mfgpt_tick(int irq, void *dev_id)
  227. {
  228. if (mfgpt_tick_mode == CLOCK_EVT_MODE_SHUTDOWN)
  229. return IRQ_HANDLED;
  230. /* Turn off the clock */
  231. mfgpt_disable_timer(mfgpt_event_clock);
  232. /* Clear the counter */
  233. geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_COUNTER, 0);
  234. /* Restart the clock in periodic mode */
  235. if (mfgpt_tick_mode == CLOCK_EVT_MODE_PERIODIC) {
  236. geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_SETUP,
  237. MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP2);
  238. }
  239. mfgpt_clockevent.event_handler(&mfgpt_clockevent);
  240. return IRQ_HANDLED;
  241. }
  242. static struct irqaction mfgptirq = {
  243. .handler = mfgpt_tick,
  244. .flags = IRQF_DISABLED | IRQF_NOBALANCING,
  245. .mask = CPU_MASK_NONE,
  246. .name = "mfgpt-timer"
  247. };
  248. static int __init mfgpt_timer_setup(void)
  249. {
  250. int timer, ret;
  251. u16 val;
  252. timer = geode_mfgpt_alloc_timer(MFGPT_TIMER_ANY, MFGPT_DOMAIN_WORKING,
  253. THIS_MODULE);
  254. if (timer < 0) {
  255. printk(KERN_ERR
  256. "mfgpt-timer: Could not allocate a MFPGT timer\n");
  257. return -ENODEV;
  258. }
  259. mfgpt_event_clock = timer;
  260. /* Set the clock scale and enable the event mode for CMP2 */
  261. val = MFGPT_SCALE | (3 << 8);
  262. geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_SETUP, val);
  263. /* Set up the IRQ on the MFGPT side */
  264. if (geode_mfgpt_setup_irq(mfgpt_event_clock, MFGPT_CMP2, irq)) {
  265. printk(KERN_ERR "mfgpt-timer: Could not set up IRQ %d\n", irq);
  266. return -EIO;
  267. }
  268. /* And register it with the kernel */
  269. ret = setup_irq(irq, &mfgptirq);
  270. if (ret) {
  271. printk(KERN_ERR
  272. "mfgpt-timer: Unable to set up the interrupt.\n");
  273. goto err;
  274. }
  275. /* Set up the clock event */
  276. mfgpt_clockevent.mult = div_sc(MFGPT_HZ, NSEC_PER_SEC, 32);
  277. mfgpt_clockevent.min_delta_ns = clockevent_delta2ns(0xF,
  278. &mfgpt_clockevent);
  279. mfgpt_clockevent.max_delta_ns = clockevent_delta2ns(0xFFFE,
  280. &mfgpt_clockevent);
  281. printk(KERN_INFO
  282. "mfgpt-timer: registering the MFGT timer as a clock event.\n");
  283. clockevents_register_device(&mfgpt_clockevent);
  284. return 0;
  285. err:
  286. geode_mfgpt_release_irq(mfgpt_event_clock, MFGPT_CMP2, irq);
  287. printk(KERN_ERR
  288. "mfgpt-timer: Unable to set up the MFGPT clock source\n");
  289. return -EIO;
  290. }
  291. #endif