| #ifndef _SMSCUSB_H | |
| #define _SMSCUSB_H | |
| /** @file | |
| * | |
| * SMSC USB Ethernet drivers | |
| * | |
| */ | |
| FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); | |
| FILE_SECBOOT ( PERMITTED ); | |
| #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 */ |