ptp_ixp46x.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  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. unsigned long flags;
  151. struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
  152. struct ixp46x_ts_regs *regs = ixp_clock->regs;
  153. spin_lock_irqsave(&register_lock, flags);
  154. ns = ixp_systime_read(regs);
  155. spin_unlock_irqrestore(&register_lock, flags);
  156. *ts = ns_to_timespec64(ns);
  157. return 0;
  158. }
  159. static int ptp_ixp_settime(struct ptp_clock_info *ptp,
  160. const struct timespec64 *ts)
  161. {
  162. u64 ns;
  163. unsigned long flags;
  164. struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
  165. struct ixp46x_ts_regs *regs = ixp_clock->regs;
  166. ns = timespec64_to_ns(ts);
  167. spin_lock_irqsave(&register_lock, flags);
  168. ixp_systime_write(regs, ns);
  169. spin_unlock_irqrestore(&register_lock, flags);
  170. return 0;
  171. }
  172. static int ptp_ixp_enable(struct ptp_clock_info *ptp,
  173. struct ptp_clock_request *rq, int on)
  174. {
  175. struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
  176. switch (rq->type) {
  177. case PTP_CLK_REQ_EXTTS:
  178. switch (rq->extts.index) {
  179. case 0:
  180. ixp_clock->exts0_enabled = on ? 1 : 0;
  181. break;
  182. case 1:
  183. ixp_clock->exts1_enabled = on ? 1 : 0;
  184. break;
  185. default:
  186. return -EINVAL;
  187. }
  188. return 0;
  189. default:
  190. break;
  191. }
  192. return -EOPNOTSUPP;
  193. }
  194. static struct ptp_clock_info ptp_ixp_caps = {
  195. .owner = THIS_MODULE,
  196. .name = "IXP46X timer",
  197. .max_adj = 66666655,
  198. .n_ext_ts = N_EXT_TS,
  199. .n_pins = 0,
  200. .pps = 0,
  201. .adjfreq = ptp_ixp_adjfreq,
  202. .adjtime = ptp_ixp_adjtime,
  203. .gettime64 = ptp_ixp_gettime,
  204. .settime64 = ptp_ixp_settime,
  205. .enable = ptp_ixp_enable,
  206. };
  207. /* module operations */
  208. static struct ixp_clock ixp_clock;
  209. static int setup_interrupt(int gpio)
  210. {
  211. int irq;
  212. int err;
  213. err = gpio_request(gpio, "ixp4-ptp");
  214. if (err)
  215. return err;
  216. err = gpio_direction_input(gpio);
  217. if (err)
  218. return err;
  219. irq = gpio_to_irq(gpio);
  220. if (irq < 0)
  221. return irq;
  222. err = irq_set_irq_type(irq, IRQF_TRIGGER_FALLING);
  223. if (err) {
  224. pr_err("cannot set trigger type for irq %d\n", irq);
  225. return err;
  226. }
  227. err = request_irq(irq, isr, 0, DRIVER, &ixp_clock);
  228. if (err) {
  229. pr_err("request_irq failed for irq %d\n", irq);
  230. return err;
  231. }
  232. return irq;
  233. }
  234. static void __exit ptp_ixp_exit(void)
  235. {
  236. free_irq(MASTER_IRQ, &ixp_clock);
  237. free_irq(SLAVE_IRQ, &ixp_clock);
  238. ixp46x_phc_index = -1;
  239. ptp_clock_unregister(ixp_clock.ptp_clock);
  240. }
  241. static int __init ptp_ixp_init(void)
  242. {
  243. if (!cpu_is_ixp46x())
  244. return -ENODEV;
  245. ixp_clock.regs =
  246. (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT;
  247. ixp_clock.caps = ptp_ixp_caps;
  248. ixp_clock.ptp_clock = ptp_clock_register(&ixp_clock.caps, NULL);
  249. if (IS_ERR(ixp_clock.ptp_clock))
  250. return PTR_ERR(ixp_clock.ptp_clock);
  251. ixp46x_phc_index = ptp_clock_index(ixp_clock.ptp_clock);
  252. __raw_writel(DEFAULT_ADDEND, &ixp_clock.regs->addend);
  253. __raw_writel(1, &ixp_clock.regs->trgt_lo);
  254. __raw_writel(0, &ixp_clock.regs->trgt_hi);
  255. __raw_writel(TTIPEND, &ixp_clock.regs->event);
  256. if (MASTER_IRQ != setup_interrupt(MASTER_GPIO)) {
  257. pr_err("failed to setup gpio %d as irq\n", MASTER_GPIO);
  258. goto no_master;
  259. }
  260. if (SLAVE_IRQ != setup_interrupt(SLAVE_GPIO)) {
  261. pr_err("failed to setup gpio %d as irq\n", SLAVE_GPIO);
  262. goto no_slave;
  263. }
  264. return 0;
  265. no_slave:
  266. free_irq(MASTER_IRQ, &ixp_clock);
  267. no_master:
  268. ptp_clock_unregister(ixp_clock.ptp_clock);
  269. return -ENODEV;
  270. }
  271. module_init(ptp_ixp_init);
  272. module_exit(ptp_ixp_exit);
  273. MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>");
  274. MODULE_DESCRIPTION("PTP clock using the IXP46X timer");
  275. MODULE_LICENSE("GPL");