xref: /netbsd-src/sys/arch/sandpoint/stand/altboot/nvt.c (revision 767f3b03241924e2ec88723fffb28eda0453ea3b)
1 /* $NetBSD: nvt.c,v 1.3 2011/10/30 21:08:33 phx Exp $ */
2 
3 /*-
4  * Copyright (c) 2007 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Tohru Nishimura.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/param.h>
33 
34 #include <netinet/in.h>
35 #include <netinet/in_systm.h>
36 
37 #include <lib/libsa/stand.h>
38 #include <lib/libsa/net.h>
39 
40 #include "globals.h"
41 
42 /*
43  * - reverse endian access every CSR.
44  * - no vtophys() translation, vaddr_t == paddr_t.
45  * - PIPT writeback cache aware.
46  */
47 #define CSR_WRITE_1(l, r, v)	out8((l)->csr+(r), (v))
48 #define CSR_READ_1(l, r)	in8((l)->csr+(r))
49 #define CSR_WRITE_2(l, r, v)	out16rb((l)->csr+(r), (v))
50 #define CSR_READ_2(l, r)	in16rb((l)->csr+(r))
51 #define CSR_WRITE_4(l, r, v)	out32rb((l)->csr+(r), (v))
52 #define CSR_READ_4(l, r)	in32rb((l)->csr+(r))
53 #define VTOPHYS(va)		(uint32_t)(va)
54 #define DEVTOV(pa)		(uint32_t)(pa)
55 #define wbinv(adr, siz)		_wbinv(VTOPHYS(adr), (uint32_t)(siz))
56 #define inv(adr, siz)		_inv(VTOPHYS(adr), (uint32_t)(siz))
57 #define DELAY(n)		delay(n)
58 #define ALLOC(T,A)		(T *)allocaligned(sizeof(T),(A))
59 
60 struct desc {
61 	uint32_t xd0, xd1, xd2, xd3;
62 };
63 #define T0_OWN		(1U << 31)	/* 1: loaded for HW to send */
64 #define T0_TERR		(1U << 15)	/* Tx error; ABT|CBH */
65 #define T0_UDF		(1U << 11)	/* FIFO underflow */
66 #define T0_CRS		(1U << 10)	/* found carrier sense lost */
67 #define T0_OWC		(1U << 9)	/* found out of window collision */
68 #define T0_ABT		(1U << 8)	/* excess collision Tx abort */
69 #define T0_CBH		(1U << 7)	/* heartbeat check failure */
70 #define T0_COLS		(1U << 4)	/* collision detected */
71 #define T0_NCRMASK	0x3		/* number of collision retries */
72 #define T1_IC		(1U << 23)	/* post Tx done interrupt */
73 #define T1_STP		(1U << 22)	/* first frame segment */
74 #define T1_EDP		(1U << 21)	/* last frame segment */
75 #define T1_CRC		(1U << 16)	/* _disable_ CRC generation */
76 #define T1_CHN		(1U << 15)	/* "more bit," not the last seg. */
77 #define T_FLMASK	0x00007fff	/* Tx frame/segment length */
78 
79 #define R0_OWN		(1U << 31)	/* 1: empty for HW to load anew */
80 #define R0_FLMASK	0x7fff0000	/* frame length */
81 #define R0_RXOK		(1U << 15)
82 #define R0_MAR		(1U << 13)	/* multicast frame */
83 #define R0_BAR		(1U << 12)	/* broadcast frame */
84 #define R0_PHY		(1U << 11)	/* unicast frame */
85 #define R0_CHN		(1U << 10)	/* "more bit," not the last seg. */
86 #define R0_STP		(1U << 9)	/* first frame segment */
87 #define R0_EDP		(1U << 8)	/* last frame segment */
88 #define R0_BUFF		(1U << 7)	/* segment chain was broken */
89 #define R0_RUNT		(1U << 5)	/* runt frame received */
90 #define R0_LONG		(1U << 4)	/* frame too long */
91 #define R0_FOV		(1U << 3)	/* Rx FIFO overflow */
92 #define R0_FAE		(1U << 2)	/* frame alignment error */
93 #define R0_CRCE		(1U << 1)	/* CRC error */
94 #define R0_RERR		(1U << 0)	/* Rx error summary */
95 #define R1_FLMASK	0x00007ffc	/* Rx segment buffer length */
96 
97 #define VR_PAR0		0x00		/* SA [0] */
98 #define VR_PAR1		0x01		/* SA [1] */
99 #define VR_PAR2		0x02		/* SA [2] */
100 #define VR_PAR3		0x03		/* SA [3] */
101 #define VR_PAR4		0x04		/* SA [4] */
102 #define VR_PAR5		0x05		/* SA [5] */
103 #define VR_RCR		0x06		/* Rx control */
104 #define  RCR_PROM	(1U << 4)	/* accept any frame */
105 #define  RCR_AB		(1U << 3)	/* accept broadcast frame */
106 #define  RCR_AM		(1U << 2)	/* use multicast filter */
107 #define VR_TCR		0x07		/* Tx control */
108 #define VR_CTL0		0x08		/* control #0 */
109 #define  CTL0_RDMD	(1U << 6)	/* instruct Rx descriptor poll */
110 #define  CTL0_TDMD	(1U << 5)	/* instruct Tx descriptor poll */
111 #define  CTL0_TXON	(1U << 4)	/* enable Tx DMA */
112 #define  CTL0_RXON	(1U << 3)	/* enable Rx DMA */
113 #define  CTL0_STOP	(1U << 2)	/* activate stop processing */
114 #define  CTL0_START	(1U << 1)	/* start and activate */
115 #define VR_CTL1		0x09		/* control #1 */
116 #define  CTL1_RESET	(1U << 7)	/* SW reset, self-clearing */
117 #define  CTL1_DPOLL	(1U << 3)	/* _disable_ Tx auto polling */
118 #define  CTL1_FDX	(1U << 2)	/* set full duplex */
119 #define VR_ISR		0x0c		/* interrupt status */
120 #define VR_IEN		0x0e		/* interrupt enable */
121 #define VR_RDBA		0x18		/* Rx descriptor list base */
122 #define VR_TDBA		0x1c		/* Tx descriptor list base */
123 #define VR_MIICFG	0x6c		/* 4:0 PHY number */
124 #define VR_MIISR	0x6d		/* MII status */
125 #define VR_MIICR	0x70		/* MII control */
126 #define  MIICR_MAUTO	(1U << 7)	/* activate autopoll mode */
127 #define  MIICR_RCMD	(1U << 6)	/* MII read operation */
128 #define  MIICR_WCMD	(1U << 5)	/* MII write operation */
129 #define VR_MIIADR	0x71		/* MII indirect */
130 #define  MIIADR_MIDLE	(1U << 7)	/* not in auto polling */
131 #define VR_MIIDATA	0x72		/* MII read/write */
132 #define VR_RXC		0x7e		/* Rx feature control */
133 #define VR_TXC		0x7f		/* Tx feature control */
134 #define VR_MCR0		0x80		/* misc control #0 */
135 #define  MCR0_RFDXFLC	(1U << 3)	/* FCR1? */
136 #define  MCR0_HDXFLC	(1U << 2)	/* FCR2? */
137 #define VR_MCR1		0x81		/* misc control #1 */
138 
139 #define FRAMESIZE	1536
140 
141 struct local {
142 	struct desc txd[2];
143 	struct desc rxd[2];
144 	uint8_t rxstore[2][FRAMESIZE];
145 	unsigned csr, tx, rx;
146 	unsigned phy, bmsr, anlpar;
147 	unsigned ctl0;
148 };
149 
150 static void mii_autopoll(struct local *);
151 static void mii_stoppoll(struct local *);
152 static int mii_read(struct local *, int, int);
153 static void mii_write(struct local *, int, int, int);
154 static void mii_dealan(struct local *, unsigned);
155 
156 int
nvt_match(unsigned tag,void * data)157 nvt_match(unsigned tag, void *data)
158 {
159 	unsigned v;
160 
161 	v = pcicfgread(tag, PCI_ID_REG);
162 	switch (v) {
163 	case PCI_DEVICE(0x1106, 0x3053):
164 	case PCI_DEVICE(0x1106, 0x3065):
165 		return 1;
166 	}
167 	return 0;
168 }
169 
170 void *
nvt_init(unsigned tag,void * data)171 nvt_init(unsigned tag, void *data)
172 {
173 	unsigned val, fdx;
174 	struct local *l;
175 	struct desc *txd, *rxd;
176 	uint8_t *en;
177 
178 	l = ALLOC(struct local, 32); /* desc alignment */
179 	memset(l, 0, sizeof(struct local));
180 	l->csr = ~01 & DEVTOV(pcicfgread(tag, 0x10)); /* use IO space */
181 
182 	val = CTL1_RESET;
183 	CSR_WRITE_1(l, VR_CTL1, val);
184 	do {
185 		val = CSR_READ_1(l, VR_CTL1);
186 	} while (val & CTL1_RESET);
187 	/* PHY number is loaded from EEPROM */
188 	l->phy = CSR_READ_1(l, VR_MIICFG) & 0x1f;
189 
190 	en = data;
191 	en[0] = CSR_READ_1(l, VR_PAR0);
192 	en[1] = CSR_READ_1(l, VR_PAR1);
193 	en[2] = CSR_READ_1(l, VR_PAR2);
194 	en[3] = CSR_READ_1(l, VR_PAR3);
195 	en[4] = CSR_READ_1(l, VR_PAR4);
196 	en[5] = CSR_READ_1(l, VR_PAR5);
197 
198 	printf("MAC address %02x:%02x:%02x:%02x:%02x:%02x\n",
199 	    en[0], en[1], en[2], en[3], en[4], en[5]);
200 	DPRINTF(("PHY %d (%04x.%04x)\n", l->phy,
201 	    mii_read(l, l->phy, 2), mii_read(l, l->phy, 3)));
202 
203 	mii_dealan(l, 5);
204 
205 	/* speed and duplexity can be seen in MII 20 */
206 	val = mii_read(l, l->phy, 20);
207 	fdx = !!(val & (1U << 0));
208 	printf("%s", (val & (1U << 1)) ? "100Mbps" : "10Mbps");
209 	if (fdx)
210 		printf("-FDX");
211 	printf("\n");
212 
213 	txd = &l->txd[0];
214 	rxd = &l->rxd[0];
215 	rxd[0].xd0 = htole32(R0_OWN);
216 	rxd[0].xd1 = htole32(FRAMESIZE << 16);
217 	rxd[0].xd2 = htole32(VTOPHYS(l->rxstore[0]));
218 	rxd[0].xd3 = htole32(VTOPHYS(&rxd[1]));
219 	rxd[1].xd0 = htole32(R0_OWN);
220 	rxd[1].xd1 = htole32(VTOPHYS(l->rxstore[1]));
221 	rxd[1].xd2 = htole32(FRAMESIZE << 16);
222 	rxd[1].xd3 = htole32(VTOPHYS(&rxd[0]));
223 	wbinv(l, sizeof(struct local));
224 	l->tx = l->rx = 0;
225 
226 	/* enable transmitter and receiver */
227 	l->ctl0 = CTL0_TXON | CTL0_RXON | CTL0_START;
228 	CSR_WRITE_4(l, VR_RDBA, VTOPHYS(rxd));
229 	CSR_WRITE_4(l, VR_TDBA, VTOPHYS(txd));
230 	CSR_WRITE_1(l, VR_RCR, 0);
231 	CSR_WRITE_1(l, VR_TCR, 0);
232 	CSR_WRITE_2(l, VR_ISR, ~0);
233 	CSR_WRITE_2(l, VR_IEN, 0);
234 	if (fdx)
235 		CSR_WRITE_1(l, VR_CTL1, CTL1_FDX);
236 	CSR_WRITE_1(l, VR_CTL0, CTL0_START);
237 	CSR_WRITE_1(l, VR_CTL0, l->ctl0);
238 
239 	return l;
240 }
241 
242 int
nvt_send(void * dev,char * buf,unsigned len)243 nvt_send(void *dev, char *buf, unsigned len)
244 {
245 	struct local *l = dev;
246 	volatile struct desc *txd;
247 	unsigned loop;
248 
249 	len = (len & T_FLMASK);
250 	if (len < 60)
251 		len = 60; /* needs to stretch to ETHER_MIN_LEN - 4 */
252 	wbinv(buf, len);
253 	txd = &l->txd[l->tx];
254 	txd->xd3 = htole32(txd);
255 	txd->xd2 = htole32(VTOPHYS(buf));
256 	txd->xd1 = htole32(T1_STP | T1_EDP | len);
257 	txd->xd0 = htole32(T0_OWN);
258 	wbinv(txd, sizeof(struct desc));
259 	CSR_WRITE_1(l, VR_CTL0, l->ctl0 | CTL0_TDMD);
260 	loop = 100;
261 	do {
262 		if ((le32toh(txd->xd0) & T0_OWN) == 0)
263 			goto done;
264 		DELAY(10);
265 		inv(txd, sizeof(struct desc));
266 	} while (--loop > 0);
267 	printf("xmit failed\n");
268 	return -1;
269   done:
270 	l->tx ^= 1;
271 	return len;
272 }
273 
274 int
nvt_recv(void * dev,char * buf,unsigned maxlen,unsigned timo)275 nvt_recv(void *dev, char *buf, unsigned maxlen, unsigned timo)
276 {
277 	struct local *l = dev;
278 	volatile struct desc *rxd;
279 	unsigned bound, rxstat, len;
280 	uint8_t *ptr;
281 
282 	bound = 1000 * timo;
283 printf("recving with %u sec. timeout\n", timo);
284   again:
285 	rxd = &l->rxd[l->rx];
286 	do {
287 		inv(rxd, sizeof(struct desc));
288 		rxstat = le32toh(rxd->xd0);
289 		if ((rxstat & R0_OWN) == 0)
290 			goto gotone;
291 		DELAY(1000);	/* 1 milli second */
292 	} while (--bound > 0);
293 	errno = 0;
294 	return -1;
295   gotone:
296 	if ((rxstat & R0_RXOK) == 0) {
297 		rxd->xd0 = htole32(R0_OWN);
298 		wbinv(rxd, sizeof(struct desc));
299 		l->rx ^= 1;
300 		goto again;
301 	}
302 	len = ((rxstat & R0_FLMASK) >> 16) - 4 /* HASFCS */;
303 	if (len > maxlen)
304 		len = maxlen;
305 	ptr = l->rxstore[l->rx];
306 	inv(ptr, len);
307 	memcpy(buf, ptr, len);
308 	rxd->xd0 = htole32(R0_OWN);
309 	wbinv(rxd, sizeof(struct desc));
310 	l->rx ^= 1;
311 	return len;
312 }
313 
314 static void
mii_autopoll(struct local * l)315 mii_autopoll(struct local *l)
316 {
317 	int v;
318 
319 	CSR_WRITE_1(l, VR_MIICR, 0);
320 	do {
321 		DELAY(1);
322 		v = CSR_READ_1(l, VR_MIISR);
323 	} while ((v & MIIADR_MIDLE) == 0);
324 	CSR_WRITE_1(l, VR_MIICR, MIICR_MAUTO);
325 	do {
326 		DELAY(1);
327 		v = CSR_READ_1(l, VR_MIISR);
328 	} while ((v & MIIADR_MIDLE) != 0);
329 }
330 
331 static void
mii_stoppoll(struct local * l)332 mii_stoppoll(struct local *l)
333 {
334 	int v;
335 
336 	CSR_WRITE_1(l, VR_MIICR, 0);
337 	do {
338 		DELAY(1);
339 		v = CSR_READ_1(l, VR_MIISR);
340 	} while ((v & MIIADR_MIDLE) == 0);
341 }
342 
343 static int
mii_read(struct local * l,int phy,int reg)344 mii_read(struct local *l, int phy, int reg)
345 {
346 	int v;
347 
348 	mii_stoppoll(l);
349 	CSR_WRITE_1(l, VR_MIICFG, phy);
350 	CSR_WRITE_1(l, VR_MIIADR, reg);
351 	CSR_WRITE_1(l, VR_MIICR, MIICR_RCMD);
352 	do {
353 		v = CSR_READ_1(l, VR_MIICR);
354 	} while (v & MIICR_RCMD);
355 	v = CSR_READ_2(l, VR_MIIDATA);
356 	mii_autopoll(l);
357 	return v;
358 }
359 
360 static void
mii_write(struct local * l,int phy,int reg,int data)361 mii_write(struct local *l, int phy, int reg, int data)
362 {
363 	int v;
364 
365 	mii_stoppoll(l);
366 	CSR_WRITE_2(l, VR_MIIDATA, data);
367 	CSR_WRITE_1(l, VR_MIICFG, phy);
368 	CSR_WRITE_1(l, VR_MIIADR, reg);
369 	CSR_WRITE_1(l, VR_MIICR, MIICR_WCMD);
370 	do {
371 		v = CSR_READ_1(l, VR_MIICR);
372 	} while (v & MIICR_WCMD);
373 	mii_autopoll(l);
374 }
375 
376 #define MII_BMCR	0x00	/* Basic mode control register (rw) */
377 #define  BMCR_RESET	0x8000	/* reset */
378 #define  BMCR_AUTOEN	0x1000	/* autonegotiation enable */
379 #define  BMCR_ISO	0x0400	/* isolate */
380 #define  BMCR_STARTNEG	0x0200	/* restart autonegotiation */
381 #define MII_BMSR	0x01	/* Basic mode status register (ro) */
382 #define  BMSR_ACOMP	0x0020	/* Autonegotiation complete */
383 #define  BMSR_LINK	0x0004	/* Link status */
384 #define MII_ANAR	0x04	/* Autonegotiation advertisement (rw) */
385 #define  ANAR_FC	0x0400	/* local device supports PAUSE */
386 #define  ANAR_TX_FD	0x0100	/* local device supports 100bTx FD */
387 #define  ANAR_TX	0x0080	/* local device supports 100bTx */
388 #define  ANAR_10_FD	0x0040	/* local device supports 10bT FD */
389 #define  ANAR_10	0x0020	/* local device supports 10bT */
390 #define  ANAR_CSMA	0x0001	/* protocol selector CSMA/CD */
391 #define MII_ANLPAR	0x05	/* Autonegotiation lnk partner abilities (rw) */
392 
393 void
mii_dealan(struct local * l,unsigned timo)394 mii_dealan(struct local *l, unsigned timo)
395 {
396 	unsigned anar, bound;
397 
398 	anar = ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10 | ANAR_CSMA;
399 	mii_write(l, l->phy, MII_ANAR, anar);
400 	mii_write(l, l->phy, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
401 	l->anlpar = 0;
402 	bound = getsecs() + timo;
403 	do {
404 		l->bmsr = mii_read(l, l->phy, MII_BMSR) |
405 		   mii_read(l, l->phy, MII_BMSR); /* read twice */
406 		if ((l->bmsr & BMSR_LINK) && (l->bmsr & BMSR_ACOMP)) {
407 			l->anlpar = mii_read(l, l->phy, MII_ANLPAR);
408 			break;
409 		}
410 		DELAY(10 * 1000);
411 	} while (getsecs() < bound);
412 	return;
413 }
414