idtphy.c revision 142384
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: head/sys/dev/utopia/idtphy.c 142384 2005-02-24 16:56:36Z harti $");
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 */
60static int
61idt77105_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
104static int
105idt77105_inval(struct utopia *utp, int what __unused)
106{
107
108	return (EINVAL);
109}
110
111static int
112idt77105_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
126static int
127idt77105_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 */
159static void
160idt77105_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
175static void
176idt77105_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
218struct 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 */
235static int
236idt77155_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 */
253static void
254idt77155_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 */
270static int
271idt77155_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 */
294static int
295idt77155_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 */
316static int
317idt77155_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 */
348static int
349idt77155_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 */
385static int
386idt77155_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 */
414static int
415idt77155_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 */
436static void
437idt77155_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
482const 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