summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_ptp.c
blob: f54a3d679425631a0ce5ac89d026bd58c171e3d3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
/*
 * DPAA Ethernet Driver -- PTP 1588 clock using the dTSEC
 *
 * Author: Yangbo Lu <yangbo.lu@freescale.com>
 *
 * Copyright 2014 Freescale Semiconductor, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
*/

#include <linux/device.h>
#include <linux/hrtimer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/timex.h>
#include <linux/io.h>

#include <linux/ptp_clock_kernel.h>

#include "dpaa_eth.h"
#include "mac.h"

static struct mac_device *mac_dev;
static u32 freqCompensation;

/* Bit definitions for the TMR_CTRL register */
#define ALM1P                 (1<<31) /* Alarm1 output polarity */
#define ALM2P                 (1<<30) /* Alarm2 output polarity */
#define FS                    (1<<28) /* FIPER start indication */
#define PP1L                  (1<<27) /* Fiper1 pulse loopback mode enabled. */
#define PP2L                  (1<<26) /* Fiper2 pulse loopback mode enabled. */
#define TCLK_PERIOD_SHIFT     (16) /* 1588 timer reference clock period. */
#define TCLK_PERIOD_MASK      (0x3ff)
#define RTPE                  (1<<15) /* Record Tx Timestamp to PAL Enable. */
#define FRD                   (1<<14) /* FIPER Realignment Disable */
#define ESFDP                 (1<<11) /* External Tx/Rx SFD Polarity. */
#define ESFDE                 (1<<10) /* External Tx/Rx SFD Enable. */
#define ETEP2                 (1<<9) /* External trigger 2 edge polarity */
#define ETEP1                 (1<<8) /* External trigger 1 edge polarity */
#define COPH                  (1<<7) /* Generated clock output phase. */
#define CIPH                  (1<<6) /* External oscillator input clock phase */
#define TMSR                  (1<<5) /* Timer soft reset. */
#define BYP                   (1<<3) /* Bypass drift compensated clock */
#define TE                    (1<<2) /* 1588 timer enable. */
#define CKSEL_SHIFT           (0)    /* 1588 Timer reference clock source */
#define CKSEL_MASK            (0x3)

/* Bit definitions for the TMR_TEVENT register */
#define ETS2                  (1<<25) /* External trigger 2 timestamp sampled */
#define ETS1                  (1<<24) /* External trigger 1 timestamp sampled */
#define ALM2                  (1<<17) /* Current time = alarm time register 2 */
#define ALM1                  (1<<16) /* Current time = alarm time register 1 */
#define PP1                   (1<<7)  /* periodic pulse generated on FIPER1 */
#define PP2                   (1<<6)  /* periodic pulse generated on FIPER2 */
#define PP3                   (1<<5)  /* periodic pulse generated on FIPER3 */

/* Bit definitions for the TMR_TEMASK register */
#define ETS2EN                (1<<25) /* External trigger 2 timestamp enable */
#define ETS1EN                (1<<24) /* External trigger 1 timestamp enable */
#define ALM2EN                (1<<17) /* Timer ALM2 event enable */
#define ALM1EN                (1<<16) /* Timer ALM1 event enable */
#define PP1EN                 (1<<7) /* Periodic pulse event 1 enable */
#define PP2EN                 (1<<6) /* Periodic pulse event 2 enable */

/* Bit definitions for the TMR_PEVENT register */
#define TXP2                  (1<<9) /* PTP transmitted timestamp im TXTS2 */
#define TXP1                  (1<<8) /* PTP transmitted timestamp in TXTS1 */
#define RXP                   (1<<0) /* PTP frame has been received */

/* Bit definitions for the TMR_PEMASK register */
#define TXP2EN                (1<<9) /* Transmit PTP packet event 2 enable */
#define TXP1EN                (1<<8) /* Transmit PTP packet event 1 enable */
#define RXPEN                 (1<<0) /* Receive PTP packet event enable */

/* Bit definitions for the TMR_STAT register */
#define STAT_VEC_SHIFT        (0) /* Timer general purpose status vector */
#define STAT_VEC_MASK         (0x3f)

/* Bit definitions for the TMR_PRSC register */
#define PRSC_OCK_SHIFT        (0) /* Output clock division/prescale factor. */
#define PRSC_OCK_MASK         (0xffff)


#define N_EXT_TS	2

static void set_alarm(void)
{
	u64 ns;

	if (mac_dev->fm_rtc_get_cnt)
		mac_dev->fm_rtc_get_cnt(mac_dev->fm_dev, &ns);
	ns += 1500000000ULL;
	ns = div_u64(ns, 1000000000UL) * 1000000000ULL;
	ns -= DPA_PTP_NOMINAL_FREQ_PERIOD_NS;
	if (mac_dev->fm_rtc_set_alarm)
		mac_dev->fm_rtc_set_alarm(mac_dev->fm_dev, 0, ns);
}

static void set_fipers(void)
{
	u64 fiper;

	if (mac_dev->fm_rtc_disable)
		mac_dev->fm_rtc_disable(mac_dev->fm_dev);

	set_alarm();
	fiper = 1000000000ULL - DPA_PTP_NOMINAL_FREQ_PERIOD_NS;
	if (mac_dev->fm_rtc_set_fiper)
		mac_dev->fm_rtc_set_fiper(mac_dev->fm_dev, 0, fiper);

	if (mac_dev->fm_rtc_enable)
		mac_dev->fm_rtc_enable(mac_dev->fm_dev);
}

