> +static u32 > +qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum) > +{ > + u16 lo, hi; > + > + lo = bus->read(bus, phy_id, regnum); > + hi = bus->read(bus, phy_id, regnum + 1); > + > + return (hi << 16) | lo; > +} > + > +static void > +qca8k_mii_write32(struct mii_bus *bus, int phy_id, u32 regnum, u32 val) > +{ > + u16 lo, hi; > + > + lo = val & 0xffff; > + hi = (u16)(val >> 16); > + > + bus->write(bus, phy_id, regnum, lo); > + bus->write(bus, phy_id, regnum + 1, hi); > +}
Hi John Thanks for picking up this driver and continuing its journey towards mainline. bus->read() and bus->write() can return an error code. There is a lot of error handling in the mv88e6xxx driver looking at the error code from these two functions. And it very rarely happens. So it seems overkill to me. However, i have had errors, when bringing up a new board and the device tree is wrong somehow. But ignoring potential error altogether does not seem wise. I think at minimum you should look at the return code in these functions and do a rate limited dev_err(). > + > +static void > +qca8k_set_page(struct mii_bus *bus, u16 page) > +{ > + if (page == qca8k_current_page) > + return; > + > + bus->write(bus, 0x18, 0, page); > + udelay(5); Is that delay in the data sheet? What about other accesses, like reading the stats which is going to generate a lot of back to back reads? > + qca8k_current_page = page; > +} > + > +static u32 > +qca8k_read(struct qca8k_priv *priv, u32 reg) > +{ > + u16 r1, r2, page; > + u32 val; > + > + qca8k_split_addr(reg, &r1, &r2, &page); > + > + mutex_lock(&priv->bus->mdio_lock); > + > + qca8k_set_page(priv->bus, page); > + val = qca8k_mii_read32(priv->bus, 0x10 | r2, r1); > + > + mutex_unlock(&priv->bus->mdio_lock); > + > + return val; > +} > + > +static void > +qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val) > +{ > + u16 r1, r2, page; > + > + qca8k_split_addr(reg, &r1, &r2, &page); > + > + mutex_lock(&priv->bus->mdio_lock); > + > + qca8k_set_page(priv->bus, page); > + qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val); > + > + mutex_unlock(&priv->bus->mdio_lock); > +} > + > +static u32 > +qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 val) > +{ > + u16 r1, r2, page; > + u32 ret; > + > + qca8k_split_addr(reg, &r1, &r2, &page); > + > + mutex_lock(&priv->bus->mdio_lock); > + > + qca8k_set_page(priv->bus, page); > + ret = qca8k_mii_read32(priv->bus, 0x10 | r2, r1); > + ret &= ~mask; > + ret |= val; > + qca8k_mii_write32(priv->bus, 0x10 | r2, r1, ret); > + > + mutex_unlock(&priv->bus->mdio_lock); > + > + return ret; > +} > + > +static inline void > +qca8k_reg_set(struct qca8k_priv *priv, u32 reg, u32 val) > +{ > + qca8k_rmw(priv, reg, 0, val); > +} > + > +static inline void > +qca8k_reg_clear(struct qca8k_priv *priv, u32 reg, u32 val) > +{ > + qca8k_rmw(priv, reg, val, 0); > +} Drop the inline please. > +static u16 > +qca8k_phy_mmd_read(struct qca8k_priv *priv, int phy_addr, u16 addr, u16 reg) > +{ > + u16 data; > + > + mutex_lock(&priv->bus->mdio_lock); > + > + priv->bus->write(priv->bus, phy_addr, MII_ATH_MMD_ADDR, addr); > + priv->bus->write(priv->bus, phy_addr, MII_ATH_MMD_DATA, reg); > + priv->bus->write(priv->bus, phy_addr, MII_ATH_MMD_ADDR, addr | 0x4000); > + data = priv->bus->read(priv->bus, phy_addr, MII_ATH_MMD_DATA); > + > + mutex_unlock(&priv->bus->mdio_lock); Have you run lockdep on this? The mv88e6xxx has something similar, and were getying false positive splats. We needed to use mdiobus_write_nested(). Since you are using bus->write directly, not using the wrapper, maybe this is not an issue. But i'm wondering if ignoring the wrapped is the right thing to do, with nested MDIO busses. Something i need to think about. > +static int > +qca8k_fdb_busy_wait(struct qca8k_priv *priv) > +{ > + unsigned long timeout; > + > + timeout = jiffies + msecs_to_jiffies(20); > + > + /* loop until the busy flag has cleared */ > + do { > + u32 reg = qca8k_read(priv, QCA8K_REG_ATU_FUNC); > + int busy = reg & QCA8K_ATU_FUNC_BUSY; > + > + if (!busy) > + break; > + } while (!time_after_eq(jiffies, timeout)); > + > + return time_after_eq(jiffies, timeout); > +} No sleep? You busy loop for 20ms? > + > +static void > +qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) > +{ > + u32 reg[4]; > + int i; > + > + /* load the ARL table into an array */ > + for (i = 0; i < 4; i++) > + reg[i] = qca8k_read(priv, QCA8K_REG_ATU_DATA0 + (i * 4)); > + > + /* vid - 83:72 */ > + fdb->vid = (reg[2] >> QCA8K_ATU_VID_S) & QCA8K_ATU_VID_M; > + /* aging - 67:64 */ > + fdb->aging = reg[2] & QCA8K_ATU_STATUS_M; > + /* portmask - 54:48 */ > + fdb->port_mask = (reg[1] >> QCA8K_ATU_PORT_S) & QCA8K_ATU_PORT_M; > + /* mac - 47:0 */ > + fdb->mac[0] = (reg[1] >> QCA8K_ATU_ADDR0_S) & 0xff; > + fdb->mac[1] = reg[1] & 0xff; > + fdb->mac[2] = (reg[0] >> QCA8K_ATU_ADDR2_S) & 0xff; > + fdb->mac[3] = (reg[0] >> QCA8K_ATU_ADDR3_S) & 0xff; > + fdb->mac[4] = (reg[0] >> QCA8K_ATU_ADDR4_S) & 0xff; > + fdb->mac[5] = reg[0] & 0xff; > +} > + > +static void > +qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 > *mac, > + u8 aging) > +{ > + u32 reg[3] = { 0 }; > + int i; > + > + /* vid - 83:72 */ > + reg[2] = (vid & QCA8K_ATU_VID_M) << QCA8K_ATU_VID_S; > + /* aging - 67:64 */ > + reg[2] |= aging & QCA8K_ATU_STATUS_M; > + /* portmask - 54:48 */ > + reg[1] = (port_mask & QCA8K_ATU_PORT_M) << QCA8K_ATU_PORT_S; > + /* mac - 47:0 */ > + reg[1] |= mac[0] << QCA8K_ATU_ADDR0_S; > + reg[1] |= mac[1]; > + reg[0] |= mac[2] << QCA8K_ATU_ADDR2_S; > + reg[0] |= mac[3] << QCA8K_ATU_ADDR3_S; > + reg[0] |= mac[4] << QCA8K_ATU_ADDR4_S; > + reg[0] |= mac[5]; > + > + /* load the array into the ARL table */ > + for (i = 0; i < 3; i++) > + qca8k_write(priv, QCA8K_REG_ATU_DATA0 + (i * 4), reg[i]); > +} > + > +static int > +qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port) > +{ > + u32 reg; > + > + /* Set the command and FDB index */ > + reg = QCA8K_ATU_FUNC_BUSY; > + reg |= cmd; > + if (port >= 0) { > + reg |= QCA8K_ATU_FUNC_PORT_EN; > + reg |= (port && QCA8K_ATU_FUNC_PORT_M) << QCA8K_ATU_FUNC_PORT_S; > + } > + > + /* Write the function register triggering the table access */ > + qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg); > + > + /* wait for completion */ > + if (qca8k_fdb_busy_wait(priv)) > + return -1; > + > + return 0; > +} > + > +static int > +qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, int port) > +{ > + int ret; > + > + qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging); > + ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port); > + if (ret >= 0) > + qca8k_fdb_read(priv, fdb); > + > + return ret; > +} So a big picture question. How does locking work? qca8k_fdb_write() will take and release the MDIO bus lock. qca8k_fdb_access() will also multiple times take and release the lock and then qca8k_fdb_read() will take and release the lock a few times. So it seems like there are multiple opportunities for a race condition, multiple parallel calls to qca8k_fdb_next() for different ports. Is this addresses somewhere? I'm out of time for the moment. More comments later. Andrew