1/* ********************************************************************* 2 * Broadcom Common Firmware Environment (CFE) 3 * 4 * USB Ethernet File: dev_usb_pegasus.c 5 * 6 * Driver for USB Ethernet devices using ADMTEK Pegasus chip. 7 * 8 ********************************************************************* 9 * 10 * Copyright 2000,2001,2002,2003,2005 11 * Broadcom Corporation. All rights reserved. 12 * 13 * This software is furnished under license and may be used and 14 * copied only in accordance with the following terms and 15 * conditions. Subject to these conditions, you may download, 16 * copy, install, use, modify and distribute modified or unmodified 17 * copies of this software in source and/or binary form. No title 18 * or ownership is transferred hereby. 19 * 20 * 1) Any source code used, modified or distributed must reproduce 21 * and retain this copyright notice and list of conditions 22 * as they appear in the source file. 23 * 24 * 2) No right is granted to use any trade name, trademark, or 25 * logo of Broadcom Corporation. The "Broadcom Corporation" 26 * name may not be used to endorse or promote products derived 27 * from this software without the prior written permission of 28 * Broadcom Corporation. 29 * 30 * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR 31 * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED 32 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 33 * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT 34 * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN 35 * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, 36 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 37 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 38 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 39 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 40 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 41 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF 42 * THE POSSIBILITY OF SUCH DAMAGE. 43 ********************************************************************* */ 44 45/* ********************************************************************* 46 * USB-Ethernet driver - CFE Network Layer Interfaces 47 ********************************************************************* */ 48 49#include "cfe.h" 50 51#include "usbd.h" 52#include "usbeth.h" 53 54#if 0 55#define USBETH_TRACE( x, y ... ) xprintf( x, ##y ) 56#else 57#define USBETH_TRACE( x, y ... ) ((void)0) 58#endif 59 60#define FAIL -1 61 62#define CACHE_ALIGN 32 /* XXX place holder, big enough to now. */ 63#define ALIGN(n,align) (((n)+((align)-1)) & ~((align)-1)) 64 65#define usb_dma_alloc(n) (KMALLOC(ALIGN((n),CACHE_ALIGN),CACHE_ALIGN)) 66#define usb_dma_free(p) (KFREE(p)) 67 68/****************************************************************************** 69 Debug functions 70******************************************************************************/ 71 72#ifndef USBETH_DEBUG 73#define USBETH_DEBUG 0 74#endif 75 76#if USBETH_DEBUG 77static void hexdump( unsigned char * src, int srclen, int rowlen, int rows ) 78{ 79 unsigned char *rowptr; 80 unsigned char *srcstp; 81 unsigned char *byteptr; 82 83 srcstp = src + srclen; 84 85 for( rowptr = src; rowptr < src + rowlen * rows; rowptr += rowlen ) { 86 for( byteptr = rowptr; byteptr < rowptr + rowlen && byteptr < srcstp; byteptr++ ) { 87 xprintf( "%2X ", *byteptr ); 88 } 89 xprintf( "\n" ); 90 } 91 xprintf( "\n" ); 92} 93#else 94#define hexdump(src,srclen,rowlen,rows) ((void)0) 95#endif 96 97 98/* ********************************************************************* 99 * Interface functions for USB-Ethernet adapters 100 ********************************************************************* */ 101 102enum { PEGASUS, PEGASUS_II}; 103enum { VEN_NONE, _3_COM, LINKSYS, LINKSYS_10, LINKSYS_100, LINKSYS_100M }; 104static const char *VENDOR_NAMES[] = { 105 "?", "3-COM", "LinkSys", "LinkSys-10TX", "LinkSys-100TX", "Yikes!" 106}; 107 108static const int ID_TBL[] = { 109 0x0506, 0x4601, PEGASUS_II, _3_COM, /* 3-Com */ 110 0x066b, 0x2202, PEGASUS_II, LINKSYS_10, /* LinkSys */ 111 0x066b, 0x2203, PEGASUS, LINKSYS_100, 112 0x066b, 0x2204, PEGASUS, LINKSYS_100, 113 0x066b, 0x2206, PEGASUS, LINKSYS, 114 0x066b, 0x400b, PEGASUS_II, LINKSYS_10, 115 0x066b, 0x200c, PEGASUS_II, LINKSYS_10, 116 -1 117}; 118 119typedef struct peg_softc_s { 120 usbdev_t *dev; 121 int bulk_inpipe; 122 int bulk_outpipe; 123 int dev_id; 124 int ven_code; 125 uint8_t mac_addr[6]; 126 usbreq_t *rx_ur; 127 uint8_t rxbuf[1600]; /* arbitrary but enough for ethernet packet */ 128} peg_softc_t; 129 130 131/* ************************************** 132 * ADMTEK PEGASUS I/F Functions 133 ************************************** */ 134 135static int peg_send_eth_frame( void *ctx, hsaddr_t buf, int len ); 136static int peg_get_eth_frame( void *ctx, hsaddr_t buf ); 137static int peg_data_rx( void *ctx ); 138static int peg_get_dev_addr( void *ctx, hsaddr_t mac_addr ); 139 140static usbeth_disp_t usbeth_peg = { 141 peg_get_eth_frame, 142 peg_data_rx, 143 peg_send_eth_frame, 144 peg_get_dev_addr 145}; 146 147 148static int peg_get_reg( usbdev_t *dev, int16_t reg, uint8_t *val, int16_t len ) 149{ 150 return usb_std_request( dev, (USBREQ_TYPE_VENDOR | USBREQ_DIR_IN), 151 PEG_GET_REG, 0, reg, val, len ); 152} 153 154static int peg_set_reg( usbdev_t *dev, int16_t reg, int16_t val ) 155{ 156 unsigned char data = (uint8_t) val & 0xff; 157 158 return usb_std_request( dev, (USBREQ_TYPE_VENDOR | USBREQ_DIR_OUT), 159 PEG_SET_REG, val, reg, &data, 1 ); 160} 161 162static int peg_set_regs( usbdev_t *dev, int16_t reg, int8_t *vals, int16_t len ) 163{ 164 return usb_std_request( dev, (USBREQ_TYPE_VENDOR | USBREQ_DIR_OUT), 165 PEG_SET_REG, 0, reg, (uint8_t *)vals, len ); 166} 167 168static int peg_get_eep_word( usbdev_t *dev, int16_t ofs, uint8_t *val ) 169{ 170 int status=0, tries=20; 171 uint8_t data[2]; 172 173 if( peg_set_reg( dev, R_PEG_EEPROM_CTL, 0 ) == FAIL ) 174 return FAIL; 175 if( peg_set_reg( dev, R_PEG_EEPROM_OFS, ofs ) == FAIL ) 176 return FAIL; 177 if( peg_set_reg( dev, R_PEG_EEPROM_CTL, 0x02 ) == FAIL ) /* read */ 178 return FAIL; 179 while( --tries ) { 180 if( peg_get_reg( dev, R_PEG_EEPROM_CTL, data, 1 ) == FAIL ) 181 return FAIL; 182 if( data[0] & 0x04 ) 183 break; /* eeprom data ready */ 184 } 185 if( !tries ) { 186 xprintf( "Pegasus Eeprom read failed!\n" ); 187 return FAIL; 188 } 189 if( peg_get_reg( dev, R_PEG_EEPROM_DATA, data, 2 ) == FAIL ) 190 return FAIL; 191 val[0] = data[0]; 192 val[1] = data[1]; 193 194 return( status ); 195} 196 197static int peg_get_mac_addr( usbdev_t *dev, uint8_t *mac_addr ) 198{ 199 int i, status; 200 201 for( i = 0; i < 3; ++i ) { 202 status = peg_get_eep_word( dev, i, &mac_addr[i*2] ); 203 } 204 return( status ); 205} 206 207static void peg_init_phy( usbdev_t *dev ) 208{ 209 /* needed for earlier versions (before Rev B) of the USB-100TX adapters */ 210 static uint8_t phy_magic_wr[] = { 0, 4, 0, 0x1b }; 211 static uint8_t read_status[] = { 0, 0, 0, 1 }; 212 uint8_t data[4]; 213 214 /* reset the MAC and set up GPIOs */ 215 peg_set_reg( dev, R_PEG_ETH_CTL1, 0x08 ); 216 peg_get_reg( dev, R_PEG_ETH_CTL1, data, 1 ); 217 218 /* do following steps to enable link activitiy LED */ 219 peg_set_reg( dev, R_PEG_GPIO1, 0x26 ); 220 peg_set_reg( dev, R_PEG_GPIO0, 0x24 ); 221 peg_set_reg( dev, R_PEG_GPIO0, 0x26 ); 222 223 /* do following set of steps to enable LINK LED */ 224 memcpy( data, phy_magic_wr, 4 ); 225 peg_set_regs( dev, R_PEG_PHY_ADDR, (int8_t *)data, 4); /* magic word */ 226 peg_set_reg( dev, R_PEG_PHY_CTRL, (0x1b | PEG_PHY_WRITE) ); 227 peg_get_reg( dev, R_PEG_PHY_CTRL, data, 1 ); /* status of write */ 228 memcpy( data, read_status, 4 ); 229 peg_set_regs( dev, R_PEG_PHY_ADDR, (int8_t *)data, 4); /* phy status reg */ 230 peg_set_reg( dev, R_PEG_PHY_CTRL, (1 | PEG_PHY_READ) ); 231 peg_get_reg( dev, R_PEG_PHY_CTRL, data, 1 ); /* status of read */ 232 peg_get_reg( dev, R_PEG_PHY_DATA, data, 2 ); /* read status regs */ 233} 234 235static int peg_init_device( peg_softc_t * softc ) 236{ 237 usb_device_descr_t dev_desc; 238 uint16_t vendor_id, product_id; 239 const int *ptr=ID_TBL; 240 usbdev_t *dev = softc->dev; 241 242 /* find out which device is connected */ 243 usb_get_device_descriptor( softc->dev, &dev_desc, 0 ); 244 vendor_id = (dev_desc.idVendorHigh << 8) + dev_desc.idVendorLow; 245 product_id = (dev_desc.idProductHigh << 8) + dev_desc.idProductLow; 246 247 while( *ptr != -1 ) { 248 if( (vendor_id == ptr[0]) && (product_id == ptr[1]) ) { 249 softc->dev_id = ptr[2]; 250 softc->ven_code = ptr[3]; 251 break; 252 } 253 ptr += 4; 254 } 255 if( *ptr == -1 ) { 256 xprintf( "Unrecognized ADMTEK USB-Ethernet device\n" ); 257 return -1; 258 } 259 260 /* init the adapter */ 261 if( softc->dev_id == PEGASUS_II ) 262 peg_set_reg( dev, R_PEG_INT_PHY, 0x02 ); /* enable internal PHY */ 263 else 264 peg_init_phy( dev ); 265 266 /* Read the adapter's MAC addr */ 267 peg_get_mac_addr( dev, softc->mac_addr ); 268 269 /* display adapter info */ 270 xprintf( "%s USB-Ethernet Adapter (%a)\n", 271 VENDOR_NAMES[softc->ven_code], softc->mac_addr); 272 273 return 0; 274} 275 276static int peg_get_dev_addr( void *ctx, hsaddr_t mac_addr ) 277{ 278 peg_softc_t *softc = (peg_softc_t *) ctx; 279 hs_memcpy_to_hs( mac_addr, softc->mac_addr, 6 ); 280 return 0; 281} 282 283static void peg_queue_rx( peg_softc_t *softc ) 284{ 285 softc->rx_ur = usb_make_request(softc->dev, softc->bulk_inpipe, 286 softc->rxbuf, sizeof(softc->rxbuf), 287 (UR_FLAG_IN | UR_FLAG_SHORTOK)); 288 usb_queue_request(softc->rx_ur); 289} 290 291static int peg_data_rx( void *ctx ) 292{ 293 peg_softc_t *softc = (peg_softc_t *) ctx; 294 usb_poll(softc->dev->ud_bus); 295 return( !softc->rx_ur->ur_inprogress ); 296} 297 298static int peg_get_eth_frame( void *ctx, hsaddr_t buf ) 299{ 300 int len = 0; 301 peg_softc_t *softc = (peg_softc_t *) ctx; 302 uint8_t *rxbuf; 303 304 if( !softc->rx_ur->ur_inprogress ) { 305 rxbuf = softc->rxbuf; 306 len = softc->rx_ur->ur_xferred; 307 if (len > 0) { 308#if USBETH_DEBUG 309 xprintf( "Incoming packet :\n" ); 310 hexdump( rxbuf, len, 16, len / 16 + 1 ); 311#endif 312 hs_memcpy_to_hs( buf, rxbuf, len ); 313 } 314 usb_free_request(softc->rx_ur); 315 peg_queue_rx( softc ); 316 } 317 else 318 xprintf( "Bulk data is not available yet!\n" ); 319 320 return( len ); 321} 322 323static int peg_send_eth_frame( void *ctx, hsaddr_t buf, int len ) 324{ 325 peg_softc_t *softc = (peg_softc_t *) ctx; 326 usbreq_t *ur; 327 int txlen = len; 328 unsigned char *txbuf; 329 330 txbuf = usb_dma_alloc(len+2); 331 txbuf[0] = txlen & 0xff; 332 txbuf[1] = (txlen >> 8) & 0xff; /* 1st two bytes...little endian */ 333 hs_memcpy_from_hs( &txbuf[2], buf, txlen ); 334 txlen += 2; 335#if USBETH_DEBUG 336 xprintf( "Outgoing packet :\n" ); 337 hexdump( txbuf, txlen, 16, txlen / 16 + 1 ); 338#endif 339 ur = usb_make_request(softc->dev, softc->bulk_outpipe, 340 txbuf, txlen, UR_FLAG_OUT); 341 usb_sync_request(ur); 342 usb_free_request(ur); 343 usb_dma_free(txbuf); 344 345 return( len ); 346} 347 348 349static void peg_open_device( peg_softc_t *softc ) 350{ 351 usbdev_t *dev = softc->dev; 352 353 /* setup adapter's receiver with MAC address */ 354 peg_set_regs( dev, R_PEG_MAC_ADDR_0, (int8_t *)softc->mac_addr, 6 ); 355 356 /* enable adapter to receive and transmit packets */ 357 peg_set_reg( dev, R_PEG_ETH_CTL0, 0xc1 ); 358 peg_set_reg( dev, R_PEG_ETH_CTL1, 0x30 ); 359 360 /* kick start the receive */ 361 peg_queue_rx( softc ); 362} 363 364static void peg_close_device( peg_softc_t *softc ) 365{ 366 usbdev_t *dev = softc->dev; 367 368 /* Disable adapter from receiving or transmitting packets */ 369 peg_set_reg( dev, R_PEG_ETH_CTL1, 0 ); 370} 371 372 373/* ********************************************************************* 374 * CFE-USB interfaces 375 ********************************************************************* */ 376 377/* ********************************************************************* 378 * peg_attach(dev,drv) 379 * 380 * This routine is called when the bus scan stuff finds a usb-ethernet 381 * device. We finish up the initialization by configuring the 382 * device and allocating our softc here. 383 * 384 * Input parameters: 385 * dev - usb device, in the "addressed" state. 386 * drv - the driver table entry that matched 387 * 388 * Return value: 389 * 0 390 ********************************************************************* */ 391 392const cfe_driver_t usbpegdrv; /* forward declaration */ 393 394static int peg_attach(usbdev_t *dev, usb_driver_t *drv) 395{ 396 usb_config_descr_t *cfgdscr = dev->ud_cfgdescr; 397 usb_endpoint_descr_t *epdscr; 398 usb_endpoint_descr_t *indscr = NULL; 399 usb_endpoint_descr_t *outdscr = NULL; 400 usb_interface_descr_t *ifdscr; 401 peg_softc_t *softc; 402 int idx; 403 404 dev->ud_drv = drv; 405 406 softc = (peg_softc_t *) KMALLOC( sizeof(peg_softc_t), 0 ); 407 if( softc == NULL ) { 408 xprintf( "Failed to allocate softc memory.\n" ); 409 return -1; 410 } 411 memset( softc, 0, sizeof(peg_softc_t) ); 412 dev->ud_private = softc; 413 softc->dev = dev; 414 415 ifdscr = usb_find_cfg_descr(dev,USB_INTERFACE_DESCRIPTOR_TYPE,0); 416 if (ifdscr == NULL) { 417 xprintf("USBETH: ERROR...no interace descriptor\n"); 418 return -1; 419 } 420 421 for (idx = 0; idx < 2; idx++) { 422 epdscr = usb_find_cfg_descr(dev,USB_ENDPOINT_DESCRIPTOR_TYPE,idx); 423 if (USB_ENDPOINT_DIR_OUT(epdscr->bEndpointAddress)) 424 outdscr = epdscr; 425 else 426 indscr = epdscr; 427 } 428 429 if (!indscr || !outdscr) { 430 /* 431 * Could not get descriptors, something is very wrong. 432 * Leave device addressed but not configured. 433 */ 434 xprintf("USBETH: ERROR...no endpoint descriptors\n"); 435 return -1; 436 } 437 438 /* Choose the standard configuration. */ 439 usb_set_configuration(dev,cfgdscr->bConfigurationValue); 440 441 /* Quit if not able to initialize the device */ 442 if (peg_init_device(softc) < 0) 443 return -1; 444 445 /* Open the pipes. */ 446 softc->bulk_inpipe = usb_open_pipe(dev,indscr); 447 softc->bulk_outpipe = usb_open_pipe(dev,outdscr); 448 449 /* Register the device */ 450 usbeth_register(&usbeth_peg,softc); 451 452 /* Open the device */ 453 peg_open_device( softc ); 454 455 return 0; 456} 457 458/* ********************************************************************* 459 * peg_detach(dev) 460 * 461 * This routine is called when the bus scanner notices that 462 * this device has been removed from the system. We should 463 * do any cleanup that is required. The pending requests 464 * will be cancelled automagically. 465 * 466 * Input parameters: 467 * dev - usb device 468 * 469 * Return value: 470 * 0 471 ********************************************************************* */ 472 473static int peg_detach(usbdev_t *dev) 474{ 475 peg_softc_t *softc = (peg_softc_t *) dev->ud_private; 476 477 if (softc != NULL) { 478 usbeth_unregister( softc ); 479 peg_close_device ( softc ); 480 dev->ud_private = NULL; 481 softc->dev = NULL; 482 KFREE(softc); 483 } 484 485 return 0; 486} 487 488/* CFE USB device interface structure */ 489usb_driver_t usbpeg_driver = 490{ 491 "Ethernet Device", 492 peg_attach, 493 peg_detach 494}; 495 496 497