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