1/*  *********************************************************************
2    *  Broadcom Common Firmware Environment (CFE)
3    *
4    *  USB Ethernet				File: dev_usb_asix.c
5    *
6    *  Driver for USB Ethernet devices using the ASIX AX8817 chip.
7    *
8    *********************************************************************
9    *
10    *  Copyright 2000,2001,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 as
22    *     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. Neither the "Broadcom
26    *     Corporation" name nor any trademark or logo of Broadcom
27    *     Corporation may be used to endorse or promote products
28    *     derived from this software without the prior written
29    *     permission of Broadcom Corporation.
30    *
31    *  3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
32    *     IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
33    *     WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
34    *     PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
35    *     SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
36    *     PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
37    *     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38    *     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
39    *     GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
40    *     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
41    *     OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
42    *     TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
43    *     THE POSSIBILITY OF SUCH DAMAGE.
44    ********************************************************************* */
45
46/*  *********************************************************************
47    *  USB-Ethernet driver - CFE Network Layer Interfaces
48    ********************************************************************* */
49
50#include "cfe.h"
51
52#include "usbd.h"
53#include "usbeth.h"
54
55/* XXX Move to usbeth.h */
56/* **************************************
57   *  ASIX AX8817 adapter
58   ************************************** */
59
60#define ASIX_MII_SWOP_CMD	0x06
61#define ASIX_MII_READ_CMD	0x07
62#define ASIX_MII_WRITE_CMD	0x08
63#define ASIX_MII_HWOP_CMD	0x0a
64#define ASIX_RXCTL_CMD		0x10
65#define ASIX_IPG1_CMD		0x12
66#define ASIX_IPG2_CMD		0x13
67#define ASIX_IPG3_CMD		0x14
68#define ASIX_MAC_ADDR_CMD	0x17
69#define ASIX_PHYID_CMD		0x19
70#define ASIX_MED_WRITE_CMD	0x1b
71#define ASIX_GPIO_WRITE_CMD	0x1f
72
73
74#if 0
75#define USBETH_TRACE( x, y ... ) xprintf( x, ##y )
76#else
77#define USBETH_TRACE( x, y ... )
78#endif
79
80#define FAIL				-1
81
82#define USB_MALLOC_VALUE 32
83
84
85/******************************************************************************
86  Debug functions
87******************************************************************************/
88
89#ifndef USBETH_DEBUG
90#define USBETH_DEBUG 0
91#endif
92
93
94#if USBETH_DEBUG
95static void hexdump( unsigned char *src, int srclen, int rowlen, int rows )
96{
97    unsigned char *rowptr;
98    unsigned char *srcstp;
99    unsigned char *byteptr;
100
101    srcstp = src + srclen;
102
103    for( rowptr = src; rowptr < src + rowlen * rows; rowptr += rowlen ) {
104	for( byteptr = rowptr; byteptr < rowptr + rowlen && byteptr < srcstp; byteptr++ ) {
105	    xprintf( "%2X ", *byteptr );
106	    }
107	xprintf( "\n" );
108	}
109    xprintf( "\n" );
110}
111#else
112#define hexdump(src,srclen,rowlen,rows) ((void)0)
113#endif
114
115
116/*  *********************************************************************
117    * Interface functions for USB-Ethernet adapters
118    ********************************************************************* */
119
120enum { VEN_NONE, HAWKING, NETGEAR };
121static char *VENDOR_NAMES[] = {
122    "?", "Hawking", "Netgear", "Yikes!"
123};
124
125static const int ID_TBL[] = {
126    0x0846, 0x1040, NETGEAR,        /* Netgear FA120 */
127    0x07b8, 0x420a, HAWKING,        /* Hawking UF200 */
128    -1
129};
130
131typedef struct asix_softc_s
132{
133    usbdev_t *dev;
134    int bulk_inpipe;
135    int bulk_outpipe;
136    int dev_id;
137    int ven_code;
138    uint8_t mac_addr[6];
139    usbreq_t *rx_ur;
140    uint8_t rxbuf[1600];	/* artbitrary but enough for ethernet packet */
141} asix_softc_t;
142
143
144/* **************************************
145   *  ASIX AX8817x I/F Functions
146   ************************************** */
147
148static int asix_send_eth_frame( void *ctx, hsaddr_t buf, int len );
149static int asix_get_eth_frame( void *ctx, hsaddr_t buf );
150static int asix_data_rx( void *ctx );
151static int asix_get_dev_addr( void *ctx, hsaddr_t mac_addr );
152
153static usbeth_disp_t usbeth_asix = {
154    asix_get_eth_frame,
155    asix_data_rx,
156    asix_send_eth_frame,
157    asix_get_dev_addr
158};
159
160
161static int asix_get_reg( usbdev_t *dev, uint8_t cmd, int16_t val, int16_t index, uint8_t *buf, int16_t len )
162{
163    return usb_std_request( dev, (USBREQ_TYPE_VENDOR | USBREQ_DIR_IN),
164			    cmd, val, index, buf, len );
165}
166
167static int asix_set_reg( usbdev_t *dev, uint8_t cmd, int16_t val, int16_t index, uint8_t *buf, int16_t len )
168{
169    return usb_std_request( dev, (USBREQ_TYPE_VENDOR | USBREQ_DIR_OUT),
170			    cmd, val, index, buf, len );
171}
172
173static int asix_get_mac_addr( usbdev_t *dev, uint8_t *mac_addr )
174{
175    return asix_get_reg( dev, ASIX_MAC_ADDR_CMD, 0, 0, mac_addr, 6 );
176}
177
178static int asix_init_device( asix_softc_t *softc )
179{
180    usb_device_descr_t dev_desc;
181    uint16_t vendor_id, device_id;
182    const int *ptr=ID_TBL;
183
184    /* find out which device is connected */
185    usb_get_device_descriptor( softc->dev, &dev_desc, 0 );
186    vendor_id = (dev_desc.idVendorHigh  << 8) + dev_desc.idVendorLow;
187    device_id = (dev_desc.idProductHigh << 8) + dev_desc.idProductLow;
188    xprintf( "USB device:  vendor id %04x, device id %04x\n",
189	     vendor_id, device_id );
190
191    while( *ptr != -1 ) {
192	if( (vendor_id == ptr[0]) && (device_id == ptr[1]) ) {
193	    softc->dev_id = ptr[2];
194	    softc->ven_code = ptr[3];
195	    break;
196	    }
197	ptr += 4;
198	}
199    if( *ptr == -1 ) {
200	xprintf( "Unrecognized USB-Ethernet device\n" );
201	return -1;
202	}
203
204    /* Read the adapter's MAC addr */
205    asix_get_mac_addr( softc->dev, softc->mac_addr );
206
207    /* display adapter info */
208    xprintf( "%s USB-Ethernet Adapter (%a)\n",
209	     VENDOR_NAMES[softc->ven_code], softc->mac_addr);
210
211    return 0;
212}
213
214static int asix_get_dev_addr( void *ctx, hsaddr_t mac_addr )
215{
216    asix_softc_t *softc = (asix_softc_t *) ctx;
217
218    hs_memcpy_to_hs( mac_addr, softc->mac_addr, 6 );
219    return 0;
220}
221
222static void asix_queue_rx( asix_softc_t *softc )
223{
224    softc->rx_ur = usb_make_request(softc->dev, softc->bulk_inpipe,
225				    softc->rxbuf, sizeof(softc->rxbuf),
226				    (UR_FLAG_IN | UR_FLAG_SHORTOK));
227    usb_queue_request(softc->rx_ur);
228}
229
230static int asix_data_rx( void *ctx )
231{
232    asix_softc_t *softc = (asix_softc_t *) ctx;
233
234    usb_poll(softc->dev->ud_bus);
235    return( !softc->rx_ur->ur_inprogress );
236}
237
238static int asix_get_eth_frame( void *ctx, hsaddr_t buf )
239{
240    asix_softc_t *softc = (asix_softc_t *) ctx;
241    int len = 0;
242
243    if( !softc->rx_ur->ur_inprogress ) {
244	len = softc->rx_ur->ur_xferred;
245	hs_memcpy_to_hs( buf, softc->rxbuf, len );
246	usb_free_request(softc->rx_ur);
247	asix_queue_rx( softc );
248	}
249    else
250	xprintf( "Bulk data is not available yet!\n" );
251
252    return( len );
253}
254
255static int asix_send_eth_frame( void *ctx, hsaddr_t buf, int len )
256{
257    asix_softc_t *softc = (asix_softc_t *) ctx;
258    usbreq_t *ur;
259    int txlen = len;
260    unsigned char *txbuf;
261
262    txbuf = KMALLOC(txlen, USB_MALLOC_VALUE);
263    hs_memcpy_from_hs( txbuf, buf, txlen );
264    ur = usb_make_request(softc->dev, softc->bulk_outpipe,
265			  txbuf, txlen, UR_FLAG_OUT);
266    usb_sync_request(ur);
267    usb_free_request(ur);
268    KFREE(txbuf);
269
270    return( len );
271}
272
273static void asix_open_device( asix_softc_t *softc )
274{
275    usbdev_t *dev = softc->dev;
276    uint8_t data[2];
277    int16_t phyid;
278
279    asix_set_reg( dev, ASIX_MII_SWOP_CMD, 0, 0, NULL, 0 );
280    asix_get_reg( dev, ASIX_PHYID_CMD, 0, 0, data, 2 );
281
282    /* UF200 seems to need a GPIO settings */
283    asix_set_reg( dev, ASIX_GPIO_WRITE_CMD, 0x11, 0, NULL, 0 );
284    asix_set_reg( dev, ASIX_GPIO_WRITE_CMD, 0x13, 0, NULL, 0 );
285    asix_set_reg( dev, ASIX_GPIO_WRITE_CMD, 0x0c, 0, NULL, 0 );
286
287    phyid = data[1];
288    data[0] = 0; data[1] = 0;
289    asix_set_reg( dev, ASIX_MII_WRITE_CMD, phyid, 0, data, 2 );
290    data[1] = 0x80;
291    asix_set_reg( dev, ASIX_MII_WRITE_CMD, phyid, 0, data, 2 );
292    asix_set_reg( dev, ASIX_MED_WRITE_CMD, 6, 0, NULL, 0 );
293    asix_set_reg( dev, ASIX_IPG1_CMD, 0x15, 0, NULL, 0 );
294    asix_set_reg( dev, ASIX_IPG2_CMD, 0x0c, 0, NULL, 0 );
295    asix_set_reg( dev, ASIX_IPG3_CMD, 0x12, 0, NULL, 0 );
296    data[0] = 0x01; data[1] = 0x05;
297    asix_set_reg( dev, ASIX_MII_WRITE_CMD, phyid, 4, data, 2 );
298    data[0] = 0; data[1] = 0x12;
299    asix_set_reg( dev, ASIX_MII_WRITE_CMD, phyid, 0, data, 2 );
300    asix_set_reg( dev, ASIX_MII_HWOP_CMD, 0, 0, NULL, 0 );
301    asix_set_reg( dev, ASIX_RXCTL_CMD, 0x81, 0, NULL, 0 );
302
303    /* kick start the receive */
304    asix_queue_rx( softc );
305}
306
307static void asix_close_device( asix_softc_t *softc )
308{
309    /* disable adapter from receiving packets */
310    asix_set_reg( softc->dev, ASIX_RXCTL_CMD, 0, 0, NULL, 0 );
311}
312
313
314/*  *********************************************************************
315    * CFE-USB interfaces
316    ********************************************************************* */
317
318/*  *********************************************************************
319    *  asix_attach(dev,drv)
320    *
321    *  This routine is called when the bus scan stuff finds a usb-ethernet
322    *  device.  We finish up the initialization by configuring the
323    *  device and allocating our softc here.
324    *
325    *  Input parameters:
326    *      dev - usb device, in the "addressed" state.
327    *      drv - the driver table entry that matched
328    *
329    *  Return value:
330    *      0
331    ********************************************************************* */
332
333const cfe_driver_t usbasix_driver;		/* forward declaration */
334
335static int asix_attach(usbdev_t *dev,usb_driver_t *drv)
336{
337    usb_config_descr_t *cfgdscr = dev->ud_cfgdescr;
338    usb_endpoint_descr_t *epdscr;
339    usb_endpoint_descr_t *indscr = NULL;
340    usb_endpoint_descr_t *outdscr = NULL;
341    usb_interface_descr_t *ifdscr;
342    asix_softc_t *softc;
343    int idx;
344
345    dev->ud_drv = drv;
346
347    softc = (asix_softc_t *) KMALLOC( sizeof(asix_softc_t), USB_MALLOC_VALUE );
348    if( softc == NULL ) {
349	xprintf( "Failed to allocate softc memory.\n" );
350	return -1;
351	}
352    memset( softc, 0, sizeof(asix_softc_t) );
353    dev->ud_private = softc;
354    softc->dev = dev;
355
356    ifdscr = usb_find_cfg_descr(dev,USB_INTERFACE_DESCRIPTOR_TYPE,0);
357    if (ifdscr == NULL) {
358	xprintf("USBETH: ERROR...no interace descriptor\n");
359	return -1;
360	}
361
362    for (idx = 0; idx < 3; idx++) {
363	epdscr = usb_find_cfg_descr(dev,USB_ENDPOINT_DESCRIPTOR_TYPE,idx);
364	if((epdscr->bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_BULK) {
365	    if (USB_ENDPOINT_DIR_OUT(epdscr->bEndpointAddress))
366		outdscr = epdscr;
367	    else
368		indscr = epdscr;
369	    }
370	}
371
372    if (!indscr || !outdscr) {
373	/*
374	 * Could not get descriptors, something is very wrong.
375	 * Leave device addressed but not configured.
376	 */
377	xprintf("USBETH: ERROR...bulk endpoint descriptor(s) missing\n");
378	return -1;
379	}
380
381    /* Choose the standard configuration. */
382    usb_set_configuration(dev,cfgdscr->bConfigurationValue);
383
384    /* Quit if not able to initialize the device */
385    if (asix_init_device(softc) < 0)
386	return -1;
387
388    /* Open the pipes. */
389    softc->bulk_inpipe     = usb_open_pipe(dev,indscr);
390    softc->bulk_outpipe    = usb_open_pipe(dev,outdscr);
391
392    /* Register the device */
393    usbeth_register(&usbeth_asix, softc);
394
395    /* Open the device */
396    asix_open_device( softc );
397
398    return 0;
399}
400
401/*  *********************************************************************
402    *  asix_detach(dev)
403    *
404    *  This routine is called when the bus scanner notices that
405    *  this device has been removed from the system.  We should
406    *  do any cleanup that is required.  The pending requests
407    *  will be cancelled automagically.
408    *
409    *  Input parameters:
410    *      dev - usb device
411    *
412    *  Return value:
413    *      0
414    ********************************************************************* */
415
416static int asix_detach(usbdev_t *dev)
417{
418    asix_softc_t *softc = (asix_softc_t *) dev->ud_private;
419
420    if (softc != NULL) {
421	usbeth_unregister( softc );
422	asix_close_device( softc );
423	dev->ud_private = NULL;
424	softc->dev = NULL;
425	KFREE(softc);
426	}
427
428    return 0;
429}
430
431/* CFE USB device interface structure */
432usb_driver_t usbeth_driver =
433{
434    "Ethernet Device",
435    asix_attach,
436    asix_detach
437};
438