utopia.c revision 118202
1258945Sroberto/*
2258945Sroberto * Copyright (c) 2003
3258945Sroberto *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4258945Sroberto * 	All rights reserved.
5258945Sroberto *
6258945Sroberto * Redistribution and use in source and binary forms, with or without
7258945Sroberto * modification, are permitted provided that the following conditions
8258945Sroberto * are met:
9258945Sroberto * 1. Redistributions of source code must retain the above copyright
10258945Sroberto *    notice, this list of conditions and the following disclaimer.
11258945Sroberto * 2. Redistributions in binary form must reproduce the above copyright
12258945Sroberto *    notice, this list of conditions and the following disclaimer in the
13258945Sroberto *    documentation and/or other materials provided with the distribution.
14258945Sroberto *
15258945Sroberto * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16258945Sroberto * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17258945Sroberto * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18258945Sroberto * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19258945Sroberto * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20258945Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21258945Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22258945Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23258945Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24258945Sroberto * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25258945Sroberto * SUCH DAMAGE.
26258945Sroberto *
27258945Sroberto * Author: Hartmut Brandt <harti@freebsd.org>
28258945Sroberto */
29258945Sroberto
30258945Sroberto#include <sys/cdefs.h>
31258945Sroberto__FBSDID("$FreeBSD: head/sys/dev/utopia/utopia.c 118202 2003-07-30 08:35:58Z harti $");
32258945Sroberto
33258945Sroberto#include <sys/param.h>
34258945Sroberto#include <sys/systm.h>
35258945Sroberto#include <sys/unistd.h>
36258945Sroberto#include <sys/kernel.h>
37258945Sroberto#include <sys/kthread.h>
38258945Sroberto#include <sys/proc.h>
39258945Sroberto#include <sys/bus.h>
40258945Sroberto#include <sys/malloc.h>
41258945Sroberto#include <sys/sysctl.h>
42258945Sroberto#include <sys/lock.h>
43258945Sroberto#include <sys/mutex.h>
44258945Sroberto#include <sys/socket.h>
45258945Sroberto
46258945Sroberto#include <net/if.h>
47258945Sroberto#include <net/if_var.h>
48258945Sroberto#include <net/if_media.h>
49258945Sroberto#include <net/if_atm.h>
50258945Sroberto
51258945Sroberto#include <dev/utopia/suni.h>
52258945Sroberto#include <dev/utopia/idtphy.h>
53258945Sroberto#include <dev/utopia/utopia.h>
54258945Sroberto
55258945Sroberto#define READREGS(UTOPIA, REG, VALP, NP)				\
56258945Sroberto    (UTOPIA)->methods->readregs((UTOPIA)->ifatm, REG, VALP, NP)
57258945Sroberto#define WRITEREG(UTOPIA, REG, MASK, VAL)			\
58258945Sroberto    (UTOPIA)->methods->writereg((UTOPIA)->ifatm, REG, MASK, VAL)
59258945Sroberto
60258945Sroberto/*
61258945Sroberto * Global list of all registered interfaces
62258945Sroberto */
63258945Srobertostatic struct mtx utopia_list_mtx;
64258945Srobertostatic LIST_HEAD(, utopia) utopia_list = LIST_HEAD_INITIALIZER(utopia_list);
65258945Sroberto
66258945Sroberto#define UTP_RLOCK_LIST()	mtx_lock(&utopia_list_mtx)
67258945Sroberto#define UTP_RUNLOCK_LIST()	mtx_unlock(&utopia_list_mtx)
68258945Sroberto#define UTP_WLOCK_LIST()	mtx_lock(&utopia_list_mtx)
69258945Sroberto#define UTP_WUNLOCK_LIST()	mtx_unlock(&utopia_list_mtx)
70258945Sroberto
71258945Sroberto#define UTP_LOCK(UTP)		mtx_lock((UTP)->lock)
72258945Sroberto#define UTP_UNLOCK(UTP)		mtx_unlock((UTP)->lock)
73258945Sroberto#define UTP_LOCK_ASSERT(UTP)	mtx_assert((UTP)->lock, MA_OWNED)
74258945Sroberto
75258945Srobertostatic struct proc *utopia_kproc;
76258945Sroberto
77258945Srobertostatic void utopia_dump(struct utopia *) __unused;
78258945Sroberto
79258945Sroberto/*
80258945Sroberto * Statistics update inlines
81258945Sroberto */
82258945Srobertostatic uint32_t
83258945Srobertoutp_update(struct utopia *utp, u_int reg, u_int nreg, uint32_t mask)
84258945Sroberto{
85258945Sroberto	int err;
86258945Sroberto	u_int n;
87258945Sroberto	uint8_t regs[4];
88258945Sroberto	uint32_t val;
89258945Sroberto
90	n = nreg;
91	if ((err = READREGS(utp, reg, regs, &n)) != 0) {
92#ifdef DIAGNOSTIC
93		printf("%s: register read error %s(%u,%u): %d\n", __func__,
94		    utp->chip->name, reg, nreg, err);
95#endif
96		return (0);
97	}
98	if (n < nreg) {
99#ifdef DIAGNOSTIC
100		printf("%s: got only %u regs %s(%u,%u): %d\n", __func__, n,
101		    utp->chip->name, reg, nreg, err);
102#endif
103		return (0);
104	}
105	val = 0;
106	for (n = nreg; n > 0; n--) {
107		val <<= 8;
108		val |= regs[n - 1];
109	}
110	return (val & mask);
111}
112
113#define	UPDATE8(UTP, REG)	utp_update(UTP, REG, 1, 0xff)
114#define	UPDATE12(UTP, REG)	utp_update(UTP, REG, 2, 0xfff)
115#define	UPDATE16(UTP, REG)	utp_update(UTP, REG, 2, 0xffff)
116#define	UPDATE19(UTP, REG)	utp_update(UTP, REG, 3, 0x7ffff)
117#define	UPDATE20(UTP, REG)	utp_update(UTP, REG, 3, 0xfffff)
118#define	UPDATE21(UTP, REG)	utp_update(UTP, REG, 3, 0x1fffff)
119
120/*
121 * Debugging - dump all registers.
122 */
123static void
124utopia_dump(struct utopia *utp)
125{
126	uint8_t regs[256];
127	u_int n = 256, i;
128	int err;
129
130	if ((err = READREGS(utp, SUNI_REGO_MRESET, regs, &n)) != 0) {
131		printf("SUNI read error %d\n", err);
132		return;
133	}
134	for (i = 0; i < n; i++) {
135		if (i % 16 == 0)
136			printf("%02x:", i);
137		if (i % 16 == 8)
138			printf(" ");
139		printf(" %02x", regs[i]);
140		if (i % 16 == 15)
141			printf("\n");
142	}
143	if (i % 16 != 0)
144		printf("\n");
145}
146
147/*
148 * Update the carrier status
149 */
150static void
151utopia_check_carrier(struct utopia *utp, u_int carr_ok)
152{
153	int old;
154
155	old = utp->carrier;
156	if (carr_ok) {
157		/* carrier */
158		utp->carrier = UTP_CARR_OK;
159		if (old != UTP_CARR_OK) {
160			if_printf(&utp->ifatm->ifnet, "carrier detected\n");
161			ATMEV_SEND_IFSTATE_CHANGED(utp->ifatm, 1);
162		}
163	} else {
164		/* no carrier */
165		utp->carrier = UTP_CARR_LOST;
166		if (old == UTP_CARR_OK) {
167			if_printf(&utp->ifatm->ifnet, "carrier lost\n");
168			ATMEV_SEND_IFSTATE_CHANGED(utp->ifatm, 0);
169		}
170	}
171}
172
173static int
174utopia_update_carrier_default(struct utopia *utp)
175{
176	int err;
177	uint8_t reg;
178	u_int n = 1;
179
180	if ((err = READREGS(utp, SUNI_REGO_RSOPSIS, &reg, &n)) != 0) {
181		utp->carrier = UTP_CARR_UNKNOWN;
182		return (err);
183	}
184	utopia_check_carrier(utp, !(reg & SUNI_REGM_RSOPSIS_LOSV));
185	return (0);
186}
187
188/*
189 * enable/disable scrambling
190 */
191static int
192utopia_set_noscramb_default(struct utopia *utp, int noscramb)
193{
194	int err;
195
196	if (noscramb) {
197		err = WRITEREG(utp, SUNI_REGO_TACPCTRL,
198		    SUNI_REGM_TACPCTRL_DSCR, SUNI_REGM_TACPCTRL_DSCR);
199		if (err)
200			return (err);
201		err = WRITEREG(utp, SUNI_REGO_RACPCTRL,
202		    SUNI_REGM_RACPCTRL_DDSCR, SUNI_REGM_RACPCTRL_DDSCR);
203		if (err)
204			return (err);
205		utp->state |= UTP_ST_NOSCRAMB;
206	} else {
207		err = WRITEREG(utp, SUNI_REGO_TACPCTRL,
208		    SUNI_REGM_TACPCTRL_DSCR, 0);
209		if (err)
210			return (err);
211		err = WRITEREG(utp, SUNI_REGO_RACPCTRL,
212		    SUNI_REGM_RACPCTRL_DDSCR, 0);
213		if (err)
214			return (err);
215		utp->state &= ~UTP_ST_NOSCRAMB;
216	}
217	return (0);
218}
219
220/*
221 * set SONET/SDH mode
222 */
223static int
224utopia_set_sdh_default(struct utopia *utp, int sdh)
225{
226	int err;
227
228	if (sdh)
229		err = WRITEREG(utp, SUNI_REGO_TPOPAPTR + 1,
230		    SUNI_REGM_TPOPAPTR_S,
231		    SUNI_REGM_SDH << SUNI_REGS_TPOPAPTR_S);
232	else
233		err = WRITEREG(utp, SUNI_REGO_TPOPAPTR + 1,
234		    SUNI_REGM_TPOPAPTR_S,
235		    SUNI_REGM_SONET << SUNI_REGS_TPOPAPTR_S);
236	if (err != 0)
237		return (err);
238
239	utp->state &= ~UTP_ST_SDH;
240	if (sdh)
241		utp->state |= UTP_ST_SDH;
242
243	return (0);
244}
245
246/*
247 * set idle/unassigned cells
248 */
249static int
250utopia_set_unass_default(struct utopia *utp, int unass)
251{
252	int err;
253
254	if (unass)
255		err = WRITEREG(utp, SUNI_REGO_TACPIDLEH,
256		    0xff, (0 << SUNI_REGS_TACPIDLEH_CLP));
257	else
258		err = WRITEREG(utp, SUNI_REGO_TACPIDLEH,
259		    0xff, (1 << SUNI_REGS_TACPIDLEH_CLP));
260	if (err != 0)
261		return (err);
262
263	utp->state &= ~UTP_ST_UNASS;
264	if (unass)
265		utp->state |= UTP_ST_UNASS;
266
267	return (0);
268}
269
270/*
271 * Set loopback mode for the Lite
272 */
273static int
274utopia_set_loopback_lite(struct utopia *utp, u_int mode)
275{
276	int err;
277	uint32_t val;
278	u_int nmode;
279
280	val = 0;
281	nmode = mode;
282	if (mode & UTP_LOOP_TIME) {
283		nmode &= ~UTP_LOOP_TIME;
284		val |= SUNI_REGM_MCTRL_LOOPT;
285	}
286	if (mode & UTP_LOOP_DIAG) {
287		nmode &= ~UTP_LOOP_DIAG;
288		val |= SUNI_REGM_MCTRL_DLE;
289	}
290	if (mode & UTP_LOOP_LINE) {
291		nmode &= ~UTP_LOOP_LINE;
292		if (val & SUNI_REGM_MCTRL_DLE)
293			return (EINVAL);
294		val |= SUNI_REGM_MCTRL_LLE;
295	}
296	if (nmode != 0)
297		return (EINVAL);
298
299	err = WRITEREG(utp, SUNI_REGO_MCTRL,
300	    SUNI_REGM_MCTRL_LLE | SUNI_REGM_MCTRL_DLE | SUNI_REGM_MCTRL_LOOPT,
301	    val);
302	if (err)
303		return (err);
304	utp->loopback = mode;
305
306	return (0);
307}
308
309/*
310 * Set loopback mode for the Ultra
311 */
312static int
313utopia_set_loopback_ultra(struct utopia *utp, u_int mode)
314{
315	int err;
316	uint32_t val;
317	u_int nmode;
318
319	val = 0;
320	nmode = mode;
321	if (mode & UTP_LOOP_TIME) {
322		nmode &= ~UTP_LOOP_TIME;
323		val |= SUNI_REGM_MCTRL_LOOPT;
324	}
325	if (mode & UTP_LOOP_DIAG) {
326		nmode &= ~UTP_LOOP_DIAG;
327		if (val & SUNI_REGM_MCTRL_LOOPT)
328			return (EINVAL);
329		val |= SUNI_REGM_MCTRL_SDLE;
330	}
331	if (mode & UTP_LOOP_LINE) {
332		nmode &= ~UTP_LOOP_LINE;
333		if (val & (SUNI_REGM_MCTRL_LOOPT | SUNI_REGM_MCTRL_SDLE))
334			return (EINVAL);
335		val |= SUNI_REGM_MCTRL_LLE;
336	}
337	if (mode & UTP_LOOP_PARAL) {
338		nmode &= ~UTP_LOOP_PARAL;
339		val |= SUNI_REGM_MCTRL_PDLE;
340	}
341	if (mode & UTP_LOOP_TWIST) {
342		nmode &= ~UTP_LOOP_TWIST;
343		val |= SUNI_REGM_MCTRL_TPLE;
344	}
345	if (nmode != 0)
346		return (EINVAL);
347
348	err = WRITEREG(utp, SUNI_REGO_MCTRL,
349	    SUNI_REGM_MCTRL_LLE | SUNI_REGM_MCTRL_SDLE | SUNI_REGM_MCTRL_LOOPT |
350	    SUNI_REGM_MCTRL_PDLE | SUNI_REGM_MCTRL_TPLE, val);
351	if (err)
352		return (err);
353	utp->loopback = mode;
354
355	return (0);
356}
357
358/*
359 * Set loopback mode for the Ultra
360 */
361static int
362utopia_set_loopback_622(struct utopia *utp, u_int mode)
363{
364	int err;
365	uint32_t val;
366	uint8_t config;
367	int smode;
368	u_int nmode;
369	u_int n = 1;
370
371	val = 0;
372	nmode = mode;
373	if (mode & UTP_LOOP_PATH) {
374		nmode &= ~UTP_LOOP_PATH;
375		val |= SUNI_REGM_MCTRLM_DPLE;
376	}
377
378	err = READREGS(utp, SUNI_REGO_MCONFIG, &config, &n);
379	if (err != 0)
380		return (err);
381	smode = ((config & SUNI_REGM_MCONFIG_TMODE_622) ==
382	    SUNI_REGM_MCONFIG_TMODE_STS1_BIT &&
383	    (config & SUNI_REGM_MCONFIG_RMODE_622) ==
384	    SUNI_REGM_MCONFIG_RMODE_STS1_BIT);
385
386	if (mode & UTP_LOOP_TIME) {
387		if (!smode)
388			return (EINVAL);
389		nmode &= ~UTP_LOOP_TIME;
390		val |= SUNI_REGM_MCTRLM_LOOPT;
391	}
392	if (mode & UTP_LOOP_DIAG) {
393		nmode &= ~UTP_LOOP_DIAG;
394		if (val & SUNI_REGM_MCTRLM_LOOPT)
395			return (EINVAL);
396		val |= SUNI_REGM_MCTRLM_DLE;
397	}
398	if (mode & UTP_LOOP_LINE) {
399		nmode &= ~UTP_LOOP_LINE;
400		if (val & (SUNI_REGM_MCTRLM_LOOPT | SUNI_REGM_MCTRLM_DLE))
401			return (EINVAL);
402		val |= SUNI_REGM_MCTRLM_LLE;
403	}
404	if (nmode != 0)
405		return (EINVAL);
406
407	err = WRITEREG(utp, SUNI_REGO_MCTRLM,
408	    SUNI_REGM_MCTRLM_LLE | SUNI_REGM_MCTRLM_DLE |
409	    SUNI_REGM_MCTRLM_DPLE | SUNI_REGM_MCTRL_LOOPT, val);
410	if (err)
411		return (err);
412	utp->loopback = mode;
413
414	return (0);
415}
416
417/*
418 * Set the SUNI chip to reflect the current state in utopia.
419 * Assume, that the chip has been reset.
420 */
421static int
422utopia_set_chip(struct utopia *utp)
423{
424	int err = 0;
425
426	/* set sonet/sdh */
427	err |= utopia_set_sdh(utp, utp->state & UTP_ST_SDH);
428
429	/* unassigned or idle cells */
430	err |= utopia_set_unass(utp, utp->state & UTP_ST_UNASS);
431	err |= WRITEREG(utp, SUNI_REGO_TACPIDLEP, 0xff, 0x6a);
432
433	/* loopback */
434	err |= utopia_set_loopback(utp, utp->loopback);
435
436	/* update carrier state */
437	err |= utopia_update_carrier(utp);
438
439	/* enable interrupts on LOS */
440	err |= WRITEREG(utp, SUNI_REGO_RSOPCIE,
441	    SUNI_REGM_RSOPCIE_LOSE, SUNI_REGM_RSOPCIE_LOSE);
442
443	return (err ? EIO : 0);
444}
445
446/*
447 * Reset the SUNI chip to reflect the current state of utopia.
448 */
449static int
450utopia_reset_default(struct utopia *utp)
451{
452	int err = 0;
453
454	if (!(utp->flags & UTP_FL_NORESET)) {
455		err |= WRITEREG(utp, SUNI_REGO_MRESET, SUNI_REGM_MRESET_RESET,
456		    SUNI_REGM_MRESET_RESET);
457		err |= WRITEREG(utp, SUNI_REGO_MRESET, SUNI_REGM_MRESET_RESET,
458		    0);
459	}
460
461	/* disable test mode */
462	err |= WRITEREG(utp, SUNI_REGO_MTEST, 0xff, 0x00);
463
464	err |= utopia_set_chip(utp);
465
466	return (err ? EIO : 0);
467}
468
469/*
470 * Reset the SUNI chip to reflect the current state of utopia.
471 */
472static int
473utopia_reset_622(struct utopia *utp)
474{
475	int err = 0;
476
477	if (!(utp->flags & UTP_FL_NORESET)) {
478		err |= WRITEREG(utp, SUNI_REGO_MRESET, SUNI_REGM_MRESET_RESET,
479		    SUNI_REGM_MRESET_RESET);
480		err |= WRITEREG(utp, SUNI_REGO_MRESET, SUNI_REGM_MRESET_RESET,
481		    0);
482	}
483
484	/* disable test mode */
485	err |= WRITEREG(utp, SUNI_REGO_MTEST, 0xff,
486	    SUNI_REGM_MTEST_DS27_53_622);
487
488	err |= utopia_set_chip(utp);
489
490	return (err ? EIO : 0);
491}
492
493/*
494 * Handle interrupt on lite chip
495 */
496static void
497utopia_intr_default(struct utopia *utp)
498{
499	uint8_t regs[SUNI_REGO_MTEST];
500	u_int n = SUNI_REGO_MTEST;
501	int err;
502
503	/* Read all registers. This acks the interrupts */
504	if ((err = READREGS(utp, SUNI_REGO_MRESET, regs, &n)) != 0) {
505		printf("SUNI read error %d\n", err);
506		return;
507	}
508	if (n <= SUNI_REGO_RSOPSIS) {
509		printf("%s: could not read RSOPSIS", __func__);
510		return;
511	}
512	/* check for LOSI (loss of signal) */
513	if ((regs[SUNI_REGO_MISTATUS] & SUNI_REGM_MISTATUS_RSOPI) &&
514	    (regs[SUNI_REGO_RSOPSIS] & SUNI_REGM_RSOPSIS_LOSI))
515		utopia_check_carrier(utp, !(regs[SUNI_REGO_RSOPSIS]
516		    & SUNI_REGM_RSOPSIS_LOSV));
517}
518
519/*
520 * Update statistics from a SUNI/LITE or SUNI/ULTRA
521 */
522static void
523suni_lite_update_stats(struct utopia *utp)
524{
525	int err;
526
527	/* write to the master if we can */
528	if (!(utp->flags & UTP_FL_NORESET)) {
529		err = WRITEREG(utp, SUNI_REGO_MRESET, 0, 0);
530	} else {
531		err = WRITEREG(utp, SUNI_REGO_RSOP_BIP8, 0, 0);
532		err |= WRITEREG(utp, SUNI_REGO_RLOPBIP8_24, 0, 0);
533		err |= WRITEREG(utp, SUNI_REGO_RPOPBIP8, 0, 0);
534		err |= WRITEREG(utp, SUNI_REGO_RACPCHCS, 0, 0);
535		err |= WRITEREG(utp, SUNI_REGO_TACPCNT, 0, 0);
536
537	}
538	if (err) {
539#ifdef DIAGNOSTIC
540		printf("%s: register write error %s: %d\n", __func__,
541		    utp->chip->name, err);
542#endif
543		return;
544	}
545
546	DELAY(8);
547
548	utp->stats.rx_sbip += UPDATE16(utp, SUNI_REGO_RSOP_BIP8);
549	utp->stats.rx_lbip += UPDATE20(utp, SUNI_REGO_RLOPBIP8_24);
550	utp->stats.rx_lfebe += UPDATE20(utp, SUNI_REGO_RLOPFEBE);
551	utp->stats.rx_pbip += UPDATE16(utp, SUNI_REGO_RPOPBIP8);
552	utp->stats.rx_pfebe += UPDATE16(utp, SUNI_REGO_RPOPFEBE);
553	utp->stats.rx_corr += UPDATE8(utp, SUNI_REGO_RACPCHCS);
554	utp->stats.rx_uncorr += UPDATE8(utp, SUNI_REGO_RACPUHCS);
555	utp->stats.rx_cells += UPDATE19(utp, SUNI_REGO_RACPCNT);
556	utp->stats.tx_cells += UPDATE19(utp, SUNI_REGO_TACPCNT);
557}
558
559/*
560 * Update statistics from a SUNI/622
561 */
562static void
563suni_622_update_stats(struct utopia *utp)
564{
565	int err;
566
567	/* write to the master if we can */
568	if (!(utp->flags & UTP_FL_NORESET)) {
569		err = WRITEREG(utp, SUNI_REGO_MRESET, 0, 0);
570	} else {
571		err = WRITEREG(utp, SUNI_REGO_RSOP_BIP8, 0, 0);
572		err |= WRITEREG(utp, SUNI_REGO_RLOPBIP8_24, 0, 0);
573		err |= WRITEREG(utp, SUNI_REGO_RPOPBIP8, 0, 0);
574		err |= WRITEREG(utp, SUNI_REGO_RACPCHCS, 0, 0);
575		err |= WRITEREG(utp, SUNI_REGO_TACPCNT, 0, 0);
576	}
577	if (err) {
578#ifdef DIAGNOSTIC
579		printf("%s: register write error %s: %d\n", __func__,
580		    utp->chip->name, err);
581#endif
582		return;
583	}
584
585	DELAY(8);
586
587	utp->stats.rx_sbip += UPDATE16(utp, SUNI_REGO_RSOP_BIP8);
588	utp->stats.rx_lbip += UPDATE20(utp, SUNI_REGO_RLOPBIP8_24);
589	utp->stats.rx_lfebe += UPDATE20(utp, SUNI_REGO_RLOPFEBE);
590	utp->stats.rx_pbip += UPDATE16(utp, SUNI_REGO_RPOPBIP8);
591	utp->stats.rx_pfebe += UPDATE16(utp, SUNI_REGO_RPOPFEBE);
592	utp->stats.rx_corr += UPDATE12(utp, SUNI_REGO_RACPCHCS_622);
593	utp->stats.rx_uncorr += UPDATE12(utp, SUNI_REGO_RACPUHCS_622);
594	utp->stats.rx_cells += UPDATE21(utp, SUNI_REGO_RACPCNT_622);
595	utp->stats.tx_cells += UPDATE21(utp, SUNI_REGO_TACPCNT);
596}
597
598static const struct utopia_chip chip_622 = {
599	UTP_TYPE_SUNI_622,
600	"Suni/622 (PMC-5355)",
601	256,
602	utopia_reset_622,
603	utopia_set_sdh_default,
604	utopia_set_unass_default,
605	utopia_set_noscramb_default,
606	utopia_update_carrier_default,
607	utopia_set_loopback_622,
608	utopia_intr_default,
609	suni_622_update_stats,
610};
611static const struct utopia_chip chip_lite = {
612	UTP_TYPE_SUNI_LITE,
613	"Suni/Lite (PMC-5346)",
614	256,
615	utopia_reset_default,
616	utopia_set_sdh_default,
617	utopia_set_unass_default,
618	utopia_set_noscramb_default,
619	utopia_update_carrier_default,
620	utopia_set_loopback_lite,
621	utopia_intr_default,
622	suni_lite_update_stats,
623};
624static const struct utopia_chip chip_ultra = {
625	UTP_TYPE_SUNI_ULTRA,
626	"Suni/Ultra (PMC-5350)",
627	256,
628	utopia_reset_default,
629	utopia_set_sdh_default,
630	utopia_set_unass_default,
631	utopia_set_noscramb_default,
632	utopia_update_carrier_default,
633	utopia_set_loopback_ultra,
634	utopia_intr_default,
635	suni_lite_update_stats,
636};
637
638/*
639 * Reset IDT77105. There is really no way to reset this thing by acessing
640 * the registers. Load the registers with default values.
641 */
642static int
643idt77105_reset(struct utopia *utp)
644{
645	int err = 0;
646	u_int n;
647	uint8_t val[2];
648
649	err |= WRITEREG(utp, IDTPHY_REGO_MCR, 0xff,
650	    IDTPHY_REGM_MCR_DRIC | IDTPHY_REGM_MCR_EI);
651	n = 1;
652	err |= READREGS(utp, IDTPHY_REGO_ISTAT, val, &n);
653	err |= WRITEREG(utp, IDTPHY_REGO_DIAG, 0xff, 0);
654	err |= WRITEREG(utp, IDTPHY_REGO_LHEC, 0xff, 0);
655
656	err |= WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, IDTPHY_REGM_CNTS_SEC);
657	n = 2;
658	err |= READREGS(utp, IDTPHY_REGO_CNT, val, &n);
659
660	err |= WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, IDTPHY_REGM_CNTS_TX);
661	n = 2;
662	err |= READREGS(utp, IDTPHY_REGO_CNT, val, &n);
663
664	err |= WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, IDTPHY_REGM_CNTS_RX);
665	n = 2;
666	err |= READREGS(utp, IDTPHY_REGO_CNT, val, &n);
667
668	err |= WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, IDTPHY_REGM_CNTS_HECE);
669	n = 2;
670	err |= READREGS(utp, IDTPHY_REGO_CNT, val, &n);
671
672	err |= WRITEREG(utp, IDTPHY_REGO_MCR, IDTPHY_REGM_MCR_DREC,
673	    IDTPHY_REGM_MCR_DREC);
674	err |= WRITEREG(utp, IDTPHY_REGO_DIAG, IDTPHY_REGM_DIAG_RFLUSH,
675	    IDTPHY_REGM_DIAG_RFLUSH);
676
677	/* loopback */
678	err |= utopia_set_loopback(utp, utp->loopback);
679
680	/* update carrier state */
681	err |= utopia_update_carrier(utp);
682
683	return (err ? EIO : 0);
684}
685
686static int
687unknown_inval(struct utopia *utp, int what __unused)
688{
689	return (EINVAL);
690}
691
692static int
693idt77105_update_carrier(struct utopia *utp)
694{
695	int err;
696	uint8_t reg;
697	u_int n = 1;
698
699	if ((err = READREGS(utp, IDTPHY_REGO_ISTAT, &reg, &n)) != 0) {
700		utp->carrier = UTP_CARR_UNKNOWN;
701		return (err);
702	}
703	utopia_check_carrier(utp, reg & IDTPHY_REGM_ISTAT_GOOD);
704	return (0);
705}
706
707static int
708idt77105_set_loopback(struct utopia *utp, u_int mode)
709{
710	int err;
711
712	switch (mode) {
713	  case UTP_LOOP_NONE:
714		err = WRITEREG(utp, IDTPHY_REGO_DIAG,
715		    IDTPHY_REGM_DIAG_LOOP, IDTPHY_REGM_DIAG_LOOP_NONE);
716		break;
717
718	  case UTP_LOOP_DIAG:
719		err = WRITEREG(utp, IDTPHY_REGO_DIAG,
720		    IDTPHY_REGM_DIAG_LOOP, IDTPHY_REGM_DIAG_LOOP_PHY);
721		break;
722
723	  case UTP_LOOP_LINE:
724		err = WRITEREG(utp, IDTPHY_REGO_DIAG,
725		    IDTPHY_REGM_DIAG_LOOP, IDTPHY_REGM_DIAG_LOOP_LINE);
726		break;
727
728	  default:
729		return (EINVAL);
730	}
731	if (err)
732		return (err);
733	utp->loopback = mode;
734	return (0);
735}
736
737/*
738 * Handle interrupt on IDT77105 chip
739 */
740static void
741idt77105_intr(struct utopia *utp)
742{
743	uint8_t reg;
744	u_int n = 1;
745	int err;
746
747	/* Interrupt status and ack the interrupt */
748	if ((err = READREGS(utp, IDTPHY_REGO_ISTAT, &reg, &n)) != 0) {
749		printf("IDT77105 read error %d\n", err);
750		return;
751	}
752	/* check for signal condition */
753	utopia_check_carrier(utp, reg & IDTPHY_REGM_ISTAT_GOOD);
754}
755
756static void
757idt77105_update_stats(struct utopia *utp)
758{
759	int err = 0;
760	uint8_t regs[2];
761	u_int n;
762
763#ifdef DIAGNOSTIC
764#define UDIAG(F,A,B)	printf(F, A, B)
765#else
766#define	UDIAG(F,A,B)	do { } while (0)
767#endif
768
769#define	UPD(FIELD, CODE, N, MASK)					\
770	err = WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, CODE);		\
771	if (err != 0) {							\
772		UDIAG("%s: cannot write CNTS: %d\n", __func__, err);	\
773		return;							\
774	}								\
775	n = N;								\
776	err = READREGS(utp, IDTPHY_REGO_CNT, regs, &n);			\
777	if (err != 0) {							\
778		UDIAG("%s: cannot read CNT: %d\n", __func__, err);	\
779		return;							\
780	}								\
781	if (n != N) {							\
782		UDIAG("%s: got only %u registers\n", __func__, n);	\
783		return;							\
784	}								\
785	if (N == 1)							\
786		utp->stats.FIELD += (regs[0] & MASK);			\
787	else								\
788		utp->stats.FIELD += (regs[0] | (regs[1] << 8)) & MASK;
789
790	UPD(rx_symerr, IDTPHY_REGM_CNTS_SEC, 1, 0xff);
791	UPD(tx_cells, IDTPHY_REGM_CNTS_TX, 2, 0xffff);
792	UPD(rx_cells, IDTPHY_REGM_CNTS_RX, 2, 0xffff);
793	UPD(rx_uncorr, IDTPHY_REGM_CNTS_HECE, 1, 0x1f);
794
795#undef	UDIAG
796#undef	UPD
797}
798
799static const struct utopia_chip chip_idt77105 = {
800	UTP_TYPE_IDT77105,
801	"IDT77105",
802	7,
803	idt77105_reset,
804	unknown_inval,
805	unknown_inval,
806	unknown_inval,
807	idt77105_update_carrier,
808	idt77105_set_loopback,
809	idt77105_intr,
810	idt77105_update_stats,
811};
812
813/*
814 * Update the carrier status
815 */
816static int
817idt77155_update_carrier(struct utopia *utp)
818{
819	int err;
820	uint8_t reg;
821	u_int n = 1;
822
823	if ((err = READREGS(utp, IDTPHY_REGO_RSOS, &reg, &n)) != 0) {
824		utp->carrier = UTP_CARR_UNKNOWN;
825		return (err);
826	}
827	utopia_check_carrier(utp, !(reg & IDTPHY_REGM_RSOS_LOS));
828	return (0);
829}
830
831/*
832 * Handle interrupt on IDT77155 chip
833 */
834static void
835idt77155_intr(struct utopia *utp)
836{
837	uint8_t reg;
838	u_int n = 1;
839	int err;
840
841	if ((err = READREGS(utp, IDTPHY_REGO_RSOS, &reg, &n)) != 0) {
842		printf("IDT77105 read error %d\n", err);
843		return;
844	}
845	utopia_check_carrier(utp, !(reg & IDTPHY_REGM_RSOS_LOS));
846}
847
848/*
849 * set SONET/SDH mode
850 */
851static int
852idt77155_set_sdh(struct utopia *utp, int sdh)
853{
854	int err;
855
856	if (sdh)
857		err = WRITEREG(utp, IDTPHY_REGO_PTRM,
858		    IDTPHY_REGM_PTRM_SS, IDTPHY_REGM_PTRM_SDH);
859	else
860		err = WRITEREG(utp, IDTPHY_REGO_PTRM,
861		    IDTPHY_REGM_PTRM_SS, IDTPHY_REGM_PTRM_SONET);
862	if (err != 0)
863		return (err);
864
865	utp->state &= ~UTP_ST_SDH;
866	if (sdh)
867		utp->state |= UTP_ST_SDH;
868
869	return (0);
870}
871
872/*
873 * set idle/unassigned cells
874 */
875static int
876idt77155_set_unass(struct utopia *utp, int unass)
877{
878	int err;
879
880	if (unass)
881		err = WRITEREG(utp, IDTPHY_REGO_TCHP, 0xff, 0);
882	else
883		err = WRITEREG(utp, IDTPHY_REGO_TCHP, 0xff, 1);
884	if (err != 0)
885		return (err);
886
887	utp->state &= ~UTP_ST_UNASS;
888	if (unass)
889		utp->state |= UTP_ST_UNASS;
890
891	return (0);
892}
893
894/*
895 * enable/disable scrambling
896 */
897static int
898idt77155_set_noscramb(struct utopia *utp, int noscramb)
899{
900	int err;
901
902	if (noscramb) {
903		err = WRITEREG(utp, IDTPHY_REGO_TCC,
904		    IDTPHY_REGM_TCC_DSCR, IDTPHY_REGM_TCC_DSCR);
905		if (err)
906			return (err);
907		err = WRITEREG(utp, IDTPHY_REGO_RCC,
908		    IDTPHY_REGM_RCC_DSCR, IDTPHY_REGM_RCC_DSCR);
909		if (err)
910			return (err);
911		utp->state |= UTP_ST_NOSCRAMB;
912	} else {
913		err = WRITEREG(utp, IDTPHY_REGO_TCC,
914		    IDTPHY_REGM_TCC_DSCR, 0);
915		if (err)
916			return (err);
917		err = WRITEREG(utp, IDTPHY_REGO_RCC,
918		    IDTPHY_REGM_RCC_DSCR, 0);
919		if (err)
920			return (err);
921		utp->state &= ~UTP_ST_NOSCRAMB;
922	}
923	return (0);
924}
925
926/*
927 * Set loopback mode for the 77155
928 */
929static int
930idt77155_set_loopback(struct utopia *utp, u_int mode)
931{
932	int err;
933	uint32_t val;
934	u_int nmode;
935
936	val = 0;
937	nmode = mode;
938	if (mode & UTP_LOOP_TIME) {
939		nmode &= ~UTP_LOOP_TIME;
940		val |= IDTPHY_REGM_MCTL_TLOOP;
941	}
942	if (mode & UTP_LOOP_DIAG) {
943		nmode &= ~UTP_LOOP_DIAG;
944		val |= IDTPHY_REGM_MCTL_DLOOP;
945	}
946	if (mode & UTP_LOOP_LINE) {
947		nmode &= ~UTP_LOOP_LINE;
948		val |= IDTPHY_REGM_MCTL_LLOOP;
949	}
950	if (nmode != 0)
951		return (EINVAL);
952
953	err = WRITEREG(utp, IDTPHY_REGO_MCTL, IDTPHY_REGM_MCTL_TLOOP |
954	    IDTPHY_REGM_MCTL_DLOOP | IDTPHY_REGM_MCTL_LLOOP, val);
955	if (err)
956		return (err);
957	utp->loopback = mode;
958
959	return (0);
960}
961
962/*
963 * Set the chip to reflect the current state in utopia.
964 * Assume, that the chip has been reset.
965 */
966static int
967idt77155_set_chip(struct utopia *utp)
968{
969	int err = 0;
970
971	/* set sonet/sdh */
972	err |= idt77155_set_sdh(utp, utp->state & UTP_ST_SDH);
973
974	/* unassigned or idle cells */
975	err |= idt77155_set_unass(utp, utp->state & UTP_ST_UNASS);
976
977	/* loopback */
978	err |= idt77155_set_loopback(utp, utp->loopback);
979
980	/* update carrier state */
981	err |= idt77155_update_carrier(utp);
982
983	/* enable interrupts on LOS */
984	err |= WRITEREG(utp, IDTPHY_REGO_INT,
985	    IDTPHY_REGM_INT_RXSOHI, IDTPHY_REGM_INT_RXSOHI);
986	err |= WRITEREG(utp, IDTPHY_REGO_RSOC,
987	    IDTPHY_REGM_RSOC_LOSI, IDTPHY_REGM_RSOC_LOSI);
988
989	return (err ? EIO : 0);
990}
991
992/*
993 * Reset the chip to reflect the current state of utopia.
994 */
995static int
996idt77155_reset(struct utopia *utp)
997{
998	int err = 0;
999
1000	if (!(utp->flags & UTP_FL_NORESET)) {
1001		err |= WRITEREG(utp, IDTPHY_REGO_MRID, IDTPHY_REGM_MRID_RESET,
1002		    IDTPHY_REGM_MRID_RESET);
1003		err |= WRITEREG(utp, IDTPHY_REGO_MRID, IDTPHY_REGM_MRID_RESET,
1004		    0);
1005	}
1006
1007	err |= idt77155_set_chip(utp);
1008
1009	return (err ? EIO : 0);
1010}
1011
1012/*
1013 * Update statistics from a IDT77155
1014 * This appears to be the same as for the Suni/Lite and Ultra. IDT however
1015 * makes no assessment about the transfer time. Assume 7us.
1016 */
1017static void
1018idt77155_update_stats(struct utopia *utp)
1019{
1020	int err;
1021
1022	/* write to the master if we can */
1023	if (!(utp->flags & UTP_FL_NORESET)) {
1024		err = WRITEREG(utp, IDTPHY_REGO_MRID, 0, 0);
1025	} else {
1026		err = WRITEREG(utp, IDTPHY_REGO_BIPC, 0, 0);
1027		err |= WRITEREG(utp, IDTPHY_REGO_B2EC, 0, 0);
1028		err |= WRITEREG(utp, IDTPHY_REGO_B3EC, 0, 0);
1029		err |= WRITEREG(utp, IDTPHY_REGO_CEC, 0, 0);
1030		err |= WRITEREG(utp, IDTPHY_REGO_TXCNT, 0, 0);
1031
1032	}
1033	if (err) {
1034#ifdef DIAGNOSTIC
1035		printf("%s: register write error %s: %d\n", __func__,
1036		    utp->chip->name, err);
1037#endif
1038		return;
1039	}
1040
1041	DELAY(8);
1042
1043	utp->stats.rx_sbip += UPDATE16(utp, IDTPHY_REGO_BIPC);
1044	utp->stats.rx_lbip += UPDATE20(utp, IDTPHY_REGO_B2EC);
1045	utp->stats.rx_lfebe += UPDATE20(utp, IDTPHY_REGO_FEBEC);
1046	utp->stats.rx_pbip += UPDATE16(utp, IDTPHY_REGO_B3EC);
1047	utp->stats.rx_pfebe += UPDATE16(utp, IDTPHY_REGO_PFEBEC);
1048	utp->stats.rx_corr += UPDATE8(utp, IDTPHY_REGO_CEC);
1049	utp->stats.rx_uncorr += UPDATE8(utp, IDTPHY_REGO_UEC);
1050	utp->stats.rx_cells += UPDATE19(utp, IDTPHY_REGO_RCCNT);
1051	utp->stats.tx_cells += UPDATE19(utp, IDTPHY_REGO_TXCNT);
1052}
1053
1054
1055static const struct utopia_chip chip_idt77155 = {
1056	UTP_TYPE_IDT77155,
1057	"IDT77155",
1058	0x80,
1059	idt77155_reset,
1060	idt77155_set_sdh,
1061	idt77155_set_unass,
1062	idt77155_set_noscramb,
1063	idt77155_update_carrier,
1064	idt77155_set_loopback,
1065	idt77155_intr,
1066	idt77155_update_stats,
1067};
1068
1069static int
1070unknown_reset(struct utopia *utp __unused)
1071{
1072	return (EIO);
1073}
1074
1075static int
1076unknown_update_carrier(struct utopia *utp)
1077{
1078	utp->carrier = UTP_CARR_UNKNOWN;
1079	return (0);
1080}
1081
1082static int
1083unknown_set_loopback(struct utopia *utp __unused, u_int mode __unused)
1084{
1085	return (EINVAL);
1086}
1087
1088static void
1089unknown_intr(struct utopia *utp __unused)
1090{
1091}
1092
1093static void
1094unknown_update_stats(struct utopia *utp __unused)
1095{
1096}
1097
1098static const struct utopia_chip chip_unknown = {
1099	UTP_TYPE_UNKNOWN,
1100	"unknown",
1101	0,
1102	unknown_reset,
1103	unknown_inval,
1104	unknown_inval,
1105	unknown_inval,
1106	unknown_update_carrier,
1107	unknown_set_loopback,
1108	unknown_intr,
1109	unknown_update_stats,
1110};
1111
1112/*
1113 * Callbacks for the ifmedia infrastructure.
1114 */
1115static int
1116utopia_media_change(struct ifnet *ifp)
1117{
1118	struct ifatm *ifatm = (struct ifatm *)ifp->if_softc;
1119	struct utopia *utp = ifatm->phy;
1120	int error = 0;
1121
1122	UTP_LOCK(utp);
1123	if (utp->chip->type != UTP_TYPE_UNKNOWN && utp->state & UTP_ST_ACTIVE) {
1124		if (utp->media->ifm_media & IFM_ATM_SDH) {
1125			if (!(utp->state & UTP_ST_SDH))
1126				error = utopia_set_sdh(utp, 1);
1127		} else {
1128			if (utp->state & UTP_ST_SDH)
1129				error = utopia_set_sdh(utp, 0);
1130		}
1131		if (utp->media->ifm_media & IFM_ATM_UNASSIGNED) {
1132			if (!(utp->state & UTP_ST_UNASS))
1133				error = utopia_set_unass(utp, 1);
1134		} else {
1135			if (utp->state & UTP_ST_UNASS)
1136				error = utopia_set_unass(utp, 0);
1137		}
1138		if (utp->media->ifm_media & IFM_ATM_NOSCRAMB) {
1139			if (!(utp->state & UTP_ST_NOSCRAMB))
1140				error = utopia_set_noscramb(utp, 1);
1141		} else {
1142			if (utp->state & UTP_ST_NOSCRAMB)
1143				error = utopia_set_noscramb(utp, 0);
1144		}
1145	} else
1146		error = EIO;
1147	UTP_UNLOCK(utp);
1148	return (error);
1149}
1150
1151/*
1152 * Look at the carrier status.
1153 */
1154static void
1155utopia_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
1156{
1157	struct utopia *utp = ((struct ifatm *)ifp->if_softc)->phy;
1158
1159	UTP_LOCK(utp);
1160	if (utp->chip->type != UTP_TYPE_UNKNOWN && utp->state & UTP_ST_ACTIVE) {
1161		ifmr->ifm_active = IFM_ATM | utp->ifatm->mib.media;
1162
1163		switch (utp->carrier) {
1164
1165		  case UTP_CARR_OK:
1166			ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE;
1167			break;
1168
1169		  case UTP_CARR_LOST:
1170			ifmr->ifm_status = IFM_AVALID;
1171			break;
1172
1173		  default:
1174			ifmr->ifm_status = 0;
1175			break;
1176		}
1177		if (utp->state & UTP_ST_SDH) {
1178			ifmr->ifm_active |= IFM_ATM_SDH;
1179			ifmr->ifm_current |= IFM_ATM_SDH;
1180		}
1181		if (utp->state & UTP_ST_UNASS) {
1182			ifmr->ifm_active |= IFM_ATM_UNASSIGNED;
1183			ifmr->ifm_current |= IFM_ATM_UNASSIGNED;
1184		}
1185		if (utp->state & UTP_ST_NOSCRAMB) {
1186			ifmr->ifm_active |= IFM_ATM_NOSCRAMB;
1187			ifmr->ifm_current |= IFM_ATM_NOSCRAMB;
1188		}
1189	} else {
1190		ifmr->ifm_active = 0;
1191		ifmr->ifm_status = 0;
1192	}
1193	UTP_UNLOCK(utp);
1194}
1195
1196/*
1197 * Initialize media from the mib
1198 */
1199void
1200utopia_init_media(struct utopia *utp)
1201{
1202
1203	ifmedia_removeall(utp->media);
1204	ifmedia_add(utp->media, IFM_ATM | utp->ifatm->mib.media, 0, NULL);
1205	ifmedia_set(utp->media, IFM_ATM | utp->ifatm->mib.media);
1206}
1207
1208/*
1209 * Reset all media
1210 */
1211void
1212utopia_reset_media(struct utopia *utp)
1213{
1214
1215	ifmedia_removeall(utp->media);
1216}
1217
1218/*
1219 * This is called by the driver as soon as the SUNI registers are accessible.
1220 * This may be either in the attach routine or the init routine of the driver.
1221 */
1222int
1223utopia_start(struct utopia *utp)
1224{
1225	uint8_t reg;
1226	int err;
1227	u_int n = 1;
1228
1229	if ((err = READREGS(utp, SUNI_REGO_MRESET, &reg, &n)) != 0)
1230		return (err);
1231
1232	switch (reg & SUNI_REGM_MRESET_TYPE) {
1233
1234	  case SUNI_REGM_MRESET_TYPE_622:
1235		utp->chip = &chip_622;
1236		break;
1237
1238	  case SUNI_REGM_MRESET_TYPE_LITE:
1239		/* this may be either a SUNI LITE or a IDT77155 *
1240		 * Read register 0x70. The SUNI doesn't have it */
1241		n = 1;
1242		if ((err = READREGS(utp, IDTPHY_REGO_RBER, &reg, &n)) != 0)
1243			return (err);
1244		if ((reg & ~IDTPHY_REGM_RBER_RESV) ==
1245		    (IDTPHY_REGM_RBER_FAIL | IDTPHY_REGM_RBER_WARN))
1246			utp->chip = &chip_idt77155;
1247		else
1248			utp->chip = &chip_lite;
1249		break;
1250
1251	  case SUNI_REGM_MRESET_TYPE_ULTRA:
1252		utp->chip = &chip_ultra;
1253		break;
1254
1255	  default:
1256		if (reg == (IDTPHY_REGM_MCR_DRIC | IDTPHY_REGM_MCR_EI))
1257			utp->chip = &chip_idt77105;
1258		else {
1259			if_printf(&utp->ifatm->ifnet,
1260			    "unknown ATM-PHY chip %#x\n", reg);
1261			utp->chip = &chip_unknown;
1262		}
1263		break;
1264	}
1265	utp->state |= UTP_ST_ACTIVE;
1266	return (0);
1267}
1268
1269/*
1270 * Stop the chip
1271 */
1272void
1273utopia_stop(struct utopia *utp)
1274{
1275	utp->state &= ~UTP_ST_ACTIVE;
1276}
1277
1278/*
1279 * Handle the sysctls
1280 */
1281static int
1282utopia_sysctl_regs(SYSCTL_HANDLER_ARGS)
1283{
1284	struct utopia *utp = (struct utopia *)arg1;
1285	int error;
1286	u_int n;
1287	uint8_t *val;
1288	uint8_t new[3];
1289
1290	if ((n = utp->chip->nregs) == 0)
1291		return (EIO);
1292	val = malloc(sizeof(uint8_t) * n, M_TEMP, M_WAITOK);
1293
1294	UTP_LOCK(utp);
1295	error = READREGS(utp, 0, val, &n);
1296	UTP_UNLOCK(utp);
1297
1298	if (error) {
1299		free(val, M_TEMP);
1300		return (error);
1301	}
1302
1303	error = SYSCTL_OUT(req, val, sizeof(uint8_t) * n);
1304	free(val, M_TEMP);
1305	if (error != 0 || req->newptr == NULL)
1306		return (error);
1307
1308	error = SYSCTL_IN(req, new, sizeof(new));
1309	if (error)
1310		return (error);
1311
1312	UTP_LOCK(utp);
1313	error = WRITEREG(utp, new[0], new[1], new[2]);
1314	UTP_UNLOCK(utp);
1315
1316	return (error);
1317}
1318
1319static int
1320utopia_sysctl_stats(SYSCTL_HANDLER_ARGS)
1321{
1322	struct utopia *utp = (struct utopia *)arg1;
1323	void *val;
1324	int error;
1325
1326	val = malloc(sizeof(utp->stats), M_TEMP, M_WAITOK);
1327
1328	UTP_LOCK(utp);
1329	bcopy(&utp->stats, val, sizeof(utp->stats));
1330	if (req->newptr != NULL)
1331		bzero((char *)&utp->stats + sizeof(utp->stats.version),
1332		    sizeof(utp->stats) - sizeof(utp->stats.version));
1333	UTP_UNLOCK(utp);
1334
1335	error = SYSCTL_OUT(req, val, sizeof(utp->stats));
1336	free(val, M_TEMP);
1337
1338	if (error && req->newptr != NULL)
1339		bcopy(val, &utp->stats, sizeof(utp->stats));
1340
1341	/* ignore actual new value */
1342
1343	return (error);
1344}
1345
1346/*
1347 * Handle the loopback sysctl
1348 */
1349static int
1350utopia_sysctl_loopback(SYSCTL_HANDLER_ARGS)
1351{
1352	struct utopia *utp = (struct utopia *)arg1;
1353	int error;
1354	u_int loopback;
1355
1356	error = SYSCTL_OUT(req, &utp->loopback, sizeof(u_int));
1357	if (error != 0 || req->newptr == NULL)
1358		return (error);
1359
1360	error = SYSCTL_IN(req, &loopback, sizeof(u_int));
1361	if (error)
1362		return (error);
1363
1364	UTP_LOCK(utp);
1365	error = utopia_set_loopback(utp, loopback);
1366	UTP_UNLOCK(utp);
1367
1368	return (error);
1369}
1370
1371/*
1372 * Handle the type sysctl
1373 */
1374static int
1375utopia_sysctl_type(SYSCTL_HANDLER_ARGS)
1376{
1377	struct utopia *utp = (struct utopia *)arg1;
1378
1379	return (SYSCTL_OUT(req, &utp->chip->type, sizeof(utp->chip->type)));
1380}
1381
1382/*
1383 * Handle the name sysctl
1384 */
1385static int
1386utopia_sysctl_name(SYSCTL_HANDLER_ARGS)
1387{
1388	struct utopia *utp = (struct utopia *)arg1;
1389
1390	return (SYSCTL_OUT(req, utp->chip->name, strlen(utp->chip->name) + 1));
1391}
1392
1393/*
1394 * Initialize the state. This is called from the drivers attach
1395 * function. The mutex must be already initialized.
1396 */
1397int
1398utopia_attach(struct utopia *utp, struct ifatm *ifatm, struct ifmedia *media,
1399    struct mtx *lock, struct sysctl_ctx_list *ctx,
1400    struct sysctl_oid_list *children, const struct utopia_methods *m)
1401{
1402
1403	bzero(utp, sizeof(*utp));
1404	utp->ifatm = ifatm;
1405	utp->methods = m;
1406	utp->media = media;
1407	utp->lock = lock;
1408	utp->chip = &chip_unknown;
1409	utp->stats.version = 1;
1410
1411	ifmedia_init(media,
1412	    IFM_ATM_SDH | IFM_ATM_UNASSIGNED | IFM_ATM_NOSCRAMB,
1413	    utopia_media_change, utopia_media_status);
1414
1415	if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_regs",
1416	    CTLFLAG_RW | CTLTYPE_OPAQUE, utp, 0, utopia_sysctl_regs, "S",
1417	    "phy registers") == NULL)
1418		return (-1);
1419
1420	if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_loopback",
1421	    CTLFLAG_RW | CTLTYPE_UINT, utp, 0, utopia_sysctl_loopback, "IU",
1422	    "phy loopback mode") == NULL)
1423		return (-1);
1424
1425	if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_type",
1426	    CTLFLAG_RD | CTLTYPE_UINT, utp, 0, utopia_sysctl_type, "IU",
1427	    "phy type") == NULL)
1428		return (-1);
1429
1430	if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_name",
1431	    CTLFLAG_RD | CTLTYPE_STRING, utp, 0, utopia_sysctl_name, "A",
1432	    "phy name") == NULL)
1433		return (-1);
1434
1435	if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_stats",
1436	    CTLFLAG_RW | CTLTYPE_OPAQUE, utp, 0, utopia_sysctl_stats, "S",
1437	    "phy statistics") == NULL)
1438		return (-1);
1439
1440	if (SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "phy_state",
1441	    CTLFLAG_RD, &utp->state, 0, "phy state") == NULL)
1442		return (-1);
1443
1444	if (SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "phy_carrier",
1445	    CTLFLAG_RD, &utp->carrier, 0, "phy carrier") == NULL)
1446		return (-1);
1447
1448	UTP_WLOCK_LIST();
1449	LIST_INSERT_HEAD(&utopia_list, utp, link);
1450	UTP_WUNLOCK_LIST();
1451
1452	utp->state |= UTP_ST_ATTACHED;
1453	return (0);
1454}
1455
1456/*
1457 * Detach. We set a flag here, wakeup the daemon and let him do it.
1458 * Here we need the lock for synchronisation with the daemon.
1459 */
1460void
1461utopia_detach(struct utopia *utp)
1462{
1463
1464	UTP_LOCK_ASSERT(utp);
1465	if (utp->state & UTP_ST_ATTACHED) {
1466		utp->state |= UTP_ST_DETACH;
1467		while (utp->state & UTP_ST_DETACH) {
1468			wakeup(&utopia_list);
1469			msleep(utp, utp->lock, PZERO, "utopia_detach", hz);
1470		}
1471	}
1472}
1473
1474/*
1475 * The carrier state kernel proc for those adapters that do not interrupt.
1476 *
1477 * We assume, that utopia_attach can safely add a new utopia while we are going
1478 * through the list without disturbing us (we lock the list while getting
1479 * the address of the first element, adding is always done at the head).
1480 * Removing is entirely handled here.
1481 */
1482static void
1483utopia_daemon(void *arg __unused)
1484{
1485	struct utopia *utp, *next;
1486
1487	UTP_RLOCK_LIST();
1488	while (utopia_kproc != NULL) {
1489		utp = LIST_FIRST(&utopia_list);
1490		UTP_RUNLOCK_LIST();
1491
1492		while (utp != NULL) {
1493			mtx_lock(&Giant);	/* XXX depend on MPSAFE */
1494			UTP_LOCK(utp);
1495			next = LIST_NEXT(utp, link);
1496			if (utp->state & UTP_ST_DETACH) {
1497				LIST_REMOVE(utp, link);
1498				utp->state &= ~UTP_ST_DETACH;
1499				wakeup_one(utp);
1500			} else if (utp->state & UTP_ST_ACTIVE) {
1501				if (utp->flags & UTP_FL_POLL_CARRIER)
1502					utopia_update_carrier(utp);
1503				utopia_update_stats(utp);
1504			}
1505			UTP_UNLOCK(utp);
1506			mtx_unlock(&Giant);	/* XXX depend on MPSAFE */
1507			utp = next;
1508		}
1509
1510		UTP_RLOCK_LIST();
1511		msleep(&utopia_list, &utopia_list_mtx, PZERO, "*idle*", hz);
1512	}
1513	wakeup_one(&utopia_list);
1514	UTP_RUNLOCK_LIST();
1515	mtx_lock(&Giant);
1516	kthread_exit(0);
1517}
1518
1519/*
1520 * Module initialisation
1521 */
1522static int
1523utopia_mod_init(module_t mod, int what, void *arg)
1524{
1525	int err;
1526	struct proc *kp;
1527
1528	switch (what) {
1529
1530	  case MOD_LOAD:
1531		mtx_init(&utopia_list_mtx, "utopia list mutex", NULL, MTX_DEF);
1532		err = kthread_create(utopia_daemon, NULL, &utopia_kproc,
1533		    RFHIGHPID, 0, "utopia");
1534		if (err != 0) {
1535			printf("cannot created utopia thread %d\n", err);
1536			return (err);
1537		}
1538		break;
1539
1540	  case MOD_UNLOAD:
1541		UTP_WLOCK_LIST();
1542		if ((kp = utopia_kproc) != NULL) {
1543			utopia_kproc = NULL;
1544			wakeup_one(&utopia_list);
1545			PROC_LOCK(kp);
1546			UTP_WUNLOCK_LIST();
1547			msleep(kp, &kp->p_mtx, PWAIT, "utopia_destroy", 0);
1548			PROC_UNLOCK(kp);
1549		} else
1550			UTP_WUNLOCK_LIST();
1551		mtx_destroy(&utopia_list_mtx);
1552		break;
1553	}
1554	return (0);
1555}
1556
1557static moduledata_t utopia_mod = {
1558        "utopia",
1559        utopia_mod_init,
1560        0
1561};
1562
1563DECLARE_MODULE(utopia, utopia_mod, SI_SUB_INIT_IF, SI_ORDER_ANY);
1564MODULE_VERSION(utopia, 1);
1565