hpet.c 6.3 KB

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