1df4fb1c3SGerhard Sittig /* 2df4fb1c3SGerhard Sittig * Copyright (c) 2013 Gerhard Sittig <gsi@denx.de> 3df4fb1c3SGerhard Sittig * based on the U-Boot Asix driver as well as information 4df4fb1c3SGerhard Sittig * from the Linux Moschip driver 5df4fb1c3SGerhard Sittig * 6df4fb1c3SGerhard Sittig * SPDX-License-Identifier: GPL-2.0+ 7df4fb1c3SGerhard Sittig */ 8df4fb1c3SGerhard Sittig 9df4fb1c3SGerhard Sittig /* 10df4fb1c3SGerhard Sittig * MOSCHIP MCS7830 based (7730/7830/7832) USB 2.0 Ethernet Devices 11df4fb1c3SGerhard Sittig */ 12df4fb1c3SGerhard Sittig 13df4fb1c3SGerhard Sittig #include <common.h> 14*d4f847ecSSimon Glass #include <dm.h> 15df4fb1c3SGerhard Sittig #include <errno.h> 16df4fb1c3SGerhard Sittig #include <linux/mii.h> 17df4fb1c3SGerhard Sittig #include <malloc.h> 18cf92e05cSSimon Glass #include <memalign.h> 19df4fb1c3SGerhard Sittig #include <usb.h> 20df4fb1c3SGerhard Sittig 21df4fb1c3SGerhard Sittig #include "usb_ether.h" 22df4fb1c3SGerhard Sittig 23df4fb1c3SGerhard Sittig #define MCS7830_BASE_NAME "mcs" 24df4fb1c3SGerhard Sittig 25df4fb1c3SGerhard Sittig #define USBCALL_TIMEOUT 1000 26df4fb1c3SGerhard Sittig #define LINKSTATUS_TIMEOUT 5000 /* link status, connect timeout */ 27df4fb1c3SGerhard Sittig #define LINKSTATUS_TIMEOUT_RES 50 /* link status, resolution in msec */ 28df4fb1c3SGerhard Sittig 29df4fb1c3SGerhard Sittig #define MCS7830_RX_URB_SIZE 2048 30df4fb1c3SGerhard Sittig 31df4fb1c3SGerhard Sittig /* command opcodes */ 32df4fb1c3SGerhard Sittig #define MCS7830_WR_BREQ 0x0d 33df4fb1c3SGerhard Sittig #define MCS7830_RD_BREQ 0x0e 34df4fb1c3SGerhard Sittig 35df4fb1c3SGerhard Sittig /* register layout, numerical offset specs for USB API calls */ 36df4fb1c3SGerhard Sittig struct mcs7830_regs { 37df4fb1c3SGerhard Sittig uint8_t multicast_hashes[8]; 38df4fb1c3SGerhard Sittig uint8_t packet_gap[2]; 39df4fb1c3SGerhard Sittig uint8_t phy_data[2]; 40df4fb1c3SGerhard Sittig uint8_t phy_command[2]; 41df4fb1c3SGerhard Sittig uint8_t configuration; 42df4fb1c3SGerhard Sittig uint8_t ether_address[6]; 43df4fb1c3SGerhard Sittig uint8_t frame_drop_count; 44df4fb1c3SGerhard Sittig uint8_t pause_threshold; 45df4fb1c3SGerhard Sittig }; 46df4fb1c3SGerhard Sittig #define REG_MULTICAST_HASH offsetof(struct mcs7830_regs, multicast_hashes) 47df4fb1c3SGerhard Sittig #define REG_PHY_DATA offsetof(struct mcs7830_regs, phy_data) 48df4fb1c3SGerhard Sittig #define REG_PHY_CMD offsetof(struct mcs7830_regs, phy_command) 49df4fb1c3SGerhard Sittig #define REG_CONFIG offsetof(struct mcs7830_regs, configuration) 50df4fb1c3SGerhard Sittig #define REG_ETHER_ADDR offsetof(struct mcs7830_regs, ether_address) 51df4fb1c3SGerhard Sittig #define REG_FRAME_DROP_COUNTER offsetof(struct mcs7830_regs, frame_drop_count) 52df4fb1c3SGerhard Sittig #define REG_PAUSE_THRESHOLD offsetof(struct mcs7830_regs, pause_threshold) 53df4fb1c3SGerhard Sittig 54df4fb1c3SGerhard Sittig /* bit masks and default values for the above registers */ 55df4fb1c3SGerhard Sittig #define PHY_CMD1_READ 0x40 56df4fb1c3SGerhard Sittig #define PHY_CMD1_WRITE 0x20 57df4fb1c3SGerhard Sittig #define PHY_CMD1_PHYADDR 0x01 58df4fb1c3SGerhard Sittig 59df4fb1c3SGerhard Sittig #define PHY_CMD2_PEND 0x80 60df4fb1c3SGerhard Sittig #define PHY_CMD2_READY 0x40 61df4fb1c3SGerhard Sittig 62df4fb1c3SGerhard Sittig #define CONF_CFG 0x80 63df4fb1c3SGerhard Sittig #define CONF_SPEED100 0x40 64df4fb1c3SGerhard Sittig #define CONF_FDX_ENABLE 0x20 65df4fb1c3SGerhard Sittig #define CONF_RXENABLE 0x10 66df4fb1c3SGerhard Sittig #define CONF_TXENABLE 0x08 67df4fb1c3SGerhard Sittig #define CONF_SLEEPMODE 0x04 68df4fb1c3SGerhard Sittig #define CONF_ALLMULTICAST 0x02 69df4fb1c3SGerhard Sittig #define CONF_PROMISCUOUS 0x01 70df4fb1c3SGerhard Sittig 71df4fb1c3SGerhard Sittig #define PAUSE_THRESHOLD_DEFAULT 0 72df4fb1c3SGerhard Sittig 73df4fb1c3SGerhard Sittig /* bit masks for the status byte which follows received ethernet frames */ 74df4fb1c3SGerhard Sittig #define STAT_RX_FRAME_CORRECT 0x20 75df4fb1c3SGerhard Sittig #define STAT_RX_LARGE_FRAME 0x10 76df4fb1c3SGerhard Sittig #define STAT_RX_CRC_ERROR 0x08 77df4fb1c3SGerhard Sittig #define STAT_RX_ALIGNMENT_ERROR 0x04 78df4fb1c3SGerhard Sittig #define STAT_RX_LENGTH_ERROR 0x02 79df4fb1c3SGerhard Sittig #define STAT_RX_SHORT_FRAME 0x01 80df4fb1c3SGerhard Sittig 81df4fb1c3SGerhard Sittig /* 82df4fb1c3SGerhard Sittig * struct mcs7830_private - private driver data for an individual adapter 83df4fb1c3SGerhard Sittig * @config: shadow for the network adapter's configuration register 84df4fb1c3SGerhard Sittig * @mchash: shadow for the network adapter's multicast hash registers 85df4fb1c3SGerhard Sittig */ 86df4fb1c3SGerhard Sittig struct mcs7830_private { 87*d4f847ecSSimon Glass #ifdef CONFIG_DM_ETH 88*d4f847ecSSimon Glass uint8_t rx_buf[MCS7830_RX_URB_SIZE]; 89*d4f847ecSSimon Glass struct ueth_data ueth; 90*d4f847ecSSimon Glass #endif 91df4fb1c3SGerhard Sittig uint8_t config; 92df4fb1c3SGerhard Sittig uint8_t mchash[8]; 93df4fb1c3SGerhard Sittig }; 94df4fb1c3SGerhard Sittig 95df4fb1c3SGerhard Sittig /* 96df4fb1c3SGerhard Sittig * mcs7830_read_reg() - read a register of the network adapter 97ce932c70SSimon Glass * @udev: network device to read from 98df4fb1c3SGerhard Sittig * @idx: index of the register to start reading from 99df4fb1c3SGerhard Sittig * @size: number of bytes to read 100df4fb1c3SGerhard Sittig * @data: buffer to read into 101df4fb1c3SGerhard Sittig * Return: zero upon success, negative upon error 102df4fb1c3SGerhard Sittig */ 103ce932c70SSimon Glass static int mcs7830_read_reg(struct usb_device *udev, uint8_t idx, 104df4fb1c3SGerhard Sittig uint16_t size, void *data) 105df4fb1c3SGerhard Sittig { 106df4fb1c3SGerhard Sittig int len; 107df4fb1c3SGerhard Sittig ALLOC_CACHE_ALIGN_BUFFER(uint8_t, buf, size); 108df4fb1c3SGerhard Sittig 109df4fb1c3SGerhard Sittig debug("%s() idx=0x%04X sz=%d\n", __func__, idx, size); 110df4fb1c3SGerhard Sittig 111ce932c70SSimon Glass len = usb_control_msg(udev, 112ce932c70SSimon Glass usb_rcvctrlpipe(udev, 0), 113df4fb1c3SGerhard Sittig MCS7830_RD_BREQ, 114df4fb1c3SGerhard Sittig USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 115df4fb1c3SGerhard Sittig 0, idx, buf, size, 116df4fb1c3SGerhard Sittig USBCALL_TIMEOUT); 117df4fb1c3SGerhard Sittig if (len != size) { 118df4fb1c3SGerhard Sittig debug("%s() len=%d != sz=%d\n", __func__, len, size); 119df4fb1c3SGerhard Sittig return -EIO; 120df4fb1c3SGerhard Sittig } 121df4fb1c3SGerhard Sittig memcpy(data, buf, size); 122df4fb1c3SGerhard Sittig return 0; 123df4fb1c3SGerhard Sittig } 124df4fb1c3SGerhard Sittig 125df4fb1c3SGerhard Sittig /* 126df4fb1c3SGerhard Sittig * mcs7830_write_reg() - write a register of the network adapter 127ce932c70SSimon Glass * @udev: network device to write to 128df4fb1c3SGerhard Sittig * @idx: index of the register to start writing to 129df4fb1c3SGerhard Sittig * @size: number of bytes to write 130df4fb1c3SGerhard Sittig * @data: buffer holding the data to write 131df4fb1c3SGerhard Sittig * Return: zero upon success, negative upon error 132df4fb1c3SGerhard Sittig */ 133ce932c70SSimon Glass static int mcs7830_write_reg(struct usb_device *udev, uint8_t idx, 134df4fb1c3SGerhard Sittig uint16_t size, void *data) 135df4fb1c3SGerhard Sittig { 136df4fb1c3SGerhard Sittig int len; 137df4fb1c3SGerhard Sittig ALLOC_CACHE_ALIGN_BUFFER(uint8_t, buf, size); 138df4fb1c3SGerhard Sittig 139df4fb1c3SGerhard Sittig debug("%s() idx=0x%04X sz=%d\n", __func__, idx, size); 140df4fb1c3SGerhard Sittig 141df4fb1c3SGerhard Sittig memcpy(buf, data, size); 142ce932c70SSimon Glass len = usb_control_msg(udev, 143ce932c70SSimon Glass usb_sndctrlpipe(udev, 0), 144df4fb1c3SGerhard Sittig MCS7830_WR_BREQ, 145df4fb1c3SGerhard Sittig USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 146df4fb1c3SGerhard Sittig 0, idx, buf, size, 147df4fb1c3SGerhard Sittig USBCALL_TIMEOUT); 148df4fb1c3SGerhard Sittig if (len != size) { 149df4fb1c3SGerhard Sittig debug("%s() len=%d != sz=%d\n", __func__, len, size); 150df4fb1c3SGerhard Sittig return -EIO; 151df4fb1c3SGerhard Sittig } 152df4fb1c3SGerhard Sittig return 0; 153df4fb1c3SGerhard Sittig } 154df4fb1c3SGerhard Sittig 155df4fb1c3SGerhard Sittig /* 156df4fb1c3SGerhard Sittig * mcs7830_phy_emit_wait() - emit PHY read/write access, wait for its execution 157ce932c70SSimon Glass * @udev: network device to talk to 158df4fb1c3SGerhard Sittig * @rwflag: PHY_CMD1_READ or PHY_CMD1_WRITE opcode 159df4fb1c3SGerhard Sittig * @index: number of the PHY register to read or write 160df4fb1c3SGerhard Sittig * Return: zero upon success, negative upon error 161df4fb1c3SGerhard Sittig */ 162ce932c70SSimon Glass static int mcs7830_phy_emit_wait(struct usb_device *udev, 163df4fb1c3SGerhard Sittig uint8_t rwflag, uint8_t index) 164df4fb1c3SGerhard Sittig { 165df4fb1c3SGerhard Sittig int rc; 166df4fb1c3SGerhard Sittig int retry; 167df4fb1c3SGerhard Sittig uint8_t cmd[2]; 168df4fb1c3SGerhard Sittig 169df4fb1c3SGerhard Sittig /* send the PHY read/write request */ 170df4fb1c3SGerhard Sittig cmd[0] = rwflag | PHY_CMD1_PHYADDR; 171df4fb1c3SGerhard Sittig cmd[1] = PHY_CMD2_PEND | (index & 0x1f); 172ce932c70SSimon Glass rc = mcs7830_write_reg(udev, REG_PHY_CMD, sizeof(cmd), cmd); 173df4fb1c3SGerhard Sittig if (rc < 0) 174df4fb1c3SGerhard Sittig return rc; 175df4fb1c3SGerhard Sittig 176df4fb1c3SGerhard Sittig /* wait for the response to become available (usually < 1ms) */ 177df4fb1c3SGerhard Sittig retry = 10; 178df4fb1c3SGerhard Sittig do { 179ce932c70SSimon Glass rc = mcs7830_read_reg(udev, REG_PHY_CMD, sizeof(cmd), cmd); 180df4fb1c3SGerhard Sittig if (rc < 0) 181df4fb1c3SGerhard Sittig return rc; 182df4fb1c3SGerhard Sittig if (cmd[1] & PHY_CMD2_READY) 183df4fb1c3SGerhard Sittig return 0; 184df4fb1c3SGerhard Sittig if (!retry--) 185df4fb1c3SGerhard Sittig return -ETIMEDOUT; 186df4fb1c3SGerhard Sittig mdelay(1); 187df4fb1c3SGerhard Sittig } while (1); 188df4fb1c3SGerhard Sittig /* UNREACH */ 189df4fb1c3SGerhard Sittig } 190df4fb1c3SGerhard Sittig 191df4fb1c3SGerhard Sittig /* 192df4fb1c3SGerhard Sittig * mcs7830_read_phy() - read a PHY register of the network adapter 193ce932c70SSimon Glass * @udev: network device to read from 194df4fb1c3SGerhard Sittig * @index: index of the PHY register to read from 195df4fb1c3SGerhard Sittig * Return: non-negative 16bit register content, negative upon error 196df4fb1c3SGerhard Sittig */ 197ce932c70SSimon Glass static int mcs7830_read_phy(struct usb_device *udev, uint8_t index) 198df4fb1c3SGerhard Sittig { 199df4fb1c3SGerhard Sittig int rc; 200df4fb1c3SGerhard Sittig uint16_t val; 201df4fb1c3SGerhard Sittig 202df4fb1c3SGerhard Sittig /* issue the PHY read request and wait for its execution */ 203ce932c70SSimon Glass rc = mcs7830_phy_emit_wait(udev, PHY_CMD1_READ, index); 204df4fb1c3SGerhard Sittig if (rc < 0) 205df4fb1c3SGerhard Sittig return rc; 206df4fb1c3SGerhard Sittig 207df4fb1c3SGerhard Sittig /* fetch the PHY data which was read */ 208ce932c70SSimon Glass rc = mcs7830_read_reg(udev, REG_PHY_DATA, sizeof(val), &val); 209df4fb1c3SGerhard Sittig if (rc < 0) 210df4fb1c3SGerhard Sittig return rc; 211df4fb1c3SGerhard Sittig rc = le16_to_cpu(val); 212ce932c70SSimon Glass debug("%s(%d) => 0x%04X\n", __func__, index, rc); 213df4fb1c3SGerhard Sittig return rc; 214df4fb1c3SGerhard Sittig } 215df4fb1c3SGerhard Sittig 216df4fb1c3SGerhard Sittig /* 217df4fb1c3SGerhard Sittig * mcs7830_write_phy() - write a PHY register of the network adapter 218ce932c70SSimon Glass * @udev: network device to write to 219df4fb1c3SGerhard Sittig * @index: index of the PHY register to write to 220df4fb1c3SGerhard Sittig * @val: value to write to the PHY register 221df4fb1c3SGerhard Sittig * Return: zero upon success, negative upon error 222df4fb1c3SGerhard Sittig */ 223ce932c70SSimon Glass static int mcs7830_write_phy(struct usb_device *udev, uint8_t index, 224ce932c70SSimon Glass uint16_t val) 225df4fb1c3SGerhard Sittig { 226df4fb1c3SGerhard Sittig int rc; 227df4fb1c3SGerhard Sittig 228ce932c70SSimon Glass debug("%s(%d, 0x%04X)\n", __func__, index, val); 229df4fb1c3SGerhard Sittig 230df4fb1c3SGerhard Sittig /* setup the PHY data which is to get written */ 231df4fb1c3SGerhard Sittig val = cpu_to_le16(val); 232ce932c70SSimon Glass rc = mcs7830_write_reg(udev, REG_PHY_DATA, sizeof(val), &val); 233df4fb1c3SGerhard Sittig if (rc < 0) 234df4fb1c3SGerhard Sittig return rc; 235df4fb1c3SGerhard Sittig 236df4fb1c3SGerhard Sittig /* issue the PHY write request and wait for its execution */ 237ce932c70SSimon Glass rc = mcs7830_phy_emit_wait(udev, PHY_CMD1_WRITE, index); 238df4fb1c3SGerhard Sittig if (rc < 0) 239df4fb1c3SGerhard Sittig return rc; 240df4fb1c3SGerhard Sittig 241df4fb1c3SGerhard Sittig return 0; 242df4fb1c3SGerhard Sittig } 243df4fb1c3SGerhard Sittig 244df4fb1c3SGerhard Sittig /* 245df4fb1c3SGerhard Sittig * mcs7830_write_config() - write to the network adapter's config register 246ce932c70SSimon Glass * @udev: network device to write to 247ce932c70SSimon Glass * @priv: private data 248df4fb1c3SGerhard Sittig * Return: zero upon success, negative upon error 249df4fb1c3SGerhard Sittig * 250df4fb1c3SGerhard Sittig * the data which gets written is taken from the shadow config register 251df4fb1c3SGerhard Sittig * within the device driver's private data 252df4fb1c3SGerhard Sittig */ 253ce932c70SSimon Glass static int mcs7830_write_config(struct usb_device *udev, 254ce932c70SSimon Glass struct mcs7830_private *priv) 255df4fb1c3SGerhard Sittig { 256df4fb1c3SGerhard Sittig int rc; 257df4fb1c3SGerhard Sittig 258df4fb1c3SGerhard Sittig debug("%s()\n", __func__); 259df4fb1c3SGerhard Sittig 260ce932c70SSimon Glass rc = mcs7830_write_reg(udev, REG_CONFIG, 261df4fb1c3SGerhard Sittig sizeof(priv->config), &priv->config); 262df4fb1c3SGerhard Sittig if (rc < 0) { 263df4fb1c3SGerhard Sittig debug("writing config to adapter failed\n"); 264df4fb1c3SGerhard Sittig return rc; 265df4fb1c3SGerhard Sittig } 266df4fb1c3SGerhard Sittig 267df4fb1c3SGerhard Sittig return 0; 268df4fb1c3SGerhard Sittig } 269df4fb1c3SGerhard Sittig 270df4fb1c3SGerhard Sittig /* 271df4fb1c3SGerhard Sittig * mcs7830_write_mchash() - write the network adapter's multicast filter 272ce932c70SSimon Glass * @udev: network device to write to 273ce932c70SSimon Glass * @priv: private data 274df4fb1c3SGerhard Sittig * Return: zero upon success, negative upon error 275df4fb1c3SGerhard Sittig * 276df4fb1c3SGerhard Sittig * the data which gets written is taken from the shadow multicast hashes 277df4fb1c3SGerhard Sittig * within the device driver's private data 278df4fb1c3SGerhard Sittig */ 279ce932c70SSimon Glass static int mcs7830_write_mchash(struct usb_device *udev, 280ce932c70SSimon Glass struct mcs7830_private *priv) 281df4fb1c3SGerhard Sittig { 282df4fb1c3SGerhard Sittig int rc; 283df4fb1c3SGerhard Sittig 284df4fb1c3SGerhard Sittig debug("%s()\n", __func__); 285df4fb1c3SGerhard Sittig 286ce932c70SSimon Glass rc = mcs7830_write_reg(udev, REG_MULTICAST_HASH, 287df4fb1c3SGerhard Sittig sizeof(priv->mchash), &priv->mchash); 288df4fb1c3SGerhard Sittig if (rc < 0) { 289df4fb1c3SGerhard Sittig debug("writing multicast hash to adapter failed\n"); 290df4fb1c3SGerhard Sittig return rc; 291df4fb1c3SGerhard Sittig } 292df4fb1c3SGerhard Sittig 293df4fb1c3SGerhard Sittig return 0; 294df4fb1c3SGerhard Sittig } 295df4fb1c3SGerhard Sittig 296df4fb1c3SGerhard Sittig /* 297df4fb1c3SGerhard Sittig * mcs7830_set_autoneg() - setup and trigger ethernet link autonegotiation 298ce932c70SSimon Glass * @udev: network device to run link negotiation on 299df4fb1c3SGerhard Sittig * Return: zero upon success, negative upon error 300df4fb1c3SGerhard Sittig * 301df4fb1c3SGerhard Sittig * the routine advertises available media and starts autonegotiation 302df4fb1c3SGerhard Sittig */ 303ce932c70SSimon Glass static int mcs7830_set_autoneg(struct usb_device *udev) 304df4fb1c3SGerhard Sittig { 305df4fb1c3SGerhard Sittig int adv, flg; 306df4fb1c3SGerhard Sittig int rc; 307df4fb1c3SGerhard Sittig 308df4fb1c3SGerhard Sittig debug("%s()\n", __func__); 309df4fb1c3SGerhard Sittig 310df4fb1c3SGerhard Sittig /* 311df4fb1c3SGerhard Sittig * algorithm taken from the Linux driver, which took it from 312df4fb1c3SGerhard Sittig * "the original mcs7830 version 1.4 driver": 313df4fb1c3SGerhard Sittig * 314df4fb1c3SGerhard Sittig * enable all media, reset BMCR, enable auto neg, restart 315df4fb1c3SGerhard Sittig * auto neg while keeping the enable auto neg flag set 316df4fb1c3SGerhard Sittig */ 317df4fb1c3SGerhard Sittig 318df4fb1c3SGerhard Sittig adv = ADVERTISE_PAUSE_CAP | ADVERTISE_ALL | ADVERTISE_CSMA; 319ce932c70SSimon Glass rc = mcs7830_write_phy(udev, MII_ADVERTISE, adv); 320df4fb1c3SGerhard Sittig 321df4fb1c3SGerhard Sittig flg = 0; 322df4fb1c3SGerhard Sittig if (!rc) 323ce932c70SSimon Glass rc = mcs7830_write_phy(udev, MII_BMCR, flg); 324df4fb1c3SGerhard Sittig 325df4fb1c3SGerhard Sittig flg |= BMCR_ANENABLE; 326df4fb1c3SGerhard Sittig if (!rc) 327ce932c70SSimon Glass rc = mcs7830_write_phy(udev, MII_BMCR, flg); 328df4fb1c3SGerhard Sittig 329df4fb1c3SGerhard Sittig flg |= BMCR_ANRESTART; 330df4fb1c3SGerhard Sittig if (!rc) 331ce932c70SSimon Glass rc = mcs7830_write_phy(udev, MII_BMCR, flg); 332df4fb1c3SGerhard Sittig 333df4fb1c3SGerhard Sittig return rc; 334df4fb1c3SGerhard Sittig } 335df4fb1c3SGerhard Sittig 336df4fb1c3SGerhard Sittig /* 337df4fb1c3SGerhard Sittig * mcs7830_get_rev() - identify a network adapter's chip revision 338ce932c70SSimon Glass * @udev: network device to identify 339df4fb1c3SGerhard Sittig * Return: non-negative number, reflecting the revision number 340df4fb1c3SGerhard Sittig * 341df4fb1c3SGerhard Sittig * currently, only "rev C and higher" and "below rev C" are needed, so 342df4fb1c3SGerhard Sittig * the return value is #1 for "below rev C", and #2 for "rev C and above" 343df4fb1c3SGerhard Sittig */ 344ce932c70SSimon Glass static int mcs7830_get_rev(struct usb_device *udev) 345df4fb1c3SGerhard Sittig { 346df4fb1c3SGerhard Sittig uint8_t buf[2]; 347df4fb1c3SGerhard Sittig int rc; 348df4fb1c3SGerhard Sittig int rev; 349df4fb1c3SGerhard Sittig 350df4fb1c3SGerhard Sittig /* register 22 is readable in rev C and higher */ 351ce932c70SSimon Glass rc = mcs7830_read_reg(udev, REG_FRAME_DROP_COUNTER, sizeof(buf), buf); 352df4fb1c3SGerhard Sittig if (rc < 0) 353df4fb1c3SGerhard Sittig rev = 1; 354df4fb1c3SGerhard Sittig else 355df4fb1c3SGerhard Sittig rev = 2; 356df4fb1c3SGerhard Sittig debug("%s() rc=%d, rev=%d\n", __func__, rc, rev); 357df4fb1c3SGerhard Sittig return rev; 358df4fb1c3SGerhard Sittig } 359df4fb1c3SGerhard Sittig 360df4fb1c3SGerhard Sittig /* 361df4fb1c3SGerhard Sittig * mcs7830_apply_fixup() - identify an adapter and potentially apply fixups 362ce932c70SSimon Glass * @udev: network device to identify and apply fixups to 363df4fb1c3SGerhard Sittig * Return: zero upon success (no errors emitted from here) 364df4fb1c3SGerhard Sittig * 365df4fb1c3SGerhard Sittig * this routine identifies the network adapter's chip revision, and applies 366df4fb1c3SGerhard Sittig * fixups for known issues 367df4fb1c3SGerhard Sittig */ 368ce932c70SSimon Glass static int mcs7830_apply_fixup(struct usb_device *udev) 369df4fb1c3SGerhard Sittig { 370df4fb1c3SGerhard Sittig int rev; 371df4fb1c3SGerhard Sittig int i; 372df4fb1c3SGerhard Sittig uint8_t thr; 373df4fb1c3SGerhard Sittig 374ce932c70SSimon Glass rev = mcs7830_get_rev(udev); 375df4fb1c3SGerhard Sittig debug("%s() rev=%d\n", __func__, rev); 376df4fb1c3SGerhard Sittig 377df4fb1c3SGerhard Sittig /* 378df4fb1c3SGerhard Sittig * rev C requires setting the pause threshold (the Linux driver 379df4fb1c3SGerhard Sittig * is inconsistent, the implementation does it for "rev C 380df4fb1c3SGerhard Sittig * exactly", the introductory comment says "rev C and above") 381df4fb1c3SGerhard Sittig */ 382df4fb1c3SGerhard Sittig if (rev == 2) { 383ce932c70SSimon Glass debug("%s: applying rev C fixup\n", __func__); 384df4fb1c3SGerhard Sittig thr = PAUSE_THRESHOLD_DEFAULT; 385df4fb1c3SGerhard Sittig for (i = 0; i < 2; i++) { 386ce932c70SSimon Glass (void)mcs7830_write_reg(udev, REG_PAUSE_THRESHOLD, 387df4fb1c3SGerhard Sittig sizeof(thr), &thr); 388df4fb1c3SGerhard Sittig mdelay(1); 389df4fb1c3SGerhard Sittig } 390df4fb1c3SGerhard Sittig } 391df4fb1c3SGerhard Sittig 392df4fb1c3SGerhard Sittig return 0; 393df4fb1c3SGerhard Sittig } 394df4fb1c3SGerhard Sittig 395df4fb1c3SGerhard Sittig /* 396df4fb1c3SGerhard Sittig * mcs7830_basic_reset() - bring the network adapter into a known first state 397df4fb1c3SGerhard Sittig * @eth: network device to act upon 398df4fb1c3SGerhard Sittig * Return: zero upon success, negative upon error 399df4fb1c3SGerhard Sittig * 400df4fb1c3SGerhard Sittig * this routine initializes the network adapter such that subsequent invocations 401df4fb1c3SGerhard Sittig * of the interface callbacks can exchange ethernet frames; link negotiation is 402df4fb1c3SGerhard Sittig * triggered from here already and continues in background 403df4fb1c3SGerhard Sittig */ 404ce932c70SSimon Glass static int mcs7830_basic_reset(struct usb_device *udev, 405ce932c70SSimon Glass struct mcs7830_private *priv) 406df4fb1c3SGerhard Sittig { 407df4fb1c3SGerhard Sittig int rc; 408df4fb1c3SGerhard Sittig 409df4fb1c3SGerhard Sittig debug("%s()\n", __func__); 410df4fb1c3SGerhard Sittig 411df4fb1c3SGerhard Sittig /* 412df4fb1c3SGerhard Sittig * comment from the respective Linux driver, which 413df4fb1c3SGerhard Sittig * unconditionally sets the ALLMULTICAST flag as well: 414df4fb1c3SGerhard Sittig * should not be needed, but does not work otherwise 415df4fb1c3SGerhard Sittig */ 416df4fb1c3SGerhard Sittig priv->config = CONF_TXENABLE; 417df4fb1c3SGerhard Sittig priv->config |= CONF_ALLMULTICAST; 418df4fb1c3SGerhard Sittig 419ce932c70SSimon Glass rc = mcs7830_set_autoneg(udev); 420df4fb1c3SGerhard Sittig if (rc < 0) { 421df4fb1c3SGerhard Sittig error("setting autoneg failed\n"); 422df4fb1c3SGerhard Sittig return rc; 423df4fb1c3SGerhard Sittig } 424df4fb1c3SGerhard Sittig 425ce932c70SSimon Glass rc = mcs7830_write_mchash(udev, priv); 426df4fb1c3SGerhard Sittig if (rc < 0) { 427df4fb1c3SGerhard Sittig error("failed to set multicast hash\n"); 428df4fb1c3SGerhard Sittig return rc; 429df4fb1c3SGerhard Sittig } 430df4fb1c3SGerhard Sittig 431ce932c70SSimon Glass rc = mcs7830_write_config(udev, priv); 432df4fb1c3SGerhard Sittig if (rc < 0) { 433df4fb1c3SGerhard Sittig error("failed to set configuration\n"); 434df4fb1c3SGerhard Sittig return rc; 435df4fb1c3SGerhard Sittig } 436df4fb1c3SGerhard Sittig 437ce932c70SSimon Glass rc = mcs7830_apply_fixup(udev); 438df4fb1c3SGerhard Sittig if (rc < 0) { 439df4fb1c3SGerhard Sittig error("fixup application failed\n"); 440df4fb1c3SGerhard Sittig return rc; 441df4fb1c3SGerhard Sittig } 442df4fb1c3SGerhard Sittig 443df4fb1c3SGerhard Sittig return 0; 444df4fb1c3SGerhard Sittig } 445df4fb1c3SGerhard Sittig 446df4fb1c3SGerhard Sittig /* 447df4fb1c3SGerhard Sittig * mcs7830_read_mac() - read an ethernet adapter's MAC address 448ce932c70SSimon Glass * @udev: network device to read from 449ce932c70SSimon Glass * @enetaddr: place to put ethernet MAC address 450df4fb1c3SGerhard Sittig * Return: zero upon success, negative upon error 451df4fb1c3SGerhard Sittig * 452df4fb1c3SGerhard Sittig * this routine fetches the MAC address stored within the ethernet adapter, 453df4fb1c3SGerhard Sittig * and stores it in the ethernet interface's data structure 454df4fb1c3SGerhard Sittig */ 455ce932c70SSimon Glass static int mcs7830_read_mac(struct usb_device *udev, unsigned char enetaddr[]) 456df4fb1c3SGerhard Sittig { 457df4fb1c3SGerhard Sittig int rc; 458df4fb1c3SGerhard Sittig uint8_t buf[ETH_ALEN]; 459df4fb1c3SGerhard Sittig 460df4fb1c3SGerhard Sittig debug("%s()\n", __func__); 461df4fb1c3SGerhard Sittig 462ce932c70SSimon Glass rc = mcs7830_read_reg(udev, REG_ETHER_ADDR, ETH_ALEN, buf); 463df4fb1c3SGerhard Sittig if (rc < 0) { 464df4fb1c3SGerhard Sittig debug("reading MAC from adapter failed\n"); 465df4fb1c3SGerhard Sittig return rc; 466df4fb1c3SGerhard Sittig } 467df4fb1c3SGerhard Sittig 468ce932c70SSimon Glass memcpy(enetaddr, buf, ETH_ALEN); 469df4fb1c3SGerhard Sittig return 0; 470df4fb1c3SGerhard Sittig } 471df4fb1c3SGerhard Sittig 472ce932c70SSimon Glass static int mcs7830_write_mac_common(struct usb_device *udev, 473ce932c70SSimon Glass unsigned char enetaddr[]) 474df4fb1c3SGerhard Sittig { 475df4fb1c3SGerhard Sittig int rc; 476df4fb1c3SGerhard Sittig 477df4fb1c3SGerhard Sittig debug("%s()\n", __func__); 478df4fb1c3SGerhard Sittig 479ce932c70SSimon Glass rc = mcs7830_write_reg(udev, REG_ETHER_ADDR, ETH_ALEN, enetaddr); 480df4fb1c3SGerhard Sittig if (rc < 0) { 481df4fb1c3SGerhard Sittig debug("writing MAC to adapter failed\n"); 482df4fb1c3SGerhard Sittig return rc; 483df4fb1c3SGerhard Sittig } 484df4fb1c3SGerhard Sittig return 0; 485df4fb1c3SGerhard Sittig } 486df4fb1c3SGerhard Sittig 487ce932c70SSimon Glass static int mcs7830_init_common(struct usb_device *udev) 488df4fb1c3SGerhard Sittig { 489df4fb1c3SGerhard Sittig int timeout; 490df4fb1c3SGerhard Sittig int have_link; 491df4fb1c3SGerhard Sittig 492df4fb1c3SGerhard Sittig debug("%s()\n", __func__); 493df4fb1c3SGerhard Sittig 494df4fb1c3SGerhard Sittig timeout = 0; 495df4fb1c3SGerhard Sittig do { 496ce932c70SSimon Glass have_link = mcs7830_read_phy(udev, MII_BMSR) & BMSR_LSTATUS; 497df4fb1c3SGerhard Sittig if (have_link) 498df4fb1c3SGerhard Sittig break; 499df4fb1c3SGerhard Sittig udelay(LINKSTATUS_TIMEOUT_RES * 1000); 500df4fb1c3SGerhard Sittig timeout += LINKSTATUS_TIMEOUT_RES; 501df4fb1c3SGerhard Sittig } while (timeout < LINKSTATUS_TIMEOUT); 502df4fb1c3SGerhard Sittig if (!have_link) { 503df4fb1c3SGerhard Sittig debug("ethernet link is down\n"); 504df4fb1c3SGerhard Sittig return -ETIMEDOUT; 505df4fb1c3SGerhard Sittig } 506df4fb1c3SGerhard Sittig return 0; 507df4fb1c3SGerhard Sittig } 508df4fb1c3SGerhard Sittig 509ce932c70SSimon Glass static int mcs7830_send_common(struct ueth_data *ueth, void *packet, 510ce932c70SSimon Glass int length) 511df4fb1c3SGerhard Sittig { 512ce932c70SSimon Glass struct usb_device *udev = ueth->pusb_dev; 513df4fb1c3SGerhard Sittig int rc; 514df4fb1c3SGerhard Sittig int gotlen; 515df4fb1c3SGerhard Sittig /* there is a status byte after the ethernet frame */ 516df4fb1c3SGerhard Sittig ALLOC_CACHE_ALIGN_BUFFER(uint8_t, buf, PKTSIZE + sizeof(uint8_t)); 517df4fb1c3SGerhard Sittig 518df4fb1c3SGerhard Sittig memcpy(buf, packet, length); 519ce932c70SSimon Glass rc = usb_bulk_msg(udev, 520ce932c70SSimon Glass usb_sndbulkpipe(udev, ueth->ep_out), 521df4fb1c3SGerhard Sittig &buf[0], length, &gotlen, 522df4fb1c3SGerhard Sittig USBCALL_TIMEOUT); 523df4fb1c3SGerhard Sittig debug("%s() TX want len %d, got len %d, rc %d\n", 524df4fb1c3SGerhard Sittig __func__, length, gotlen, rc); 525df4fb1c3SGerhard Sittig return rc; 526df4fb1c3SGerhard Sittig } 527df4fb1c3SGerhard Sittig 528ce932c70SSimon Glass static int mcs7830_recv_common(struct ueth_data *ueth, uint8_t *buf) 529df4fb1c3SGerhard Sittig { 530df4fb1c3SGerhard Sittig int rc, wantlen, gotlen; 531df4fb1c3SGerhard Sittig uint8_t sts; 532df4fb1c3SGerhard Sittig 533df4fb1c3SGerhard Sittig debug("%s()\n", __func__); 534df4fb1c3SGerhard Sittig 535df4fb1c3SGerhard Sittig /* fetch input data from the adapter */ 536df4fb1c3SGerhard Sittig wantlen = MCS7830_RX_URB_SIZE; 537ce932c70SSimon Glass rc = usb_bulk_msg(ueth->pusb_dev, 538ce932c70SSimon Glass usb_rcvbulkpipe(ueth->pusb_dev, ueth->ep_in), 539df4fb1c3SGerhard Sittig &buf[0], wantlen, &gotlen, 540df4fb1c3SGerhard Sittig USBCALL_TIMEOUT); 541df4fb1c3SGerhard Sittig debug("%s() RX want len %d, got len %d, rc %d\n", 542df4fb1c3SGerhard Sittig __func__, wantlen, gotlen, rc); 543df4fb1c3SGerhard Sittig if (rc != 0) { 544df4fb1c3SGerhard Sittig error("RX: failed to receive\n"); 545df4fb1c3SGerhard Sittig return rc; 546df4fb1c3SGerhard Sittig } 547df4fb1c3SGerhard Sittig if (gotlen > wantlen) { 548df4fb1c3SGerhard Sittig error("RX: got too many bytes (%d)\n", gotlen); 549df4fb1c3SGerhard Sittig return -EIO; 550df4fb1c3SGerhard Sittig } 551df4fb1c3SGerhard Sittig 552df4fb1c3SGerhard Sittig /* 553df4fb1c3SGerhard Sittig * the bulk message that we received from USB contains exactly 554df4fb1c3SGerhard Sittig * one ethernet frame and a trailing status byte 555df4fb1c3SGerhard Sittig */ 556df4fb1c3SGerhard Sittig if (gotlen < sizeof(sts)) 557df4fb1c3SGerhard Sittig return -EIO; 558df4fb1c3SGerhard Sittig gotlen -= sizeof(sts); 559df4fb1c3SGerhard Sittig sts = buf[gotlen]; 560df4fb1c3SGerhard Sittig 561df4fb1c3SGerhard Sittig if (sts == STAT_RX_FRAME_CORRECT) { 562df4fb1c3SGerhard Sittig debug("%s() got a frame, len=%d\n", __func__, gotlen); 563ce932c70SSimon Glass return gotlen; 564df4fb1c3SGerhard Sittig } 565df4fb1c3SGerhard Sittig 566df4fb1c3SGerhard Sittig debug("RX: frame error (sts 0x%02X, %s %s %s %s %s)\n", 567df4fb1c3SGerhard Sittig sts, 568df4fb1c3SGerhard Sittig (sts & STAT_RX_LARGE_FRAME) ? "large" : "-", 569df4fb1c3SGerhard Sittig (sts & STAT_RX_LENGTH_ERROR) ? "length" : "-", 570df4fb1c3SGerhard Sittig (sts & STAT_RX_SHORT_FRAME) ? "short" : "-", 571df4fb1c3SGerhard Sittig (sts & STAT_RX_CRC_ERROR) ? "crc" : "-", 572df4fb1c3SGerhard Sittig (sts & STAT_RX_ALIGNMENT_ERROR) ? "align" : "-"); 573df4fb1c3SGerhard Sittig return -EIO; 574df4fb1c3SGerhard Sittig } 575df4fb1c3SGerhard Sittig 576*d4f847ecSSimon Glass #ifndef CONFIG_DM_ETH 577df4fb1c3SGerhard Sittig /* 578ce932c70SSimon Glass * mcs7830_init() - network interface's init callback 579ce932c70SSimon Glass * @udev: network device to initialize 580ce932c70SSimon Glass * @bd: board information 581ce932c70SSimon Glass * Return: zero upon success, negative upon error 582ce932c70SSimon Glass * 583ce932c70SSimon Glass * after initial setup during probe() and get_info(), this init() callback 584ce932c70SSimon Glass * ensures that the link is up and subsequent send() and recv() calls can 585ce932c70SSimon Glass * exchange ethernet frames 586ce932c70SSimon Glass */ 587ce932c70SSimon Glass static int mcs7830_init(struct eth_device *eth, bd_t *bd) 588ce932c70SSimon Glass { 589ce932c70SSimon Glass struct ueth_data *dev = eth->priv; 590ce932c70SSimon Glass 591ce932c70SSimon Glass return mcs7830_init_common(dev->pusb_dev); 592ce932c70SSimon Glass } 593ce932c70SSimon Glass 594ce932c70SSimon Glass /* 595ce932c70SSimon Glass * mcs7830_send() - network interface's send callback 596ce932c70SSimon Glass * @eth: network device to send the frame from 597ce932c70SSimon Glass * @packet: ethernet frame content 598ce932c70SSimon Glass * @length: ethernet frame length 599ce932c70SSimon Glass * Return: zero upon success, negative upon error 600ce932c70SSimon Glass * 601ce932c70SSimon Glass * this routine send an ethernet frame out of the network interface 602ce932c70SSimon Glass */ 603ce932c70SSimon Glass static int mcs7830_send(struct eth_device *eth, void *packet, int length) 604ce932c70SSimon Glass { 605ce932c70SSimon Glass struct ueth_data *dev = eth->priv; 606ce932c70SSimon Glass 607ce932c70SSimon Glass return mcs7830_send_common(dev, packet, length); 608ce932c70SSimon Glass } 609ce932c70SSimon Glass 610ce932c70SSimon Glass /* 611ce932c70SSimon Glass * mcs7830_recv() - network interface's recv callback 612ce932c70SSimon Glass * @eth: network device to receive frames from 613ce932c70SSimon Glass * Return: zero upon success, negative upon error 614ce932c70SSimon Glass * 615ce932c70SSimon Glass * this routine checks for available ethernet frames that the network 616ce932c70SSimon Glass * interface might have received, and notifies the network stack 617ce932c70SSimon Glass */ 618ce932c70SSimon Glass static int mcs7830_recv(struct eth_device *eth) 619ce932c70SSimon Glass { 620ce932c70SSimon Glass ALLOC_CACHE_ALIGN_BUFFER(uint8_t, buf, MCS7830_RX_URB_SIZE); 621ce932c70SSimon Glass struct ueth_data *ueth = eth->priv; 622ce932c70SSimon Glass int len; 623ce932c70SSimon Glass 624ce932c70SSimon Glass len = mcs7830_recv_common(ueth, buf); 625ce932c70SSimon Glass if (len <= 0) 626ce932c70SSimon Glass net_process_received_packet(buf, len); 627ce932c70SSimon Glass 628ce932c70SSimon Glass return 0; 629ce932c70SSimon Glass } 630ce932c70SSimon Glass 631ce932c70SSimon Glass /* 632df4fb1c3SGerhard Sittig * mcs7830_halt() - network interface's halt callback 633df4fb1c3SGerhard Sittig * @eth: network device to cease operation of 634df4fb1c3SGerhard Sittig * Return: none 635df4fb1c3SGerhard Sittig * 636df4fb1c3SGerhard Sittig * this routine is supposed to undo the effect of previous initialization and 637df4fb1c3SGerhard Sittig * ethernet frames exchange; in this implementation it's a NOP 638df4fb1c3SGerhard Sittig */ 639df4fb1c3SGerhard Sittig static void mcs7830_halt(struct eth_device *eth) 640df4fb1c3SGerhard Sittig { 641df4fb1c3SGerhard Sittig debug("%s()\n", __func__); 642df4fb1c3SGerhard Sittig } 643df4fb1c3SGerhard Sittig 644df4fb1c3SGerhard Sittig /* 645ce932c70SSimon Glass * mcs7830_write_mac() - write an ethernet adapter's MAC address 646ce932c70SSimon Glass * @eth: network device to write to 647ce932c70SSimon Glass * Return: zero upon success, negative upon error 648ce932c70SSimon Glass * 649ce932c70SSimon Glass * this routine takes the MAC address from the ethernet interface's data 650ce932c70SSimon Glass * structure, and writes it into the ethernet adapter such that subsequent 651ce932c70SSimon Glass * exchange of ethernet frames uses this address 652ce932c70SSimon Glass */ 653ce932c70SSimon Glass static int mcs7830_write_mac(struct eth_device *eth) 654ce932c70SSimon Glass { 655ce932c70SSimon Glass struct ueth_data *ueth = eth->priv; 656ce932c70SSimon Glass 657ce932c70SSimon Glass return mcs7830_write_mac_common(ueth->pusb_dev, eth->enetaddr); 658ce932c70SSimon Glass } 659ce932c70SSimon Glass 660ce932c70SSimon Glass /* 661df4fb1c3SGerhard Sittig * mcs7830_iface_idx - index of detected network interfaces 662df4fb1c3SGerhard Sittig * 663df4fb1c3SGerhard Sittig * this counter keeps track of identified supported interfaces, 664df4fb1c3SGerhard Sittig * to assign unique names as more interfaces are found 665df4fb1c3SGerhard Sittig */ 666df4fb1c3SGerhard Sittig static int mcs7830_iface_idx; 667df4fb1c3SGerhard Sittig 668df4fb1c3SGerhard Sittig /* 669df4fb1c3SGerhard Sittig * mcs7830_eth_before_probe() - network driver's before_probe callback 670df4fb1c3SGerhard Sittig * Return: none 671df4fb1c3SGerhard Sittig * 672df4fb1c3SGerhard Sittig * this routine initializes driver's internal data in preparation of 673df4fb1c3SGerhard Sittig * subsequent probe callbacks 674df4fb1c3SGerhard Sittig */ 675df4fb1c3SGerhard Sittig void mcs7830_eth_before_probe(void) 676df4fb1c3SGerhard Sittig { 677df4fb1c3SGerhard Sittig mcs7830_iface_idx = 0; 678df4fb1c3SGerhard Sittig } 679df4fb1c3SGerhard Sittig 680df4fb1c3SGerhard Sittig /* 681df4fb1c3SGerhard Sittig * struct mcs7830_dongle - description of a supported Moschip ethernet dongle 682df4fb1c3SGerhard Sittig * @vendor: 16bit USB vendor identification 683df4fb1c3SGerhard Sittig * @product: 16bit USB product identification 684df4fb1c3SGerhard Sittig * 685df4fb1c3SGerhard Sittig * this structure describes a supported USB ethernet dongle by means of the 686df4fb1c3SGerhard Sittig * vendor and product codes found during USB enumeration; no flags are held 687df4fb1c3SGerhard Sittig * here since all supported dongles have identical behaviour, and required 688df4fb1c3SGerhard Sittig * fixups get determined at runtime, such that no manual configuration is 689df4fb1c3SGerhard Sittig * needed 690df4fb1c3SGerhard Sittig */ 691df4fb1c3SGerhard Sittig struct mcs7830_dongle { 692df4fb1c3SGerhard Sittig uint16_t vendor; 693df4fb1c3SGerhard Sittig uint16_t product; 694df4fb1c3SGerhard Sittig }; 695df4fb1c3SGerhard Sittig 696df4fb1c3SGerhard Sittig /* 697df4fb1c3SGerhard Sittig * mcs7830_dongles - the list of supported Moschip based USB ethernet dongles 698df4fb1c3SGerhard Sittig */ 69951afc2c6SJeroen Hofstee static const struct mcs7830_dongle mcs7830_dongles[] = { 700df4fb1c3SGerhard Sittig { 0x9710, 0x7832, }, /* Moschip 7832 */ 701df4fb1c3SGerhard Sittig { 0x9710, 0x7830, }, /* Moschip 7830 */ 702df4fb1c3SGerhard Sittig { 0x9710, 0x7730, }, /* Moschip 7730 */ 703df4fb1c3SGerhard Sittig { 0x0df6, 0x0021, }, /* Sitecom LN 30 */ 704df4fb1c3SGerhard Sittig }; 705df4fb1c3SGerhard Sittig 706df4fb1c3SGerhard Sittig /* 707df4fb1c3SGerhard Sittig * mcs7830_eth_probe() - network driver's probe callback 708df4fb1c3SGerhard Sittig * @dev: detected USB device to check 709df4fb1c3SGerhard Sittig * @ifnum: detected USB interface to check 710df4fb1c3SGerhard Sittig * @ss: USB ethernet data structure to fill in upon match 711df4fb1c3SGerhard Sittig * Return: #1 upon match, #0 upon mismatch or error 712df4fb1c3SGerhard Sittig * 713df4fb1c3SGerhard Sittig * this routine checks whether the found USB device is supported by 714df4fb1c3SGerhard Sittig * this ethernet driver, and upon match fills in the USB ethernet 715df4fb1c3SGerhard Sittig * data structure which later is passed to the get_info callback 716df4fb1c3SGerhard Sittig */ 717df4fb1c3SGerhard Sittig int mcs7830_eth_probe(struct usb_device *dev, unsigned int ifnum, 718df4fb1c3SGerhard Sittig struct ueth_data *ss) 719df4fb1c3SGerhard Sittig { 720df4fb1c3SGerhard Sittig struct usb_interface *iface; 721df4fb1c3SGerhard Sittig struct usb_interface_descriptor *iface_desc; 722df4fb1c3SGerhard Sittig int i; 723df4fb1c3SGerhard Sittig struct mcs7830_private *priv; 724df4fb1c3SGerhard Sittig int ep_in_found, ep_out_found, ep_intr_found; 725df4fb1c3SGerhard Sittig 726df4fb1c3SGerhard Sittig debug("%s()\n", __func__); 727df4fb1c3SGerhard Sittig 728df4fb1c3SGerhard Sittig /* iterate the list of supported dongles */ 729df4fb1c3SGerhard Sittig iface = &dev->config.if_desc[ifnum]; 730df4fb1c3SGerhard Sittig iface_desc = &iface->desc; 731df4fb1c3SGerhard Sittig for (i = 0; i < ARRAY_SIZE(mcs7830_dongles); i++) { 732df4fb1c3SGerhard Sittig if (dev->descriptor.idVendor == mcs7830_dongles[i].vendor && 733df4fb1c3SGerhard Sittig dev->descriptor.idProduct == mcs7830_dongles[i].product) 734df4fb1c3SGerhard Sittig break; 735df4fb1c3SGerhard Sittig } 736df4fb1c3SGerhard Sittig if (i == ARRAY_SIZE(mcs7830_dongles)) 737df4fb1c3SGerhard Sittig return 0; 738df4fb1c3SGerhard Sittig debug("detected USB ethernet device: %04X:%04X\n", 739df4fb1c3SGerhard Sittig dev->descriptor.idVendor, dev->descriptor.idProduct); 740df4fb1c3SGerhard Sittig 741df4fb1c3SGerhard Sittig /* fill in driver private data */ 742df4fb1c3SGerhard Sittig priv = calloc(1, sizeof(*priv)); 743df4fb1c3SGerhard Sittig if (!priv) 744df4fb1c3SGerhard Sittig return 0; 745df4fb1c3SGerhard Sittig 746df4fb1c3SGerhard Sittig /* fill in the ueth_data structure, attach private data */ 747df4fb1c3SGerhard Sittig memset(ss, 0, sizeof(*ss)); 748df4fb1c3SGerhard Sittig ss->ifnum = ifnum; 749df4fb1c3SGerhard Sittig ss->pusb_dev = dev; 750df4fb1c3SGerhard Sittig ss->subclass = iface_desc->bInterfaceSubClass; 751df4fb1c3SGerhard Sittig ss->protocol = iface_desc->bInterfaceProtocol; 752df4fb1c3SGerhard Sittig ss->dev_priv = priv; 753df4fb1c3SGerhard Sittig 754df4fb1c3SGerhard Sittig /* 755df4fb1c3SGerhard Sittig * a minimum of three endpoints is expected: in (bulk), 756df4fb1c3SGerhard Sittig * out (bulk), and interrupt; ignore all others 757df4fb1c3SGerhard Sittig */ 758df4fb1c3SGerhard Sittig ep_in_found = ep_out_found = ep_intr_found = 0; 759df4fb1c3SGerhard Sittig for (i = 0; i < iface_desc->bNumEndpoints; i++) { 760df4fb1c3SGerhard Sittig uint8_t eptype, epaddr; 761df4fb1c3SGerhard Sittig bool is_input; 762df4fb1c3SGerhard Sittig 763df4fb1c3SGerhard Sittig eptype = iface->ep_desc[i].bmAttributes; 764df4fb1c3SGerhard Sittig eptype &= USB_ENDPOINT_XFERTYPE_MASK; 765df4fb1c3SGerhard Sittig 766df4fb1c3SGerhard Sittig epaddr = iface->ep_desc[i].bEndpointAddress; 767df4fb1c3SGerhard Sittig is_input = epaddr & USB_DIR_IN; 768df4fb1c3SGerhard Sittig epaddr &= USB_ENDPOINT_NUMBER_MASK; 769df4fb1c3SGerhard Sittig 770df4fb1c3SGerhard Sittig if (eptype == USB_ENDPOINT_XFER_BULK) { 771df4fb1c3SGerhard Sittig if (is_input && !ep_in_found) { 772df4fb1c3SGerhard Sittig ss->ep_in = epaddr; 773df4fb1c3SGerhard Sittig ep_in_found++; 774df4fb1c3SGerhard Sittig } 775df4fb1c3SGerhard Sittig if (!is_input && !ep_out_found) { 776df4fb1c3SGerhard Sittig ss->ep_out = epaddr; 777df4fb1c3SGerhard Sittig ep_out_found++; 778df4fb1c3SGerhard Sittig } 779df4fb1c3SGerhard Sittig } 780df4fb1c3SGerhard Sittig 781df4fb1c3SGerhard Sittig if (eptype == USB_ENDPOINT_XFER_INT) { 782df4fb1c3SGerhard Sittig if (is_input && !ep_intr_found) { 783df4fb1c3SGerhard Sittig ss->ep_int = epaddr; 784df4fb1c3SGerhard Sittig ss->irqinterval = iface->ep_desc[i].bInterval; 785df4fb1c3SGerhard Sittig ep_intr_found++; 786df4fb1c3SGerhard Sittig } 787df4fb1c3SGerhard Sittig } 788df4fb1c3SGerhard Sittig } 789df4fb1c3SGerhard Sittig debug("endpoints: in %d, out %d, intr %d\n", 790df4fb1c3SGerhard Sittig ss->ep_in, ss->ep_out, ss->ep_int); 791df4fb1c3SGerhard Sittig 792df4fb1c3SGerhard Sittig /* apply basic sanity checks */ 793df4fb1c3SGerhard Sittig if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) || 794df4fb1c3SGerhard Sittig !ss->ep_in || !ss->ep_out || !ss->ep_int) { 795df4fb1c3SGerhard Sittig debug("device probe incomplete\n"); 796df4fb1c3SGerhard Sittig return 0; 797df4fb1c3SGerhard Sittig } 798df4fb1c3SGerhard Sittig 799df4fb1c3SGerhard Sittig dev->privptr = ss; 800df4fb1c3SGerhard Sittig return 1; 801df4fb1c3SGerhard Sittig } 802df4fb1c3SGerhard Sittig 803df4fb1c3SGerhard Sittig /* 804df4fb1c3SGerhard Sittig * mcs7830_eth_get_info() - network driver's get_info callback 805df4fb1c3SGerhard Sittig * @dev: detected USB device 806df4fb1c3SGerhard Sittig * @ss: USB ethernet data structure filled in at probe() 807df4fb1c3SGerhard Sittig * @eth: ethernet interface data structure to fill in 808df4fb1c3SGerhard Sittig * Return: #1 upon success, #0 upon error 809df4fb1c3SGerhard Sittig * 810df4fb1c3SGerhard Sittig * this routine registers the mandatory init(), send(), recv(), and 811df4fb1c3SGerhard Sittig * halt() callbacks with the ethernet interface, can register the 812df4fb1c3SGerhard Sittig * optional write_hwaddr() callback with the ethernet interface, 813df4fb1c3SGerhard Sittig * and initiates configuration of the interface such that subsequent 814df4fb1c3SGerhard Sittig * calls to those callbacks results in network communication 815df4fb1c3SGerhard Sittig */ 816df4fb1c3SGerhard Sittig int mcs7830_eth_get_info(struct usb_device *dev, struct ueth_data *ss, 817df4fb1c3SGerhard Sittig struct eth_device *eth) 818df4fb1c3SGerhard Sittig { 819df4fb1c3SGerhard Sittig debug("%s()\n", __func__); 820df4fb1c3SGerhard Sittig if (!eth) { 821df4fb1c3SGerhard Sittig debug("%s: missing parameter.\n", __func__); 822df4fb1c3SGerhard Sittig return 0; 823df4fb1c3SGerhard Sittig } 824df4fb1c3SGerhard Sittig 825df4fb1c3SGerhard Sittig snprintf(eth->name, sizeof(eth->name), "%s%d", 826df4fb1c3SGerhard Sittig MCS7830_BASE_NAME, mcs7830_iface_idx++); 827df4fb1c3SGerhard Sittig eth->init = mcs7830_init; 828df4fb1c3SGerhard Sittig eth->send = mcs7830_send; 829df4fb1c3SGerhard Sittig eth->recv = mcs7830_recv; 830df4fb1c3SGerhard Sittig eth->halt = mcs7830_halt; 831df4fb1c3SGerhard Sittig eth->write_hwaddr = mcs7830_write_mac; 832df4fb1c3SGerhard Sittig eth->priv = ss; 833df4fb1c3SGerhard Sittig 834ce932c70SSimon Glass if (mcs7830_basic_reset(ss->pusb_dev, ss->dev_priv)) 835df4fb1c3SGerhard Sittig return 0; 836df4fb1c3SGerhard Sittig 837ce932c70SSimon Glass if (mcs7830_read_mac(ss->pusb_dev, eth->enetaddr)) 838df4fb1c3SGerhard Sittig return 0; 839df4fb1c3SGerhard Sittig debug("MAC %pM\n", eth->enetaddr); 840df4fb1c3SGerhard Sittig 841df4fb1c3SGerhard Sittig return 1; 842df4fb1c3SGerhard Sittig } 843*d4f847ecSSimon Glass #endif 844*d4f847ecSSimon Glass 845*d4f847ecSSimon Glass 846*d4f847ecSSimon Glass #ifdef CONFIG_DM_ETH 847*d4f847ecSSimon Glass static int mcs7830_eth_start(struct udevice *dev) 848*d4f847ecSSimon Glass { 849*d4f847ecSSimon Glass struct usb_device *udev = dev_get_parent_priv(dev); 850*d4f847ecSSimon Glass 851*d4f847ecSSimon Glass return mcs7830_init_common(udev); 852*d4f847ecSSimon Glass } 853*d4f847ecSSimon Glass 854*d4f847ecSSimon Glass void mcs7830_eth_stop(struct udevice *dev) 855*d4f847ecSSimon Glass { 856*d4f847ecSSimon Glass debug("** %s()\n", __func__); 857*d4f847ecSSimon Glass } 858*d4f847ecSSimon Glass 859*d4f847ecSSimon Glass int mcs7830_eth_send(struct udevice *dev, void *packet, int length) 860*d4f847ecSSimon Glass { 861*d4f847ecSSimon Glass struct mcs7830_private *priv = dev_get_priv(dev); 862*d4f847ecSSimon Glass struct ueth_data *ueth = &priv->ueth; 863*d4f847ecSSimon Glass 864*d4f847ecSSimon Glass return mcs7830_send_common(ueth, packet, length); 865*d4f847ecSSimon Glass } 866*d4f847ecSSimon Glass 867*d4f847ecSSimon Glass int mcs7830_eth_recv(struct udevice *dev, int flags, uchar **packetp) 868*d4f847ecSSimon Glass { 869*d4f847ecSSimon Glass struct mcs7830_private *priv = dev_get_priv(dev); 870*d4f847ecSSimon Glass struct ueth_data *ueth = &priv->ueth; 871*d4f847ecSSimon Glass int len; 872*d4f847ecSSimon Glass 873*d4f847ecSSimon Glass len = mcs7830_recv_common(ueth, priv->rx_buf); 874*d4f847ecSSimon Glass *packetp = priv->rx_buf; 875*d4f847ecSSimon Glass 876*d4f847ecSSimon Glass return len; 877*d4f847ecSSimon Glass } 878*d4f847ecSSimon Glass 879*d4f847ecSSimon Glass static int mcs7830_free_pkt(struct udevice *dev, uchar *packet, int packet_len) 880*d4f847ecSSimon Glass { 881*d4f847ecSSimon Glass struct mcs7830_private *priv = dev_get_priv(dev); 882*d4f847ecSSimon Glass 883*d4f847ecSSimon Glass packet_len = ALIGN(packet_len, 4); 884*d4f847ecSSimon Glass usb_ether_advance_rxbuf(&priv->ueth, sizeof(u32) + packet_len); 885*d4f847ecSSimon Glass 886*d4f847ecSSimon Glass return 0; 887*d4f847ecSSimon Glass } 888*d4f847ecSSimon Glass 889*d4f847ecSSimon Glass int mcs7830_write_hwaddr(struct udevice *dev) 890*d4f847ecSSimon Glass { 891*d4f847ecSSimon Glass struct usb_device *udev = dev_get_parent_priv(dev); 892*d4f847ecSSimon Glass struct eth_pdata *pdata = dev_get_platdata(dev); 893*d4f847ecSSimon Glass 894*d4f847ecSSimon Glass return mcs7830_write_mac_common(udev, pdata->enetaddr); 895*d4f847ecSSimon Glass } 896*d4f847ecSSimon Glass 897*d4f847ecSSimon Glass static int mcs7830_eth_probe(struct udevice *dev) 898*d4f847ecSSimon Glass { 899*d4f847ecSSimon Glass struct usb_device *udev = dev_get_parent_priv(dev); 900*d4f847ecSSimon Glass struct mcs7830_private *priv = dev_get_priv(dev); 901*d4f847ecSSimon Glass struct eth_pdata *pdata = dev_get_platdata(dev); 902*d4f847ecSSimon Glass struct ueth_data *ueth = &priv->ueth; 903*d4f847ecSSimon Glass 904*d4f847ecSSimon Glass if (mcs7830_basic_reset(udev, priv)) 905*d4f847ecSSimon Glass return 0; 906*d4f847ecSSimon Glass 907*d4f847ecSSimon Glass if (mcs7830_read_mac(udev, pdata->enetaddr)) 908*d4f847ecSSimon Glass return 0; 909*d4f847ecSSimon Glass 910*d4f847ecSSimon Glass return usb_ether_register(dev, ueth, MCS7830_RX_URB_SIZE); 911*d4f847ecSSimon Glass } 912*d4f847ecSSimon Glass 913*d4f847ecSSimon Glass static const struct eth_ops mcs7830_eth_ops = { 914*d4f847ecSSimon Glass .start = mcs7830_eth_start, 915*d4f847ecSSimon Glass .send = mcs7830_eth_send, 916*d4f847ecSSimon Glass .recv = mcs7830_eth_recv, 917*d4f847ecSSimon Glass .free_pkt = mcs7830_free_pkt, 918*d4f847ecSSimon Glass .stop = mcs7830_eth_stop, 919*d4f847ecSSimon Glass .write_hwaddr = mcs7830_write_hwaddr, 920*d4f847ecSSimon Glass }; 921*d4f847ecSSimon Glass 922*d4f847ecSSimon Glass U_BOOT_DRIVER(mcs7830_eth) = { 923*d4f847ecSSimon Glass .name = "mcs7830_eth", 924*d4f847ecSSimon Glass .id = UCLASS_ETH, 925*d4f847ecSSimon Glass .probe = mcs7830_eth_probe, 926*d4f847ecSSimon Glass .ops = &mcs7830_eth_ops, 927*d4f847ecSSimon Glass .priv_auto_alloc_size = sizeof(struct mcs7830_private), 928*d4f847ecSSimon Glass .platdata_auto_alloc_size = sizeof(struct eth_pdata), 929*d4f847ecSSimon Glass .flags = DM_FLAG_ALLOC_PRIV_DMA, 930*d4f847ecSSimon Glass }; 931*d4f847ecSSimon Glass 932*d4f847ecSSimon Glass static const struct usb_device_id mcs7830_eth_id_table[] = { 933*d4f847ecSSimon Glass { USB_DEVICE(0x9710, 0x7832) }, /* Moschip 7832 */ 934*d4f847ecSSimon Glass { USB_DEVICE(0x9710, 0x7830), }, /* Moschip 7830 */ 935*d4f847ecSSimon Glass { USB_DEVICE(0x9710, 0x7730), }, /* Moschip 7730 */ 936*d4f847ecSSimon Glass { USB_DEVICE(0x0df6, 0x0021), }, /* Sitecom LN 30 */ 937*d4f847ecSSimon Glass { } /* Terminating entry */ 938*d4f847ecSSimon Glass }; 939*d4f847ecSSimon Glass 940*d4f847ecSSimon Glass U_BOOT_USB_DEVICE(mcs7830_eth, mcs7830_eth_id_table); 941*d4f847ecSSimon Glass #endif 942