BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / kernel / drivers / network / pegasus / if_aue.c
blob4f8432b1391bb828765428b37dce8496b08e5185
1 /*
2 * Pegasus BeOS Driver
3 *
4 * Copyright 2006, Haiku, Inc. All Rights Reserved.
5 * Distributed under the terms of the MIT License.
7 * Authors:
8 * Jérôme Duval
9 */
11 /*-
12 * Copyright (c) 1997, 1998, 1999, 2000
13 * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
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.
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.
43 #include <string.h>
44 #include "driver.h"
45 #include "usbdevs.h"
47 extern usb_module_info *usb;
49 #define AUE_SETBIT(sc, reg, x) \
50 aue_csr_write_1(sc, reg, aue_csr_read_1(sc, reg) | (x))
52 #define AUE_CLRBIT(sc, reg, x) \
53 aue_csr_write_1(sc, reg, aue_csr_read_1(sc, reg) & ~(x))
55 static int
56 aue_csr_read_1(pegasus_dev *sc, int reg)
58 status_t err;
59 uint8 val = 0;
60 size_t length;
62 if (sc->aue_dying)
63 return (0);
65 AUE_LOCK(sc);
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);
71 if (err) {
72 return (0);
75 return (val);
78 static int
79 aue_csr_read_2(pegasus_dev *sc, int reg)
81 status_t err;
82 uint16 val = 0;
83 size_t length;
85 if (sc->aue_dying)
86 return (0);
88 AUE_LOCK(sc);
90 err = usb->send_request(sc->dev, USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN,
91 AUE_UR_READREG, 0, reg, 2, &val, &length);
93 AUE_UNLOCK(sc);
95 if (err) {
96 return (0);
99 return (val);
102 static int
103 aue_csr_write_1(pegasus_dev *sc, int reg, int val)
105 status_t err;
106 size_t length = 1;
108 if (sc->aue_dying)
109 return (0);
111 AUE_LOCK(sc);
113 err = usb->send_request(sc->dev, USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
114 AUE_UR_WRITEREG, val, reg, 1, &val, &length);
116 AUE_UNLOCK(sc);
118 if (err) {
119 return (-1);
122 return (0);
125 static int
126 aue_csr_write_2(pegasus_dev *sc, int reg, int val)
128 status_t err;
129 size_t length = 2;
131 if (sc->aue_dying)
132 return (0);
134 AUE_LOCK(sc);
136 err = usb->send_request(sc->dev, USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
137 AUE_UR_WRITEREG, val, reg, 2, &val, &length);
139 AUE_UNLOCK(sc);
141 if (err) {
142 return (-1);
145 return (0);
149 * Read a word of data stored in the EEPROM at address 'addr.'
151 static void
152 aue_eeprom_getword(pegasus_dev *sc, int addr, u_int16_t *dest)
154 int i;
155 u_int16_t word = 0;
157 aue_csr_write_1(sc, AUE_EE_REG, addr);
158 aue_csr_write_1(sc, AUE_EE_CTL, AUE_EECTL_READ);
160 for (i = 0; i < AUE_TIMEOUT; i++) {
161 if (aue_csr_read_1(sc, AUE_EE_CTL) & AUE_EECTL_DONE)
162 break;
165 if (i == AUE_TIMEOUT) {
166 dprintf("aue%d: EEPROM read timed out\n",
167 sc->aue_unit);
170 word = aue_csr_read_2(sc, AUE_EE_DATA);
171 *dest = word;
173 return;
180 * Read a sequence of words from the EEPROM.
182 static void
183 aue_read_eeprom(pegasus_dev *sc, caddr_t dest, int off, int cnt, int swap)
185 int i;
186 u_int16_t word = 0, *ptr;
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 = ntohs(word);
193 else
194 *ptr = word;
197 return;
201 static int
202 aue_miibus_readreg(pegasus_dev *sc, int phy, int reg)
204 int i;
205 u_int16_t val = 0;
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.
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
227 aue_csr_write_1(sc, AUE_PHY_ADDR, phy);
228 aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_READ);
230 for (i = 0; i < AUE_TIMEOUT; i++) {
231 if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE)
232 break;
235 if (i == AUE_TIMEOUT) {
236 dprintf("aue%d: MII read timed out\n", sc->aue_unit);
239 val = aue_csr_read_2(sc, AUE_PHY_DATA);
241 return (val);
245 static int
246 aue_miibus_writereg(pegasus_dev *sc, int phy, int reg, int data)
248 int i;
250 if (phy == 3)
251 return (0);
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);
257 for (i = 0; i < AUE_TIMEOUT; i++) {
258 if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE)
259 break;
262 if (i == AUE_TIMEOUT) {
263 dprintf("aue%d: MII read timed out\n",
264 sc->aue_unit);
267 return (0);
271 static uint16
272 aue_miibus_read(pegasus_dev *dev, uint16 reg)
274 return aue_miibus_readreg(dev, dev->phy, reg);
278 static void
279 aue_miibus_write(pegasus_dev *dev, uint16 reg, uint16 value)
281 aue_miibus_writereg(dev, dev->phy, reg, value);
285 static uint16
286 aue_miibus_status_from_phy(pegasus_dev *dev, uint16 phy)
288 uint16 status;
289 int i = 0;
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);
296 return status;
300 static uint16
301 aue_miibus_status(pegasus_dev *dev)
303 return aue_miibus_status_from_phy(dev, dev->phy);
307 static status_t
308 aue_init_phy(pegasus_dev *dev)
310 uint16 phy, status;
311 int i;
313 dev->phy = 255;
315 // search for total of 32 possible MII PHY addresses
316 for (phy = 0; phy < 32; phy++) {
317 uint16 status;
319 status = aue_miibus_status_from_phy(dev, phy);
320 if (status == 0xffff || status == 0x0000)
321 // this MII is not accessable
322 continue;
324 dev->phy = phy;
327 if (dev->phy == 255) {
328 DPRINTF_ERR("No MII PHY transceiver found!\n");
329 return B_ENTRY_NOT_FOUND;
331 DPRINTF_INFO("aue_init_phy MII PHY found at %d\n", dev->phy);
333 status = aue_miibus_read(dev, MII_CONTROL);
334 status &= ~MII_CONTROL_ISOLATE;
336 aue_miibus_write(dev, MII_CONTROL, status);
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);
345 dev->link = aue_miibus_status(dev) & MII_STATUS_LINK;
347 return B_OK;
351 static void
352 aue_reset_pegasus_II(pegasus_dev *sc)
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);
365 static void
366 aue_reset(pegasus_dev *sc)
368 int i;
370 AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_RESETMAC);
372 for (i = 0; i < AUE_TIMEOUT; i++) {
373 if (!(aue_csr_read_1(sc, AUE_CTL1) & AUE_CTL1_RESETMAC))
374 break;
377 if (i == AUE_TIMEOUT)
378 dprintf("aue%d: reset failed\n", sc->aue_unit);
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.
386 * Note: We force all of the GPIO pins low first, *then*
387 * enable the ones we want.
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);
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);
400 if (sc->aue_flags & PII)
401 aue_reset_pegasus_II(sc);
403 /* Wait a little while for the chip to get its brains in order. */
404 DELAY(10000);
406 return;
411 * Attach
413 void
414 aue_attach(pegasus_dev *sc)
416 u_char eaddr[ETHER_ADDRESS_LENGTH];
418 AUE_LOCK(sc);
420 /* Reset the adapter. */
421 aue_reset(sc);
424 * Get station address from the EEPROM.
426 aue_read_eeprom(sc, (caddr_t)&eaddr, 0, 3, 0);
428 memcpy(sc->macaddr, eaddr, ETHER_ADDRESS_LENGTH);
430 if (aue_init_phy(sc) != B_OK)
431 goto done;
433 sc->aue_dying = 0;
435 done:
436 AUE_UNLOCK(sc);
440 void
441 aue_init(pegasus_dev *sc)
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);
452 void
453 aue_uninit(pegasus_dev *sc)
455 aue_csr_write_1(sc, AUE_CTL0, 0);
456 aue_csr_write_1(sc, AUE_CTL1, 0);
457 aue_reset(sc);