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         /* Configure receive filters */
00202         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_RFE_CTL,
00203                                      ( LAN78XX_RFE_CTL_AB |
00204                                        LAN78XX_RFE_CTL_AM |
00205                                        LAN78XX_RFE_CTL_AU ) ) ) != 0 )
00206                 goto err_rfe_ctl;
00207 
00208         /* Configure receive FIFO */
00209         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_FCT_RX_CTL,
00210                                      ( LAN78XX_FCT_RX_CTL_EN |
00211                                        LAN78XX_FCT_RX_CTL_BAD ) ) ) != 0 )
00212                 goto err_fct_rx_ctl;
00213 
00214         /* Configure transmit FIFO */
00215         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_FCT_TX_CTL,
00216                                      LAN78XX_FCT_TX_CTL_EN ) ) != 0 )
00217                 goto err_fct_tx_ctl;
00218 
00219         /* Configure receive datapath */
00220         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_MAC_RX,
00221                                      ( LAN78XX_MAC_RX_MAX_SIZE_DEFAULT |
00222                                        LAN78XX_MAC_RX_FCS |
00223                                        LAN78XX_MAC_RX_EN ) ) ) != 0 )
00224                 goto err_mac_rx;
00225 
00226         /* Configure transmit datapath */
00227         if ( ( rc = smscusb_writel ( smscusb, LAN78XX_MAC_TX,
00228                                      LAN78XX_MAC_TX_EN ) ) != 0 )
00229                 goto err_mac_tx;
00230 
00231         /* Set MAC address */
00232         if ( ( rc = smscusb_set_address ( smscusb,
00233                                           LAN78XX_RX_ADDR_BASE ) ) != 0 )
00234                 goto err_set_address;
00235 
00236         /* Set MAC address perfect filter */
00237         if ( ( rc = smscusb_set_filter ( smscusb,
00238                                          LAN78XX_ADDR_FILT_BASE ) ) != 0 )
00239                 goto err_set_filter;
00240 
00241         /* Enable PHY interrupts and update link status */
00242         if ( ( rc = smscusb_mii_open ( smscusb, LAN78XX_MII_PHY_INTR_MASK,
00243                                        ( LAN78XX_PHY_INTR_ENABLE |
00244                                          LAN78XX_PHY_INTR_LINK |
00245                                          LAN78XX_PHY_INTR_ANEG_ERR |
00246                                          LAN78XX_PHY_INTR_ANEG_DONE ) ) ) != 0 )
00247                 goto err_mii_open;
00248 
00249         return 0;
00250 
00251  err_mii_open:
00252  err_set_filter:
00253  err_set_address:
00254  err_mac_tx:
00255  err_mac_rx:
00256  err_fct_tx_ctl:
00257  err_fct_rx_ctl:
00258  err_rfe_ctl:
00259  err_bulk_in_dly:
00260  err_int_ep_ctl:
00261         usbnet_close ( &smscusb->usbnet );
00262  err_open:
00263  err_usb_cfg0_write:
00264  err_usb_cfg0_read:
00265         lan78xx_reset ( smscusb );
00266         return rc;
00267 }
00268 
00269 /**
00270  * Close network device
00271  *
00272  * @v netdev            Network device
00273  */
00274 static void lan78xx_close ( struct net_device *netdev ) {
00275         struct smscusb_device *smscusb = netdev->priv;
00276 
00277         /* Close USB network device */
00278         usbnet_close ( &smscusb->usbnet );
00279 
00280         /* Dump statistics (for debugging) */
00281         if ( DBG_LOG )
00282                 smsc75xx_dump_statistics ( smscusb );
00283 
00284         /* Reset device */
00285         lan78xx_reset ( smscusb );
00286 }
00287 
00288 /** LAN78xx network device operations */
00289 static struct net_device_operations lan78xx_operations = {
00290         .open           = lan78xx_open,
00291         .close          = lan78xx_close,
00292         .transmit       = smsc75xx_transmit,
00293         .poll           = smsc75xx_poll,
00294 };
00295 
00296 /******************************************************************************
00297  *
00298  * USB interface
00299  *
00300  ******************************************************************************
00301  */
00302 
00303 /**
00304  * Probe device
00305  *
00306  * @v func              USB function
00307  * @v config            Configuration descriptor
00308  * @ret rc              Return status code
00309  */
00310 static int lan78xx_probe ( struct usb_function *func,
00311                            struct usb_configuration_descriptor *config ) {
00312         struct net_device *netdev;
00313         struct smscusb_device *smscusb;
00314         int rc;
00315 
00316         /* Allocate and initialise structure */
00317         netdev = alloc_etherdev ( sizeof ( *smscusb ) );
00318         if ( ! netdev ) {
00319                 rc = -ENOMEM;
00320                 goto err_alloc;
00321         }
00322         netdev_init ( netdev, &lan78xx_operations );
00323         netdev->dev = &func->dev;
00324         smscusb = netdev->priv;
00325         memset ( smscusb, 0, sizeof ( *smscusb ) );
00326         smscusb_init ( smscusb, netdev, func, &smsc75xx_in_operations );
00327         smscusb_mii_init ( smscusb, LAN78XX_MII_BASE,
00328                            LAN78XX_MII_PHY_INTR_SOURCE );
00329         usb_refill_init ( &smscusb->usbnet.in, 0, SMSC75XX_IN_MTU,
00330                           SMSC75XX_IN_MAX_FILL );
00331         DBGC ( smscusb, "LAN78XX %p on %s\n", smscusb, func->name );
00332 
00333         /* Describe USB network device */
00334         if ( ( rc = usbnet_describe ( &smscusb->usbnet, config ) ) != 0 ) {
00335                 DBGC ( smscusb, "LAN78XX %p could not describe: %s\n",
00336                        smscusb, strerror ( rc ) );
00337                 goto err_describe;
00338         }
00339 
00340         /* Reset device */
00341         if ( ( rc = lan78xx_reset ( smscusb ) ) != 0 )
00342                 goto err_reset;
00343 
00344         /* Read MAC address */
00345         if ( ( rc = lan78xx_fetch_mac ( smscusb ) ) != 0 )
00346                 goto err_fetch_mac;
00347 
00348         /* Register network device */
00349         if ( ( rc = register_netdev ( netdev ) ) != 0 )
00350                 goto err_register;
00351 
00352         usb_func_set_drvdata ( func, netdev );
00353         return 0;
00354 
00355         unregister_netdev ( netdev );
00356  err_register:
00357  err_fetch_mac:
00358  err_reset:
00359  err_describe:
00360         netdev_nullify ( netdev );
00361         netdev_put ( netdev );
00362  err_alloc:
00363         return rc;
00364 }
00365 
00366 /**
00367  * Remove device
00368  *
00369  * @v func              USB function
00370  */
00371 static void lan78xx_remove ( struct usb_function *func ) {
00372         struct net_device *netdev = usb_func_get_drvdata ( func );
00373 
00374         unregister_netdev ( netdev );
00375         netdev_nullify ( netdev );
00376         netdev_put ( netdev );
00377 }
00378 
00379 /** LAN78xx device IDs */
00380 static struct usb_device_id lan78xx_ids[] = {
00381         {
00382                 .name = "lan7800",
00383                 .vendor = 0x0424,
00384                 .product = 0x7800,
00385         },
00386         {
00387                 .name = "lan7850",
00388                 .vendor = 0x0424,
00389                 .product = 0x7850,
00390         },
00391 };
00392 
00393 /** LAN78xx driver */
00394 struct usb_driver lan78xx_driver __usb_driver = {
00395         .ids = lan78xx_ids,
00396         .id_count = ( sizeof ( lan78xx_ids ) / sizeof ( lan78xx_ids[0] ) ),
00397         .class = USB_CLASS_ID ( 0xff, 0x00, 0xff ),
00398         .score = USB_SCORE_NORMAL,
00399         .probe = lan78xx_probe,
00400         .remove = lan78xx_remove,
00401 };