hpet.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. #include <linux/init.h>
  2. #include <linux/pci.h>
  3. #include <linux/percpu.h>
  4. #include <linux/delay.h>
  5. #include <linux/spinlock.h>
  6. #include <linux/interrupt.h>
  7. #include <asm/hpet.h>
  8. #include <asm/time.h>
  9. #define SMBUS_CFG_BASE (loongson_sysconf.ht_control_base + 0x0300a000)
  10. #define SMBUS_PCI_REG40 0x40
  11. #define SMBUS_PCI_REG64 0x64
  12. #define SMBUS_PCI_REGB4 0xb4
  13. #define HPET_MIN_CYCLES 16
  14. #define HPET_MIN_PROG_DELTA (HPET_MIN_CYCLES * 12)
  15. static DEFINE_SPINLOCK(hpet_lock);
  16. DEFINE_PER_CPU(struct clock_event_device, hpet_clockevent_device);
  17. static unsigned int smbus_read(int offset)
  18. {
  19. return *(volatile unsigned int *)(SMBUS_CFG_BASE + offset);
  20. }
  21. static void smbus_write(int offset, int data)
  22. {
  23. *(volatile unsigned int *)(SMBUS_CFG_BASE + offset) = data;
  24. }
  25. static void smbus_enable(int offset, int bit)
  26. {
  27. unsigned int cfg = smbus_read(offset);
  28. cfg |= bit;
  29. smbus_write(offset, cfg);
  30. }
  31. static int hpet_read(int offset)
  32. {
  33. return *(volatile unsigned int *)(HPET_MMIO_ADDR + offset);
  34. }
  35. static void hpet_write(int offset, int data)
  36. {
  37. *(volatile unsigned int *)(HPET_MMIO_ADDR + offset) = data;
  38. }
  39. static void hpet_start_counter(void)
  40. {
  41. unsigned int cfg = hpet_read(HPET_CFG);
  42. cfg |= HPET_CFG_ENABLE;
  43. hpet_write(HPET_CFG, cfg);
  44. }
  45. static void hpet_stop_counter(void)
  46. {
  47. unsigned int cfg = hpet_read(HPET_CFG);
  48. cfg &= ~HPET_CFG_ENABLE;
  49. hpet_write(HPET_CFG, cfg);
  50. }
  51. static void hpet_reset_counter(void)
  52. {
  53. hpet_write(HPET_COUNTER, 0);
  54. hpet_write(HPET_COUNTER + 4, 0);
  55. }
  56. static void hpet_restart_counter(void)
  57. {
  58. hpet_stop_counter();
  59. hpet_reset_counter();
  60. hpet_start_counter();
  61. }
  62. static void hpet_enable_legacy_int(void)
  63. {
  64. /* Do nothing on Loongson-3 */
  65. }
  66. static int hpet_set_state_periodic(struct clock_event_device *evt)
  67. {
  68. int cfg;
  69. spin_lock(&hpet_lock);
  70. pr_info("set clock event to periodic mode!\n");
  71. /* stop counter */
  72. hpet_stop_counter();
  73. /* enables the timer0 to generate a periodic interrupt */
  74. cfg = hpet_read(HPET_T0_CFG);
  75. cfg &= ~HPET_TN_LEVEL;
  76. cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | HPET_TN_SETVAL |
  77. HPET_TN_32BIT;
  78. hpet_write(HPET_T0_CFG, cfg);
  79. /* set the comparator */
  80. hpet_write(HPET_T0_CMP, HPET_COMPARE_VAL);
  81. udelay(1);
  82. hpet_write(HPET_T0_CMP, HPET_COMPARE_VAL);
  83. /* start counter */
  84. hpet_start_counter();
  85. spin_unlock(&hpet_lock);
  86. return 0;
  87. }
  88. static int hpet_set_state_shutdown(struct clock_event_device *evt)
  89. {
  90. int cfg;
  91. spin_lock(&hpet_lock);
  92. cfg = hpet_read(HPET_T0_CFG);
  93. cfg &= ~HPET_TN_ENABLE;
  94. hpet_write(HPET_T0_CFG, cfg);
  95. spin_unlock(&hpet_lock);
  96. return 0;
  97. }
  98. static int hpet_set_state_oneshot(struct clock_event_device *evt)
  99. {
  100. int cfg;
  101. spin_lock(&hpet_lock);
  102. pr_info("set clock event to one shot mode!\n");
  103. cfg = hpet_read(HPET_T0_CFG);
  104. /*
  105. * set timer0 type
  106. * 1 : periodic interrupt
  107. * 0 : non-periodic(oneshot) interrupt
  108. */
  109. cfg &= ~HPET_TN_PERIODIC;
  110. cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
  111. hpet_write(HPET_T0_CFG, cfg);
  112. spin_unlock(&hpet_lock);
  113. return 0;
  114. }
  115. static int hpet_tick_resume(struct clock_event_device *evt)
  116. {
  117. spin_lock(&hpet_lock);
  118. hpet_enable_legacy_int();
  119. spin_unlock(&hpet_lock);
  120. return 0;
  121. }
  122. static int hpet_next_event(unsigned long delta,
  123. struct clock_event_device *evt)
  124. {
  125. u32 cnt;
  126. s32 res;
  127. cnt = hpet_read(HPET_COUNTER);
  128. cnt += (u32) delta;
  129. hpet_write(HPET_T0_CMP, cnt);
  130. res = (s32)(cnt - hpet_read(HPET_COUNTER));
  131. return res < HPET_MIN_CYCLES ? -ETIME : 0;
  132. }
  133. static irqreturn_t hpet_irq_handler(int irq, void *data)
  134. {
  135. int is_irq;
  136. struct clock_event_device *cd;
  137. unsigned int cpu = smp_processor_id();
  138. is_irq = hpet_read(HPET_STATUS);
  139. if (is_irq & HPET_T0_IRS) {
  140. /* clear the TIMER0 irq status register */
  141. hpet_write(HPET_STATUS, HPET_T0_IRS);
  142. cd = &per_cpu(hpet_clockevent_device, cpu);
  143. cd->event_handler(cd);
  144. return IRQ_HANDLED;
  145. }
  146. return IRQ_NONE;
  147. }
  148. static struct irqaction hpet_irq = {
  149. .handler = hpet_irq_handler,
  150. .flags = IRQF_NOBALANCING | IRQF_TIMER,
  151. .name = "hpet",
  152. };
  153. /*
  154. * hpet address assignation and irq setting should be done in bios.
  155. * but pmon don't do this, we just setup here directly.
  156. * The operation under is normal. unfortunately, hpet_setup process
  157. * is before pci initialize.
  158. *
  159. * {
  160. * struct pci_dev *pdev;
  161. *
  162. * pdev = pci_get_device(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL);
  163. * pci_write_config_word(pdev, SMBUS_PCI_REGB4, HPET_ADDR);
  164. *
  165. * ...
  166. * }
  167. */
  168. static void hpet_setup(void)
  169. {
  170. /* set hpet base address */
  171. smbus_write(SMBUS_PCI_REGB4, HPET_ADDR);
  172. /* enable decoding of access to HPET MMIO*/
  173. smbus_enable(SMBUS_PCI_REG40, (1 << 28));
  174. /* HPET irq enable */
  175. smbus_enable(SMBUS_PCI_REG64, (1 << 10));
  176. hpet_enable_legacy_int();
  177. }
  178. void __init setup_hpet_timer(void)
  179. {
  180. unsigned int cpu = smp_processor_id();
  181. struct clock_event_device *cd;
  182. hpet_setup();
  183. cd = &per_cpu(hpet_clockevent_device, cpu);
  184. cd->name = "hpet";
  185. cd->rating = 100;
  186. cd->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
  187. cd->set_state_shutdown = hpet_set_state_shutdown;
  188. cd->set_state_periodic = hpet_set_state_periodic;
  189. cd->set_state_oneshot = hpet_set_state_oneshot;
  190. cd->tick_resume = hpet_tick_resume;
  191. cd->set_next_event = hpet_next_event;
  192. cd->irq = HPET_T0_IRQ;
  193. cd->cpumask = cpumask_of(cpu);
  194. clockevent_set_clock(cd, HPET_FREQ);
  195. cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd);
  196. cd->min_delta_ns = clockevent_delta2ns(HPET_MIN_PROG_DELTA, cd);
  197. clockevents_register_device(cd);
  198. setup_irq(HPET_T0_IRQ, &hpet_irq);
  199. pr_info("hpet clock event device register\n");
  200. }
  201. static cycle_t hpet_read_counter(struct clocksource *cs)
  202. {
  203. return (cycle_t)hpet_read(HPET_COUNTER);
  204. }
  205. static void hpet_suspend(struct clocksource *cs)
  206. {
  207. }
  208. static void hpet_resume(struct clocksource *cs)
  209. {
  210. hpet_setup();
  211. hpet_restart_counter();
  212. }
  213. static struct clocksource csrc_hpet = {
  214. .name = "hpet",
  215. /* mips clocksource rating is less than 300, so hpet is better. */
  216. .rating = 300,
  217. .read = hpet_read_counter,
  218. .mask = CLOCKSOURCE_MASK(32),
  219. /* oneshot mode work normal with this flag */
  220. .flags = CLOCK_SOURCE_IS_CONTINUOUS,
  221. .suspend = hpet_suspend,
  222. .resume = hpet_resume,
  223. .mult = 0,
  224. .shift = 10,
  225. };
  226. int __init init_hpet_clocksource(void)
  227. {
  228. csrc_hpet.mult = clocksource_hz2mult(HPET_FREQ, csrc_hpet.shift);
  229. return clocksource_register_hz(&csrc_hpet, HPET_FREQ);
  230. }
  231. arch_initcall(init_hpet_clocksource);