xref: /NextBSD/sys/dev/utopia/suni.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/suni.h>
53 #include <dev/utopia/utopia.h>
54 #include <dev/utopia/utopia_priv.h>
55 
56 /*
57  * set SONET/SDH mode
58  */
59 static int
suni_set_sdh(struct utopia * utp,int sdh)60 suni_set_sdh(struct utopia *utp, int sdh)
61 {
62 	int err;
63 
64 	if (sdh)
65 		err = UTP_WRITEREG(utp, SUNI_REGO_TPOPAPTR + 1,
66 		    SUNI_REGM_TPOPAPTR_S,
67 		    SUNI_REGM_SDH << SUNI_REGS_TPOPAPTR_S);
68 	else
69 		err = UTP_WRITEREG(utp, SUNI_REGO_TPOPAPTR + 1,
70 		    SUNI_REGM_TPOPAPTR_S,
71 		    SUNI_REGM_SONET << SUNI_REGS_TPOPAPTR_S);
72 	if (err != 0)
73 		return (err);
74 
75 	utp->state &= ~UTP_ST_SDH;
76 	if (sdh)
77 		utp->state |= UTP_ST_SDH;
78 
79 	return (0);
80 }
81 
82 /*
83  * set idle/unassigned cells
84  */
85 static int
suni_set_unass(struct utopia * utp,int unass)86 suni_set_unass(struct utopia *utp, int unass)
87 {
88 	int err;
89 
90 	if (unass)
91 		err = UTP_WRITEREG(utp, SUNI_REGO_TACPIDLEH,
92 		    0xff, (0 << SUNI_REGS_TACPIDLEH_CLP));
93 	else
94 		err = UTP_WRITEREG(utp, SUNI_REGO_TACPIDLEH,
95 		    0xff, (1 << SUNI_REGS_TACPIDLEH_CLP));
96 	if (err != 0)
97 		return (err);
98 
99 	utp->state &= ~UTP_ST_UNASS;
100 	if (unass)
101 		utp->state |= UTP_ST_UNASS;
102 
103 	return (0);
104 }
105 
106 /*
107  * enable/disable scrambling
108  */
109 static int
suni_set_noscramb(struct utopia * utp,int noscramb)110 suni_set_noscramb(struct utopia *utp, int noscramb)
111 {
112 	int err;
113 
114 	if (noscramb) {
115 		err = UTP_WRITEREG(utp, SUNI_REGO_TACPCTRL,
116 		    SUNI_REGM_TACPCTRL_DSCR, SUNI_REGM_TACPCTRL_DSCR);
117 		if (err)
118 			return (err);
119 		err = UTP_WRITEREG(utp, SUNI_REGO_RACPCTRL,
120 		    SUNI_REGM_RACPCTRL_DDSCR, SUNI_REGM_RACPCTRL_DDSCR);
121 		if (err)
122 			return (err);
123 		utp->state |= UTP_ST_NOSCRAMB;
124 	} else {
125 		err = UTP_WRITEREG(utp, SUNI_REGO_TACPCTRL,
126 		    SUNI_REGM_TACPCTRL_DSCR, 0);
127 		if (err)
128 			return (err);
129 		err = UTP_WRITEREG(utp, SUNI_REGO_RACPCTRL,
130 		    SUNI_REGM_RACPCTRL_DDSCR, 0);
131 		if (err)
132 			return (err);
133 		utp->state &= ~UTP_ST_NOSCRAMB;
134 	}
135 	return (0);
136 }
137 
138 /*
139  * Get current carrier state
140  */
141 static int
suni_update_carrier(struct utopia * utp)142 suni_update_carrier(struct utopia *utp)
143 {
144 	int err;
145 	uint8_t reg;
146 	u_int n = 1;
147 
148 	if ((err = UTP_READREGS(utp, SUNI_REGO_RSOPSIS, &reg, &n)) != 0) {
149 		utp->carrier = UTP_CARR_UNKNOWN;
150 		return (err);
151 	}
152 	utopia_check_carrier(utp, !(reg & SUNI_REGM_RSOPSIS_LOSV));
153 	return (0);
154 }
155 
156 /*
157  * Set the SUNI chip to reflect the current state in utopia.
158  * Assume, that the chip has been reset.
159  */
160 static int
suni_set_chip(struct utopia * utp)161 suni_set_chip(struct utopia *utp)
162 {
163 	int err = 0;
164 
165 	/* set sonet/sdh */
166 	err |= utopia_set_sdh(utp, utp->state & UTP_ST_SDH);
167 
168 	/* unassigned or idle cells */
169 	err |= utopia_set_unass(utp, utp->state & UTP_ST_UNASS);
170 	err |= UTP_WRITEREG(utp, SUNI_REGO_TACPIDLEP, 0xff, 0x6a);
171 
172 	/* set scrambling */
173 	err |= utopia_set_noscramb(utp, utp->state & UTP_ST_NOSCRAMB);
174 
175 	/* loopback */
176 	err |= utopia_set_loopback(utp, utp->loopback);
177 
178 	/* update carrier state */
179 	err |= utopia_update_carrier(utp);
180 
181 	/* enable interrupts on LOS */
182 	err |= UTP_WRITEREG(utp, SUNI_REGO_RSOPCIE,
183 	    SUNI_REGM_RSOPCIE_LOSE, SUNI_REGM_RSOPCIE_LOSE);
184 
185 	return (err ? EIO : 0);
186 }
187 
188 /*
189  * Reset the SUNI chip to reflect the current state of utopia.
190  */
191 static int
suni_reset_default(struct utopia * utp)192 suni_reset_default(struct utopia *utp)
193 {
194 	int err = 0;
195 
196 	if (!(utp->flags & UTP_FL_NORESET)) {
197 		err |= UTP_WRITEREG(utp, SUNI_REGO_MRESET,
198 		    SUNI_REGM_MRESET_RESET, SUNI_REGM_MRESET_RESET);
199 		err |= UTP_WRITEREG(utp, SUNI_REGO_MRESET,
200 		    SUNI_REGM_MRESET_RESET, 0);
201 	}
202 
203 	/* disable test mode */
204 	err |= UTP_WRITEREG(utp, SUNI_REGO_MTEST, 0xff, 0x00);
205 
206 	err |= suni_set_chip(utp);
207 
208 	return (err ? EIO : 0);
209 }
210 
211 /*
212  * Set loopback mode for the Lite
213  */
214 static int
suni_set_loopback_lite(struct utopia * utp,u_int mode)215 suni_set_loopback_lite(struct utopia *utp, u_int mode)
216 {
217 	int err;
218 	uint32_t val;
219 	u_int nmode;
220 
221 	val = 0;
222 	nmode = mode;
223 	if (mode & UTP_LOOP_TIME) {
224 		nmode &= ~UTP_LOOP_TIME;
225 		val |= SUNI_REGM_MCTRL_LOOPT;
226 	}
227 	if (mode & UTP_LOOP_DIAG) {
228 		nmode &= ~UTP_LOOP_DIAG;
229 		val |= SUNI_REGM_MCTRL_DLE;
230 	}
231 	if (mode & UTP_LOOP_LINE) {
232 		nmode &= ~UTP_LOOP_LINE;
233 		if (val & SUNI_REGM_MCTRL_DLE)
234 			return (EINVAL);
235 		val |= SUNI_REGM_MCTRL_LLE;
236 	}
237 	if (nmode != 0)
238 		return (EINVAL);
239 
240 	err = UTP_WRITEREG(utp, SUNI_REGO_MCTRL,
241 	    SUNI_REGM_MCTRL_LLE | SUNI_REGM_MCTRL_DLE | SUNI_REGM_MCTRL_LOOPT,
242 	    val);
243 	if (err)
244 		return (err);
245 	utp->loopback = mode;
246 
247 	return (0);
248 }
249 
250 /*
251  * Update statistics from a SUNI/LITE or SUNI/ULTRA
252  */
253 static void
suni_lite_update_stats(struct utopia * utp)254 suni_lite_update_stats(struct utopia *utp)
255 {
256 	int err;
257 
258 	/* write to the master if we can */
259 	if (!(utp->flags & UTP_FL_NORESET)) {
260 		err = UTP_WRITEREG(utp, SUNI_REGO_MRESET, 0, 0);
261 	} else {
262 		err = UTP_WRITEREG(utp, SUNI_REGO_RSOP_BIP8, 0, 0);
263 		err |= UTP_WRITEREG(utp, SUNI_REGO_RLOPBIP8_24, 0, 0);
264 		err |= UTP_WRITEREG(utp, SUNI_REGO_RPOPBIP8, 0, 0);
265 		err |= UTP_WRITEREG(utp, SUNI_REGO_RACPCHCS, 0, 0);
266 		err |= UTP_WRITEREG(utp, SUNI_REGO_TACPCNT, 0, 0);
267 
268 	}
269 	if (err) {
270 #ifdef DIAGNOSTIC
271 		printf("%s: register write error %s: %d\n", __func__,
272 		    utp->chip->name, err);
273 #endif
274 		return;
275 	}
276 
277 	DELAY(8);
278 
279 	utp->stats.rx_sbip += utopia_update(utp,
280 	    SUNI_REGO_RSOP_BIP8, 2, 0xffff);
281 	utp->stats.rx_lbip += utopia_update(utp,
282 	    SUNI_REGO_RLOPBIP8_24, 3, 0xfffff);
283 	utp->stats.rx_lfebe += utopia_update(utp,
284 	    SUNI_REGO_RLOPFEBE, 3, 0xfffff);
285 	utp->stats.rx_pbip += utopia_update(utp,
286 	    SUNI_REGO_RPOPBIP8, 2, 0xffff);
287 	utp->stats.rx_pfebe += utopia_update(utp,
288 	    SUNI_REGO_RPOPFEBE, 2, 0xffff);
289 	utp->stats.rx_corr += utopia_update(utp,
290 	    SUNI_REGO_RACPCHCS, 1, 0xff);
291 	utp->stats.rx_uncorr += utopia_update(utp,
292 	    SUNI_REGO_RACPUHCS, 1, 0xff);
293 	utp->stats.rx_cells += utopia_update(utp,
294 	    SUNI_REGO_RACPCNT, 3, 0x7ffff);
295 	utp->stats.tx_cells += utopia_update(utp,
296 	    SUNI_REGO_TACPCNT, 3, 0x7ffff);
297 }
298 
299 /*
300  * Handle interrupt on SUNI chip
301  */
302 static void
suni_intr_default(struct utopia * utp)303 suni_intr_default(struct utopia *utp)
304 {
305 	uint8_t regs[SUNI_REGO_MTEST];
306 	u_int n = SUNI_REGO_MTEST;
307 	int err;
308 
309 	/* Read all registers. This acks the interrupts */
310 	if ((err = UTP_READREGS(utp, SUNI_REGO_MRESET, regs, &n)) != 0) {
311 		printf("SUNI read error %d\n", err);
312 		return;
313 	}
314 	if (n <= SUNI_REGO_RSOPSIS) {
315 		printf("%s: could not read RSOPSIS", __func__);
316 		return;
317 	}
318 	/* check for LOSI (loss of signal) */
319 	if ((regs[SUNI_REGO_MISTATUS] & SUNI_REGM_MISTATUS_RSOPI) &&
320 	    (regs[SUNI_REGO_RSOPSIS] & SUNI_REGM_RSOPSIS_LOSI))
321 		utopia_check_carrier(utp, !(regs[SUNI_REGO_RSOPSIS]
322 		    & SUNI_REGM_RSOPSIS_LOSV));
323 }
324 
325 const struct utopia_chip utopia_chip_lite = {
326 	UTP_TYPE_SUNI_LITE,
327 	"Suni/Lite (PMC-5346)",
328 	256,
329 	suni_reset_default,
330 	suni_set_sdh,
331 	suni_set_unass,
332 	suni_set_noscramb,
333 	suni_update_carrier,
334 	suni_set_loopback_lite,
335 	suni_intr_default,
336 	suni_lite_update_stats,
337 };
338 
339 /*
340  * Set loopback mode for the Ultra
341  */
342 static int
suni_set_loopback_ultra(struct utopia * utp,u_int mode)343 suni_set_loopback_ultra(struct utopia *utp, u_int mode)
344 {
345 	int err;
346 	uint32_t val;
347 	u_int nmode;
348 
349 	val = 0;
350 	nmode = mode;
351 	if (mode & UTP_LOOP_TIME) {
352 		nmode &= ~UTP_LOOP_TIME;
353 		val |= SUNI_REGM_MCTRL_LOOPT;
354 	}
355 	if (mode & UTP_LOOP_DIAG) {
356 		nmode &= ~UTP_LOOP_DIAG;
357 		if (val & SUNI_REGM_MCTRL_LOOPT)
358 			return (EINVAL);
359 		val |= SUNI_REGM_MCTRL_SDLE;
360 	}
361 	if (mode & UTP_LOOP_LINE) {
362 		nmode &= ~UTP_LOOP_LINE;
363 		if (val & (SUNI_REGM_MCTRL_LOOPT | SUNI_REGM_MCTRL_SDLE))
364 			return (EINVAL);
365 		val |= SUNI_REGM_MCTRL_LLE;
366 	}
367 	if (mode & UTP_LOOP_PARAL) {
368 		nmode &= ~UTP_LOOP_PARAL;
369 		val |= SUNI_REGM_MCTRL_PDLE;
370 	}
371 	if (mode & UTP_LOOP_TWIST) {
372 		nmode &= ~UTP_LOOP_TWIST;
373 		val |= SUNI_REGM_MCTRL_TPLE;
374 	}
375 	if (nmode != 0)
376 		return (EINVAL);
377 
378 	err = UTP_WRITEREG(utp, SUNI_REGO_MCTRL,
379 	    SUNI_REGM_MCTRL_LLE | SUNI_REGM_MCTRL_SDLE | SUNI_REGM_MCTRL_LOOPT |
380 	    SUNI_REGM_MCTRL_PDLE | SUNI_REGM_MCTRL_TPLE, val);
381 	if (err)
382 		return (err);
383 	utp->loopback = mode;
384 
385 	return (0);
386 }
387 
388 const struct utopia_chip utopia_chip_ultra = {
389 	UTP_TYPE_SUNI_ULTRA,
390 	"Suni/Ultra (PMC-5350)",
391 	256,
392 	suni_reset_default,
393 	suni_set_sdh,
394 	suni_set_unass,
395 	suni_set_noscramb,
396 	suni_update_carrier,
397 	suni_set_loopback_ultra,
398 	suni_intr_default,
399 	suni_lite_update_stats,
400 };
401 
402 /*
403  * Set loopback mode for the 622
404  */
405 static int
suni_set_loopback_622(struct utopia * utp,u_int mode)406 suni_set_loopback_622(struct utopia *utp, u_int mode)
407 {
408 	int err;
409 	uint32_t val;
410 	uint8_t config;
411 	int smode;
412 	u_int nmode;
413 	u_int n = 1;
414 
415 	val = 0;
416 	nmode = mode;
417 	if (mode & UTP_LOOP_PATH) {
418 		nmode &= ~UTP_LOOP_PATH;
419 		val |= SUNI_REGM_MCTRLM_DPLE;
420 	}
421 
422 	err = UTP_READREGS(utp, SUNI_REGO_MCONFIG, &config, &n);
423 	if (err != 0)
424 		return (err);
425 	smode = ((config & SUNI_REGM_MCONFIG_TMODE_622) ==
426 	    SUNI_REGM_MCONFIG_TMODE_STS1_BIT &&
427 	    (config & SUNI_REGM_MCONFIG_RMODE_622) ==
428 	    SUNI_REGM_MCONFIG_RMODE_STS1_BIT);
429 
430 	if (mode & UTP_LOOP_TIME) {
431 		if (!smode)
432 			return (EINVAL);
433 		nmode &= ~UTP_LOOP_TIME;
434 		val |= SUNI_REGM_MCTRLM_LOOPT;
435 	}
436 	if (mode & UTP_LOOP_DIAG) {
437 		nmode &= ~UTP_LOOP_DIAG;
438 		if (val & SUNI_REGM_MCTRLM_LOOPT)
439 			return (EINVAL);
440 		val |= SUNI_REGM_MCTRLM_DLE;
441 	}
442 	if (mode & UTP_LOOP_LINE) {
443 		nmode &= ~UTP_LOOP_LINE;
444 		if (val & (SUNI_REGM_MCTRLM_LOOPT | SUNI_REGM_MCTRLM_DLE))
445 			return (EINVAL);
446 		val |= SUNI_REGM_MCTRLM_LLE;
447 	}
448 	if (nmode != 0)
449 		return (EINVAL);
450 
451 	err = UTP_WRITEREG(utp, SUNI_REGO_MCTRLM,
452 	    SUNI_REGM_MCTRLM_LLE | SUNI_REGM_MCTRLM_DLE |
453 	    SUNI_REGM_MCTRLM_DPLE | SUNI_REGM_MCTRL_LOOPT, val);
454 	if (err)
455 		return (err);
456 	utp->loopback = mode;
457 
458 	return (0);
459 }
460 
461 /*
462  * Reset the SUNI chip to reflect the current state of utopia.
463  */
464 static int
suni_reset_622(struct utopia * utp)465 suni_reset_622(struct utopia *utp)
466 {
467 	int err = 0;
468 
469 	if (!(utp->flags & UTP_FL_NORESET)) {
470 		err |= UTP_WRITEREG(utp, SUNI_REGO_MRESET,
471 		    SUNI_REGM_MRESET_RESET, SUNI_REGM_MRESET_RESET);
472 		err |= UTP_WRITEREG(utp, SUNI_REGO_MRESET,
473 		    SUNI_REGM_MRESET_RESET, 0);
474 	}
475 
476 	/* disable test mode */
477 	err |= UTP_WRITEREG(utp, SUNI_REGO_MTEST, 0xff,
478 	    SUNI_REGM_MTEST_DS27_53_622);
479 
480 	err |= suni_set_chip(utp);
481 
482 	return (err ? EIO : 0);
483 }
484 
485 /*
486  * Update statistics from a SUNI/622
487  */
488 static void
suni_622_update_stats(struct utopia * utp)489 suni_622_update_stats(struct utopia *utp)
490 {
491 	int err;
492 
493 	/* write to the master if we can */
494 	if (!(utp->flags & UTP_FL_NORESET)) {
495 		err = UTP_WRITEREG(utp, SUNI_REGO_MRESET, 0, 0);
496 	} else {
497 		err = UTP_WRITEREG(utp, SUNI_REGO_RSOP_BIP8, 0, 0);
498 		err |= UTP_WRITEREG(utp, SUNI_REGO_RLOPBIP8_24, 0, 0);
499 		err |= UTP_WRITEREG(utp, SUNI_REGO_RPOPBIP8, 0, 0);
500 		err |= UTP_WRITEREG(utp, SUNI_REGO_RACPCHCS, 0, 0);
501 		err |= UTP_WRITEREG(utp, SUNI_REGO_TACPCNT, 0, 0);
502 	}
503 	if (err) {
504 #ifdef DIAGNOSTIC
505 		printf("%s: register write error %s: %d\n", __func__,
506 		    utp->chip->name, err);
507 #endif
508 		return;
509 	}
510 
511 	DELAY(8);
512 
513 	utp->stats.rx_sbip += utopia_update(utp,
514 	    SUNI_REGO_RSOP_BIP8, 2, 0xffff);
515 	utp->stats.rx_lbip += utopia_update(utp,
516 	    SUNI_REGO_RLOPBIP8_24, 3, 0xfffff);
517 	utp->stats.rx_lfebe += utopia_update(utp,
518 	    SUNI_REGO_RLOPFEBE, 3, 0xfffff);
519 	utp->stats.rx_pbip += utopia_update(utp,
520 	    SUNI_REGO_RPOPBIP8, 2, 0xffff);
521 	utp->stats.rx_pfebe += utopia_update(utp,
522 	    SUNI_REGO_RPOPFEBE, 2, 0xffff);
523 	utp->stats.rx_corr += utopia_update(utp,
524 	    SUNI_REGO_RACPCHCS_622, 2, 0xfff);
525 	utp->stats.rx_uncorr += utopia_update(utp,
526 	    SUNI_REGO_RACPUHCS_622, 2, 0xfff);
527 	utp->stats.rx_cells += utopia_update(utp,
528 	    SUNI_REGO_RACPCNT_622, 3, 0x1fffff);
529 	utp->stats.tx_cells += utopia_update(utp,
530 	    SUNI_REGO_TACPCNT, 3, 0x1fffff);
531 }
532 
533 const struct utopia_chip utopia_chip_622 = {
534 	UTP_TYPE_SUNI_622,
535 	"Suni/622 (PMC-5355)",
536 	256,
537 	suni_reset_622,
538 	suni_set_sdh,
539 	suni_set_unass,
540 	suni_set_noscramb,
541 	suni_update_carrier,
542 	suni_set_loopback_622,
543 	suni_intr_default,
544 	suni_622_update_stats,
545 };
546