xref: /haiku/src/add-ons/kernel/drivers/network/ether/pegasus/if_aue.c (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
1 /*
2  * Pegasus BeOS Driver
3  *
4  * Copyright 2006, Haiku, Inc. All Rights Reserved.
5  * Distributed under the terms of the MIT License.
6  *
7  * Authors:
8  *		Jérôme Duval
9  */
10 
11 /*-
12  * Copyright (c) 1997, 1998, 1999, 2000
13  *	Bill Paul <wpaul@ee.columbia.edu>.  All rights reserved.
14  *
15  * Redistribution and use in source and binary forms, with or without
16  * modification, are permitted provided that the following conditions
17  * are met:
18  * 1. Redistributions of source code must retain the above copyright
19  *    notice, this list of conditions and the following disclaimer.
20  * 2. Redistributions in binary form must reproduce the above copyright
21  *    notice, this list of conditions and the following disclaimer in the
22  *    documentation and/or other materials provided with the distribution.
23  * 3. All advertising materials mentioning features or use of this software
24  *    must display the following acknowledgement:
25  *	This product includes software developed by Bill Paul.
26  * 4. Neither the name of the author nor the names of any co-contributors
27  *    may be used to endorse or promote products derived from this software
28  *    without specific prior written permission.
29  *
30  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
31  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
34  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
35  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
36  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
37  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
38  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
39  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
40  * THE POSSIBILITY OF SUCH DAMAGE.
41  */
42 
43 #include <string.h>
44 #include "driver.h"
45 #include "usbdevs.h"
46 
47 extern usb_module_info *usb;
48 
49 #define AUE_SETBIT(sc, reg, x)				\
50 	aue_csr_write_1(sc, reg, aue_csr_read_1(sc, reg) | (x))
51 
52 #define AUE_CLRBIT(sc, reg, x)				\
53 	aue_csr_write_1(sc, reg, aue_csr_read_1(sc, reg) & ~(x))
54 
55 static int
56 aue_csr_read_1(pegasus_dev *sc, int reg)
57 {
58 	status_t		err;
59 	uint8		val = 0;
60 	size_t		length;
61 
62 	if (sc->aue_dying)
63 		return (0);
64 
65 	AUE_LOCK(sc);
66 
67 	err = usb->send_request(sc->dev, USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN,
68 		AUE_UR_READREG, 0, reg, 1, &val, &length);
69 	AUE_UNLOCK(sc);
70 
71 	if (err) {
72 		return (0);
73 	}
74 
75 	return (val);
76 }
77 
78 static int
79 aue_csr_read_2(pegasus_dev *sc, int reg)
80 {
81 	status_t		err;
82 	uint16		val = 0;
83 	size_t		length;
84 
85 	if (sc->aue_dying)
86 		return (0);
87 
88 	AUE_LOCK(sc);
89 
90 	err = usb->send_request(sc->dev, USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN,
91 		AUE_UR_READREG, 0, reg, 2, &val, &length);
92 
93 	AUE_UNLOCK(sc);
94 
95 	if (err) {
96 		return (0);
97 	}
98 
99 	return (val);
100 }
101 
102 static int
103 aue_csr_write_1(pegasus_dev *sc, int reg, int val)
104 {
105 	status_t		err;
106 	size_t		length = 1;
107 
108 	if (sc->aue_dying)
109 		return (0);
110 
111 	AUE_LOCK(sc);
112 
113 	err = usb->send_request(sc->dev, USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
114 		AUE_UR_WRITEREG, val, reg, 1, &val, &length);
115 
116 	AUE_UNLOCK(sc);
117 
118 	if (err) {
119 		return (-1);
120 	}
121 
122 	return (0);
123 }
124 
125 static int
126 aue_csr_write_2(pegasus_dev *sc, int reg, int val)
127 {
128 	status_t		err;
129 	size_t			length = 2;
130 
131 	if (sc->aue_dying)
132 		return (0);
133 
134 	AUE_LOCK(sc);
135 
136 	err = usb->send_request(sc->dev, USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
137 		AUE_UR_WRITEREG, val, reg, 2, &val, &length);
138 
139 	AUE_UNLOCK(sc);
140 
141 	if (err) {
142 		return (-1);
143 	}
144 
145 	return (0);
146 }
147 
148 /*
149  * Read a word of data stored in the EEPROM at address 'addr.'
150  */
151 static void
152 aue_eeprom_getword(pegasus_dev *sc, int addr, u_int16_t *dest)
153 {
154 	int		i;
155 	u_int16_t	word = 0;
156 
157 	aue_csr_write_1(sc, AUE_EE_REG, addr);
158 	aue_csr_write_1(sc, AUE_EE_CTL, AUE_EECTL_READ);
159 
160 	for (i = 0; i < AUE_TIMEOUT; i++) {
161 		if (aue_csr_read_1(sc, AUE_EE_CTL) & AUE_EECTL_DONE)
162 			break;
163 	}
164 
165 	if (i == AUE_TIMEOUT) {
166 		dprintf("aue%d: EEPROM read timed out\n",
167 		    sc->aue_unit);
168 	}
169 
170 	word = aue_csr_read_2(sc, AUE_EE_DATA);
171 	*dest = word;
172 
173 	return;
174 }
175 
176 
177 
178 
179 /*
180  * Read a sequence of words from the EEPROM.
181  */
182 static void
183 aue_read_eeprom(pegasus_dev *sc, caddr_t dest, int off, int cnt, int swap)
184 {
185 	int			i;
186 	u_int16_t		word = 0, *ptr;
187 
188 	for (i = 0; i < cnt; i++) {
189 		aue_eeprom_getword(sc, off + i, &word);
190 		ptr = (u_int16_t *)(dest + (i * 2));
191 		if (swap)
192 			*ptr = B_BENDIAN_TO_HOST_INT16(word);
193 		else
194 			*ptr = word;
195 	}
196 
197 	return;
198 }
199 
200 
201 static int
202 aue_miibus_readreg(pegasus_dev *sc, int phy, int reg)
203 {
204 	int			i;
205 	u_int16_t		val = 0;
206 
207 	/*
208 	 * The Am79C901 HomePNA PHY actually contains
209 	 * two transceivers: a 1Mbps HomePNA PHY and a
210 	 * 10Mbps full/half duplex ethernet PHY with
211 	 * NWAY autoneg. However in the ADMtek adapter,
212 	 * only the 1Mbps PHY is actually connected to
213 	 * anything, so we ignore the 10Mbps one. It
214 	 * happens to be configured for MII address 3,
215 	 * so we filter that out.
216 	 */
217 	if (sc->aue_vendor == USB_VENDOR_ADMTEK &&
218 	    sc->aue_product == USB_PRODUCT_ADMTEK_PEGASUS) {
219 		if (phy == 3)
220 			return (0);
221 #ifdef notdef
222 		if (phy != 1)
223 			return (0);
224 #endif
225 	}
226 
227 	aue_csr_write_1(sc, AUE_PHY_ADDR, phy);
228 	aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_READ);
229 
230 	for (i = 0; i < AUE_TIMEOUT; i++) {
231 		if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE)
232 			break;
233 	}
234 
235 	if (i == AUE_TIMEOUT) {
236 		dprintf("aue%d: MII read timed out\n", sc->aue_unit);
237 	}
238 
239 	val = aue_csr_read_2(sc, AUE_PHY_DATA);
240 
241 	return (val);
242 }
243 
244 
245 static int
246 aue_miibus_writereg(pegasus_dev *sc, int phy, int reg, int data)
247 {
248 	int			i;
249 
250 	if (phy == 3)
251 		return (0);
252 
253 	aue_csr_write_2(sc, AUE_PHY_DATA, data);
254 	aue_csr_write_1(sc, AUE_PHY_ADDR, phy);
255 	aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_WRITE);
256 
257 	for (i = 0; i < AUE_TIMEOUT; i++) {
258 		if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE)
259 			break;
260 	}
261 
262 	if (i == AUE_TIMEOUT) {
263 		dprintf("aue%d: MII read timed out\n",
264 		    sc->aue_unit);
265 	}
266 
267 	return (0);
268 }
269 
270 
271 static uint16
272 aue_miibus_read(pegasus_dev *dev, uint16 reg)
273 {
274 	return aue_miibus_readreg(dev, dev->phy, reg);
275 }
276 
277 
278 static void
279 aue_miibus_write(pegasus_dev *dev, uint16 reg, uint16 value)
280 {
281 	aue_miibus_writereg(dev, dev->phy, reg, value);
282 }
283 
284 
285 static uint16
286 aue_miibus_status_from_phy(pegasus_dev *dev, uint16 phy)
287 {
288 	uint16 status;
289 	int i = 0;
290 
291 	// the status must be retrieved two times, because the first
292 	// one may not work on some PHYs (notably ICS 1893)
293 	while (i++ < 2)
294 		status = aue_miibus_readreg(dev, phy, MII_STATUS);
295 
296 	return status;
297 }
298 
299 
300 static uint16
301 aue_miibus_status(pegasus_dev *dev)
302 {
303 	return aue_miibus_status_from_phy(dev, dev->phy);
304 }
305 
306 
307 static status_t
308 aue_init_phy(pegasus_dev *dev)
309 {
310 	uint16 phy, status;
311 	int i;
312 
313 	dev->phy = 255;
314 
315 	// search for total of 32 possible MII PHY addresses
316 	for (phy = 0; phy < 32; phy++) {
317 		uint16 status;
318 
319 		status = aue_miibus_status_from_phy(dev, phy);
320 		if (status == 0xffff || status == 0x0000)
321 			// this MII is not accessable
322 			continue;
323 
324 		dev->phy = phy;
325 	}
326 
327 	if (dev->phy == 255) {
328 		DPRINTF_ERR("No MII PHY transceiver found!\n");
329 		return B_ENTRY_NOT_FOUND;
330 	}
331 	DPRINTF_INFO("aue_init_phy MII PHY found at %d\n", dev->phy);
332 
333 	status = aue_miibus_read(dev, MII_CONTROL);
334 	status &= ~MII_CONTROL_ISOLATE;
335 
336 	aue_miibus_write(dev, MII_CONTROL, status);
337 
338 	aue_miibus_write(dev, MII_CONTROL, MII_CONTROL_RESET);
339 	for (i = 0; i < 100; i++) {
340 		if ((aue_miibus_read(dev, MII_STATUS) & MII_CONTROL_RESET) == 0)
341 			break;
342 		DELAY(1000);
343 	}
344 
345 	dev->link = aue_miibus_status(dev) & MII_STATUS_LINK;
346 
347 	return B_OK;
348 }
349 
350 
351 static void
352 aue_reset_pegasus_II(pegasus_dev *sc)
353 {
354 	/* Magic constants taken from Linux driver. */
355 	aue_csr_write_1(sc, AUE_REG_1D, 0);
356 	aue_csr_write_1(sc, AUE_REG_7B, 2);
357 #if 0
358 	if ((sc->aue_flags & HAS_HOME_PNA) && mii_mode)
359 		aue_csr_write_1(sc, AUE_REG_81, 6);
360 	else
361 #endif
362 		aue_csr_write_1(sc, AUE_REG_81, 2);
363 }
364 
365 static void
366 aue_reset(pegasus_dev *sc)
367 {
368 	int		i;
369 
370 	AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_RESETMAC);
371 
372 	for (i = 0; i < AUE_TIMEOUT; i++) {
373 		if (!(aue_csr_read_1(sc, AUE_CTL1) & AUE_CTL1_RESETMAC))
374 			break;
375 	}
376 
377 	if (i == AUE_TIMEOUT)
378 		dprintf("aue%d: reset failed\n", sc->aue_unit);
379 
380 	/*
381 	 * The PHY(s) attached to the Pegasus chip may be held
382 	 * in reset until we flip on the GPIO outputs. Make sure
383 	 * to set the GPIO pins high so that the PHY(s) will
384 	 * be enabled.
385 	 *
386 	 * Note: We force all of the GPIO pins low first, *then*
387 	 * enable the ones we want.
388 	 */
389 	aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_OUT0|AUE_GPIO_SEL0);
390 	aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_OUT0|AUE_GPIO_SEL0|AUE_GPIO_SEL1);
391 
392 	if (sc->aue_flags & LSYS) {
393 		/* Grrr. LinkSys has to be different from everyone else. */
394 		aue_csr_write_1(sc, AUE_GPIO0,
395 			AUE_GPIO_SEL0 | AUE_GPIO_SEL1);
396 		aue_csr_write_1(sc, AUE_GPIO0,
397 			AUE_GPIO_SEL0 | AUE_GPIO_SEL1 | AUE_GPIO_OUT0);
398 	}
399 
400 	if (sc->aue_flags & PII)
401 		aue_reset_pegasus_II(sc);
402 
403 	/* Wait a little while for the chip to get its brains in order. */
404 	DELAY(10000);
405 
406 	return;
407 }
408 
409 
410 /*
411  * Attach
412  */
413 void
414 aue_attach(pegasus_dev *sc)
415 {
416 	u_char		eaddr[ETHER_ADDRESS_LENGTH];
417 
418 	AUE_LOCK(sc);
419 
420 	/* Reset the adapter. */
421 	aue_reset(sc);
422 
423 	/*
424 	 * Get station address from the EEPROM.
425 	 */
426 	aue_read_eeprom(sc, (caddr_t)&eaddr, 0, 3, 0);
427 
428 	memcpy(sc->macaddr, eaddr, ETHER_ADDRESS_LENGTH);
429 
430 	if (aue_init_phy(sc) != B_OK)
431 		goto done;
432 
433 	sc->aue_dying = 0;
434 
435 done:
436 	AUE_UNLOCK(sc);
437 }
438 
439 
440 void
441 aue_init(pegasus_dev *sc)
442 {
443 
444 	/* Enable RX and TX */
445 	aue_csr_write_1(sc, AUE_CTL0, AUE_CTL0_RXSTAT_APPEND | AUE_CTL0_RX_ENB);
446 	AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_TX_ENB);
447 	AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_EP3_CLR);
448 
449 }
450 
451 
452 void
453 aue_uninit(pegasus_dev *sc)
454 {
455 	aue_csr_write_1(sc, AUE_CTL0, 0);
456 	aue_csr_write_1(sc, AUE_CTL1, 0);
457 	aue_reset(sc);
458 }
459