]> rtime.felk.cvut.cz Git - hercules2020/nv-tegra/linux-4.4.git/blob - drivers/net/ethernet/nvidia/eqos/ptp.c
e79501ebcae4ebe43aeed1d25456a973a1740b4c
[hercules2020/nv-tegra/linux-4.4.git] / drivers / net / ethernet / nvidia / eqos / ptp.c
1 /* =========================================================================
2  * The Synopsys DWC ETHER QOS Software Driver and documentation (hereinafter
3  * "Software") is an unsupported proprietary work of Synopsys, Inc. unless
4  * otherwise expressly agreed to in writing between Synopsys and you.
5  *
6  * The Software IS NOT an item of Licensed Software or Licensed Product under
7  * any End User Software License Agreement or Agreement for Licensed Product
8  * with Synopsys or any supplement thereto.  Permission is hereby granted,
9  * free of charge, to any person obtaining a copy of this software annotated
10  * with this license and the Software, to deal in the Software without
11  * restriction, including without limitation the rights to use, copy, modify,
12  * merge, publish, distribute, sublicense, and/or sell copies of the Software,
13  * and to permit persons to whom the Software is furnished to do so, subject
14  * to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included in
17  * all copies or substantial portions of the Software.
18  *
19  * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
23  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
29  * DAMAGE.
30  * =========================================================================
31  */
32 /*
33  * Copyright (c) 2015-2016, NVIDIA CORPORATION.  All rights reserved.
34  *
35  * This program is free software; you can redistribute it and/or modify it
36  * under the terms and conditions of the GNU General Public License,
37  * version 2, as published by the Free Software Foundation.
38  *
39  * This program is distributed in the hope it will be useful, but WITHOUT
40  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
41  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
42  * more details.
43  */
44 /*!@file: ptp.c
45  * @brief: Driver functions.
46  */
47 #include "yheader.h"
48
49 /*!
50  * \brief API to adjust the frequency of hardware clock.
51  *
52  * \details This function is used to adjust the frequency of the
53  * hardware clock.
54  *
55  * \param[in] ptp – pointer to ptp_clock_info structure.
56  * \param[in] delta – desired period change in parts per billion.
57  *
58  * \return int
59  *
60  * \retval 0 on success and -ve number on failure.
61  */
62
63 static int eqos_adjust_freq(struct ptp_clock_info *ptp, s32 ppb)
64 {
65         struct eqos_prv_data *pdata =
66             container_of(ptp, struct eqos_prv_data, ptp_clock_ops);
67         struct hw_if_struct *hw_if = &(pdata->hw_if);
68         u64 adj;
69         u32 diff, addend;
70         int neg_adj = 0;
71
72         DBGPR_PTP("-->eqos_adjust_freq: %d\n", ppb);
73
74         if (ppb < 0) {
75                 neg_adj = 1;
76                 ppb = -ppb;
77         }
78
79         addend = pdata->default_addend;
80         adj = addend;
81         adj *= ppb;
82         /*
83          * div_u64 will divided the "adj" by "1000000000ULL"
84          * and return the quotient.
85          */
86         diff = div_u64(adj, 1000000000ULL);
87         addend = neg_adj ? (addend - diff) : (addend + diff);
88
89         raw_spin_lock(&pdata->ptp_lock);
90
91         hw_if->config_addend(addend);
92
93         raw_spin_unlock(&pdata->ptp_lock);
94
95         DBGPR_PTP("<--eqos_adjust_freq\n");
96
97         return 0;
98 }
99
100 /*!
101  * \brief API to adjust the hardware time.
102  *
103  * \details This function is used to shift/adjust the time of the
104  * hardware clock.
105  *
106  * \param[in] ptp – pointer to ptp_clock_info structure.
107  * \param[in] delta – desired change in nanoseconds.
108  *
109  * \return int
110  *
111  * \retval 0 on success and -ve number on failure.
112  */
113
114 static int eqos_adjust_time(struct ptp_clock_info *ptp, s64 delta)
115 {
116         struct eqos_prv_data *pdata =
117             container_of(ptp, struct eqos_prv_data, ptp_clock_ops);
118         struct hw_if_struct *hw_if = &(pdata->hw_if);
119         u32 sec, nsec;
120         u32 quotient, reminder;
121         int neg_adj = 0;
122
123         DBGPR_PTP("-->eqos_adjust_time: delta = %lld\n", delta);
124
125         if (delta < 0) {
126                 neg_adj = 1;
127                 delta = -delta;
128         }
129
130         quotient = div_u64_rem(delta, 1000000000ULL, &reminder);
131         sec = quotient;
132         nsec = reminder;
133
134         raw_spin_lock(&pdata->ptp_lock);
135
136         hw_if->adjust_systime(sec, nsec, neg_adj, pdata->one_nsec_accuracy);
137
138         raw_spin_unlock(&pdata->ptp_lock);
139
140         /* Register broadcasting MAC timestamp to clients */
141         tegra_register_hwtime_source(hw_if->get_systime);
142
143         DBGPR_PTP("<--eqos_adjust_time\n");
144
145         return 0;
146 }
147
148 /*!
149  * \brief API to get the current time.
150  *
151  * \details This function is used to read the current time from the
152  * hardware clock.
153  *
154  * \param[in] ptp – pointer to ptp_clock_info structure.
155  * \param[in] ts – pointer to hold the time/result.
156  *
157  * \return int
158  *
159  * \retval 0 on success and -ve number on failure.
160  */
161
162 static int eqos_get_time(struct ptp_clock_info *ptp, struct timespec *ts)
163 {
164         struct eqos_prv_data *pdata =
165             container_of(ptp, struct eqos_prv_data, ptp_clock_ops);
166         struct hw_if_struct *hw_if = &(pdata->hw_if);
167         unsigned long flags;
168         u64 ns;
169         u32 reminder;
170
171         DBGPR_PTP("-->eqos_get_time\n");
172
173         raw_spin_lock_irqsave(&pdata->ptp_lock, flags);
174
175         ns = hw_if->get_systime();
176
177         raw_spin_unlock_irqrestore(&pdata->ptp_lock, flags);
178
179         ts->tv_sec = div_u64_rem(ns, 1000000000ULL, &reminder);
180         ts->tv_nsec = reminder;
181
182         DBGPR_PTP("<--eqos_get_time: ts->tv_sec = %ld, ts->tv_nsec = %ld\n",
183                 ts->tv_sec, ts->tv_nsec);
184
185         return 0;
186 }
187
188 /*!
189  * \brief API to set the current time.
190  *
191  * \details This function is used to set the current time on the
192  * hardware clock.
193  *
194  * \param[in] ptp – pointer to ptp_clock_info structure.
195  * \param[in] ts – time value to set.
196  *
197  * \return int
198  *
199  * \retval 0 on success and -ve number on failure.
200  */
201
202 static int eqos_set_time(struct ptp_clock_info *ptp, const struct timespec *ts)
203 {
204         struct eqos_prv_data *pdata =
205             container_of(ptp, struct eqos_prv_data, ptp_clock_ops);
206         struct hw_if_struct *hw_if = &(pdata->hw_if);
207
208         DBGPR_PTP("-->eqos_set_time: ts->tv_sec = %ld, ts->tv_nsec = %ld\n",
209                 ts->tv_sec, ts->tv_nsec);
210
211         raw_spin_lock(&pdata->ptp_lock);
212
213         hw_if->init_systime(ts->tv_sec, ts->tv_nsec);
214
215         raw_spin_unlock(&pdata->ptp_lock);
216
217         DBGPR_PTP("<--eqos_set_time\n");
218
219         return 0;
220 }
221
222 /*!
223  * \brief API to enable/disable an ancillary feature.
224  *
225  * \details This function is used to enable or disable an ancillary
226  * device feature like PPS, PEROUT and EXTTS.
227  *
228  * \param[in] ptp – pointer to ptp_clock_info structure.
229  * \param[in] rq – desired resource to enable or disable.
230  * \param[in] on – caller passes one to enable or zero to disable.
231  *
232  * \return int
233  *
234  * \retval 0 on success and -ve(EINVAL or EOPNOTSUPP) number on failure.
235  */
236
237 static int eqos_enable(struct ptp_clock_info *ptp,
238                        struct ptp_clock_request *rq, int on)
239 {
240         return -EOPNOTSUPP;
241 }
242
243 /* structure describing a PTP hardware clock */
244 static struct ptp_clock_info eqos_ptp_clock_ops = {
245         .owner = THIS_MODULE,
246         .name = "eqos_clk",
247         .max_adj = EQOS_SYSCLOCK,
248         /* the max possible frequency adjustment in parts per billion */
249         .n_alarm = 0,           /* the number of programmable alarms */
250         .n_ext_ts = 0,          /* the number of externel time stamp channels */
251         .n_per_out = 0, /* the number of programmable periodic signals */
252         .pps = 0,       /* indicates whether the clk supports a PPS callback */
253         .adjfreq = eqos_adjust_freq,
254         .adjtime = eqos_adjust_time,
255 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0)
256         .gettime = eqos_get_time,
257         .settime = eqos_set_time,
258 #else
259         .gettime64 = eqos_get_time,
260         .settime64 = eqos_set_time,
261 #endif
262         .enable = eqos_enable,
263 };
264
265 /*!
266  * \brief API to register ptp clock driver.
267  *
268  * \details This function is used to register the ptp clock
269  * driver to kernel. It also does some housekeeping work.
270  *
271  * \param[in] pdata – pointer to private data structure.
272  *
273  * \return int
274  *
275  * \retval 0 on success and -ve number on failure.
276  */
277
278 int eqos_ptp_init(struct eqos_prv_data *pdata)
279 {
280         int ret = 0;
281
282         DBGPR_PTP("-->eqos_ptp_init\n");
283
284         if (!pdata->hw_feat.tsstssel) {
285                 ret = -1;
286                 pdata->ptp_clock = NULL;
287                 pr_err("No PTP supports in HW\n"
288                        "Aborting PTP clock driver registration\n");
289                 goto no_hw_ptp;
290         }
291
292         raw_spin_lock_init(&pdata->ptp_lock);
293
294         pdata->ptp_clock_ops = eqos_ptp_clock_ops;
295
296         pdata->ptp_clock =
297             ptp_clock_register(&pdata->ptp_clock_ops, &pdata->pdev->dev);
298         if (IS_ERR(pdata->ptp_clock)) {
299                 pdata->ptp_clock = NULL;
300                 pr_err("ptp_clock_register() failed\n");
301         }
302
303         DBGPR_PTP("<--eqos_ptp_init\n");
304
305         return ret;
306
307  no_hw_ptp:
308         return ret;
309 }
310
311 /*!
312  * \brief API to unregister ptp clock driver.
313  *
314  * \details This function is used to remove/unregister the ptp
315  * clock driver from the kernel.
316  *
317  * \param[in] pdata – pointer to private data structure.
318  *
319  * \return void
320  */
321
322 void eqos_ptp_remove(struct eqos_prv_data *pdata)
323 {
324         DBGPR_PTP("-->eqos_ptp_remove\n");
325
326         if (pdata->ptp_clock) {
327                 ptp_clock_unregister(pdata->ptp_clock);
328                 pr_err("Removed PTP HW clock successfully\n");
329         }
330
331         DBGPR_PTP("<--eqos_ptp_remove\n");
332 }