2 * PTP 1588 clock using the IXP46X
4 * Copyright (C) 2010 OMICRON electronics GmbH
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.
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.
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.
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>
26 #include <linux/irq.h>
27 #include <linux/kernel.h>
28 #include <linux/module.h>
30 #include <linux/ptp_clock_kernel.h>
31 #include <mach/ixp46x_ts.h>
33 #define DRIVER "ptp_ixp46x"
41 struct ixp46x_ts_regs
*regs
;
42 struct ptp_clock
*ptp_clock
;
43 struct ptp_clock_info caps
;
48 DEFINE_SPINLOCK(register_lock
);
51 * Register access functions
54 static u64
ixp_systime_read(struct ixp46x_ts_regs
*regs
)
59 lo
= __raw_readl(®s
->systime_lo
);
60 hi
= __raw_readl(®s
->systime_hi
);
62 ns
= ((u64
) hi
) << 32;
64 ns
<<= TICKS_NS_SHIFT
;
69 static void ixp_systime_write(struct ixp46x_ts_regs
*regs
, u64 ns
)
73 ns
>>= TICKS_NS_SHIFT
;
77 __raw_writel(lo
, ®s
->systime_lo
);
78 __raw_writel(hi
, ®s
->systime_hi
);
82 * Interrupt service routine
85 static irqreturn_t
isr(int irq
, void *priv
)
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
;
92 val
= __raw_readl(®s
->event
);
96 if (ixp_clock
->exts0_enabled
) {
97 hi
= __raw_readl(®s
->asms_hi
);
98 lo
= __raw_readl(®s
->asms_lo
);
99 event
.type
= PTP_CLOCK_EXTTS
;
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
);
108 if (val
& TSER_SNM
) {
110 if (ixp_clock
->exts1_enabled
) {
111 hi
= __raw_readl(®s
->amms_hi
);
112 lo
= __raw_readl(®s
->amms_lo
);
113 event
.type
= PTP_CLOCK_EXTTS
;
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
);
123 ack
|= TTIPEND
; /* this bit seems to be always set */
126 __raw_writel(ack
, ®s
->event
);
133 * PTP clock operations
136 static int ptp_ixp_adjfreq(struct ptp_clock_info
*ptp
, s32 ppb
)
141 struct ixp_clock
*ixp_clock
= container_of(ptp
, struct ixp_clock
, caps
);
142 struct ixp46x_ts_regs
*regs
= ixp_clock
->regs
;
148 addend
= DEFAULT_ADDEND
;
151 diff
= div_u64(adj
, 1000000000ULL);
153 addend
= neg_adj
? addend
- diff
: addend
+ diff
;
155 __raw_writel(addend
, ®s
->addend
);
160 static int ptp_ixp_adjtime(struct ptp_clock_info
*ptp
, s64 delta
)
164 struct ixp_clock
*ixp_clock
= container_of(ptp
, struct ixp_clock
, caps
);
165 struct ixp46x_ts_regs
*regs
= ixp_clock
->regs
;
167 spin_lock_irqsave(®ister_lock
, flags
);
169 now
= ixp_systime_read(regs
);
171 ixp_systime_write(regs
, now
);
173 spin_unlock_irqrestore(®ister_lock
, flags
);
178 static int ptp_ixp_gettime(struct ptp_clock_info
*ptp
, struct timespec64
*ts
)
182 struct ixp_clock
*ixp_clock
= container_of(ptp
, struct ixp_clock
, caps
);
183 struct ixp46x_ts_regs
*regs
= ixp_clock
->regs
;
185 spin_lock_irqsave(®ister_lock
, flags
);
187 ns
= ixp_systime_read(regs
);
189 spin_unlock_irqrestore(®ister_lock
, flags
);
191 *ts
= ns_to_timespec64(ns
);
195 static int ptp_ixp_settime(struct ptp_clock_info
*ptp
,
196 const struct timespec64
*ts
)
200 struct ixp_clock
*ixp_clock
= container_of(ptp
, struct ixp_clock
, caps
);
201 struct ixp46x_ts_regs
*regs
= ixp_clock
->regs
;
203 ns
= timespec64_to_ns(ts
);
205 spin_lock_irqsave(®ister_lock
, flags
);
207 ixp_systime_write(regs
, ns
);
209 spin_unlock_irqrestore(®ister_lock
, flags
);
214 static int ptp_ixp_enable(struct ptp_clock_info
*ptp
,
215 struct ptp_clock_request
*rq
, int on
)
217 struct ixp_clock
*ixp_clock
= container_of(ptp
, struct ixp_clock
, caps
);
220 case PTP_CLK_REQ_EXTTS
:
221 switch (rq
->extts
.index
) {
223 ixp_clock
->exts0_enabled
= on
? 1 : 0;
226 ixp_clock
->exts1_enabled
= on
? 1 : 0;
239 static struct ptp_clock_info ptp_ixp_caps
= {
240 .owner
= THIS_MODULE
,
241 .name
= "IXP46X timer",
243 .n_ext_ts
= N_EXT_TS
,
246 .adjfreq
= ptp_ixp_adjfreq
,
247 .adjtime
= ptp_ixp_adjtime
,
248 .gettime64
= ptp_ixp_gettime
,
249 .settime64
= ptp_ixp_settime
,
250 .enable
= ptp_ixp_enable
,
253 /* module operations */
255 static struct ixp_clock ixp_clock
;
257 static int setup_interrupt(int gpio
)
262 err
= gpio_request(gpio
, "ixp4-ptp");
266 err
= gpio_direction_input(gpio
);
270 irq
= gpio_to_irq(gpio
);
274 err
= irq_set_irq_type(irq
, IRQF_TRIGGER_FALLING
);
276 pr_err("cannot set trigger type for irq %d\n", irq
);
280 err
= request_irq(irq
, isr
, 0, DRIVER
, &ixp_clock
);
282 pr_err("request_irq failed for irq %d\n", irq
);
289 static void __exit
ptp_ixp_exit(void)
291 free_irq(MASTER_IRQ
, &ixp_clock
);
292 free_irq(SLAVE_IRQ
, &ixp_clock
);
293 ixp46x_phc_index
= -1;
294 ptp_clock_unregister(ixp_clock
.ptp_clock
);
297 static int __init
ptp_ixp_init(void)
299 if (!cpu_is_ixp46x())
303 (struct ixp46x_ts_regs __iomem
*) IXP4XX_TIMESYNC_BASE_VIRT
;
305 ixp_clock
.caps
= ptp_ixp_caps
;
307 ixp_clock
.ptp_clock
= ptp_clock_register(&ixp_clock
.caps
, NULL
);
309 if (IS_ERR(ixp_clock
.ptp_clock
))
310 return PTR_ERR(ixp_clock
.ptp_clock
);
312 ixp46x_phc_index
= ptp_clock_index(ixp_clock
.ptp_clock
);
314 __raw_writel(DEFAULT_ADDEND
, &ixp_clock
.regs
->addend
);
315 __raw_writel(1, &ixp_clock
.regs
->trgt_lo
);
316 __raw_writel(0, &ixp_clock
.regs
->trgt_hi
);
317 __raw_writel(TTIPEND
, &ixp_clock
.regs
->event
);
319 if (MASTER_IRQ
!= setup_interrupt(MASTER_GPIO
)) {
320 pr_err("failed to setup gpio %d as irq\n", MASTER_GPIO
);
323 if (SLAVE_IRQ
!= setup_interrupt(SLAVE_GPIO
)) {
324 pr_err("failed to setup gpio %d as irq\n", SLAVE_GPIO
);
330 free_irq(MASTER_IRQ
, &ixp_clock
);
332 ptp_clock_unregister(ixp_clock
.ptp_clock
);
336 module_init(ptp_ixp_init
);
337 module_exit(ptp_ixp_exit
);
339 MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>");
340 MODULE_DESCRIPTION("PTP clock using the IXP46X timer");
341 MODULE_LICENSE("GPL");