Browse Source

[lan78xx] Add driver for Microchip LAN78xx USB Ethernet NICs

Originally-implemented-by: Ravi Hegde <ravi.hegde@microchip.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 8 years ago
parent
commit
0600d3ae94
3 changed files with 495 additions and 0 deletions
  1. 397
    0
      src/drivers/net/lan78xx.c
  2. 97
    0
      src/drivers/net/lan78xx.h
  3. 1
    0
      src/include/ipxe/errfile.h

+ 397
- 0
src/drivers/net/lan78xx.c View File

@@ -0,0 +1,397 @@
1
+/*
2
+ * Copyright (C) 2017 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or (at your option) any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
+ * 02110-1301, USA.
18
+ *
19
+ * You can also choose to distribute this program under the terms of
20
+ * the Unmodified Binary Distribution Licence (as given in the file
21
+ * COPYING.UBDL), provided that you have satisfied its requirements.
22
+ */
23
+
24
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
+
26
+#include <string.h>
27
+#include <unistd.h>
28
+#include <errno.h>
29
+#include <ipxe/ethernet.h>
30
+#include <ipxe/usb.h>
31
+#include <ipxe/usbnet.h>
32
+#include "lan78xx.h"
33
+
34
+/** @file
35
+ *
36
+ * Microchip LAN78xx USB Ethernet driver
37
+ *
38
+ */
39
+
40
+/******************************************************************************
41
+ *
42
+ * MAC address
43
+ *
44
+ ******************************************************************************
45
+ */
46
+
47
+/**
48
+ * Fetch MAC address from EEPROM
49
+ *
50
+ * @v smscusb		SMSC USB device
51
+ * @ret rc		Return status code
52
+ */
53
+static int lan78xx_eeprom_fetch_mac ( struct smscusb_device *smscusb ) {
54
+	uint32_t hw_cfg;
55
+	uint32_t orig_hw_cfg;
56
+	int rc;
57
+
58
+	/* Read original HW_CFG value */
59
+	if ( ( rc = smscusb_readl ( smscusb, LAN78XX_HW_CFG, &hw_cfg ) ) != 0 )
60
+		goto err_read_hw_cfg;
61
+	orig_hw_cfg = hw_cfg;
62
+
63
+	/* Temporarily disable LED0 and LED1 (which share physical
64
+	 * pins with EEDO and EECLK respectively).
65
+	 */
66
+	hw_cfg &= ~( LAN78XX_HW_CFG_LED0_EN | LAN78XX_HW_CFG_LED1_EN );
67
+	if ( ( rc = smscusb_writel ( smscusb, LAN78XX_HW_CFG, hw_cfg ) ) != 0 )
68
+		goto err_write_hw_cfg;
69
+
70
+	/* Fetch MAC address from EEPROM */
71
+	if ( ( rc = smscusb_eeprom_fetch_mac ( smscusb,
72
+					       LAN78XX_E2P_BASE ) ) != 0 )
73
+		goto err_fetch_mac;
74
+
75
+ err_fetch_mac:
76
+	smscusb_writel ( smscusb, LAN78XX_HW_CFG, orig_hw_cfg );
77
+ err_write_hw_cfg:
78
+ err_read_hw_cfg:
79
+	return rc;
80
+}
81
+
82
+/**
83
+ * Fetch MAC address
84
+ *
85
+ * @v smscusb		SMSC USB device
86
+ * @ret rc		Return status code
87
+ */
88
+static int lan78xx_fetch_mac ( struct smscusb_device *smscusb ) {
89
+	struct net_device *netdev = smscusb->netdev;
90
+	int rc;
91
+
92
+	/* Read MAC address from EEPROM, if present */
93
+	if ( ( rc = lan78xx_eeprom_fetch_mac ( smscusb ) ) == 0 )
94
+		return 0;
95
+
96
+	/* Read MAC address from OTP, if present */
97
+	if ( ( rc = smscusb_otp_fetch_mac ( smscusb, LAN78XX_OTP_BASE ) ) == 0 )
98
+		return 0;
99
+
100
+	/* Otherwise, generate a random MAC address */
101
+	eth_random_addr ( netdev->hw_addr );
102
+	DBGC ( smscusb, "LAN78XX %p using random MAC %s\n",
103
+	       smscusb, eth_ntoa ( netdev->hw_addr ) );
104
+	return 0;
105
+}
106
+
107
+/******************************************************************************
108
+ *
109
+ * Device reset
110
+ *
111
+ ******************************************************************************
112
+ */
113
+
114
+/**
115
+ * Reset device
116
+ *
117
+ * @v smscusb		SMSC USB device
118
+ * @ret rc		Return status code
119
+ */
120
+static int lan78xx_reset ( struct smscusb_device *smscusb ) {
121
+	uint32_t hw_cfg;
122
+	unsigned int i;
123
+	int rc;
124
+
125
+	/* Reset device */
126
+	if ( ( rc = smscusb_writel ( smscusb, LAN78XX_HW_CFG,
127
+				     LAN78XX_HW_CFG_LRST ) ) != 0 )
128
+		return rc;
129
+
130
+	/* Wait for reset to complete */
131
+	for ( i = 0 ; i < LAN78XX_RESET_MAX_WAIT_MS ; i++ ) {
132
+
133
+		/* Check if reset has completed */
134
+		if ( ( rc = smscusb_readl ( smscusb, LAN78XX_HW_CFG,
135
+					    &hw_cfg ) ) != 0 )
136
+			return rc;
137
+		if ( ! ( hw_cfg & LAN78XX_HW_CFG_LRST ) )
138
+			return 0;
139
+
140
+		/* Delay */
141
+		mdelay ( 1 );
142
+	}
143
+
144
+	DBGC ( smscusb, "LAN78XX %p timed out waiting for reset\n",
145
+	       smscusb );
146
+	return -ETIMEDOUT;
147
+}
148
+
149
+/******************************************************************************
150
+ *
151
+ * Network device interface
152
+ *
153
+ ******************************************************************************
154
+ */
155
+
156
+/**
157
+ * Open network device
158
+ *
159
+ * @v netdev		Network device
160
+ * @ret rc		Return status code
161
+ */
162
+static int lan78xx_open ( struct net_device *netdev ) {
163
+	struct smscusb_device *smscusb = netdev->priv;
164
+	uint32_t usb_cfg0;
165
+	int rc;
166
+
167
+	/* Clear stored interrupt status */
168
+	smscusb->int_sts = 0;
169
+
170
+	/* Configure bulk IN empty response */
171
+	if ( ( rc = smscusb_readl ( smscusb, LAN78XX_USB_CFG0,
172
+				    &usb_cfg0 ) ) != 0 )
173
+		goto err_usb_cfg0_read;
174
+	usb_cfg0 |= LAN78XX_USB_CFG0_BIR;
175
+	if ( ( rc = smscusb_writel ( smscusb, LAN78XX_USB_CFG0,
176
+				     usb_cfg0 ) ) != 0 )
177
+		goto err_usb_cfg0_write;
178
+
179
+	/* Open USB network device */
180
+	if ( ( rc = usbnet_open ( &smscusb->usbnet ) ) != 0 ) {
181
+		DBGC ( smscusb, "LAN78XX %p could not open: %s\n",
182
+		       smscusb, strerror ( rc ) );
183
+		goto err_open;
184
+	}
185
+
186
+	/* Configure interrupt endpoint */
187
+	if ( ( rc = smscusb_writel ( smscusb, LAN78XX_INT_EP_CTL,
188
+				     ( LAN78XX_INT_EP_CTL_RDFO_EN |
189
+				       LAN78XX_INT_EP_CTL_PHY_EN ) ) ) != 0 )
190
+		goto err_int_ep_ctl;
191
+
192
+	/* Configure bulk IN delay */
193
+	if ( ( rc = smscusb_writel ( smscusb, LAN78XX_BULK_IN_DLY,
194
+				     LAN78XX_BULK_IN_DLY_SET ( 0 ) ) ) != 0 )
195
+		goto err_bulk_in_dly;
196
+
197
+	/* Configure receive filters */
198
+	if ( ( rc = smscusb_writel ( smscusb, LAN78XX_RFE_CTL,
199
+				     ( LAN78XX_RFE_CTL_AB |
200
+				       LAN78XX_RFE_CTL_AM |
201
+				       LAN78XX_RFE_CTL_AU ) ) ) != 0 )
202
+		goto err_rfe_ctl;
203
+
204
+	/* Configure receive FIFO */
205
+	if ( ( rc = smscusb_writel ( smscusb, LAN78XX_FCT_RX_CTL,
206
+				     ( LAN78XX_FCT_RX_CTL_EN |
207
+				       LAN78XX_FCT_RX_CTL_BAD ) ) ) != 0 )
208
+		goto err_fct_rx_ctl;
209
+
210
+	/* Configure transmit FIFO */
211
+	if ( ( rc = smscusb_writel ( smscusb, LAN78XX_FCT_TX_CTL,
212
+				     LAN78XX_FCT_TX_CTL_EN ) ) != 0 )
213
+		goto err_fct_tx_ctl;
214
+
215
+	/* Configure receive datapath */
216
+	if ( ( rc = smscusb_writel ( smscusb, LAN78XX_MAC_RX,
217
+				     ( LAN78XX_MAC_RX_MAX_SIZE_DEFAULT |
218
+				       LAN78XX_MAC_RX_FCS |
219
+				       LAN78XX_MAC_RX_EN ) ) ) != 0 )
220
+		goto err_mac_rx;
221
+
222
+	/* Configure transmit datapath */
223
+	if ( ( rc = smscusb_writel ( smscusb, LAN78XX_MAC_TX,
224
+				     LAN78XX_MAC_TX_EN ) ) != 0 )
225
+		goto err_mac_tx;
226
+
227
+	/* Set MAC address */
228
+	if ( ( rc = smscusb_set_address ( smscusb,
229
+					  LAN78XX_RX_ADDR_BASE ) ) != 0 )
230
+		goto err_set_address;
231
+
232
+	/* Set MAC address perfect filter */
233
+	if ( ( rc = smscusb_set_filter ( smscusb,
234
+					 LAN78XX_ADDR_FILT_BASE ) ) != 0 )
235
+		goto err_set_filter;
236
+
237
+	/* Enable PHY interrupts and update link status */
238
+	if ( ( rc = smscusb_mii_open ( smscusb, LAN78XX_MII_PHY_INTR_MASK,
239
+				       ( LAN78XX_PHY_INTR_ENABLE |
240
+					 LAN78XX_PHY_INTR_LINK |
241
+					 LAN78XX_PHY_INTR_ANEG_ERR |
242
+					 LAN78XX_PHY_INTR_ANEG_DONE ) ) ) != 0 )
243
+		goto err_mii_open;
244
+
245
+	return 0;
246
+
247
+ err_mii_open:
248
+ err_set_filter:
249
+ err_set_address:
250
+ err_mac_tx:
251
+ err_mac_rx:
252
+ err_fct_tx_ctl:
253
+ err_fct_rx_ctl:
254
+ err_rfe_ctl:
255
+ err_bulk_in_dly:
256
+ err_int_ep_ctl:
257
+	usbnet_close ( &smscusb->usbnet );
258
+ err_open:
259
+ err_usb_cfg0_write:
260
+ err_usb_cfg0_read:
261
+	lan78xx_reset ( smscusb );
262
+	return rc;
263
+}
264
+
265
+/**
266
+ * Close network device
267
+ *
268
+ * @v netdev		Network device
269
+ */
270
+static void lan78xx_close ( struct net_device *netdev ) {
271
+	struct smscusb_device *smscusb = netdev->priv;
272
+
273
+	/* Close USB network device */
274
+	usbnet_close ( &smscusb->usbnet );
275
+
276
+	/* Dump statistics (for debugging) */
277
+	if ( DBG_LOG )
278
+		smsc75xx_dump_statistics ( smscusb );
279
+
280
+	/* Reset device */
281
+	lan78xx_reset ( smscusb );
282
+}
283
+
284
+/** LAN78xx network device operations */
285
+static struct net_device_operations lan78xx_operations = {
286
+	.open		= lan78xx_open,
287
+	.close		= lan78xx_close,
288
+	.transmit	= smsc75xx_transmit,
289
+	.poll		= smsc75xx_poll,
290
+};
291
+
292
+/******************************************************************************
293
+ *
294
+ * USB interface
295
+ *
296
+ ******************************************************************************
297
+ */
298
+
299
+/**
300
+ * Probe device
301
+ *
302
+ * @v func		USB function
303
+ * @v config		Configuration descriptor
304
+ * @ret rc		Return status code
305
+ */
306
+static int lan78xx_probe ( struct usb_function *func,
307
+			   struct usb_configuration_descriptor *config ) {
308
+	struct net_device *netdev;
309
+	struct smscusb_device *smscusb;
310
+	int rc;
311
+
312
+	/* Allocate and initialise structure */
313
+	netdev = alloc_etherdev ( sizeof ( *smscusb ) );
314
+	if ( ! netdev ) {
315
+		rc = -ENOMEM;
316
+		goto err_alloc;
317
+	}
318
+	netdev_init ( netdev, &lan78xx_operations );
319
+	netdev->dev = &func->dev;
320
+	smscusb = netdev->priv;
321
+	memset ( smscusb, 0, sizeof ( *smscusb ) );
322
+	smscusb_init ( smscusb, netdev, func, &smsc75xx_in_operations );
323
+	smscusb_mii_init ( smscusb, LAN78XX_MII_BASE,
324
+			   LAN78XX_MII_PHY_INTR_SOURCE );
325
+	usb_refill_init ( &smscusb->usbnet.in, 0, SMSC75XX_IN_MTU,
326
+			  SMSC75XX_IN_MAX_FILL );
327
+	DBGC ( smscusb, "LAN78XX %p on %s\n", smscusb, func->name );
328
+
329
+	/* Describe USB network device */
330
+	if ( ( rc = usbnet_describe ( &smscusb->usbnet, config ) ) != 0 ) {
331
+		DBGC ( smscusb, "LAN78XX %p could not describe: %s\n",
332
+		       smscusb, strerror ( rc ) );
333
+		goto err_describe;
334
+	}
335
+
336
+	/* Reset device */
337
+	if ( ( rc = lan78xx_reset ( smscusb ) ) != 0 )
338
+		goto err_reset;
339
+
340
+	/* Read MAC address */
341
+	if ( ( rc = lan78xx_fetch_mac ( smscusb ) ) != 0 )
342
+		goto err_fetch_mac;
343
+
344
+	/* Register network device */
345
+	if ( ( rc = register_netdev ( netdev ) ) != 0 )
346
+		goto err_register;
347
+
348
+	usb_func_set_drvdata ( func, netdev );
349
+	return 0;
350
+
351
+	unregister_netdev ( netdev );
352
+ err_register:
353
+ err_fetch_mac:
354
+ err_reset:
355
+ err_describe:
356
+	netdev_nullify ( netdev );
357
+	netdev_put ( netdev );
358
+ err_alloc:
359
+	return rc;
360
+}
361
+
362
+/**
363
+ * Remove device
364
+ *
365
+ * @v func		USB function
366
+ */
367
+static void lan78xx_remove ( struct usb_function *func ) {
368
+	struct net_device *netdev = usb_func_get_drvdata ( func );
369
+
370
+	unregister_netdev ( netdev );
371
+	netdev_nullify ( netdev );
372
+	netdev_put ( netdev );
373
+}
374
+
375
+/** LAN78xx device IDs */
376
+static struct usb_device_id lan78xx_ids[] = {
377
+	{
378
+		.name = "lan7800",
379
+		.vendor = 0x0424,
380
+		.product = 0x7800,
381
+	},
382
+	{
383
+		.name = "lan7850",
384
+		.vendor = 0x0424,
385
+		.product = 0x7850,
386
+	},
387
+};
388
+
389
+/** LAN78xx driver */
390
+struct usb_driver lan78xx_driver __usb_driver = {
391
+	.ids = lan78xx_ids,
392
+	.id_count = ( sizeof ( lan78xx_ids ) / sizeof ( lan78xx_ids[0] ) ),
393
+	.class = USB_CLASS_ID ( 0xff, 0x00, 0xff ),
394
+	.score = USB_SCORE_NORMAL,
395
+	.probe = lan78xx_probe,
396
+	.remove = lan78xx_remove,
397
+};

