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