1 // SPDX-License-Identifier: GPL-2.0-only
2 /*******************************************************************************
3 PTP 1588 clock using the STMMAC.
5 Copyright (C) 2013 Vayavya Labs Pvt Ltd
8 Author: Rayagond Kokatanur <rayagond@vayavyalabs.com>
9 *******************************************************************************/
11 #include "stmmac_ptp.h"
16 * @ptp: pointer to ptp_clock_info structure
17 * @ppb: desired period change in parts ber billion
19 * Description: this function will adjust the frequency of hardware clock.
21 static int stmmac_adjust_freq(struct ptp_clock_info
*ptp
, s32 ppb
)
23 struct stmmac_priv
*priv
=
24 container_of(ptp
, struct stmmac_priv
, ptp_clock_ops
);
35 addend
= priv
->default_addend
;
38 diff
= div_u64(adj
, 1000000000ULL);
39 addend
= neg_adj
? (addend
- diff
) : (addend
+ diff
);
41 spin_lock_irqsave(&priv
->ptp_lock
, flags
);
42 stmmac_config_addend(priv
, priv
->ptpaddr
, addend
);
43 spin_unlock_irqrestore(&priv
->ptp_lock
, flags
);
51 * @ptp: pointer to ptp_clock_info structure
52 * @delta: desired change in nanoseconds
54 * Description: this function will shift/adjust the hardware clock time.
56 static int stmmac_adjust_time(struct ptp_clock_info
*ptp
, s64 delta
)
58 struct stmmac_priv
*priv
=
59 container_of(ptp
, struct stmmac_priv
, ptp_clock_ops
);
62 u32 quotient
, reminder
;
66 xmac
= priv
->plat
->has_gmac4
|| priv
->plat
->has_xgmac
;
73 quotient
= div_u64_rem(delta
, 1000000000ULL, &reminder
);
77 spin_lock_irqsave(&priv
->ptp_lock
, flags
);
78 stmmac_adjust_systime(priv
, priv
->ptpaddr
, sec
, nsec
, neg_adj
, xmac
);
79 spin_unlock_irqrestore(&priv
->ptp_lock
, flags
);
87 * @ptp: pointer to ptp_clock_info structure
88 * @ts: pointer to hold time/result
90 * Description: this function will read the current time from the
91 * hardware clock and store it in @ts.
93 static int stmmac_get_time(struct ptp_clock_info
*ptp
, struct timespec64
*ts
)
95 struct stmmac_priv
*priv
=
96 container_of(ptp
, struct stmmac_priv
, ptp_clock_ops
);
100 spin_lock_irqsave(&priv
->ptp_lock
, flags
);
101 stmmac_get_systime(priv
, priv
->ptpaddr
, &ns
);
102 spin_unlock_irqrestore(&priv
->ptp_lock
, flags
);
104 *ts
= ns_to_timespec64(ns
);
112 * @ptp: pointer to ptp_clock_info structure
113 * @ts: time value to set
115 * Description: this function will set the current time on the
118 static int stmmac_set_time(struct ptp_clock_info
*ptp
,
119 const struct timespec64
*ts
)
121 struct stmmac_priv
*priv
=
122 container_of(ptp
, struct stmmac_priv
, ptp_clock_ops
);
125 spin_lock_irqsave(&priv
->ptp_lock
, flags
);
126 stmmac_init_systime(priv
, priv
->ptpaddr
, ts
->tv_sec
, ts
->tv_nsec
);
127 spin_unlock_irqrestore(&priv
->ptp_lock
, flags
);
132 static int stmmac_enable(struct ptp_clock_info
*ptp
,
133 struct ptp_clock_request
*rq
, int on
)
135 struct stmmac_priv
*priv
=
136 container_of(ptp
, struct stmmac_priv
, ptp_clock_ops
);
137 struct stmmac_pps_cfg
*cfg
;
138 int ret
= -EOPNOTSUPP
;
142 case PTP_CLK_REQ_PEROUT
:
143 /* Reject requests with unsupported flags */
144 if (rq
->perout
.flags
)
147 cfg
= &priv
->pps
[rq
->perout
.index
];
149 cfg
->start
.tv_sec
= rq
->perout
.start
.sec
;
150 cfg
->start
.tv_nsec
= rq
->perout
.start
.nsec
;
151 cfg
->period
.tv_sec
= rq
->perout
.period
.sec
;
152 cfg
->period
.tv_nsec
= rq
->perout
.period
.nsec
;
154 spin_lock_irqsave(&priv
->ptp_lock
, flags
);
155 ret
= stmmac_flex_pps_config(priv
, priv
->ioaddr
,
156 rq
->perout
.index
, cfg
, on
,
157 priv
->sub_second_inc
,
158 priv
->systime_flags
);
159 spin_unlock_irqrestore(&priv
->ptp_lock
, flags
);
168 /* structure describing a PTP hardware clock */
169 static struct ptp_clock_info stmmac_ptp_clock_ops
= {
170 .owner
= THIS_MODULE
,
171 .name
= "stmmac ptp",
175 .n_per_out
= 0, /* will be overwritten in stmmac_ptp_register */
178 .adjfreq
= stmmac_adjust_freq
,
179 .adjtime
= stmmac_adjust_time
,
180 .gettime64
= stmmac_get_time
,
181 .settime64
= stmmac_set_time
,
182 .enable
= stmmac_enable
,
186 * stmmac_ptp_register
187 * @priv: driver private structure
188 * Description: this function will register the ptp clock driver
189 * to kernel. It also does some house keeping work.
191 void stmmac_ptp_register(struct stmmac_priv
*priv
)
195 for (i
= 0; i
< priv
->dma_cap
.pps_out_num
; i
++) {
196 if (i
>= STMMAC_PPS_MAX
)
198 priv
->pps
[i
].available
= true;
201 if (priv
->plat
->ptp_max_adj
)
202 stmmac_ptp_clock_ops
.max_adj
= priv
->plat
->ptp_max_adj
;
204 stmmac_ptp_clock_ops
.n_per_out
= priv
->dma_cap
.pps_out_num
;
206 spin_lock_init(&priv
->ptp_lock
);
207 priv
->ptp_clock_ops
= stmmac_ptp_clock_ops
;
209 priv
->ptp_clock
= ptp_clock_register(&priv
->ptp_clock_ops
,
211 if (IS_ERR(priv
->ptp_clock
)) {
212 netdev_err(priv
->dev
, "ptp_clock_register failed\n");
213 priv
->ptp_clock
= NULL
;
214 } else if (priv
->ptp_clock
)
215 netdev_info(priv
->dev
, "registered PTP clock\n");
219 * stmmac_ptp_unregister
220 * @priv: driver private structure
221 * Description: this function will remove/unregister the ptp clock driver
224 void stmmac_ptp_unregister(struct stmmac_priv
*priv
)
226 if (priv
->ptp_clock
) {
227 ptp_clock_unregister(priv
->ptp_clock
);
228 priv
->ptp_clock
= NULL
;
229 pr_debug("Removed PTP HW clock successfully on %s\n",