+ 97
- 0
src/drivers/net/lan78xx.h View File

@@ -0,0 +1,97 @@
1
+#ifndef _LAN78XX_H
2
+#define _LAN78XX_H
3
+
4
+/** @file
5
+ *
6
+ * Microchip LAN78xx USB Ethernet driver
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
11
+
12
+#include "smscusb.h"
13
+#include "smsc75xx.h"
14
+
15
+/** Hardware configuration register */
16
+#define LAN78XX_HW_CFG 0x0010
17
+#define LAN78XX_HW_CFG_LED1_EN		0x00200000UL	/**< LED1 enable */
18
+#define LAN78XX_HW_CFG_LED0_EN		0x00100000UL	/**< LED1 enable */
19
+#define LAN78XX_HW_CFG_LRST		0x00000002UL	/**< Soft lite reset */
20
+
21
+/** Interrupt endpoint control register */
22
+#define LAN78XX_INT_EP_CTL 0x0098
23
+#define LAN78XX_INT_EP_CTL_RDFO_EN	0x00400000UL	/**< RX FIFO overflow */
24
+#define LAN78XX_INT_EP_CTL_PHY_EN	0x00020000UL	/**< PHY interrupt */
25
+
26
+/** Bulk IN delay register */
27
+#define LAN78XX_BULK_IN_DLY 0x0094
28
+#define LAN78XX_BULK_IN_DLY_SET(ticks)	( (ticks) << 0 ) /**< Delay / 16.7ns */
29
+
30
+/** EEPROM register base */
31
+#define LAN78XX_E2P_BASE 0x0040
32
+
33
+/** USB configuration register 0 */
34
+#define LAN78XX_USB_CFG0 0x0080
35
+#define LAN78XX_USB_CFG0_BIR		0x00000040UL	/**< Bulk IN use NAK */
36
+
37
+/** Receive filtering engine control register */
38
+#define LAN78XX_RFE_CTL 0x00b0
39
+#define LAN78XX_RFE_CTL_AB		0x00000400UL	/**< Accept broadcast */
40
+#define LAN78XX_RFE_CTL_AM		0x00000200UL	/**< Accept multicast */
41
+#define LAN78XX_RFE_CTL_AU		0x00000100UL	/**< Accept unicast */
42
+
43
+/** FIFO controller RX FIFO control register */
44
+#define LAN78XX_FCT_RX_CTL 0x00c0
45
+#define LAN78XX_FCT_RX_CTL_EN		0x80000000UL	/**< FCT RX enable */
46
+#define LAN78XX_FCT_RX_CTL_BAD		0x02000000UL	/**< Store bad frames */
47
+
48
+/** FIFO controller TX FIFO control register */
49
+#define LAN78XX_FCT_TX_CTL 0x00c4
50
+#define LAN78XX_FCT_TX_CTL_EN		0x80000000UL	/**< FCT TX enable */
51
+
52
+/** MAC receive register */
53
+#define LAN78XX_MAC_RX 0x0104
54
+#define LAN78XX_MAC_RX_MAX_SIZE(mtu)	( (mtu) << 16 )	/**< Max frame size */
55
+#define LAN78XX_MAC_RX_MAX_SIZE_DEFAULT \
56
+	LAN78XX_MAC_RX_MAX_SIZE ( ETH_FRAME_LEN + 4 /* VLAN */ + 4 /* CRC */ )
57
+#define LAN78XX_MAC_RX_FCS		0x00000010UL	/**< FCS stripping */
58
+#define LAN78XX_MAC_RX_EN		0x00000001UL	/**< RX enable */
59
+
60
+/** MAC transmit register */
61
+#define LAN78XX_MAC_TX 0x0108
62
+#define LAN78XX_MAC_TX_EN		0x00000001UL	/**< TX enable */
63
+
64
+/** MAC receive address register base */
65
+#define LAN78XX_RX_ADDR_BASE 0x0118
66
+
67
+/** MII register base */
68
+#define LAN78XX_MII_BASE 0x0120
69
+
70
+/** PHY interrupt mask MII register */
71
+#define LAN78XX_MII_PHY_INTR_MASK 25
72
+
73
+/** PHY interrupt source MII register */
74
+#define LAN78XX_MII_PHY_INTR_SOURCE 26
75
+
76
+/** PHY interrupt: global enable */
77
+#define LAN78XX_PHY_INTR_ENABLE 0x8000
78
+
79
+/** PHY interrupt: link state change */
80
+#define LAN78XX_PHY_INTR_LINK 0x2000
81
+
82
+/** PHY interrupt: auto-negotiation failure */
83
+#define LAN78XX_PHY_INTR_ANEG_ERR 0x0800
84
+
85
+/** PHY interrupt: auto-negotiation complete */
86
+#define LAN78XX_PHY_INTR_ANEG_DONE 0x0400
87
+
88
+/** MAC address perfect filter register base */
89
+#define LAN78XX_ADDR_FILT_BASE 0x0400
90
+
91
+/** OTP register base */
92
+#define LAN78XX_OTP_BASE 0x1000
93
+
94
+/** Maximum time to wait for reset (in milliseconds) */
95
+#define LAN78XX_RESET_MAX_WAIT_MS 100
96
+
97
+#endif /* _LAN78XX_H */

+ 1
- 0
src/include/ipxe/errfile.h View File

@@ -201,6 +201,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
201 201
 #define ERRFILE_efx_hunt	     ( ERRFILE_DRIVER | 0x00c50000 )
202 202
 #define ERRFILE_exanic		     ( ERRFILE_DRIVER | 0x00c60000 )
203 203
 #define ERRFILE_smscusb		     ( ERRFILE_DRIVER | 0x00c70000 )
204
+#define ERRFILE_lan78xx		     ( ERRFILE_DRIVER | 0x00c80000 )
204 205
 
205 206
 #define ERRFILE_aoe			( ERRFILE_NET | 0x00000000 )
206 207
 #define ERRFILE_arp			( ERRFILE_NET | 0x00010000 )

Loading…
Cancel
Save