1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * PTP 1588 clock using the IXP46X
4 *
5 * Copyright (C) 2010 OMICRON electronics GmbH
6 */
7 #include <linux/device.h>
8 #include <linux/module.h>
9 #include <linux/err.h>
10 #include <linux/init.h>
11 #include <linux/interrupt.h>
12 #include <linux/io.h>
13 #include <linux/irq.h>
14 #include <linux/kernel.h>
15 #include <linux/ptp_clock_kernel.h>
16 #include <linux/platform_device.h>
17 #include <linux/soc/ixp4xx/cpu.h>
18
19 #include "ixp46x_ts.h"
20
21 #define DRIVER "ptp_ixp46x"
22 #define N_EXT_TS 2
23
24 struct ixp_clock {
25 struct ixp46x_ts_regs *regs;
26 struct ptp_clock *ptp_clock;
27 struct ptp_clock_info caps;
28 int exts0_enabled;
29 int exts1_enabled;
30 int slave_irq;
31 int master_irq;
32 };
33
34 static DEFINE_SPINLOCK(register_lock);
35
36 /*
37 * Register access functions
38 */
39
ixp_systime_read(struct ixp46x_ts_regs * regs)40 static u64 ixp_systime_read(struct ixp46x_ts_regs *regs)
41 {
42 u64 ns;
43 u32 lo, hi;
44
45 lo = __raw_readl(®s->systime_lo);
46 hi = __raw_readl(®s->systime_hi);
47
48 ns = ((u64) hi) << 32;
49 ns |= lo;
50 ns <<= TICKS_NS_SHIFT;
51
52 return ns;
53 }
54
ixp_systime_write(struct ixp46x_ts_regs * regs,u64 ns)55 static void ixp_systime_write(struct ixp46x_ts_regs *regs, u64 ns)
56 {
57 u32 hi, lo;
58
59 ns >>= TICKS_NS_SHIFT;
60 hi = ns >> 32;
61 lo = ns & 0xffffffff;
62
63 __raw_writel(lo, ®s->systime_lo);
64 __raw_writel(hi, ®s->systime_hi);
65 }
66
67 /*
68 * Interrupt service routine
69 */
70
isr(int irq,void * priv)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
78 val = __raw_readl(®s->event);
79
80 if (val & TSER_SNS) {
81 ack |= TSER_SNS;
82 if (ixp_clock->exts0_enabled) {
83 hi = __raw_readl(®s->asms_hi);
84 lo = __raw_readl(®s->asms_lo);
85 event.type = PTP_CLOCK_EXTTS;
86 event.index = 0;
87 event.timestamp = ((u64) hi) << 32;
88 event.timestamp |= lo;
89 event.timestamp <<= TICKS_NS_SHIFT;
90 ptp_clock_event(ixp_clock->ptp_clock, &event);
91 }
92 }
93
94 if (val & TSER_SNM) {
95 ack |= TSER_SNM;
96 if (ixp_clock->exts1_enabled) {
97 hi = __raw_readl(®s->amms_hi);
98 lo = __raw_readl(®s->amms_lo);
99 event.type = PTP_CLOCK_EXTTS;
100 event.index = 1;
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 & TTIPEND)
109 ack |= TTIPEND; /* this bit seems to be always set */
110
111 if (ack) {
112 __raw_writel(ack, ®s->event);
113 return IRQ_HANDLED;
114 } else
115 return IRQ_NONE;
116 }
117
118 /*
119 * PTP clock operations
120 */
121
ptp_ixp_adjfine(struct ptp_clock_info * ptp,long scaled_ppm)122 static int ptp_ixp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
123 {
124 u32 addend;
125 struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
126 struct ixp46x_ts_regs *regs = ixp_clock->regs;
127
128 addend = adjust_by_scaled_ppm(DEFAULT_ADDEND, scaled_ppm);
129
130 __raw_writel(addend, ®s->addend);
131
132 return 0;
133 }
134
ptp_ixp_adjtime(struct ptp_clock_info * ptp,s64 delta)135 static int ptp_ixp_adjtime(struct ptp_clock_info *ptp, s64 delta)
136 {
137 s64 now;
138 unsigned long flags;
139 struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
140 struct ixp46x_ts_regs *regs = ixp_clock->regs;
141
142 spin_lock_irqsave(®ister_lock, flags);
143
144 now = ixp_systime_read(regs);
145 now += delta;
146 ixp_systime_write(regs, now);
147
148 spin_unlock_irqrestore(®ister_lock, flags);
149
150 return 0;
151 }
152
ptp_ixp_gettime(struct ptp_clock_info * ptp,struct timespec64 * ts)153 static int ptp_ixp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
154 {
155 u64 ns;
156 unsigned long flags;
157 struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
158 struct ixp46x_ts_regs *regs = ixp_clock->regs;
159
160 spin_lock_irqsave(®ister_lock, flags);
161
162 ns = ixp_systime_read(regs);
163
164 spin_unlock_irqrestore(®ister_lock, flags);
165
166 *ts = ns_to_timespec64(ns);
167 return 0;
168 }
169
ptp_ixp_settime(struct ptp_clock_info * ptp,const struct timespec64 * ts)170 static int ptp_ixp_settime(struct ptp_clock_info *ptp,
171 const struct timespec64 *ts)
172 {
173 u64 ns;
174 unsigned long flags;
175 struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
176 struct ixp46x_ts_regs *regs = ixp_clock->regs;
177
178 ns = timespec64_to_ns(ts);
179
180 spin_lock_irqsave(®ister_lock, flags);
181
182 ixp_systime_write(regs, ns);
183
184 spin_unlock_irqrestore(®ister_lock, flags);
185
186 return 0;
187 }
188
ptp_ixp_enable(struct ptp_clock_info * ptp,struct ptp_clock_request * rq,int on)189 static int ptp_ixp_enable(struct ptp_clock_info *ptp,
190 struct ptp_clock_request *rq, int on)
191 {
192 struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
193
194 switch (rq->type) {
195 case PTP_CLK_REQ_EXTTS:
196 switch (rq->extts.index) {
197 case 0:
198 ixp_clock->exts0_enabled = on ? 1 : 0;
199 break;
200 case 1:
201 ixp_clock->exts1_enabled = on ? 1 : 0;
202 break;
203 default:
204 return -EINVAL;
205 }
206 return 0;
207 default:
208 break;
209 }
210
211 return -EOPNOTSUPP;
212 }
213
214 static const struct ptp_clock_info ptp_ixp_caps = {
215 .owner = THIS_MODULE,
216 .name = "IXP46X timer",
217 .max_adj = 66666655,
218 .n_ext_ts = N_EXT_TS,
219 .n_pins = 0,
220 .pps = 0,
221 .adjfine = ptp_ixp_adjfine,
222 .adjtime = ptp_ixp_adjtime,
223 .gettime64 = ptp_ixp_gettime,
224 .settime64 = ptp_ixp_settime,
225 .enable = ptp_ixp_enable,
226 };
227
228 /* module operations */
229
230 static struct ixp_clock ixp_clock;
231
ixp46x_ptp_find(struct ixp46x_ts_regs * __iomem * regs,int * phc_index)232 int ixp46x_ptp_find(struct ixp46x_ts_regs *__iomem *regs, int *phc_index)
233 {
234 if (!cpu_is_ixp46x())
235 return -ENODEV;
236
237 *regs = ixp_clock.regs;
238 *phc_index = ptp_clock_index(ixp_clock.ptp_clock);
239
240 if (!ixp_clock.ptp_clock)
241 return -EPROBE_DEFER;
242
243 return 0;
244 }
245 EXPORT_SYMBOL_GPL(ixp46x_ptp_find);
246
247 /* Called from the registered devm action */
ptp_ixp_unregister_action(void * d)248 static void ptp_ixp_unregister_action(void *d)
249 {
250 struct ptp_clock *ptp_clock = d;
251
252 ptp_clock_unregister(ptp_clock);
253 ixp_clock.ptp_clock = NULL;
254 }
255
ptp_ixp_probe(struct platform_device * pdev)256 static int ptp_ixp_probe(struct platform_device *pdev)
257 {
258 struct device *dev = &pdev->dev;
259 int ret;
260
261 ixp_clock.regs = devm_platform_ioremap_resource(pdev, 0);
262 ixp_clock.master_irq = platform_get_irq(pdev, 0);
263 ixp_clock.slave_irq = platform_get_irq(pdev, 1);
264 if (IS_ERR(ixp_clock.regs) ||
265 ixp_clock.master_irq < 0 || ixp_clock.slave_irq < 0)
266 return -ENXIO;
267
268 ixp_clock.caps = ptp_ixp_caps;
269
270 ixp_clock.ptp_clock = ptp_clock_register(&ixp_clock.caps, NULL);
271
272 if (IS_ERR(ixp_clock.ptp_clock))
273 return PTR_ERR(ixp_clock.ptp_clock);
274
275 ret = devm_add_action_or_reset(dev, ptp_ixp_unregister_action,
276 ixp_clock.ptp_clock);
277 if (ret) {
278 dev_err(dev, "failed to install clock removal handler\n");
279 return ret;
280 }
281
282 __raw_writel(DEFAULT_ADDEND, &ixp_clock.regs->addend);
283 __raw_writel(1, &ixp_clock.regs->trgt_lo);
284 __raw_writel(0, &ixp_clock.regs->trgt_hi);
285 __raw_writel(TTIPEND, &ixp_clock.regs->event);
286
287 ret = devm_request_irq(dev, ixp_clock.master_irq, isr,
288 0, DRIVER, &ixp_clock);
289 if (ret)
290 return dev_err_probe(dev, ret,
291 "request_irq failed for irq %d\n",
292 ixp_clock.master_irq);
293
294 ret = devm_request_irq(dev, ixp_clock.slave_irq, isr,
295 0, DRIVER, &ixp_clock);
296 if (ret)
297 return dev_err_probe(dev, ret,
298 "request_irq failed for irq %d\n",
299 ixp_clock.slave_irq);
300
301 return 0;
302 }
303
304 static const struct of_device_id ptp_ixp_match[] = {
305 {
306 .compatible = "intel,ixp46x-ptp-timer",
307 },
308 { },
309 };
310
311 static struct platform_driver ptp_ixp_driver = {
312 .driver = {
313 .name = "ptp-ixp46x",
314 .of_match_table = ptp_ixp_match,
315 .suppress_bind_attrs = true,
316 },
317 .probe = ptp_ixp_probe,
318 };
319 module_platform_driver(ptp_ixp_driver);
320
321 MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>");
322 MODULE_DESCRIPTION("PTP clock using the IXP46X timer");
323 MODULE_LICENSE("GPL");
324