ptp_ixp46x.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. /*
  2. * PTP 1588 clock using the IXP46X
  3. *
  4. * Copyright (C) 2010 OMICRON electronics GmbH
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19. */
  20. #include <linux/device.h>
  21. #include <linux/err.h>
  22. #include <linux/gpio.h>
  23. #include <linux/init.h>
  24. #include <linux/interrupt.h>
  25. #include <linux/io.h>
  26. #include <linux/irq.h>
  27. #include <linux/kernel.h>
  28. #include <linux/module.h>
  29. #include <linux/ptp_clock_kernel.h>
  30. #include <mach/ixp46x_ts.h>
  31. #define DRIVER "ptp_ixp46x"
  32. #define N_EXT_TS 2
  33. #define MASTER_GPIO 8
  34. #define MASTER_IRQ 25
  35. #define SLAVE_GPIO 7
  36. #define SLAVE_IRQ 24
  37. struct ixp_clock {
  38. struct ixp46x_ts_regs *regs;
  39. struct ptp_clock *ptp_clock;
  40. struct ptp_clock_info caps;
  41. int exts0_enabled;
  42. int exts1_enabled;
  43. };
  44. DEFINE_SPINLOCK(register_lock);
  45. /*
  46. * Register access functions
  47. */
  48. static u64 ixp_systime_read(struct ixp46x_ts_regs *regs)
  49. {
  50. u64 ns;
  51. u32 lo, hi;
  52. lo = __raw_readl(&regs->systime_lo);
  53. hi = __raw_readl(&regs->systime_hi);
  54. ns = ((u64) hi) << 32;
  55. ns |= lo;
  56. ns <<= TICKS_NS_SHIFT;
  57. return ns;
  58. }
  59. static void ixp_systime_write(struct ixp46x_ts_regs *regs, u64 ns)
  60. {
  61. u32 hi, lo;
  62. ns >>= TICKS_NS_SHIFT;
  63. hi = ns >> 32;
  64. lo = ns & 0xffffffff;
  65. __raw_writel(lo, &regs->systime_lo);
  66. __raw_writel(hi, &regs->systime_hi);
  67. }
  68. /*
  69. * Interrupt service routine
  70. */
  71. static irqreturn_t isr(int irq, void *priv)
  72. {
  73. struct ixp_clock *ixp_clock = priv;
  74. struct ixp46x_ts_regs *regs = ixp_clock->regs;
  75. struct ptp_clock_event event;
  76. u32 ack = 0, lo, hi, val;
  77. val = __raw_readl(&regs->event);
  78. if (val & TSER_SNS) {
  79. ack |= TSER_SNS;
  80. if (ixp_clock->exts0_enabled) {
  81. hi = __raw_readl(&regs->asms_hi);
  82. lo = __raw_readl(&regs->asms_lo);
  83. event.type = PTP_CLOCK_EXTTS;
  84. event.index = 0;
  85. event.timestamp = ((u64) hi) << 32;
  86. event.timestamp |= lo;
  87. event.timestamp <<= TICKS_NS_SHIFT;
  88. ptp_clock_event(ixp_clock->ptp_clock, &event);
  89. }
  90. }
  91. if (val & TSER_SNM) {
  92. ack |= TSER_SNM;
  93. if (ixp_clock->exts1_enabled) {
  94. hi = __raw_readl(&regs->amms_hi);
  95. lo = __raw_readl(&regs->amms_lo);
  96. event.type = PTP_CLOCK_EXTTS;
  97. event.index = 1;
  98. event.timestamp = ((u64) hi) << 32;
  99. event.timestamp |= lo;
  100. event.timestamp <<= TICKS_NS_SHIFT;
  101. ptp_clock_event(ixp_clock->ptp_clock, &event);
  102. }
  103. }
  104. if (val & TTIPEND)
  105. ack |= TTIPEND; /* this bit seems to be always set */
  106. if (ack) {
  107. __raw_writel(ack, &regs->event);
  108. return IRQ_HANDLED;
  109. } else
  110. return IRQ_NONE;
  111. }
  112. /*
  113. * PTP clock operations
  114. */
  115. static int ptp_ixp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
  116. {
  117. u64 adj;
  118. u32 diff, addend;
  119. int neg_adj = 0;
  120. struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
  121. struct ixp46x_ts_regs *regs = ixp_clock->regs;
  122. if (ppb < 0) {
  123. neg_adj = 1;
  124. ppb = -ppb;
  125. }
  126. addend = DEFAULT_ADDEND;
  127. adj = addend;
  128. adj *= ppb;
  129. diff = div_u64(adj, 1000000000ULL);
  130. addend = neg_adj ? addend - diff : addend + diff;
  131. __raw_writel(addend, &regs->addend);
  132. return 0;
  133. }
  134. static int ptp_ixp_adjtime(struct ptp_clock_info *ptp, s64 delta)
  135. {
  136. s64 now;
  137. unsigned long flags;
  138. struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
  139. struct ixp46x_ts_regs *regs = ixp_clock->regs;
  140. spin_lock_irqsave(&register_lock, flags);
  141. now = ixp_systime_read(regs);
  142. now += delta;
  143. ixp_systime_write(regs, now);
  144. spin_unlock_irqrestore(&register_lock, flags);
  145. return 0;
  146. }
  147. static int ptp_ixp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
  148. {
  149. u64 ns;
  150. u32 remainder;
  151. unsigned long flags;
  152. struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
  153. struct ixp46x_ts_regs *regs = ixp_clock->regs;
  154. spin_lock_irqsave(&register_lock, flags);
  155. ns = ixp_systime_read(regs);
  156. spin_unlock_irqrestore(&register_lock, flags);
  157. ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder);
  158. ts->tv_nsec = remainder;
  159. return 0;
  160. }
  161. static int ptp_ixp_settime(struct ptp_clock_info *ptp,
  162. const struct timespec64 *ts)
  163. {
  164. u64 ns;
  165. unsigned long flags;
  166. struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
  167. struct ixp46x_ts_regs *regs = ixp_clock->regs;
  168. ns = ts->tv_sec * 1000000000ULL;
  169. ns += ts->tv_nsec;
  170. spin_lock_irqsave(&register_lock, flags);
  171. ixp_systime_write(regs, ns);
  172. spin_unlock_irqrestore(&register_lock, flags);
  173. return 0;
  174. }
  175. static int ptp_ixp_enable(struct ptp_clock_info *ptp,
  176. struct ptp_clock_request *rq, int on)
  177. {
  178. struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
  179. switch (rq->type) {
  180. case PTP_CLK_REQ_EXTTS:
  181. switch (rq->extts.index) {
  182. case 0:
  183. ixp_clock->exts0_enabled = on ? 1 : 0;
  184. break;
  185. case 1:
  186. ixp_clock->exts1_enabled = on ? 1 : 0;
  187. break;
  188. default:
  189. return -EINVAL;
  190. }
  191. return 0;
  192. default:
  193. break;
  194. }
  195. return -EOPNOTSUPP;
  196. }
  197. static struct ptp_clock_info ptp_ixp_caps = {
  198. .owner = THIS_MODULE,
  199. .name = "IXP46X timer",
  200. .max_adj = 66666655,
  201. .n_ext_ts = N_EXT_TS,
  202. .n_pins = 0,
  203. .pps = 0,
  204. .adjfreq = ptp_ixp_adjfreq,
  205. .adjtime = ptp_ixp_adjtime,
  206. .gettime64 = ptp_ixp_gettime,
  207. .settime64 = ptp_ixp_settime,
  208. .enable = ptp_ixp_enable,
  209. };
  210. /* module operations */
  211. static struct ixp_clock ixp_clock;
  212. static int setup_interrupt(int gpio)
  213. {
  214. int irq;
  215. int err;
  216. err = gpio_request(gpio, "ixp4-ptp");
  217. if (err)
  218. return err;
  219. err = gpio_direction_input(gpio);
  220. if (err)
  221. return err;
  222. irq = gpio_to_irq(gpio);
  223. if (NO_IRQ == irq)
  224. return NO_IRQ;
  225. if (irq_set_irq_type(irq, IRQF_TRIGGER_FALLING)) {
  226. pr_err("cannot set trigger type for irq %d\n", irq);
  227. return NO_IRQ;
  228. }
  229. if (request_irq(irq, isr, 0, DRIVER, &ixp_clock)) {
  230. pr_err("request_irq failed for irq %d\n", irq);
  231. return NO_IRQ;
  232. }
  233. return irq;
  234. }
  235. static void __exit ptp_ixp_exit(void)
  236. {
  237. free_irq(MASTER_IRQ, &ixp_clock);
  238. free_irq(SLAVE_IRQ, &ixp_clock);
  239. ixp46x_phc_index = -1;
  240. ptp_clock_unregister(ixp_clock.ptp_clock);
  241. }
  242. static int __init ptp_ixp_init(void)
  243. {
  244. if (!cpu_is_ixp46x())
  245. return -ENODEV;
  246. ixp_clock.regs =
  247. (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT;
  248. ixp_clock.caps = ptp_ixp_caps;
  249. ixp_clock.ptp_clock = ptp_clock_register(&ixp_clock.caps, NULL);
  250. if (IS_ERR(ixp_clock.ptp_clock))
  251. return PTR_ERR(ixp_clock.ptp_clock);
  252. ixp46x_phc_index = ptp_clock_index(ixp_clock.ptp_clock);
  253. __raw_writel(DEFAULT_ADDEND, &ixp_clock.regs->addend);
  254. __raw_writel(1, &ixp_clock.regs->trgt_lo);
  255. __raw_writel(0, &ixp_clock.regs->trgt_hi);
  256. __raw_writel(TTIPEND, &ixp_clock.regs->event);
  257. if (MASTER_IRQ != setup_interrupt(MASTER_GPIO)) {
  258. pr_err("failed to setup gpio %d as irq\n", MASTER_GPIO);
  259. goto no_master;
  260. }
  261. if (SLAVE_IRQ != setup_interrupt(SLAVE_GPIO)) {
  262. pr_err("failed to setup gpio %d as irq\n", SLAVE_GPIO);
  263. goto no_slave;
  264. }
  265. return 0;
  266. no_slave:
  267. free_irq(MASTER_IRQ, &ixp_clock);
  268. no_master:
  269. ptp_clock_unregister(ixp_clock.ptp_clock);
  270. return -ENODEV;
  271. }
  272. module_init(ptp_ixp_init);
  273. module_exit(ptp_ixp_exit);
  274. MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>");
  275. MODULE_DESCRIPTION("PTP clock using the IXP46X timer");
  276. MODULE_LICENSE("GPL");