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