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