iPXE
smsc75xx.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2015 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 <byteswap.h>
00030 #include <ipxe/ethernet.h>
00031 #include <ipxe/usb.h>
00032 #include <ipxe/usbnet.h>
00033 #include <ipxe/profile.h>
00034 #include "smsc75xx.h"
00035 
00036 /** @file
00037  *
00038  * SMSC LAN75xx USB Ethernet driver
00039  *
00040  */
00041 
00042 /** Bulk IN completion profiler */
00043 static struct profiler smsc75xx_in_profiler __profiler =
00044         { .name = "smsc75xx.in" };
00045 
00046 /** Bulk OUT profiler */
00047 static struct profiler smsc75xx_out_profiler __profiler =
00048         { .name = "smsc75xx.out" };
00049 
00050 /******************************************************************************
00051  *
00052  * Statistics (for debugging)
00053  *
00054  ******************************************************************************
00055  */
00056 
00057 /**
00058  * Dump statistics (for debugging)
00059  *
00060  * @v smscusb           SMSC USB device
00061  * @ret rc              Return status code
00062  */
00063 int smsc75xx_dump_statistics ( struct smscusb_device *smscusb ) {
00064         struct smsc75xx_statistics stats;
00065         int rc;
00066 
00067         /* Do nothing unless debugging is enabled */
00068         if ( ! DBG_LOG )
00069                 return 0;
00070 
00071         /* Get statistics */
00072         if ( ( rc = smscusb_get_statistics ( smscusb, 0, &stats,
00073                                              sizeof ( stats ) ) ) != 0 ) {
00074                 DBGC ( smscusb, "SMSC75XX %p could not get statistics: "
00075                        "%s\n", smscusb, strerror ( rc ) );
00076                 return rc;
00077         }
00078 
00079         /* Dump statistics */
00080         DBGC ( smscusb, "SMSC75XX %p RXE fcs %d aln %d frg %d jab %d und %d "
00081                "ovr %d drp %d\n", smscusb, le32_to_cpu ( stats.rx.err.fcs ),
00082                le32_to_cpu ( stats.rx.err.alignment ),
00083                le32_to_cpu ( stats.rx.err.fragment ),
00084                le32_to_cpu ( stats.rx.err.jabber ),
00085                le32_to_cpu ( stats.rx.err.undersize ),
00086                le32_to_cpu ( stats.rx.err.oversize ),
00087                le32_to_cpu ( stats.rx.err.dropped ) );
00088         DBGC ( smscusb, "SMSC75XX %p RXB ucast %d bcast %d mcast %d\n",
00089                smscusb, le32_to_cpu ( stats.rx.byte.unicast ),
00090                le32_to_cpu ( stats.rx.byte.broadcast ),
00091                le32_to_cpu ( stats.rx.byte.multicast ) );
00092         DBGC ( smscusb, "SMSC75XX %p RXF ucast %d bcast %d mcast %d pause "
00093                "%d\n", smscusb, le32_to_cpu ( stats.rx.frame.unicast ),
00094                le32_to_cpu ( stats.rx.frame.broadcast ),
00095                le32_to_cpu ( stats.rx.frame.multicast ),
00096                le32_to_cpu ( stats.rx.frame.pause ) );
00097         DBGC ( smscusb, "SMSC75XX %p TXE fcs %d def %d car %d cnt %d sgl %d "
00098                "mul %d exc %d lat %d\n", smscusb,
00099                le32_to_cpu ( stats.tx.err.fcs ),
00100                le32_to_cpu ( stats.tx.err.deferral ),
00101                le32_to_cpu ( stats.tx.err.carrier ),
00102                le32_to_cpu ( stats.tx.err.count ),
00103                le32_to_cpu ( stats.tx.err.single ),
00104                le32_to_cpu ( stats.tx.err.multiple ),
00105                le32_to_cpu ( stats.tx.err.excessive ),
00106                le32_to_cpu ( stats.tx.err.late ) );
00107         DBGC ( smscusb, "SMSC75XX %p TXB ucast %d bcast %d mcast %d\n",
00108                smscusb, le32_to_cpu ( stats.tx.byte.unicast ),
00109                le32_to_cpu ( stats.tx.byte.broadcast ),
00110                le32_to_cpu ( stats.tx.byte.multicast ) );
00111         DBGC ( smscusb, "SMSC75XX %p TXF ucast %d bcast %d mcast %d pause "
00112                "%d\n", smscusb, le32_to_cpu ( stats.tx.frame.unicast ),
00113                le32_to_cpu ( stats.tx.frame.broadcast ),
00114                le32_to_cpu ( stats.tx.frame.multicast ),
00115                le32_to_cpu ( stats.tx.frame.pause ) );
00116 
00117         return 0;
00118 }
00119 
00120 /******************************************************************************
00121  *
00122  * Device reset
00123  *
00124  ******************************************************************************
00125  */
00126 
00127 /**
00128  * Reset device
00129  *
00130  * @v smscusb           SMSC USB device
00131  * @ret rc              Return status code
00132  */
00133 static int smsc75xx_reset ( struct smscusb_device *smscusb ) {
00134         uint32_t hw_cfg;
00135         unsigned int i;
00136         int rc;
00137 
00138         /* Reset device */
00139         if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_HW_CFG,
00140                                      SMSC75XX_HW_CFG_LRST ) ) != 0 )
00141                 return rc;
00142 
00143         /* Wait for reset to complete */
00144         for ( i = 0 ; i < SMSC75XX_RESET_MAX_WAIT_MS ; i++ ) {
00145 
00146                 /* Check if reset has completed */
00147                 if ( ( rc = smscusb_readl ( smscusb, SMSC75XX_HW_CFG,
00148                                             &hw_cfg ) ) != 0 )
00149                         return rc;
00150                 if ( ! ( hw_cfg & SMSC75XX_HW_CFG_LRST ) )
00151                         return 0;
00152 
00153                 /* Delay */
00154                 mdelay ( 1 );
00155         }
00156 
00157         DBGC ( smscusb, "SMSC75XX %p timed out waiting for reset\n",
00158                smscusb );
00159         return -ETIMEDOUT;
00160 }
00161 
00162 /******************************************************************************
00163  *
00164  * Endpoint operations
00165  *
00166  ******************************************************************************
00167  */
00168 
00169 /**
00170  * Complete bulk IN transfer
00171  *
00172  * @v ep                USB endpoint
00173  * @v iobuf             I/O buffer
00174  * @v rc                Completion status code
00175  */
00176 static void smsc75xx_in_complete ( struct usb_endpoint *ep,
00177                                    struct io_buffer *iobuf, int rc ) {
00178         struct smscusb_device *smscusb =
00179                 container_of ( ep, struct smscusb_device, usbnet.in );
00180         struct net_device *netdev = smscusb->netdev;
00181         struct smsc75xx_rx_header *header;
00182 
00183         /* Profile completions */
00184         profile_start ( &smsc75xx_in_profiler );
00185 
00186         /* Ignore packets cancelled when the endpoint closes */
00187         if ( ! ep->open ) {
00188                 free_iob ( iobuf );
00189                 return;
00190         }
00191 
00192         /* Record USB errors against the network device */
00193         if ( rc != 0 ) {
00194                 DBGC ( smscusb, "SMSC75XX %p bulk IN failed: %s\n",
00195                        smscusb, strerror ( rc ) );
00196                 goto err;
00197         }
00198 
00199         /* Sanity check */
00200         if ( iob_len ( iobuf ) < ( sizeof ( *header ) ) ) {
00201                 DBGC ( smscusb, "SMSC75XX %p underlength bulk IN\n",
00202                        smscusb );
00203                 DBGC_HDA ( smscusb, 0, iobuf->data, iob_len ( iobuf ) );
00204                 rc = -EINVAL;
00205                 goto err;
00206         }
00207 
00208         /* Strip header */
00209         header = iobuf->data;
00210         iob_pull ( iobuf, sizeof ( *header ) );
00211 
00212         /* Check for errors */
00213         if ( header->command & cpu_to_le32 ( SMSC75XX_RX_RED ) ) {
00214                 DBGC ( smscusb, "SMSC75XX %p receive error (%08x):\n",
00215                        smscusb, le32_to_cpu ( header->command ) );
00216                 DBGC_HDA ( smscusb, 0, iobuf->data, iob_len ( iobuf ) );
00217                 rc = -EIO;
00218                 goto err;
00219         }
00220 
00221         /* Hand off to network stack */
00222         netdev_rx ( netdev, iob_disown ( iobuf ) );
00223 
00224         profile_stop ( &smsc75xx_in_profiler );
00225         return;
00226 
00227  err:
00228         /* Hand off to network stack */
00229         netdev_rx_err ( netdev, iob_disown ( iobuf ), rc );
00230 }
00231 
00232 /** Bulk IN endpoint operations */
00233 struct usb_endpoint_driver_operations smsc75xx_in_operations = {
00234         .complete = smsc75xx_in_complete,
00235 };
00236 
00237 /**
00238  * Transmit packet
00239  *
00240  * @v smscusb           SMSC USB device
00241  * @v iobuf             I/O buffer
00242  * @ret rc              Return status code
00243  */
00244 static int smsc75xx_out_transmit ( struct smscusb_device *smscusb,
00245                                    struct io_buffer *iobuf ) {
00246         struct smsc75xx_tx_header *header;
00247         size_t len = iob_len ( iobuf );
00248         int rc;
00249 
00250         /* Profile transmissions */
00251         profile_start ( &smsc75xx_out_profiler );
00252 
00253         /* Prepend header */
00254         if ( ( rc = iob_ensure_headroom ( iobuf, sizeof ( *header ) ) ) != 0 )
00255                 return rc;
00256         header = iob_push ( iobuf, sizeof ( *header ) );
00257         header->command = cpu_to_le32 ( SMSC75XX_TX_FCS | len );
00258         header->tag = 0;
00259         header->mss = 0;
00260 
00261         /* Enqueue I/O buffer */
00262         if ( ( rc = usb_stream ( &smscusb->usbnet.out, iobuf, 0 ) ) != 0 )
00263                 return rc;
00264 
00265         profile_stop ( &smsc75xx_out_profiler );
00266         return 0;
00267 }
00268 
00269 /******************************************************************************
00270  *
00271  * Network device interface
00272  *
00273  ******************************************************************************
00274  */
00275 
00276 /**
00277  * Open network device
00278  *
00279  * @v netdev            Network device
00280  * @ret rc              Return status code
00281  */
00282 static int smsc75xx_open ( struct net_device *netdev ) {
00283         struct smscusb_device *smscusb = netdev->priv;
00284         int rc;
00285 
00286         /* Clear stored interrupt status */
00287         smscusb->int_sts = 0;
00288 
00289         /* Configure bulk IN empty response */
00290         if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_HW_CFG,
00291                                      SMSC75XX_HW_CFG_BIR ) ) != 0 )
00292                 goto err_hw_cfg;
00293 
00294         /* Open USB network device */
00295         if ( ( rc = usbnet_open ( &smscusb->usbnet ) ) != 0 ) {
00296                 DBGC ( smscusb, "SMSC75XX %p could not open: %s\n",
00297                        smscusb, strerror ( rc ) );
00298                 goto err_open;
00299         }
00300 
00301         /* Configure interrupt endpoint */
00302         if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_INT_EP_CTL,
00303                                      ( SMSC75XX_INT_EP_CTL_RDFO_EN |
00304                                        SMSC75XX_INT_EP_CTL_PHY_EN ) ) ) != 0 )
00305                 goto err_int_ep_ctl;
00306 
00307         /* Configure bulk IN delay */
00308         if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_BULK_IN_DLY,
00309                                      SMSC75XX_BULK_IN_DLY_SET ( 0 ) ) ) != 0 )
00310                 goto err_bulk_in_dly;
00311 
00312         /* Configure receive filters */
00313         if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_RFE_CTL,
00314                                      ( SMSC75XX_RFE_CTL_AB |
00315                                        SMSC75XX_RFE_CTL_AM |
00316                                        SMSC75XX_RFE_CTL_AU ) ) ) != 0 )
00317                 goto err_rfe_ctl;
00318 
00319         /* Configure receive FIFO */
00320         if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_FCT_RX_CTL,
00321                                      ( SMSC75XX_FCT_RX_CTL_EN |
00322                                        SMSC75XX_FCT_RX_CTL_BAD ) ) ) != 0 )
00323                 goto err_fct_rx_ctl;
00324 
00325         /* Configure transmit FIFO */
00326         if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_FCT_TX_CTL,
00327                                      SMSC75XX_FCT_TX_CTL_EN ) ) != 0 )
00328                 goto err_fct_tx_ctl;
00329 
00330         /* Configure receive datapath */
00331         if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_MAC_RX,
00332                                      ( SMSC75XX_MAC_RX_MAX_SIZE_DEFAULT |
00333                                        SMSC75XX_MAC_RX_FCS |
00334                                        SMSC75XX_MAC_RX_EN ) ) ) != 0 )
00335                 goto err_mac_rx;
00336 
00337         /* Configure transmit datapath */
00338         if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_MAC_TX,
00339                                      SMSC75XX_MAC_TX_EN ) ) != 0 )
00340                 goto err_mac_tx;
00341 
00342         /* Set MAC address */
00343         if ( ( rc = smscusb_set_address ( smscusb,
00344                                           SMSC75XX_RX_ADDR_BASE ) ) != 0 )
00345                 goto err_set_address;
00346 
00347         /* Set MAC address perfect filter */
00348         if ( ( rc = smscusb_set_filter ( smscusb,
00349                                          SMSC75XX_ADDR_FILT_BASE ) ) != 0 )
00350                 goto err_set_filter;
00351 
00352         /* Enable PHY interrupts and update link status */
00353         if ( ( rc = smscusb_mii_open ( smscusb, SMSC75XX_MII_PHY_INTR_MASK,
00354                                        ( SMSC75XX_PHY_INTR_ANEG_DONE |
00355                                          SMSC75XX_PHY_INTR_LINK_DOWN ) ) ) != 0)
00356                 goto err_mii_open;
00357 
00358         return 0;
00359 
00360  err_mii_open:
00361  err_set_filter:
00362  err_set_address:
00363  err_mac_tx:
00364  err_mac_rx:
00365  err_fct_tx_ctl:
00366  err_fct_rx_ctl:
00367  err_rfe_ctl:
00368  err_bulk_in_dly:
00369  err_int_ep_ctl:
00370         usbnet_close ( &smscusb->usbnet );
00371  err_open:
00372  err_hw_cfg:
00373         smsc75xx_reset ( smscusb );
00374         return rc;
00375 }
00376 
00377 /**
00378  * Close network device
00379  *
00380  * @v netdev            Network device
00381  */
00382 static void smsc75xx_close ( struct net_device *netdev ) {
00383         struct smscusb_device *smscusb = netdev->priv;
00384 
00385         /* Close USB network device */
00386         usbnet_close ( &smscusb->usbnet );
00387 
00388         /* Dump statistics (for debugging) */
00389         if ( DBG_LOG )
00390                 smsc75xx_dump_statistics ( smscusb );
00391 
00392         /* Reset device */
00393         smsc75xx_reset ( smscusb );
00394 }
00395 
00396 /**
00397  * Transmit packet
00398  *
00399  * @v netdev            Network device
00400  * @v iobuf             I/O buffer
00401  * @ret rc              Return status code
00402  */
00403 int smsc75xx_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) {
00404         struct smscusb_device *smscusb = netdev->priv;
00405         int rc;
00406 
00407         /* Transmit packet */
00408         if ( ( rc = smsc75xx_out_transmit ( smscusb, iobuf ) ) != 0 )
00409                 return rc;
00410 
00411         return 0;
00412 }
00413 
00414 /**
00415  * Poll for completed and received packets
00416  *
00417  * @v netdev            Network device
00418  */
00419 void smsc75xx_poll ( struct net_device *netdev ) {
00420         struct smscusb_device *smscusb = netdev->priv;
00421         uint32_t int_sts;
00422         int rc;
00423 
00424         /* Poll USB bus */
00425         usb_poll ( smscusb->bus );
00426 
00427         /* Refill endpoints */
00428         if ( ( rc = usbnet_refill ( &smscusb->usbnet ) ) != 0 )
00429                 netdev_rx_err ( netdev, NULL, rc );
00430 
00431         /* Do nothing more unless there are interrupts to handle */
00432         int_sts = smscusb->int_sts;
00433         if ( ! int_sts )
00434                 return;
00435 
00436         /* Check link status if applicable */
00437         if ( int_sts & SMSC75XX_INT_STS_PHY_INT ) {
00438                 smscusb_mii_check_link ( smscusb );
00439                 int_sts &= ~SMSC75XX_INT_STS_PHY_INT;
00440         }
00441 
00442         /* Record RX FIFO overflow if applicable */
00443         if ( int_sts & SMSC75XX_INT_STS_RDFO_INT ) {
00444                 DBGC2 ( smscusb, "SMSC75XX %p RX FIFO overflowed\n", smscusb );
00445                 netdev_rx_err ( netdev, NULL, -ENOBUFS );
00446                 int_sts &= ~SMSC75XX_INT_STS_RDFO_INT;
00447         }
00448 
00449         /* Check for unexpected interrupts */
00450         if ( int_sts ) {
00451                 DBGC ( smscusb, "SMSC75XX %p unexpected interrupt %#08x\n",
00452                        smscusb, int_sts );
00453                 netdev_rx_err ( netdev, NULL, -ENOTTY );
00454         }
00455 
00456         /* Clear interrupts */
00457         if ( ( rc = smscusb_writel ( smscusb, SMSC75XX_INT_STS,
00458                                      smscusb->int_sts ) ) != 0 )
00459                 netdev_rx_err ( netdev, NULL, rc );
00460         smscusb->int_sts = 0;
00461 }
00462 
00463 /** SMSC75xx network device operations */
00464 static struct net_device_operations smsc75xx_operations = {
00465         .open           = smsc75xx_open,
00466         .close          = smsc75xx_close,
00467         .transmit       = smsc75xx_transmit,
00468         .poll           = smsc75xx_poll,
00469 };
00470 
00471 /******************************************************************************
00472  *
00473  * USB interface
00474  *
00475  ******************************************************************************
00476  */
00477 
00478 /**
00479  * Probe device
00480  *
00481  * @v func              USB function
00482  * @v config            Configuration descriptor
00483  * @ret rc              Return status code
00484  */
00485 static int smsc75xx_probe ( struct usb_function *func,
00486                             struct usb_configuration_descriptor *config ) {
00487         struct net_device *netdev;
00488         struct smscusb_device *smscusb;
00489         int rc;
00490 
00491         /* Allocate and initialise structure */
00492         netdev = alloc_etherdev ( sizeof ( *smscusb ) );
00493         if ( ! netdev ) {
00494                 rc = -ENOMEM;
00495                 goto err_alloc;
00496         }
00497         netdev_init ( netdev, &smsc75xx_operations );
00498         netdev->dev = &func->dev;
00499         smscusb = netdev->priv;
00500         memset ( smscusb, 0, sizeof ( *smscusb ) );
00501         smscusb_init ( smscusb, netdev, func, &smsc75xx_in_operations );
00502         smscusb_mii_init ( smscusb, SMSC75XX_MII_BASE,
00503                            SMSC75XX_MII_PHY_INTR_SOURCE );
00504         usb_refill_init ( &smscusb->usbnet.in, 0, SMSC75XX_IN_MTU,
00505                           SMSC75XX_IN_MAX_FILL );
00506         DBGC ( smscusb, "SMSC75XX %p on %s\n", smscusb, func->name );
00507 
00508         /* Describe USB network device */
00509         if ( ( rc = usbnet_describe ( &smscusb->usbnet, config ) ) != 0 ) {
00510                 DBGC ( smscusb, "SMSC75XX %p could not describe: %s\n",
00511                        smscusb, strerror ( rc ) );
00512                 goto err_describe;
00513         }
00514 
00515         /* Reset device */
00516         if ( ( rc = smsc75xx_reset ( smscusb ) ) != 0 )
00517                 goto err_reset;
00518 
00519         /* Read MAC address */
00520         if ( ( rc = smscusb_eeprom_fetch_mac ( smscusb,
00521                                                SMSC75XX_E2P_BASE ) ) != 0 )
00522                 goto err_fetch_mac;
00523 
00524         /* Register network device */
00525         if ( ( rc = register_netdev ( netdev ) ) != 0 )
00526                 goto err_register;
00527 
00528         usb_func_set_drvdata ( func, netdev );
00529         return 0;
00530 
00531         unregister_netdev ( netdev );
00532  err_register:
00533  err_fetch_mac:
00534  err_reset:
00535  err_describe:
00536         netdev_nullify ( netdev );
00537         netdev_put ( netdev );
00538  err_alloc:
00539         return rc;
00540 }
00541 
00542 /**
00543  * Remove device
00544  *
00545  * @v func              USB function
00546  */
00547 static void smsc75xx_remove ( struct usb_function *func ) {
00548         struct net_device *netdev = usb_func_get_drvdata ( func );
00549 
00550         unregister_netdev ( netdev );
00551         netdev_nullify ( netdev );
00552         netdev_put ( netdev );
00553 }
00554 
00555 /** SMSC75xx device IDs */
00556 static struct usb_device_id smsc75xx_ids[] = {
00557         {
00558                 .name = "smsc7500",
00559                 .vendor = 0x0424,
00560                 .product = 0x7500,
00561         },
00562         {
00563                 .name = "smsc7505",
00564                 .vendor = 0x0424,
00565                 .product = 0x7505,
00566         },
00567 };
00568 
00569 /** SMSC LAN75xx driver */
00570 struct usb_driver smsc75xx_driver __usb_driver = {
00571         .ids = smsc75xx_ids,
00572         .id_count = ( sizeof ( smsc75xx_ids ) / sizeof ( smsc75xx_ids[0] ) ),
00573         .class = USB_CLASS_ID ( 0xff, 0x00, 0xff ),
00574         .score = USB_SCORE_NORMAL,
00575         .probe = smsc75xx_probe,
00576         .remove = smsc75xx_remove,
00577 };