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 */
59static int
60suni_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 */
85static int
86suni_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 */
109static int
110suni_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 */
141static int
142suni_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 */
160static int
161suni_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 */
191static int
192suni_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 */
214static int
215suni_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 */
253static void
254suni_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 */
302static void
303suni_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
325const 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 */
342static int
343suni_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
388const 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 */
405static int
406suni_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 */
464static int
465suni_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 */
488static void
489suni_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
533const 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