iPXE
lan78xx.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2017 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <string.h>
00027 #include <unistd.h>
00028 #include <errno.h>
00029 #include <ipxe/ethernet.h>
00030 #include <ipxe/usb.h>
00031 #include <ipxe/usbnet.h>
00032 #include "lan78xx.h"
00033 
00034 /** @file
00035  *
00036  * Microchip LAN78xx USB Ethernet driver
00037  *
00038  */
00039 
00040 /******************************************************************************
00041  *
00042  * MAC address
00043  *
00044  ******************************************************************************
00045  */
00046 
00047 /**
00048  * Fetch MAC address from EEPROM
00049  *
00050  * @v smscusb           SMSC USB device
00051  * @ret rc              Return status code
00052  */
00053 static int lan78xx_eeprom_fetch_mac ( struct smscusb_device *smscusb ) {
00054         uint32_t hw_cfg;
00055         uint32_t orig_hw_cfg;
00056         int rc;
00057 
00058         /* Read original HW_CFG value */
00059         if ( ( rc = smscusb_readl ( smscusb, LAN78XX_HW_CFG, &hw_cfg ) ) != 0 )
00060                 goto err_read_hw_cfg;
00061         orig_hw_cfg = hw_cfg;
00062 
00063         /* Temporarily disable LED0 and LED1 (which share physical
00064          * pins with EEDO and EECLK respectively).
00065          */
00066         hw_cfg &= ~( LAN78XX_HW_CFG_LED0_EN | LAN78XX_HW_CFG_LED1_EN );
00067         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_HW_CFG, hw_cfg ) ) != 0 )
00068                 goto err_write_hw_cfg;
00069 
00070         /* Fetch MAC address from EEPROM */
00071         if ( ( rc = smscusb_eeprom_fetch_mac ( smscusb,
00072                                                LAN78XX_E2P_BASE ) ) != 0 )
00073                 goto err_fetch_mac;
00074 
00075  err_fetch_mac:
00076         smscusb_writel ( smscusb, LAN78XX_HW_CFG, orig_hw_cfg );
00077  err_write_hw_cfg:
00078  err_read_hw_cfg:
00079         return rc;
00080 }
00081 
00082 /**
00083  * Fetch MAC address
00084  *
00085  * @v smscusb           SMSC USB device
00086  * @ret rc              Return status code
00087  */
00088 static int lan78xx_fetch_mac ( struct smscusb_device *smscusb ) {
00089         struct net_device *netdev = smscusb->netdev;
00090         int rc;
00091 
00092         /* Read MAC address from EEPROM, if present */
00093         if ( ( rc = lan78xx_eeprom_fetch_mac ( smscusb ) ) == 0 )
00094                 return 0;
00095 
00096         /* Read MAC address from OTP, if present */
00097         if ( ( rc = smscusb_otp_fetch_mac ( smscusb, LAN78XX_OTP_BASE ) ) == 0 )
00098                 return 0;
00099 
00100         /* Read MAC address from device tree, if present */
00101         if ( ( rc = smscusb_fdt_fetch_mac ( smscusb ) ) == 0 )
00102                 return 0;
00103 
00104         /* Otherwise, generate a random MAC address */
00105         eth_random_addr ( netdev->hw_addr );
00106         DBGC ( smscusb, "LAN78XX %p using random MAC %s\n",
00107                smscusb, eth_ntoa ( netdev->hw_addr ) );
00108         return 0;
00109 }
00110 
00111 /******************************************************************************
00112  *
00113  * Device reset
00114  *
00115  ******************************************************************************
00116  */
00117 
00118 /**
00119  * Reset device
00120  *
00121  * @v smscusb           SMSC USB device
00122  * @ret rc              Return status code
00123  */
00124 static int lan78xx_reset ( struct smscusb_device *smscusb ) {
00125         uint32_t hw_cfg;
00126         unsigned int i;
00127         int rc;
00128 
00129         /* Reset device */
00130         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_HW_CFG,
00131                                      LAN78XX_HW_CFG_LRST ) ) != 0 )
00132                 return rc;
00133 
00134         /* Wait for reset to complete */
00135         for ( i = 0 ; i < LAN78XX_RESET_MAX_WAIT_MS ; i++ ) {
00136 
00137                 /* Check if reset has completed */
00138                 if ( ( rc = smscusb_readl ( smscusb, LAN78XX_HW_CFG,
00139                                             &hw_cfg ) ) != 0 )
00140                         return rc;
00141                 if ( ! ( hw_cfg & LAN78XX_HW_CFG_LRST ) )
00142                         return 0;
00143 
00144                 /* Delay */
00145                 mdelay ( 1 );
00146         }
00147 
00148         DBGC ( smscusb, "LAN78XX %p timed out waiting for reset\n",
00149                smscusb );
00150         return -ETIMEDOUT;
00151 }
00152 
00153 /******************************************************************************
00154  *
00155  * Network device interface
00156  *
00157  ******************************************************************************
00158  */
00159 
00160 /**
00161  * Open network device
00162  *
00163  * @v netdev            Network device
00164  * @ret rc              Return status code
00165  */
00166 static int lan78xx_open ( struct net_device *netdev ) {
00167         struct smscusb_device *smscusb = netdev->priv;
00168         uint32_t usb_cfg0;
00169         int rc;
00170 
00171         /* Clear stored interrupt status */
00172         smscusb->int_sts = 0;
00173 
00174         /* Configure bulk IN empty response */
00175         if ( ( rc = smscusb_readl ( smscusb, LAN78XX_USB_CFG0,
00176                                     &usb_cfg0 ) ) != 0 )
00177                 goto err_usb_cfg0_read;
00178         usb_cfg0 |= LAN78XX_USB_CFG0_BIR;
00179         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_USB_CFG0,
00180                                      usb_cfg0 ) ) != 0 )
00181                 goto err_usb_cfg0_write;
00182 
00183         /* Open USB network device */
00184         if ( ( rc = usbnet_open ( &smscusb->usbnet ) ) != 0 ) {
00185                 DBGC ( smscusb, "LAN78XX %p could not open: %s\n",
00186                        smscusb, strerror ( rc ) );
00187                 goto err_open;
00188         }
00189 
00190         /* Configure interrupt endpoint */
00191         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_INT_EP_CTL,
00192                                      ( LAN78XX_INT_EP_CTL_RDFO_EN |
00193                                        LAN78XX_INT_EP_CTL_PHY_EN ) ) ) != 0 )
00194                 goto err_int_ep_ctl;
00195 
00196         /* Configure bulk IN delay */
00197         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_BULK_IN_DLY,
00198                                      LAN78XX_BULK_IN_DLY_SET ( 0 ) ) ) != 0 )
00199                 goto err_bulk_in_dly;
00200 
00201         /* Enable automatic speed and duplex detection */
00202         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_MAC_CR,
00203                                      ( LAN78XX_MAC_CR_ADP |
00204                                        LAN78XX_MAC_CR_ADD |
00205                                        LAN78XX_MAC_CR_ASD ) ) ) != 0 )
00206                 goto err_mac_cr;
00207 
00208         /* Configure receive filters */
00209         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_RFE_CTL,
00210                                      ( LAN78XX_RFE_CTL_AB |
00211                                        LAN78XX_RFE_CTL_AM |
00212                                        LAN78XX_RFE_CTL_AU ) ) ) != 0 )
00213                 goto err_rfe_ctl;
00214 
00215         /* Configure receive FIFO */
00216         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_FCT_RX_CTL,
00217                                      ( LAN78XX_FCT_RX_CTL_EN |
00218                                        LAN78XX_FCT_RX_CTL_BAD ) ) ) != 0 )
00219                 goto err_fct_rx_ctl;
00220 
00221         /* Configure transmit FIFO */
00222         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_FCT_TX_CTL,
00223                                      LAN78XX_FCT_TX_CTL_EN ) ) != 0 )
00224                 goto err_fct_tx_ctl;
00225 
00226         /* Configure receive datapath */
00227         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_MAC_RX,
00228                                      ( LAN78XX_MAC_RX_MAX_SIZE_DEFAULT |
00229                                        LAN78XX_MAC_RX_FCS |
00230                                        LAN78XX_MAC_RX_EN ) ) ) != 0 )
00231                 goto err_mac_rx;
00232 
00233         /* Configure transmit datapath */
00234         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_MAC_TX,
00235                                      LAN78XX_MAC_TX_EN ) ) != 0 )
00236                 goto err_mac_tx;
00237 
00238         /* Set MAC address */
00239         if ( ( rc = smscusb_set_address ( smscusb,
00240                                           LAN78XX_RX_ADDR_BASE ) ) != 0 )
00241                 goto err_set_address;
00242 
00243         /* Set MAC address perfect filter */
00244         if ( ( rc = smscusb_set_filter ( smscusb,
00245                                          LAN78XX_ADDR_FILT_BASE ) ) != 0 )
00246                 goto err_set_filter;
00247 
00248         /* Enable PHY interrupts and update link status */
00249         if ( ( rc = smscusb_mii_open ( smscusb, LAN78XX_MII_PHY_INTR_MASK,
00250                                        ( LAN78XX_PHY_INTR_ENABLE |
00251                                          LAN78XX_PHY_INTR_LINK |
00252                                          LAN78XX_PHY_INTR_ANEG_ERR |
00253                                          LAN78XX_PHY_INTR_ANEG_DONE ) ) ) != 0 )
00254                 goto err_mii_open;
00255 
00256         return 0;
00257 
00258  err_mii_open:
00259  err_set_filter:
00260  err_set_address:
00261  err_mac_tx:
00262  err_mac_rx:
00263  err_fct_tx_ctl:
00264  err_fct_rx_ctl:
00265  err_rfe_ctl:
00266  err_mac_cr:
00267  err_bulk_in_dly:
00268  err_int_ep_ctl:
00269         usbnet_close ( &smscusb->usbnet );
00270  err_open:
00271  err_usb_cfg0_write:
00272  err_usb_cfg0_read:
00273         lan78xx_reset ( smscusb );
00274         return rc;
00275 }
00276 
00277 /**
00278  * Close network device
00279  *
00280  * @v netdev            Network device
00281  */
00282 static void lan78xx_close ( struct net_device *netdev ) {
00283         struct smscusb_device *smscusb = netdev->priv;
00284 
00285         /* Close USB network device */
00286         usbnet_close ( &smscusb->usbnet );
00287 
00288         /* Dump statistics (for debugging) */
00289         if ( DBG_LOG )
00290                 smsc75xx_dump_statistics ( smscusb );
00291 
00292         /* Reset device */
00293         lan78xx_reset ( smscusb );
00294 }
00295 
00296 /** LAN78xx network device operations */
00297 static struct net_device_operations lan78xx_operations = {
00298         .open           = lan78xx_open,
00299         .close          = lan78xx_close,
00300         .transmit       = smsc75xx_transmit,
00301         .poll           = smsc75xx_poll,
00302 };
00303 
00304 /******************************************************************************
00305  *
00306  * USB interface
00307  *
00308  ******************************************************************************
00309  */
00310 
00311 /**
00312  * Probe device
00313  *
00314  * @v func              USB function
00315  * @v config            Configuration descriptor
00316  * @ret rc              Return status code
00317  */
00318 static int lan78xx_probe ( struct usb_function *func,
00319                            struct usb_configuration_descriptor *config ) {
00320         struct net_device *netdev;
00321         struct smscusb_device *smscusb;
00322         int rc;
00323 
00324         /* Allocate and initialise structure */
00325         netdev = alloc_etherdev ( sizeof ( *smscusb ) );
00326         if ( ! netdev ) {
00327                 rc = -ENOMEM;
00328                 goto err_alloc;
00329         }
00330         netdev_init ( netdev, &lan78xx_operations );
00331         netdev->dev = &func->dev;
00332         smscusb = netdev->priv;
00333         memset ( smscusb, 0, sizeof ( *smscusb ) );
00334         smscusb_init ( smscusb, netdev, func, &smsc75xx_in_operations );
00335         smscusb_mii_init ( smscusb, LAN78XX_MII_BASE,
00336                            LAN78XX_MII_PHY_INTR_SOURCE );
00337         usb_refill_init ( &smscusb->usbnet.in, 0, SMSC75XX_IN_MTU,
00338                           SMSC75XX_IN_MAX_FILL );
00339         DBGC ( smscusb, "LAN78XX %p on %s\n", smscusb, func->name );
00340 
00341         /* Describe USB network device */
00342         if ( ( rc = usbnet_describe ( &smscusb->usbnet, config ) ) != 0 ) {
00343                 DBGC ( smscusb, "LAN78XX %p could not describe: %s\n",
00344                        smscusb, strerror ( rc ) );
00345                 goto err_describe;
00346         }
00347 
00348         /* Reset device */
00349         if ( ( rc = lan78xx_reset ( smscusb ) ) != 0 )
00350                 goto err_reset;
00351 
00352         /* Read MAC address */
00353         if ( ( rc = lan78xx_fetch_mac ( smscusb ) ) != 0 )
00354                 goto err_fetch_mac;
00355 
00356         /* Register network device */
00357         if ( ( rc = register_netdev ( netdev ) ) != 0 )
00358                 goto err_register;
00359 
00360         usb_func_set_drvdata ( func, netdev );
00361         return 0;
00362 
00363         unregister_netdev ( netdev );
00364  err_register:
00365  err_fetch_mac:
00366  err_reset:
00367  err_describe:
00368         netdev_nullify ( netdev );
00369         netdev_put ( netdev );
00370  err_alloc:
00371         return rc;
00372 }
00373 
00374 /**
00375  * Remove device
00376  *
00377  * @v func              USB function
00378  */
00379 static void lan78xx_remove ( struct usb_function *func ) {
00380         struct net_device *netdev = usb_func_get_drvdata ( func );
00381 
00382         unregister_netdev ( netdev );
00383         netdev_nullify ( netdev );
00384         netdev_put ( netdev );
00385 }
00386 
00387 /** LAN78xx device IDs */
00388 static struct usb_device_id lan78xx_ids[] = {
00389         {
00390                 .name = "lan7800",
00391                 .vendor = 0x0424,
00392                 .product = 0x7800,
00393         },
00394         {
00395                 .name = "lan7850",
00396                 .vendor = 0x0424,
00397                 .product = 0x7850,
00398         },
00399 };
00400 
00401 /** LAN78xx driver */
00402 struct usb_driver lan78xx_driver __usb_driver = {
00403         .ids = lan78xx_ids,
00404         .id_count = ( sizeof ( lan78xx_ids ) / sizeof ( lan78xx_ids[0] ) ),
00405         .class = USB_CLASS_ID ( 0xff, 0x00, 0xff ),
00406         .score = USB_SCORE_NORMAL,
00407         .probe = lan78xx_probe,
00408         .remove = lan78xx_remove,
00409 };