Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ptp_ixp46x.c
Go to the documentation of this file.
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 
30 #include <linux/ptp_clock_kernel.h>
31 #include <mach/ixp46x_ts.h>
32 
33 #define DRIVER "ptp_ixp46x"
34 #define N_EXT_TS 2
35 #define MASTER_GPIO 8
36 #define MASTER_IRQ 25
37 #define SLAVE_GPIO 7
38 #define SLAVE_IRQ 24
39 
40 struct ixp_clock {
46 };
47 
48 DEFINE_SPINLOCK(register_lock);
49 
50 /*
51  * Register access functions
52  */
53 
54 static u64 ixp_systime_read(struct ixp46x_ts_regs *regs)
55 {
56  u64 ns;
57  u32 lo, hi;
58 
59  lo = __raw_readl(&regs->systime_lo);
60  hi = __raw_readl(&regs->systime_hi);
61 
62  ns = ((u64) hi) << 32;
63  ns |= lo;
64  ns <<= TICKS_NS_SHIFT;
65 
66  return ns;
67 }
68 
69 static void ixp_systime_write(struct ixp46x_ts_regs *regs, u64 ns)
70 {
71  u32 hi, lo;
72 
73  ns >>= TICKS_NS_SHIFT;
74  hi = ns >> 32;
75  lo = ns & 0xffffffff;
76 
77  __raw_writel(lo, &regs->systime_lo);
78  __raw_writel(hi, &regs->systime_hi);
79 }
80 
81 /*
82  * Interrupt service routine
83  */
84 
85 static irqreturn_t isr(int irq, void *priv)
86 {
87  struct ixp_clock *ixp_clock = priv;
88  struct ixp46x_ts_regs *regs = ixp_clock->regs;
89  struct ptp_clock_event event;
90  u32 ack = 0, lo, hi, val;
91 
92  val = __raw_readl(&regs->event);
93 
94  if (val & TSER_SNS) {
95  ack |= TSER_SNS;
96  if (ixp_clock->exts0_enabled) {
97  hi = __raw_readl(&regs->asms_hi);
98  lo = __raw_readl(&regs->asms_lo);
99  event.type = PTP_CLOCK_EXTTS;
100  event.index = 0;
101  event.timestamp = ((u64) hi) << 32;
102  event.timestamp |= lo;
103  event.timestamp <<= TICKS_NS_SHIFT;
104  ptp_clock_event(ixp_clock->ptp_clock, &event);
105  }
106  }
107 
108  if (val & TSER_SNM) {
109  ack |= TSER_SNM;
110  if (ixp_clock->exts1_enabled) {
111  hi = __raw_readl(&regs->amms_hi);
112  lo = __raw_readl(&regs->amms_lo);
113  event.type = PTP_CLOCK_EXTTS;
114  event.index = 1;
115  event.timestamp = ((u64) hi) << 32;
116  event.timestamp |= lo;
117  event.timestamp <<= TICKS_NS_SHIFT;
118  ptp_clock_event(ixp_clock->ptp_clock, &event);
119  }
120  }
121 
122  if (val & TTIPEND)
123  ack |= TTIPEND; /* this bit seems to be always set */
124 
125  if (ack) {
126  __raw_writel(ack, &regs->event);
127  return IRQ_HANDLED;
128  } else
129  return IRQ_NONE;
130 }
131 
132 /*
133  * PTP clock operations
134  */
135 
136 static int ptp_ixp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
137 {
138  u64 adj;
139  u32 diff, addend;
140  int neg_adj = 0;
141  struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
142  struct ixp46x_ts_regs *regs = ixp_clock->regs;
143 
144  if (ppb < 0) {
145  neg_adj = 1;
146  ppb = -ppb;
147  }
148  addend = DEFAULT_ADDEND;
149  adj = addend;
150  adj *= ppb;
151  diff = div_u64(adj, 1000000000ULL);
152 
153  addend = neg_adj ? addend - diff : addend + diff;
154 
155  __raw_writel(addend, &regs->addend);
156 
157  return 0;
158 }
159 
160 static int ptp_ixp_adjtime(struct ptp_clock_info *ptp, s64 delta)
161 {
162  s64 now;
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 
167  spin_lock_irqsave(&register_lock, flags);
168 
169  now = ixp_systime_read(regs);
170  now += delta;
171  ixp_systime_write(regs, now);
172 
173  spin_unlock_irqrestore(&register_lock, flags);
174 
175  return 0;
176 }
177 
178 static int ptp_ixp_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
179 {
180  u64 ns;
181  u32 remainder;
182  unsigned long flags;
183  struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
184  struct ixp46x_ts_regs *regs = ixp_clock->regs;
185 
186  spin_lock_irqsave(&register_lock, flags);
187 
188  ns = ixp_systime_read(regs);
189 
190  spin_unlock_irqrestore(&register_lock, flags);
191 
192  ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder);
193  ts->tv_nsec = remainder;
194  return 0;
195 }
196 
197 static int ptp_ixp_settime(struct ptp_clock_info *ptp,
198  const struct timespec *ts)
199 {
200  u64 ns;
201  unsigned long flags;
202  struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
203  struct ixp46x_ts_regs *regs = ixp_clock->regs;
204 
205  ns = ts->tv_sec * 1000000000ULL;
206  ns += ts->tv_nsec;
207 
208  spin_lock_irqsave(&register_lock, flags);
209 
210  ixp_systime_write(regs, ns);
211 
212  spin_unlock_irqrestore(&register_lock, flags);
213 
214  return 0;
215 }
216 
217 static int ptp_ixp_enable(struct ptp_clock_info *ptp,
218  struct ptp_clock_request *rq, int on)
219 {
220  struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
221 
222  switch (rq->type) {
223  case PTP_CLK_REQ_EXTTS:
224  switch (rq->extts.index) {
225  case 0:
226  ixp_clock->exts0_enabled = on ? 1 : 0;
227  break;
228  case 1:
229  ixp_clock->exts1_enabled = on ? 1 : 0;
230  break;
231  default:
232  return -EINVAL;
233  }
234  return 0;
235  default:
236  break;
237  }
238 
239  return -EOPNOTSUPP;
240 }
241 
242 static struct ptp_clock_info ptp_ixp_caps = {
243  .owner = THIS_MODULE,
244  .name = "IXP46X timer",
245  .max_adj = 66666655,
246  .n_ext_ts = N_EXT_TS,
247  .pps = 0,
248  .adjfreq = ptp_ixp_adjfreq,
249  .adjtime = ptp_ixp_adjtime,
250  .gettime = ptp_ixp_gettime,
251  .settime = ptp_ixp_settime,
252  .enable = ptp_ixp_enable,
253 };
254 
255 /* module operations */
256 
257 static struct ixp_clock ixp_clock;
258 
259 static int setup_interrupt(int gpio)
260 {
261  int irq;
262 
264 
265  irq = gpio_to_irq(gpio);
266 
267  if (NO_IRQ == irq)
268  return NO_IRQ;
269 
271  pr_err("cannot set trigger type for irq %d\n", irq);
272  return NO_IRQ;
273  }
274 
275  if (request_irq(irq, isr, 0, DRIVER, &ixp_clock)) {
276  pr_err("request_irq failed for irq %d\n", irq);
277  return NO_IRQ;
278  }
279 
280  return irq;
281 }
282 
283 static void __exit ptp_ixp_exit(void)
284 {
285  free_irq(MASTER_IRQ, &ixp_clock);
286  free_irq(SLAVE_IRQ, &ixp_clock);
287  ixp46x_phc_index = -1;
288  ptp_clock_unregister(ixp_clock.ptp_clock);
289 }
290 
291 static int __init ptp_ixp_init(void)
292 {
293  if (!cpu_is_ixp46x())
294  return -ENODEV;
295 
296  ixp_clock.regs =
298 
299  ixp_clock.caps = ptp_ixp_caps;
300 
301  ixp_clock.ptp_clock = ptp_clock_register(&ixp_clock.caps, NULL);
302 
303  if (IS_ERR(ixp_clock.ptp_clock))
304  return PTR_ERR(ixp_clock.ptp_clock);
305 
307 
308  __raw_writel(DEFAULT_ADDEND, &ixp_clock.regs->addend);
309  __raw_writel(1, &ixp_clock.regs->trgt_lo);
310  __raw_writel(0, &ixp_clock.regs->trgt_hi);
311  __raw_writel(TTIPEND, &ixp_clock.regs->event);
312 
313  if (MASTER_IRQ != setup_interrupt(MASTER_GPIO)) {
314  pr_err("failed to setup gpio %d as irq\n", MASTER_GPIO);
315  goto no_master;
316  }
317  if (SLAVE_IRQ != setup_interrupt(SLAVE_GPIO)) {
318  pr_err("failed to setup gpio %d as irq\n", SLAVE_GPIO);
319  goto no_slave;
320  }
321 
322  return 0;
323 no_slave:
324  free_irq(MASTER_IRQ, &ixp_clock);
325 no_master:
326  ptp_clock_unregister(ixp_clock.ptp_clock);
327  return -ENODEV;
328 }
329 
330 module_init(ptp_ixp_init);
331 module_exit(ptp_ixp_exit);
332 
333 MODULE_AUTHOR("Richard Cochran <[email protected]>");
334 MODULE_DESCRIPTION("PTP clock using the IXP46X timer");
335 MODULE_LICENSE("GPL");