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 timespec
*ts
)
183 struct ixp_clock
*ixp_clock
= container_of(ptp
, struct ixp_clock
, caps
);
184 struct ixp46x_ts_regs
*regs
= ixp_clock
->regs
;
186 spin_lock_irqsave(®ister_lock
, flags
);
188 ns
= ixp_systime_read(regs
);
190 spin_unlock_irqrestore(®ister_lock
, flags
);
192 ts
->tv_sec
= div_u64_rem(ns
, 1000000000, &remainder
);
193 ts
->tv_nsec
= remainder
;
197 static int ptp_ixp_settime(struct ptp_clock_info
*ptp
,
198 const struct timespec
*ts
)
202 struct ixp_clock
*ixp_clock
= container_of(ptp
, struct ixp_clock
, caps
);
203 struct ixp46x_ts_regs
*regs
= ixp_clock
->regs
;
205 ns
= ts
->tv_sec
* 1000000000ULL;
208 spin_lock_irqsave(®ister_lock
, flags
);
210 ixp_systime_write(regs
, ns
);
212 spin_unlock_irqrestore(®ister_lock
, flags
);
217 static int ptp_ixp_enable(struct ptp_clock_info
*ptp
,
218 struct ptp_clock_request
*rq
, int on
)
220 struct ixp_clock
*ixp_clock
= container_of(ptp
, struct ixp_clock
, caps
);
223 case PTP_CLK_REQ_EXTTS
:
224 switch (rq
->extts
.index
) {
226 ixp_clock
->exts0_enabled
= on
? 1 : 0;
229 ixp_clock
->exts1_enabled
= on
? 1 : 0;
242 static struct ptp_clock_info ptp_ixp_caps
= {
243 .owner
= THIS_MODULE
,
244 .name
= "IXP46X timer",
246 .n_ext_ts
= N_EXT_TS
,
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
,
255 /* module operations */
257 static struct ixp_clock ixp_clock
;
259 static int setup_interrupt(int gpio
)
263 gpio_line_config(gpio
, IXP4XX_GPIO_IN
);
265 irq
= gpio_to_irq(gpio
);
270 if (irq_set_irq_type(irq
, IRQF_TRIGGER_FALLING
)) {
271 pr_err("cannot set trigger type for irq %d\n", irq
);
275 if (request_irq(irq
, isr
, 0, DRIVER
, &ixp_clock
)) {
276 pr_err("request_irq failed for irq %d\n", irq
);
283 static void __exit
ptp_ixp_exit(void)
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
);
291 static int __init
ptp_ixp_init(void)
293 if (!cpu_is_ixp46x())
297 (struct ixp46x_ts_regs __iomem
*) IXP4XX_TIMESYNC_BASE_VIRT
;
299 ixp_clock
.caps
= ptp_ixp_caps
;
301 ixp_clock
.ptp_clock
= ptp_clock_register(&ixp_clock
.caps
);
303 if (IS_ERR(ixp_clock
.ptp_clock
))
304 return PTR_ERR(ixp_clock
.ptp_clock
);
306 ixp46x_phc_index
= ptp_clock_index(ixp_clock
.ptp_clock
);
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
);
313 if (MASTER_IRQ
!= setup_interrupt(MASTER_GPIO
)) {
314 pr_err("failed to setup gpio %d as irq\n", MASTER_GPIO
);
317 if (SLAVE_IRQ
!= setup_interrupt(SLAVE_GPIO
)) {
318 pr_err("failed to setup gpio %d as irq\n", SLAVE_GPIO
);
324 free_irq(MASTER_IRQ
, &ixp_clock
);
326 ptp_clock_unregister(ixp_clock
.ptp_clock
);
330 module_init(ptp_ixp_init
);
331 module_exit(ptp_ixp_exit
);
333 MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>");
334 MODULE_DESCRIPTION("PTP clock using the IXP46X timer");
335 MODULE_LICENSE("GPL");