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         /* Otherwise, generate a random MAC address */
00101         eth_random_addr ( netdev->hw_addr );
00102         DBGC ( smscusb, "LAN78XX %p using random MAC %s\n",
00103                smscusb, eth_ntoa ( netdev->hw_addr ) );
00104         return 0;
00105 }
00106 
00107 /******************************************************************************
00108  *
00109  * Device reset
00110  *
00111  ******************************************************************************
00112  */
00113 
00114 /**
00115  * Reset device
00116  *
00117  * @v smscusb           SMSC USB device
00118  * @ret rc              Return status code
00119  */
00120 static int lan78xx_reset ( struct smscusb_device *smscusb ) {
00121         uint32_t hw_cfg;
00122         unsigned int i;
00123         int rc;
00124 
00125         /* Reset device */
00126         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_HW_CFG,
00127                                      LAN78XX_HW_CFG_LRST ) ) != 0 )
00128                 return rc;
00129 
00130         /* Wait for reset to complete */
00131         for ( i = 0 ; i < LAN78XX_RESET_MAX_WAIT_MS ; i++ ) {
00132 
00133                 /* Check if reset has completed */
00134                 if ( ( rc = smscusb_readl ( smscusb, LAN78XX_HW_CFG,
00135                                             &hw_cfg ) ) != 0 )
00136                         return rc;
00137                 if ( ! ( hw_cfg & LAN78XX_HW_CFG_LRST ) )
00138                         return 0;
00139 
00140                 /* Delay */
00141                 mdelay ( 1 );
00142         }
00143 
00144         DBGC ( smscusb, "LAN78XX %p timed out waiting for reset\n",
00145                smscusb );
00146         return -ETIMEDOUT;
00147 }
00148 
00149 /******************************************************************************
00150  *
00151  * Network device interface
00152  *
00153  ******************************************************************************
00154  */
00155 
00156 /**
00157  * Open network device
00158  *
00159  * @v netdev            Network device
00160  * @ret rc              Return status code
00161  */
00162 static int lan78xx_open ( struct net_device *netdev ) {
00163         struct smscusb_device *smscusb = netdev->priv;
00164         uint32_t usb_cfg0;
00165         int rc;
00166 
00167         /* Clear stored interrupt status */
00168         smscusb->int_sts = 0;
00169 
00170         /* Configure bulk IN empty response */
00171         if ( ( rc = smscusb_readl ( smscusb, LAN78XX_USB_CFG0,
00172                                     &usb_cfg0 ) ) != 0 )
00173                 goto err_usb_cfg0_read;
00174         usb_cfg0 |= LAN78XX_USB_CFG0_BIR;
00175         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_USB_CFG0,
00176                                      usb_cfg0 ) ) != 0 )
00177                 goto err_usb_cfg0_write;
00178 
00179         /* Open USB network device */
00180         if ( ( rc = usbnet_open ( &smscusb->usbnet ) ) != 0 ) {
00181                 DBGC ( smscusb, "LAN78XX %p could not open: %s\n",
00182                        smscusb, strerror ( rc ) );
00183                 goto err_open;
00184         }
00185 
00186         /* Configure interrupt endpoint */
00187         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_INT_EP_CTL,
00188                                      ( LAN78XX_INT_EP_CTL_RDFO_EN |
00189                                        LAN78XX_INT_EP_CTL_PHY_EN ) ) ) != 0 )
00190                 goto err_int_ep_ctl;
00191 
00192         /* Configure bulk IN delay */
00193         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_BULK_IN_DLY,
00194                                      LAN78XX_BULK_IN_DLY_SET ( 0 ) ) ) != 0 )
00195                 goto err_bulk_in_dly;
00196 
00197         /* Configure receive filters */
00198         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_RFE_CTL,
00199                                      ( LAN78XX_RFE_CTL_AB |
00200                                        LAN78XX_RFE_CTL_AM |
00201                                        LAN78XX_RFE_CTL_AU ) ) ) != 0 )
00202                 goto err_rfe_ctl;
00203 
00204         /* Configure receive FIFO */
00205         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_FCT_RX_CTL,
00206                                      ( LAN78XX_FCT_RX_CTL_EN |
00207                                        LAN78XX_FCT_RX_CTL_BAD ) ) ) != 0 )
00208                 goto err_fct_rx_ctl;
00209 
00210         /* Configure transmit FIFO */
00211         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_FCT_TX_CTL,
00212                                      LAN78XX_FCT_TX_CTL_EN ) ) != 0 )
00213                 goto err_fct_tx_ctl;
00214 
00215         /* Configure receive datapath */
00216         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_MAC_RX,
00217                                      ( LAN78XX_MAC_RX_MAX_SIZE_DEFAULT |
00218                                        LAN78XX_MAC_RX_FCS |
00219                                        LAN78XX_MAC_RX_EN ) ) ) != 0 )
00220                 goto err_mac_rx;
00221 
00222         /* Configure transmit datapath */
00223         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_MAC_TX,
00224                                      LAN78XX_MAC_TX_EN ) ) != 0 )
00225                 goto err_mac_tx;
00226 
00227         /* Set MAC address */
00228         if ( ( rc = smscusb_set_address ( smscusb,
00229                                           LAN78XX_RX_ADDR_BASE ) ) != 0 )
00230                 goto err_set_address;
00231 
00232         /* Set MAC address perfect filter */
00233         if ( ( rc = smscusb_set_filter ( smscusb,
00234                                          LAN78XX_ADDR_FILT_BASE ) ) != 0 )
00235                 goto err_set_filter;
00236 
00237         /* Enable PHY interrupts and update link status */
00238         if ( ( rc = smscusb_mii_open ( smscusb, LAN78XX_MII_PHY_INTR_MASK,
00239                                        ( LAN78XX_PHY_INTR_ENABLE |
00240                                          LAN78XX_PHY_INTR_LINK |
00241                                          LAN78XX_PHY_INTR_ANEG_ERR |
00242                                          LAN78XX_PHY_INTR_ANEG_DONE ) ) ) != 0 )
00243                 goto err_mii_open;
00244 
00245         return 0;
00246 
00247  err_mii_open:
00248  err_set_filter:
00249  err_set_address:
00250  err_mac_tx:
00251  err_mac_rx:
00252  err_fct_tx_ctl:
00253  err_fct_rx_ctl:
00254  err_rfe_ctl:
00255  err_bulk_in_dly:
00256  err_int_ep_ctl:
00257         usbnet_close ( &smscusb->usbnet );
00258  err_open:
00259  err_usb_cfg0_write:
00260  err_usb_cfg0_read:
00261         lan78xx_reset ( smscusb );
00262         return rc;
00263 }
00264 
00265 /**
00266  * Close network device
00267  *
00268  * @v netdev            Network device
00269  */
00270 static void lan78xx_close ( struct net_device *netdev ) {
00271         struct smscusb_device *smscusb = netdev->priv;
00272 
00273         /* Close USB network device */
00274         usbnet_close ( &smscusb->usbnet );
00275 
00276         /* Dump statistics (for debugging) */
00277         if ( DBG_LOG )
00278                 smsc75xx_dump_statistics ( smscusb );
00279 
00280         /* Reset device */
00281         lan78xx_reset ( smscusb );
00282 }
00283 
00284 /** LAN78xx network device operations */
00285 static struct net_device_operations lan78xx_operations = {
00286         .open           = lan78xx_open,
00287         .close          = lan78xx_close,
00288         .transmit       = smsc75xx_transmit,
00289         .poll           = smsc75xx_poll,
00290 };
00291 
00292 /******************************************************************************
00293  *
00294  * USB interface
00295  *
00296  ******************************************************************************
00297  */
00298 
00299 /**
00300  * Probe device
00301  *
00302  * @v func              USB function
00303  * @v config            Configuration descriptor
00304  * @ret rc              Return status code
00305  */
00306 static int lan78xx_probe ( struct usb_function *func,
00307                            struct usb_configuration_descriptor *config ) {
00308         struct net_device *netdev;
00309         struct smscusb_device *smscusb;
00310         int rc;
00311 
00312         /* Allocate and initialise structure */
00313         netdev = alloc_etherdev ( sizeof ( *smscusb ) );
00314         if ( ! netdev ) {
00315                 rc = -ENOMEM;
00316                 goto err_alloc;
00317         }
00318         netdev_init ( netdev, &lan78xx_operations );
00319         netdev->dev = &func->dev;
00320         smscusb = netdev->priv;
00321         memset ( smscusb, 0, sizeof ( *smscusb ) );
00322         smscusb_init ( smscusb, netdev, func, &smsc75xx_in_operations );
00323         smscusb_mii_init ( smscusb, LAN78XX_MII_BASE,
00324                            LAN78XX_MII_PHY_INTR_SOURCE );
00325         usb_refill_init ( &smscusb->usbnet.in, 0, SMSC75XX_IN_MTU,
00326                           SMSC75XX_IN_MAX_FILL );
00327         DBGC ( smscusb, "LAN78XX %p on %s\n", smscusb, func->name );
00328 
00329         /* Describe USB network device */
00330         if ( ( rc = usbnet_describe ( &smscusb->usbnet, config ) ) != 0 ) {
00331                 DBGC ( smscusb, "LAN78XX %p could not describe: %s\n",
00332                        smscusb, strerror ( rc ) );
00333                 goto err_describe;
00334         }
00335 
00336         /* Reset device */
00337         if ( ( rc = lan78xx_reset ( smscusb ) ) != 0 )
00338                 goto err_reset;
00339 
00340         /* Read MAC address */
00341         if ( ( rc = lan78xx_fetch_mac ( smscusb ) ) != 0 )
00342                 goto err_fetch_mac;
00343 
00344         /* Register network device */
00345         if ( ( rc = register_netdev ( netdev ) ) != 0 )
00346                 goto err_register;
00347 
00348         usb_func_set_drvdata ( func, netdev );
00349         return 0;
00350 
00351         unregister_netdev ( netdev );
00352  err_register:
00353  err_fetch_mac:
00354  err_reset:
00355  err_describe:
00356         netdev_nullify ( netdev );
00357         netdev_put ( netdev );
00358  err_alloc:
00359         return rc;
00360 }
00361 
00362 /**
00363  * Remove device
00364  *
00365  * @v func              USB function
00366  */
00367 static void lan78xx_remove ( struct usb_function *func ) {
00368         struct net_device *netdev = usb_func_get_drvdata ( func );
00369 
00370         unregister_netdev ( netdev );
00371         netdev_nullify ( netdev );
00372         netdev_put ( netdev );
00373 }
00374 
00375 /** LAN78xx device IDs */
00376 static struct usb_device_id lan78xx_ids[] = {
00377         {
00378                 .name = "lan7800",
00379                 .vendor = 0x0424,
00380                 .product = 0x7800,
00381         },
00382         {
00383                 .name = "lan7850",
00384                 .vendor = 0x0424,
00385                 .product = 0x7850,
00386         },
00387 };
00388 
00389 /** LAN78xx driver */
00390 struct usb_driver lan78xx_driver __usb_driver = {
00391         .ids = lan78xx_ids,
00392         .id_count = ( sizeof ( lan78xx_ids ) / sizeof ( lan78xx_ids[0] ) ),
00393         .class = USB_CLASS_ID ( 0xff, 0x00, 0xff ),
00394         .score = USB_SCORE_NORMAL,
00395         .probe = lan78xx_probe,
00396         .remove = lan78xx_remove,
00397 };