1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Renesas R-Car Gen4 gPTP device driver |
3 | * |
4 | * Copyright (C) 2022 Renesas Electronics Corporation |
5 | */ |
6 | |
7 | #include <linux/err.h> |
8 | #include <linux/etherdevice.h> |
9 | #include <linux/kernel.h> |
10 | #include <linux/module.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/slab.h> |
13 | |
14 | #include "rcar_gen4_ptp.h" |
15 | #define ptp_to_priv(ptp) container_of(ptp, struct rcar_gen4_ptp_private, info) |
16 | |
17 | static const struct rcar_gen4_ptp_reg_offset gen4_offs = { |
18 | .enable = PTPTMEC, |
19 | .disable = PTPTMDC, |
20 | .increment = PTPTIVC0, |
21 | .config_t0 = PTPTOVC00, |
22 | .config_t1 = PTPTOVC10, |
23 | .config_t2 = PTPTOVC20, |
24 | .monitor_t0 = PTPGPTPTM00, |
25 | .monitor_t1 = PTPGPTPTM10, |
26 | .monitor_t2 = PTPGPTPTM20, |
27 | }; |
28 | |
29 | static int rcar_gen4_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) |
30 | { |
31 | struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp); |
32 | bool neg_adj = scaled_ppm < 0 ? true : false; |
33 | s64 addend = ptp_priv->default_addend; |
34 | s64 diff; |
35 | |
36 | if (neg_adj) |
37 | scaled_ppm = -scaled_ppm; |
38 | diff = div_s64(dividend: addend * scaled_ppm_to_ppb(ppm: scaled_ppm), NSEC_PER_SEC); |
39 | addend = neg_adj ? addend - diff : addend + diff; |
40 | |
41 | iowrite32(addend, ptp_priv->addr + ptp_priv->offs->increment); |
42 | |
43 | return 0; |
44 | } |
45 | |
46 | /* Caller must hold the lock */ |
47 | static void _rcar_gen4_ptp_gettime(struct ptp_clock_info *ptp, |
48 | struct timespec64 *ts) |
49 | { |
50 | struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp); |
51 | |
52 | ts->tv_nsec = ioread32(ptp_priv->addr + ptp_priv->offs->monitor_t0); |
53 | ts->tv_sec = ioread32(ptp_priv->addr + ptp_priv->offs->monitor_t1) | |
54 | ((s64)ioread32(ptp_priv->addr + ptp_priv->offs->monitor_t2) << 32); |
55 | } |
56 | |
57 | static int rcar_gen4_ptp_gettime(struct ptp_clock_info *ptp, |
58 | struct timespec64 *ts) |
59 | { |
60 | struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp); |
61 | unsigned long flags; |
62 | |
63 | spin_lock_irqsave(&ptp_priv->lock, flags); |
64 | _rcar_gen4_ptp_gettime(ptp, ts); |
65 | spin_unlock_irqrestore(lock: &ptp_priv->lock, flags); |
66 | |
67 | return 0; |
68 | } |
69 | |
70 | /* Caller must hold the lock */ |
71 | static void _rcar_gen4_ptp_settime(struct ptp_clock_info *ptp, |
72 | const struct timespec64 *ts) |
73 | { |
74 | struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp); |
75 | |
76 | iowrite32(1, ptp_priv->addr + ptp_priv->offs->disable); |
77 | iowrite32(0, ptp_priv->addr + ptp_priv->offs->config_t2); |
78 | iowrite32(0, ptp_priv->addr + ptp_priv->offs->config_t1); |
79 | iowrite32(0, ptp_priv->addr + ptp_priv->offs->config_t0); |
80 | iowrite32(1, ptp_priv->addr + ptp_priv->offs->enable); |
81 | iowrite32(ts->tv_sec >> 32, ptp_priv->addr + ptp_priv->offs->config_t2); |
82 | iowrite32(ts->tv_sec, ptp_priv->addr + ptp_priv->offs->config_t1); |
83 | iowrite32(ts->tv_nsec, ptp_priv->addr + ptp_priv->offs->config_t0); |
84 | } |
85 | |
86 | static int rcar_gen4_ptp_settime(struct ptp_clock_info *ptp, |
87 | const struct timespec64 *ts) |
88 | { |
89 | struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp); |
90 | unsigned long flags; |
91 | |
92 | spin_lock_irqsave(&ptp_priv->lock, flags); |
93 | _rcar_gen4_ptp_settime(ptp, ts); |
94 | spin_unlock_irqrestore(lock: &ptp_priv->lock, flags); |
95 | |
96 | return 0; |
97 | } |
98 | |
99 | static int rcar_gen4_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) |
100 | { |
101 | struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp); |
102 | struct timespec64 ts; |
103 | unsigned long flags; |
104 | s64 now; |
105 | |
106 | spin_lock_irqsave(&ptp_priv->lock, flags); |
107 | _rcar_gen4_ptp_gettime(ptp, ts: &ts); |
108 | now = ktime_to_ns(kt: timespec64_to_ktime(ts)); |
109 | ts = ns_to_timespec64(nsec: now + delta); |
110 | _rcar_gen4_ptp_settime(ptp, ts: &ts); |
111 | spin_unlock_irqrestore(lock: &ptp_priv->lock, flags); |
112 | |
113 | return 0; |
114 | } |
115 | |
116 | static int rcar_gen4_ptp_enable(struct ptp_clock_info *ptp, |
117 | struct ptp_clock_request *rq, int on) |
118 | { |
119 | return -EOPNOTSUPP; |
120 | } |
121 | |
122 | static struct ptp_clock_info rcar_gen4_ptp_info = { |
123 | .owner = THIS_MODULE, |
124 | .name = "rcar_gen4_ptp" , |
125 | .max_adj = 50000000, |
126 | .adjfine = rcar_gen4_ptp_adjfine, |
127 | .adjtime = rcar_gen4_ptp_adjtime, |
128 | .gettime64 = rcar_gen4_ptp_gettime, |
129 | .settime64 = rcar_gen4_ptp_settime, |
130 | .enable = rcar_gen4_ptp_enable, |
131 | }; |
132 | |
133 | static int rcar_gen4_ptp_set_offs(struct rcar_gen4_ptp_private *ptp_priv, |
134 | enum rcar_gen4_ptp_reg_layout layout) |
135 | { |
136 | if (layout != RCAR_GEN4_PTP_REG_LAYOUT) |
137 | return -EINVAL; |
138 | |
139 | ptp_priv->offs = &gen4_offs; |
140 | |
141 | return 0; |
142 | } |
143 | |
144 | static s64 rcar_gen4_ptp_rate_to_increment(u32 rate) |
145 | { |
146 | /* Timer increment in ns. |
147 | * bit[31:27] - integer |
148 | * bit[26:0] - decimal |
149 | * increment[ns] = perid[ns] * 2^27 => (1ns * 2^27) / rate[hz] |
150 | */ |
151 | return div_s64(dividend: 1000000000LL << 27, divisor: rate); |
152 | } |
153 | |
154 | int rcar_gen4_ptp_register(struct rcar_gen4_ptp_private *ptp_priv, |
155 | enum rcar_gen4_ptp_reg_layout layout, u32 rate) |
156 | { |
157 | int ret; |
158 | |
159 | if (ptp_priv->initialized) |
160 | return 0; |
161 | |
162 | spin_lock_init(&ptp_priv->lock); |
163 | |
164 | ret = rcar_gen4_ptp_set_offs(ptp_priv, layout); |
165 | if (ret) |
166 | return ret; |
167 | |
168 | ptp_priv->default_addend = rcar_gen4_ptp_rate_to_increment(rate); |
169 | iowrite32(ptp_priv->default_addend, ptp_priv->addr + ptp_priv->offs->increment); |
170 | ptp_priv->clock = ptp_clock_register(info: &ptp_priv->info, NULL); |
171 | if (IS_ERR(ptr: ptp_priv->clock)) |
172 | return PTR_ERR(ptr: ptp_priv->clock); |
173 | |
174 | iowrite32(0x01, ptp_priv->addr + ptp_priv->offs->enable); |
175 | ptp_priv->initialized = true; |
176 | |
177 | return 0; |
178 | } |
179 | EXPORT_SYMBOL_GPL(rcar_gen4_ptp_register); |
180 | |
181 | int rcar_gen4_ptp_unregister(struct rcar_gen4_ptp_private *ptp_priv) |
182 | { |
183 | iowrite32(1, ptp_priv->addr + ptp_priv->offs->disable); |
184 | |
185 | return ptp_clock_unregister(ptp: ptp_priv->clock); |
186 | } |
187 | EXPORT_SYMBOL_GPL(rcar_gen4_ptp_unregister); |
188 | |
189 | struct rcar_gen4_ptp_private *rcar_gen4_ptp_alloc(struct platform_device *pdev) |
190 | { |
191 | struct rcar_gen4_ptp_private *ptp; |
192 | |
193 | ptp = devm_kzalloc(dev: &pdev->dev, size: sizeof(*ptp), GFP_KERNEL); |
194 | if (!ptp) |
195 | return NULL; |
196 | |
197 | ptp->info = rcar_gen4_ptp_info; |
198 | |
199 | return ptp; |
200 | } |
201 | EXPORT_SYMBOL_GPL(rcar_gen4_ptp_alloc); |
202 | |
203 | MODULE_AUTHOR("Yoshihiro Shimoda" ); |
204 | MODULE_DESCRIPTION("Renesas R-Car Gen4 gPTP driver" ); |
205 | MODULE_LICENSE("GPL" ); |
206 | |