/* PTP clock operations */

static int ptp_dpa_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
{
	u64 adj;
	u32 diff, tmr_add;
	int neg_adj = 0;

	if (ppb < 0) {
		neg_adj = 1;
		ppb = -ppb;
	}

	tmr_add = freqCompensation;
	adj = tmr_add;
	adj *= ppb;
	diff = div_u64(adj, 1000000000ULL);

	tmr_add = neg_adj ? tmr_add - diff : tmr_add + diff;

	if (mac_dev->fm_rtc_set_drift)
		mac_dev->fm_rtc_set_drift(mac_dev->fm_dev, tmr_add);

	return 0;
}

static int ptp_dpa_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
	s64 now;

	if (mac_dev->fm_rtc_get_cnt)
		mac_dev->fm_rtc_get_cnt(mac_dev->fm_dev, &now);

	now += delta;

	if (mac_dev->fm_rtc_set_cnt)
		mac_dev->fm_rtc_set_cnt(mac_dev->fm_dev, now);
	set_fipers();

	return 0;
}

static int ptp_dpa_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
{
	u64 ns;
	u32 remainder;

	if (mac_dev->fm_rtc_get_cnt)
		mac_dev->fm_rtc_get_cnt(mac_dev->fm_dev, &ns);

	ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder);
	ts->tv_nsec = remainder;
	return 0;
}

static int ptp_dpa_settime(struct ptp_clock_info *ptp,
			       const struct timespec64 *ts)
{
	u64 ns;

	ns = ts->tv_sec * 1000000000ULL;
	ns += ts->tv_nsec;

	if (mac_dev->fm_rtc_set_cnt)
		mac_dev->fm_rtc_set_cnt(mac_dev->fm_dev, ns);
	set_fipers();
	return 0;
}

static int ptp_dpa_enable(struct ptp_clock_info *ptp,
			      struct ptp_clock_request *rq, int on)
{
	u32 bit;

	switch (rq->type) {
	case PTP_CLK_REQ_EXTTS:
		switch (rq->extts.index) {
		case 0:
			bit = ETS1EN;
			break;
		case 1:
			bit = ETS2EN;
			break;
		default:
			return -EINVAL;
		}
		if (on) {
			if (mac_dev->fm_rtc_enable_interrupt)
				mac_dev->fm_rtc_enable_interrupt(
					mac_dev->fm_dev, bit);
		} else {
			if (mac_dev->fm_rtc_disable_interrupt)
				mac_dev->fm_rtc_disable_interrupt(
					mac_dev->fm_dev, bit);
		}
		return 0;

	case PTP_CLK_REQ_PPS:
		if (on) {
			if (mac_dev->fm_rtc_enable_interrupt)
				mac_dev->fm_rtc_enable_interrupt(
					mac_dev->fm_dev, PP1EN);
		} else {
			if (mac_dev->fm_rtc_disable_interrupt)
				mac_dev->fm_rtc_disable_interrupt(
					mac_dev->fm_dev, PP1EN);
		}
		return 0;

	default:
		break;
	}

	return -EOPNOTSUPP;
}

static struct ptp_clock_info ptp_dpa_caps = {
	.owner		= THIS_MODULE,
	.name		= "dpaa clock",
	.max_adj	= 512000,
	.n_alarm	= 0,
	.n_ext_ts	= N_EXT_TS,
	.n_per_out	= 0,
	.pps		= 1,
	.adjfreq	= ptp_dpa_adjfreq,
	.adjtime	= ptp_dpa_adjtime,
	.gettime64	= ptp_dpa_gettime,
	.settime64	= ptp_dpa_settime,
	.enable		= ptp_dpa_enable,
};

static int __init __cold dpa_ptp_load(void)
{
	struct device *ptp_dev;
	struct timespec64 now;
	struct ptp_clock *clock = ptp_priv.clock;
	int dpa_phc_index;
	int err;

	if (!(ptp_priv.of_dev && ptp_priv.mac_dev))
		return -ENODEV;

	ptp_dev = &ptp_priv.of_dev->dev;
	mac_dev = ptp_priv.mac_dev;

	if (mac_dev->fm_rtc_get_drift)
		mac_dev->fm_rtc_get_drift(mac_dev->fm_dev, &freqCompensation);

	getnstimeofday64(&now);
	ptp_dpa_settime(&ptp_dpa_caps, &now);

	clock = ptp_clock_register(&ptp_dpa_caps, ptp_dev);
	if (IS_ERR(clock)) {
		err = PTR_ERR(clock);
		return err;
	}
	dpa_phc_index = ptp_clock_index(clock);
	return 0;
}
module_init(dpa_ptp_load);

static void __exit __cold dpa_ptp_unload(void)
{
	struct ptp_clock *clock = ptp_priv.clock;

	if (mac_dev->fm_rtc_disable_interrupt)
		mac_dev->fm_rtc_disable_interrupt(mac_dev->fm_dev, 0xffffffff);
	ptp_clock_unregister(clock);
}
module_exit(dpa_ptp_unload);