xref: /NextBSD/sys/dev/utopia/idtphy.c (revision eb1a5f8de9f7ea602c373a710f531abbf81141c4)
1 /*-
2  * Copyright (c) 2003
3  *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4  * 	All rights reserved.
5  *
6  * Author: Hartmut Brandt <harti@freebsd.org>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/unistd.h>
36 #include <sys/kernel.h>
37 #include <sys/kthread.h>
38 #include <sys/proc.h>
39 #include <sys/bus.h>
40 #include <sys/malloc.h>
41 #include <sys/module.h>
42 #include <sys/sysctl.h>
43 #include <sys/lock.h>
44 #include <sys/mutex.h>
45 #include <sys/socket.h>
46 
47 #include <net/if.h>
48 #include <net/if_var.h>
49 #include <net/if_media.h>
50 #include <net/if_atm.h>
51 
52 #include <dev/utopia/idtphy.h>
53 #include <dev/utopia/utopia.h>
54 #include <dev/utopia/utopia_priv.h>
55 
56 /*
57  * Reset IDT77105. There is really no way to reset this thing by acessing
58  * the registers. Load the registers with default values.
59  */
60 static int
idt77105_reset(struct utopia * utp)61 idt77105_reset(struct utopia *utp)
62 {
63 	int err = 0;
64 	u_int n;
65 	uint8_t val[2];
66 
67 	err |= UTP_WRITEREG(utp, IDTPHY_REGO_MCR, 0xff,
68 	    IDTPHY_REGM_MCR_DRIC | IDTPHY_REGM_MCR_EI);
69 	n = 1;
70 	err |= UTP_READREGS(utp, IDTPHY_REGO_ISTAT, val, &n);
71 	err |= UTP_WRITEREG(utp, IDTPHY_REGO_DIAG, 0xff, 0);
72 	err |= UTP_WRITEREG(utp, IDTPHY_REGO_LHEC, 0xff, 0);
73 
74 	err |= UTP_WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, IDTPHY_REGM_CNTS_SEC);
75 	n = 2;
76 	err |= UTP_READREGS(utp, IDTPHY_REGO_CNT, val, &n);
77 
78 	err |= UTP_WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, IDTPHY_REGM_CNTS_TX);
79 	n = 2;
80 	err |= UTP_READREGS(utp, IDTPHY_REGO_CNT, val, &n);
81 
82 	err |= UTP_WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, IDTPHY_REGM_CNTS_RX);
83 	n = 2;
84 	err |= UTP_READREGS(utp, IDTPHY_REGO_CNT, val, &n);
85 
86 	err |= UTP_WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, IDTPHY_REGM_CNTS_HECE);
87 	n = 2;
88 	err |= UTP_READREGS(utp, IDTPHY_REGO_CNT, val, &n);
89 
90 	err |= UTP_WRITEREG(utp, IDTPHY_REGO_MCR, IDTPHY_REGM_MCR_DREC,
91 	    IDTPHY_REGM_MCR_DREC);
92 	err |= UTP_WRITEREG(utp, IDTPHY_REGO_DIAG, IDTPHY_REGM_DIAG_RFLUSH,
93 	    IDTPHY_REGM_DIAG_RFLUSH);
94 
95 	/* loopback */
96 	err |= utopia_set_loopback(utp, utp->loopback);
97 
98 	/* update carrier state */
99 	err |= utopia_update_carrier(utp);
100 
101 	return (err ? EIO : 0);
102 }
103 
104 static int
idt77105_inval(struct utopia * utp,int what __unused)105 idt77105_inval(struct utopia *utp, int what __unused)
106 {
107 
108 	return (EINVAL);
109 }
110 
111 static int
idt77105_update_carrier(struct utopia * utp)112 idt77105_update_carrier(struct utopia *utp)
113 {
114 	int err;
115 	uint8_t reg;
116 	u_int n = 1;
117 
118 	if ((err = UTP_READREGS(utp, IDTPHY_REGO_ISTAT, &reg, &n)) != 0) {
119 		utp->carrier = UTP_CARR_UNKNOWN;
120 		return (err);
121 	}
122 	utopia_check_carrier(utp, reg & IDTPHY_REGM_ISTAT_GOOD);
123 	return (0);
124 }
125 
126 static int
idt77105_set_loopback(struct utopia * utp,u_int mode)127 idt77105_set_loopback(struct utopia *utp, u_int mode)
128 {
129 	int err;
130 
131 	switch (mode) {
132 	  case UTP_LOOP_NONE:
133 		err = UTP_WRITEREG(utp, IDTPHY_REGO_DIAG,
134 		    IDTPHY_REGM_DIAG_LOOP, IDTPHY_REGM_DIAG_LOOP_NONE);
135 		break;
136 
137 	  case UTP_LOOP_DIAG:
138 		err = UTP_WRITEREG(utp, IDTPHY_REGO_DIAG,
139 		    IDTPHY_REGM_DIAG_LOOP, IDTPHY_REGM_DIAG_LOOP_PHY);
140 		break;
141 
142 	  case UTP_LOOP_LINE:
143 		err = UTP_WRITEREG(utp, IDTPHY_REGO_DIAG,
144 		    IDTPHY_REGM_DIAG_LOOP, IDTPHY_REGM_DIAG_LOOP_LINE);
145 		break;
146 
147 	  default:
148 		return (EINVAL);
149 	}
150 	if (err)
151 		return (err);
152 	utp->loopback = mode;
153 	return (0);
154 }
155 
156 /*
157  * Handle interrupt on IDT77105 chip
158  */
159 static void
idt77105_intr(struct utopia * utp)160 idt77105_intr(struct utopia *utp)
161 {
162 	uint8_t reg;
163 	u_int n = 1;
164 	int err;
165 
166 	/* Interrupt status and ack the interrupt */
167 	if ((err = UTP_READREGS(utp, IDTPHY_REGO_ISTAT, &reg, &n)) != 0) {
168 		printf("IDT77105 read error %d\n", err);
169 		return;
170 	}
171 	/* check for signal condition */
172 	utopia_check_carrier(utp, reg & IDTPHY_REGM_ISTAT_GOOD);
173 }
174 
175 static void
idt77105_update_stats(struct utopia * utp)176 idt77105_update_stats(struct utopia *utp)
177 {
178 	int err = 0;
179 	uint8_t regs[2];
180 	u_int n;
181 
182 #ifdef DIAGNOSTIC
183 #define UDIAG(F,A,B)	printf(F, A, B)
184 #else
185 #define	UDIAG(F,A,B)	do { } while (0)
186 #endif
187 
188 #define	UPD(FIELD, CODE, N, MASK)					\
189 	err = UTP_WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, CODE);		\
190 	if (err != 0) {							\
191 		UDIAG("%s: cannot write CNTS: %d\n", __func__, err);	\
192 		return;							\
193 	}								\
194 	n = N;								\
195 	err = UTP_READREGS(utp, IDTPHY_REGO_CNT, regs, &n);		\
196 	if (err != 0) {							\
197 		UDIAG("%s: cannot read CNT: %d\n", __func__, err);	\
198 		return;							\
199 	}								\
200 	if (n != N) {							\
201 		UDIAG("%s: got only %u registers\n", __func__, n);	\
202 		return;							\
203 	}								\
204 	if (N == 1)							\
205 		utp->stats.FIELD += (regs[0] & MASK);			\
206 	else								\
207 		utp->stats.FIELD += (regs[0] | (regs[1] << 8)) & MASK;
208 
209 	UPD(rx_symerr, IDTPHY_REGM_CNTS_SEC, 1, 0xff);
210 	UPD(tx_cells, IDTPHY_REGM_CNTS_TX, 2, 0xffff);
211 	UPD(rx_cells, IDTPHY_REGM_CNTS_RX, 2, 0xffff);
212 	UPD(rx_uncorr, IDTPHY_REGM_CNTS_HECE, 1, 0x1f);
213 
214 #undef	UDIAG
215 #undef	UPD
216 }
217 
218 struct utopia_chip utopia_chip_idt77105 = {
219 	UTP_TYPE_IDT77105,
220 	"IDT77105",
221 	7,
222 	idt77105_reset,
223 	idt77105_inval,
224 	idt77105_inval,
225 	idt77105_inval,
226 	idt77105_update_carrier,
227 	idt77105_set_loopback,
228 	idt77105_intr,
229 	idt77105_update_stats,
230 };
231 
232 /*
233  * Update the carrier status
234  */
235 static int
idt77155_update_carrier(struct utopia * utp)236 idt77155_update_carrier(struct utopia *utp)
237 {
238 	int err;
239 	uint8_t reg;
240 	u_int n = 1;
241 
242 	if ((err = UTP_READREGS(utp, IDTPHY_REGO_RSOS, &reg, &n)) != 0) {
243 		utp->carrier = UTP_CARR_UNKNOWN;
244 		return (err);
245 	}
246 	utopia_check_carrier(utp, !(reg & IDTPHY_REGM_RSOS_LOS));
247 	return (0);
248 }
249 
250 /*
251  * Handle interrupt on IDT77155 chip
252  */
253 static void
idt77155_intr(struct utopia * utp)254 idt77155_intr(struct utopia *utp)
255 {
256 	uint8_t reg;
257 	u_int n = 1;
258 	int err;
259 
260 	if ((err = UTP_READREGS(utp, IDTPHY_REGO_RSOS, &reg, &n)) != 0) {
261 		printf("IDT77105 read error %d\n", err);
262 		return;
263 	}
264 	utopia_check_carrier(utp, !(reg & IDTPHY_REGM_RSOS_LOS));
265 }
266 
267 /*
268  * set SONET/SDH mode
269  */
270 static int
idt77155_set_sdh(struct utopia * utp,int sdh)271 idt77155_set_sdh(struct utopia *utp, int sdh)
272 {
273 	int err;
274 
275 	if (sdh)
276 		err = UTP_WRITEREG(utp, IDTPHY_REGO_PTRM,
277 		    IDTPHY_REGM_PTRM_SS, IDTPHY_REGM_PTRM_SDH);
278 	else
279 		err = UTP_WRITEREG(utp, IDTPHY_REGO_PTRM,
280 		    IDTPHY_REGM_PTRM_SS, IDTPHY_REGM_PTRM_SONET);
281 	if (err != 0)
282 		return (err);
283 
284 	utp->state &= ~UTP_ST_SDH;
285 	if (sdh)
286 		utp->state |= UTP_ST_SDH;
287 
288 	return (0);
289 }
290 
291 /*
292  * set idle/unassigned cells
293  */
294 static int
idt77155_set_unass(struct utopia * utp,int unass)295 idt77155_set_unass(struct utopia *utp, int unass)
296 {
297 	int err;
298 
299 	if (unass)
300 		err = UTP_WRITEREG(utp, IDTPHY_REGO_TCHP, 0xff, 0);
301 	else
302 		err = UTP_WRITEREG(utp, IDTPHY_REGO_TCHP, 0xff, 1);
303 	if (err != 0)
304 		return (err);
305 
306 	utp->state &= ~UTP_ST_UNASS;
307 	if (unass)
308 		utp->state |= UTP_ST_UNASS;
309 
310 	return (0);
311 }
312 
313 /*
314  * enable/disable scrambling
315  */
316 static int
idt77155_set_noscramb(struct utopia * utp,int noscramb)317 idt77155_set_noscramb(struct utopia *utp, int noscramb)
318 {
319 	int err;
320 
321 	if (noscramb) {
322 		err = UTP_WRITEREG(utp, IDTPHY_REGO_TCC,
323 		    IDTPHY_REGM_TCC_DSCR, IDTPHY_REGM_TCC_DSCR);
324 		if (err)
325 			return (err);
326 		err = UTP_WRITEREG(utp, IDTPHY_REGO_RCC,
327 		    IDTPHY_REGM_RCC_DSCR, IDTPHY_REGM_RCC_DSCR);
328 		if (err)
329 			return (err);
330 		utp->state |= UTP_ST_NOSCRAMB;
331 	} else {
332 		err = UTP_WRITEREG(utp, IDTPHY_REGO_TCC,
333 		    IDTPHY_REGM_TCC_DSCR, 0);
334 		if (err)
335 			return (err);
336 		err = UTP_WRITEREG(utp, IDTPHY_REGO_RCC,
337 		    IDTPHY_REGM_RCC_DSCR, 0);
338 		if (err)
339 			return (err);
340 		utp->state &= ~UTP_ST_NOSCRAMB;
341 	}
342 	return (0);
343 }
344 
345 /*
346  * Set loopback mode for the 77155
347  */
348 static int
idt77155_set_loopback(struct utopia * utp,u_int mode)349 idt77155_set_loopback(struct utopia *utp, u_int mode)
350 {
351 	int err;
352 	uint32_t val;
353 	u_int nmode;
354 
355 	val = 0;
356 	nmode = mode;
357 	if (mode & UTP_LOOP_TIME) {
358 		nmode &= ~UTP_LOOP_TIME;
359 		val |= IDTPHY_REGM_MCTL_TLOOP;
360 	}
361 	if (mode & UTP_LOOP_DIAG) {
362 		nmode &= ~UTP_LOOP_DIAG;
363 		val |= IDTPHY_REGM_MCTL_DLOOP;
364 	}
365 	if (mode & UTP_LOOP_LINE) {
366 		nmode &= ~UTP_LOOP_LINE;
367 		val |= IDTPHY_REGM_MCTL_LLOOP;
368 	}
369 	if (nmode != 0)
370 		return (EINVAL);
371 
372 	err = UTP_WRITEREG(utp, IDTPHY_REGO_MCTL, IDTPHY_REGM_MCTL_TLOOP |
373 	    IDTPHY_REGM_MCTL_DLOOP | IDTPHY_REGM_MCTL_LLOOP, val);
374 	if (err)
375 		return (err);
376 	utp->loopback = mode;
377 
378 	return (0);
379 }
380 
381 /*
382  * Set the chip to reflect the current state in utopia.
383  * Assume, that the chip has been reset.
384  */
385 static int
idt77155_set_chip(struct utopia * utp)386 idt77155_set_chip(struct utopia *utp)
387 {
388 	int err = 0;
389 
390 	/* set sonet/sdh */
391 	err |= idt77155_set_sdh(utp, utp->state & UTP_ST_SDH);
392 
393 	/* unassigned or idle cells */
394 	err |= idt77155_set_unass(utp, utp->state & UTP_ST_UNASS);
395 
396 	/* loopback */
397 	err |= idt77155_set_loopback(utp, utp->loopback);
398 
399 	/* update carrier state */
400 	err |= idt77155_update_carrier(utp);
401 
402 	/* enable interrupts on LOS */
403 	err |= UTP_WRITEREG(utp, IDTPHY_REGO_INT,
404 	    IDTPHY_REGM_INT_RXSOHI, IDTPHY_REGM_INT_RXSOHI);
405 	err |= UTP_WRITEREG(utp, IDTPHY_REGO_RSOC,
406 	    IDTPHY_REGM_RSOC_LOSI, IDTPHY_REGM_RSOC_LOSI);
407 
408 	return (err ? EIO : 0);
409 }
410 
411 /*
412  * Reset the chip to reflect the current state of utopia.
413  */
414 static int
idt77155_reset(struct utopia * utp)415 idt77155_reset(struct utopia *utp)
416 {
417 	int err = 0;
418 
419 	if (!(utp->flags & UTP_FL_NORESET)) {
420 		err |= UTP_WRITEREG(utp, IDTPHY_REGO_MRID,
421 		    IDTPHY_REGM_MRID_RESET, IDTPHY_REGM_MRID_RESET);
422 		err |= UTP_WRITEREG(utp, IDTPHY_REGO_MRID,
423 		    IDTPHY_REGM_MRID_RESET, 0);
424 	}
425 
426 	err |= idt77155_set_chip(utp);
427 
428 	return (err ? EIO : 0);
429 }
430 
431 /*
432  * Update statistics from a IDT77155
433  * This appears to be the same as for the Suni/Lite and Ultra. IDT however
434  * makes no assessment about the transfer time. Assume 7us.
435  */
436 static void
idt77155_update_stats(struct utopia * utp)437 idt77155_update_stats(struct utopia *utp)
438 {
439 	int err;
440 
441 	/* write to the master if we can */
442 	if (!(utp->flags & UTP_FL_NORESET)) {
443 		err = UTP_WRITEREG(utp, IDTPHY_REGO_MRID, 0, 0);
444 	} else {
445 		err = UTP_WRITEREG(utp, IDTPHY_REGO_BIPC, 0, 0);
446 		err |= UTP_WRITEREG(utp, IDTPHY_REGO_B2EC, 0, 0);
447 		err |= UTP_WRITEREG(utp, IDTPHY_REGO_B3EC, 0, 0);
448 		err |= UTP_WRITEREG(utp, IDTPHY_REGO_CEC, 0, 0);
449 		err |= UTP_WRITEREG(utp, IDTPHY_REGO_TXCNT, 0, 0);
450 
451 	}
452 	if (err) {
453 #ifdef DIAGNOSTIC
454 		printf("%s: register write error %s: %d\n", __func__,
455 		    utp->chip->name, err);
456 #endif
457 		return;
458 	}
459 
460 	DELAY(8);
461 
462 	utp->stats.rx_sbip += utopia_update(utp,
463 	    IDTPHY_REGO_BIPC, 2, 0xffff);
464 	utp->stats.rx_lbip += utopia_update(utp,
465 	    IDTPHY_REGO_B2EC, 3, 0xfffff);
466 	utp->stats.rx_lfebe += utopia_update(utp,
467 	    IDTPHY_REGO_FEBEC, 3, 0xfffff);
468 	utp->stats.rx_pbip += utopia_update(utp,
469 	    IDTPHY_REGO_B3EC, 2, 0xffff);
470 	utp->stats.rx_pfebe += utopia_update(utp,
471 	    IDTPHY_REGO_PFEBEC, 2, 0xffff);
472 	utp->stats.rx_corr += utopia_update(utp,
473 	    IDTPHY_REGO_CEC, 1, 0xff);
474 	utp->stats.rx_uncorr += utopia_update(utp,
475 	    IDTPHY_REGO_UEC, 1, 0xff);
476 	utp->stats.rx_cells += utopia_update(utp,
477 	    IDTPHY_REGO_RCCNT, 3, 0x7ffff);
478 	utp->stats.tx_cells += utopia_update(utp,
479 	    IDTPHY_REGO_TXCNT, 3, 0x7ffff);
480 }
481 
482 const struct utopia_chip utopia_chip_idt77155 = {
483 	UTP_TYPE_IDT77155,
484 	"IDT77155",
485 	0x80,
486 	idt77155_reset,
487 	idt77155_set_sdh,
488 	idt77155_set_unass,
489 	idt77155_set_noscramb,
490 	idt77155_update_carrier,
491 	idt77155_set_loopback,
492 	idt77155_intr,
493 	idt77155_update_stats,
494 };
495