| #ifndef _SMSCUSB_H |
| #define _SMSCUSB_H |
| |
| /** @file |
| * |
| * SMSC USB Ethernet drivers |
| * |
| */ |
| |
| FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); |
| |
| #include <stdint.h> |
| #include <string.h> |
| #include <byteswap.h> |
| #include <ipxe/usb.h> |
| #include <ipxe/usbnet.h> |
| #include <ipxe/netdevice.h> |
| #include <ipxe/mii.h> |
| #include <ipxe/if_ether.h> |
| |
| /** Register write command */ |
| #define SMSCUSB_REGISTER_WRITE \ |
| ( USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ |
| USB_REQUEST_TYPE ( 0xa0 ) ) |
| |
| /** Register read command */ |
| #define SMSCUSB_REGISTER_READ \ |
| ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ |
| USB_REQUEST_TYPE ( 0xa1 ) ) |
| |
| /** Get statistics command */ |
| #define SMSCUSB_GET_STATISTICS \ |
| ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ |
| USB_REQUEST_TYPE ( 0xa2 ) ) |
| |
| /** EEPROM command register offset */ |
| #define SMSCUSB_E2P_CMD 0x000 |
| #define SMSCUSB_E2P_CMD_EPC_BSY 0x80000000UL /**< EPC busy */ |
| #define SMSCUSB_E2P_CMD_EPC_CMD_READ 0x00000000UL /**< READ command */ |
| #define SMSCUSB_E2P_CMD_EPC_ADDR(addr) ( (addr) << 0 ) /**< EPC address */ |
| |
| /** EEPROM data register offset */ |
| #define SMSCUSB_E2P_DATA 0x004 |
| #define SMSCUSB_E2P_DATA_GET(e2p_data) \ |
| ( ( (e2p_data) >> 0 ) & 0xff ) /**< EEPROM data */ |
| |
| /** MAC address EEPROM address */ |
| #define SMSCUSB_EEPROM_MAC 0x01 |
| |
| /** Maximum time to wait for EEPROM (in milliseconds) */ |
| #define SMSCUSB_EEPROM_MAX_WAIT_MS 100 |
| |
| /** OTP power register offset */ |
| #define SMSCUSB_OTP_POWER 0x000 |
| #define SMSCUSB_OTP_POWER_DOWN 0x00000001UL /**< OTP power down */ |
| |
| /** OTP address high byte register offset */ |
| #define SMSCUSB_OTP_ADDRH 0x004 |
| |
| /** OTP address low byte register offset */ |
| #define SMSCUSB_OTP_ADDRL 0x008 |
| |
| /** OTP data register offset */ |
| #define SMSCUSB_OTP_DATA 0x018 |
| #define SMSCUSB_OTP_DATA_GET(otp_data) \ |
| ( ( (otp_data) >> 0 ) & 0xff ) /**< OTP data */ |
| |
| /** OTP command selection register offset */ |
| #define SMSCUSB_OTP_CMD 0x020 |
| #define SMSCUSB_OTP_CMD_READ 0x00000001UL /**< Read command */ |
| |
| /** OTP command initiation register offset */ |
| #define SMSCUSB_OTP_GO 0x028 |
| #define SMSCUSB_OTP_GO_GO 0x00000001UL /**< Initiate command */ |
| |
| /** OTP status register offset */ |
| #define SMSCUSB_OTP_STATUS 0x030 |
| #define SMSCUSB_OTP_STATUS_BUSY 0x00000001UL /**< OTP busy */ |
| |
| /** Maximum time to wait for OTP (in milliseconds) */ |
| #define SMSCUSB_OTP_MAX_WAIT_MS 100 |
| |
| /** OTP layout 1 signature */ |
| #define SMSCUSB_OTP_1_SIG 0xf3 |
| |
| /** OTP layout 1 MAC address offset */ |
| #define SMSCUSB_OTP_1_MAC 0x001 |
| |
| /** OTP layout 2 signature */ |
| #define SMSCUSB_OTP_2_SIG 0xf7 |
| |
| /** OTP layout 2 MAC address offset */ |
| #define SMSCUSB_OTP_2_MAC 0x101 |
| |
| /** MII access register offset */ |
| #define SMSCUSB_MII_ACCESS 0x000 |
| #define SMSCUSB_MII_ACCESS_PHY_ADDRESS 0x00000800UL /**< PHY address */ |
| #define SMSCUSB_MII_ACCESS_MIIRINDA(addr) ( (addr) << 6 ) /**< MII register */ |
| #define SMSCUSB_MII_ACCESS_MIIWNR 0x00000002UL /**< MII write */ |
| #define SMSCUSB_MII_ACCESS_MIIBZY 0x00000001UL /**< MII busy */ |
| |
| /** MII data register offset */ |
| #define SMSCUSB_MII_DATA 0x004 |
| #define SMSCUSB_MII_DATA_SET(data) ( (data) << 0 ) /**< Set data */ |
| #define SMSCUSB_MII_DATA_GET(mii_data) \ |
| ( ( (mii_data) >> 0 ) & 0xffff ) /**< Get data */ |
| |
| /** Maximum time to wait for MII (in milliseconds) */ |
| #define SMSCUSB_MII_MAX_WAIT_MS 100 |
| |
| /** MAC address */ |
| union smscusb_mac { |
| /** MAC receive address registers */ |
| struct { |
| /** MAC receive address low register */ |
| uint32_t l; |
| /** MAC receive address high register */ |
| uint32_t h; |
| } __attribute__ (( packed )) addr; |
| /** Raw MAC address */ |
| uint8_t raw[ETH_ALEN]; |
| }; |
| |
| /** MAC receive address high register offset */ |
| #define SMSCUSB_RX_ADDRH 0x000 |
| |
| /** MAC receive address low register offset */ |
| #define SMSCUSB_RX_ADDRL 0x004 |
| |
| /** MAC address perfect filter N high register offset */ |
| #define SMSCUSB_ADDR_FILTH(n) ( 0x000 + ( 8 * (n) ) ) |
| #define SMSCUSB_ADDR_FILTH_VALID 0x80000000UL /**< Address valid */ |
| |
| /** MAC address perfect filter N low register offset */ |
| #define SMSCUSB_ADDR_FILTL(n) ( 0x004 + ( 8 * (n) ) ) |
| |
| /** Interrupt packet format */ |
| struct smscusb_interrupt { |
| /** Current value of INT_STS register */ |
| uint32_t int_sts; |
| } __attribute__ (( packed )); |
| |
| /** An SMSC USB device */ |
| struct smscusb_device { |
| /** USB device */ |
| struct usb_device *usb; |
| /** USB bus */ |
| struct usb_bus *bus; |
| /** Network device */ |
| struct net_device *netdev; |
| /** USB network device */ |
| struct usbnet_device usbnet; |
| /** MII interface */ |
| struct mii_interface mdio; |
| /** MII device */ |
| struct mii_device mii; |
| /** MII register base */ |
| uint16_t mii_base; |
| /** PHY interrupt source register */ |
| uint16_t phy_source; |
| /** Interrupt status */ |
| uint32_t int_sts; |
| }; |
| |
| extern int smscusb_raw_writel ( struct smscusb_device *smscusb, |
| unsigned int address, uint32_t value ); |
| extern int smscusb_raw_readl ( struct smscusb_device *smscusb, |
| unsigned int address, uint32_t *value ); |
| |
| /** |
| * Write register |
| * |
| * @v smscusb SMSC USB device |
| * @v address Register address |
| * @v value Register value |
| * @ret rc Return status code |
| */ |
| static inline __attribute__ (( always_inline )) int |
| smscusb_writel ( struct smscusb_device *smscusb, unsigned int address, |
| uint32_t value ) { |
| int rc; |
| |
| /* Write register */ |
| if ( ( rc = smscusb_raw_writel ( smscusb, address, |
| cpu_to_le32 ( value ) ) ) != 0 ) |
| return rc; |
| |
| return 0; |
| } |
| |
| /** |
| * Read register |
| * |
| * @v smscusb SMSC USB device |
| * @v address Register address |
| * @ret value Register value |
| * @ret rc Return status code |
| */ |
| static inline __attribute__ (( always_inline )) int |
| smscusb_readl ( struct smscusb_device *smscusb, unsigned int address, |
| uint32_t *value ) { |
| int rc; |
| |
| /* Read register */ |
| if ( ( rc = smscusb_raw_readl ( smscusb, address, value ) ) != 0 ) |
| return rc; |
| le32_to_cpus ( value ); |
| |
| return 0; |
| } |
| |
| /** |
| * Get statistics |
| * |
| * @v smscusb SMSC USB device |
| * @v index Statistics set index |
| * @v data Statistics data to fill in |
| * @v len Length of statistics data |
| * @ret rc Return status code |
| */ |
| static inline __attribute__ (( always_inline )) int |
| smscusb_get_statistics ( struct smscusb_device *smscusb, unsigned int index, |
| void *data, size_t len ) { |
| int rc; |
| |
| /* Read statistics */ |
| if ( ( rc = usb_control ( smscusb->usb, SMSCUSB_GET_STATISTICS, 0, |
| index, data, len ) ) != 0 ) { |
| DBGC ( smscusb, "SMSCUSB %p could not get statistics set %d: " |
| "%s\n", smscusb, index, strerror ( rc ) ); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| /** Interrupt maximum fill level |
| * |
| * This is a policy decision. |
| */ |
| #define SMSCUSB_INTR_MAX_FILL 2 |
| |
| extern struct usb_endpoint_driver_operations smscusb_intr_operations; |
| extern struct usb_endpoint_driver_operations smscusb_out_operations; |
| extern struct mii_operations smscusb_mii_operations; |
| |
| /** |
| * Initialise SMSC USB device |
| * |
| * @v smscusb SMSC USB device |
| * @v netdev Network device |
| * @v func USB function |
| * @v in Bulk IN endpoint operations |
| */ |
| static inline __attribute__ (( always_inline )) void |
| smscusb_init ( struct smscusb_device *smscusb, struct net_device *netdev, |
| struct usb_function *func, |
| struct usb_endpoint_driver_operations *in ) { |
| struct usb_device *usb = func->usb; |
| |
| smscusb->usb = usb; |
| smscusb->bus = usb->port->hub->bus; |
| smscusb->netdev = netdev; |
| usbnet_init ( &smscusb->usbnet, func, &smscusb_intr_operations, in, |
| &smscusb_out_operations ); |
| usb_refill_init ( &smscusb->usbnet.intr, 0, 0, SMSCUSB_INTR_MAX_FILL ); |
| } |
| |
| /** |
| * Initialise SMSC USB device MII interface |
| * |
| * @v smscusb SMSC USB device |
| * @v mii_base MII register base |
| * @v phy_source Interrupt source PHY register |
| */ |
| static inline __attribute__ (( always_inline )) void |
| smscusb_mii_init ( struct smscusb_device *smscusb, unsigned int mii_base, |
| unsigned int phy_source ) { |
| |
| mdio_init ( &smscusb->mdio, &smscusb_mii_operations ); |
| mii_init ( &smscusb->mii, &smscusb->mdio, 0 ); |
| smscusb->mii_base = mii_base; |
| smscusb->phy_source = phy_source; |
| } |
| |
| extern int smscusb_eeprom_fetch_mac ( struct smscusb_device *smscusb, |
| unsigned int e2p_base ); |
| extern int smscusb_otp_fetch_mac ( struct smscusb_device *smscusb, |
| unsigned int otp_base ); |
| extern int smscusb_fdt_fetch_mac ( struct smscusb_device *smscusb ); |
| extern int smscusb_mii_check_link ( struct smscusb_device *smscusb ); |
| extern int smscusb_mii_open ( struct smscusb_device *smscusb, |
| unsigned int phy_mask, unsigned int intrs ); |
| extern int smscusb_set_address ( struct smscusb_device *smscusb, |
| unsigned int addr_base ); |
| extern int smscusb_set_filter ( struct smscusb_device *smscusb, |
| unsigned int filt_base ); |
| |
| #endif /* _SMSCUSB_H */ |