diff options
Diffstat (limited to 'target/linux/generic/backport-5.10')
24 files changed, 2655 insertions, 0 deletions
diff --git a/target/linux/generic/backport-5.10/785-v5.14-01-net-dsa-qca8k-change-simple-print-to-dev-variant.patch b/target/linux/generic/backport-5.10/785-v5.14-01-net-dsa-qca8k-change-simple-print-to-dev-variant.patch new file mode 100644 index 0000000000..b8e6d9b613 --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.14-01-net-dsa-qca8k-change-simple-print-to-dev-variant.patch @@ -0,0 +1,35 @@ +From 5d9e068402dcf7354cc8ee66c2152845306d2ccb Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 22:59:51 +0200 +Subject: [PATCH] net: dsa: qca8k: change simple print to dev variant + +Change pr_err and pr_warn to dev variant. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -701,7 +701,7 @@ qca8k_setup(struct dsa_switch *ds) + + /* Make sure that port 0 is the cpu port */ + if (!dsa_is_cpu_port(ds, 0)) { +- pr_err("port 0 is not the CPU port\n"); ++ dev_err(priv->dev, "port 0 is not the CPU port"); + return -EINVAL; + } + +@@ -711,7 +711,7 @@ qca8k_setup(struct dsa_switch *ds) + priv->regmap = devm_regmap_init(ds->dev, NULL, priv, + &qca8k_regmap_config); + if (IS_ERR(priv->regmap)) +- pr_warn("regmap initialization failed"); ++ dev_warn(priv->dev, "regmap initialization failed"); + + ret = qca8k_setup_mdio_bus(priv); + if (ret) diff --git a/target/linux/generic/backport-5.10/785-v5.14-02-net-dsa-qca8k-use-iopoll-macro-for-qca8k_busy_wait.patch b/target/linux/generic/backport-5.10/785-v5.14-02-net-dsa-qca8k-use-iopoll-macro-for-qca8k_busy_wait.patch new file mode 100644 index 0000000000..ff8288d484 --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.14-02-net-dsa-qca8k-use-iopoll-macro-for-qca8k_busy_wait.patch @@ -0,0 +1,61 @@ +From 2ad255f2faaffb3af786031fba2e7955454b558a Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 22:59:52 +0200 +Subject: [PATCH] net: dsa: qca8k: use iopoll macro for qca8k_busy_wait + +Use iopoll macro instead of while loop. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 23 +++++++++++------------ + drivers/net/dsa/qca8k.h | 2 ++ + 2 files changed, 13 insertions(+), 12 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -262,21 +262,20 @@ static struct regmap_config qca8k_regmap + static int + qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) + { +- unsigned long timeout; ++ u32 val; ++ int ret; + +- timeout = jiffies + msecs_to_jiffies(20); ++ ret = read_poll_timeout(qca8k_read, val, !(val & mask), ++ 0, QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, ++ priv, reg); + +- /* loop until the busy flag has cleared */ +- do { +- u32 val = qca8k_read(priv, reg); +- int busy = val & mask; ++ /* Check if qca8k_read has failed for a different reason ++ * before returning -ETIMEDOUT ++ */ ++ if (ret < 0 && val < 0) ++ return val; + +- if (!busy) +- break; +- cond_resched(); +- } while (!time_after_eq(jiffies, timeout)); +- +- return time_after_eq(jiffies, timeout); ++ return ret; + } + + static void +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -18,6 +18,8 @@ + #define PHY_ID_QCA8337 0x004dd036 + #define QCA8K_ID_QCA8337 0x13 + ++#define QCA8K_BUSY_WAIT_TIMEOUT 20 ++ + #define QCA8K_NUM_FDB_RECORDS 2048 + + #define QCA8K_CPU_PORT 0 diff --git a/target/linux/generic/backport-5.10/785-v5.14-03-net-dsa-qca8k-improve-qca8k-read-write-rmw-bus-acces.patch b/target/linux/generic/backport-5.10/785-v5.14-03-net-dsa-qca8k-improve-qca8k-read-write-rmw-bus-acces.patch new file mode 100644 index 0000000000..c403589874 --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.14-03-net-dsa-qca8k-improve-qca8k-read-write-rmw-bus-acces.patch @@ -0,0 +1,86 @@ +From 504bf65931824eda83494e5b5d75686e27ace03e Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 22:59:53 +0200 +Subject: [PATCH] net: dsa: qca8k: improve qca8k read/write/rmw bus access + +Put bus in local variable to improve faster access to the mdio bus. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 29 ++++++++++++++++------------- + 1 file changed, 16 insertions(+), 13 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -142,17 +142,18 @@ qca8k_set_page(struct mii_bus *bus, u16 + static u32 + qca8k_read(struct qca8k_priv *priv, u32 reg) + { ++ struct mii_bus *bus = priv->bus; + u16 r1, r2, page; + u32 val; + + qca8k_split_addr(reg, &r1, &r2, &page); + +- mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +- qca8k_set_page(priv->bus, page); +- val = qca8k_mii_read32(priv->bus, 0x10 | r2, r1); ++ qca8k_set_page(bus, page); ++ val = qca8k_mii_read32(bus, 0x10 | r2, r1); + +- mutex_unlock(&priv->bus->mdio_lock); ++ mutex_unlock(&bus->mdio_lock); + + return val; + } +@@ -160,35 +161,37 @@ qca8k_read(struct qca8k_priv *priv, u32 + static void + qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val) + { ++ struct mii_bus *bus = priv->bus; + u16 r1, r2, page; + + qca8k_split_addr(reg, &r1, &r2, &page); + +- mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +- qca8k_set_page(priv->bus, page); +- qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val); ++ qca8k_set_page(bus, page); ++ qca8k_mii_write32(bus, 0x10 | r2, r1, val); + +- mutex_unlock(&priv->bus->mdio_lock); ++ mutex_unlock(&bus->mdio_lock); + } + + static u32 + qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 val) + { ++ struct mii_bus *bus = priv->bus; + u16 r1, r2, page; + u32 ret; + + qca8k_split_addr(reg, &r1, &r2, &page); + +- mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +- qca8k_set_page(priv->bus, page); +- ret = qca8k_mii_read32(priv->bus, 0x10 | r2, r1); ++ qca8k_set_page(bus, page); ++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1); + ret &= ~mask; + ret |= val; +- qca8k_mii_write32(priv->bus, 0x10 | r2, r1, ret); ++ qca8k_mii_write32(bus, 0x10 | r2, r1, ret); + +- mutex_unlock(&priv->bus->mdio_lock); ++ mutex_unlock(&bus->mdio_lock); + + return ret; + } diff --git a/target/linux/generic/backport-5.10/785-v5.14-04-net-dsa-qca8k-handle-qca8k_set_page-errors.patch b/target/linux/generic/backport-5.10/785-v5.14-04-net-dsa-qca8k-handle-qca8k_set_page-errors.patch new file mode 100644 index 0000000000..6be494a8c7 --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.14-04-net-dsa-qca8k-handle-qca8k_set_page-errors.patch @@ -0,0 +1,101 @@ +From ba5707ec58cfb6853dff41c2aae72deb6a03d389 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 22:59:54 +0200 +Subject: [PATCH] net: dsa: qca8k: handle qca8k_set_page errors + +With a remote possibility, the set_page function can fail. Since this is +a critical part of the write/read qca8k regs, propagate the error and +terminate any read/write operation. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 33 ++++++++++++++++++++++++++------- + 1 file changed, 26 insertions(+), 7 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -127,16 +127,23 @@ qca8k_mii_write32(struct mii_bus *bus, i + "failed to write qca8k 32bit register\n"); + } + +-static void ++static int + qca8k_set_page(struct mii_bus *bus, u16 page) + { ++ int ret; ++ + if (page == qca8k_current_page) +- return; ++ return 0; + +- if (bus->write(bus, 0x18, 0, page) < 0) ++ ret = bus->write(bus, 0x18, 0, page); ++ if (ret < 0) { + dev_err_ratelimited(&bus->dev, + "failed to set qca8k page\n"); ++ return ret; ++ } ++ + qca8k_current_page = page; ++ return 0; + } + + static u32 +@@ -150,11 +157,14 @@ qca8k_read(struct qca8k_priv *priv, u32 + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +- qca8k_set_page(bus, page); ++ val = qca8k_set_page(bus, page); ++ if (val < 0) ++ goto exit; ++ + val = qca8k_mii_read32(bus, 0x10 | r2, r1); + ++exit: + mutex_unlock(&bus->mdio_lock); +- + return val; + } + +@@ -163,14 +173,19 @@ qca8k_write(struct qca8k_priv *priv, u32 + { + struct mii_bus *bus = priv->bus; + u16 r1, r2, page; ++ int ret; + + qca8k_split_addr(reg, &r1, &r2, &page); + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +- qca8k_set_page(bus, page); ++ ret = qca8k_set_page(bus, page); ++ if (ret < 0) ++ goto exit; ++ + qca8k_mii_write32(bus, 0x10 | r2, r1, val); + ++exit: + mutex_unlock(&bus->mdio_lock); + } + +@@ -185,12 +200,16 @@ qca8k_rmw(struct qca8k_priv *priv, u32 r + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +- qca8k_set_page(bus, page); ++ ret = qca8k_set_page(bus, page); ++ if (ret < 0) ++ goto exit; ++ + ret = qca8k_mii_read32(bus, 0x10 | r2, r1); + ret &= ~mask; + ret |= val; + qca8k_mii_write32(bus, 0x10 | r2, r1, ret); + ++exit: + mutex_unlock(&bus->mdio_lock); + + return ret; diff --git a/target/linux/generic/backport-5.10/785-v5.14-05-net-dsa-qca8k-handle-error-with-qca8k_read-operation.patch b/target/linux/generic/backport-5.10/785-v5.14-05-net-dsa-qca8k-handle-error-with-qca8k_read-operation.patch new file mode 100644 index 0000000000..3349b7897a --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.14-05-net-dsa-qca8k-handle-error-with-qca8k_read-operation.patch @@ -0,0 +1,207 @@ +From 028f5f8ef44fcf87a456772cbb9f0d90a0a22884 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 22:59:55 +0200 +Subject: [PATCH] net: dsa: qca8k: handle error with qca8k_read operation + +qca8k_read can fail. Rework any user to handle error values and +correctly return. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 73 ++++++++++++++++++++++++++++++++--------- + 1 file changed, 58 insertions(+), 15 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -231,8 +231,13 @@ static int + qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val) + { + struct qca8k_priv *priv = (struct qca8k_priv *)ctx; ++ int ret; ++ ++ ret = qca8k_read(priv, reg); ++ if (ret < 0) ++ return ret; + +- *val = qca8k_read(priv, reg); ++ *val = ret; + + return 0; + } +@@ -300,15 +305,20 @@ qca8k_busy_wait(struct qca8k_priv *priv, + return ret; + } + +-static void ++static int + qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) + { +- u32 reg[4]; ++ u32 reg[4], val; + 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)); ++ for (i = 0; i < 4; i++) { ++ val = qca8k_read(priv, QCA8K_REG_ATU_DATA0 + (i * 4)); ++ if (val < 0) ++ return val; ++ ++ reg[i] = val; ++ } + + /* vid - 83:72 */ + fdb->vid = (reg[2] >> QCA8K_ATU_VID_S) & QCA8K_ATU_VID_M; +@@ -323,6 +333,8 @@ qca8k_fdb_read(struct qca8k_priv *priv, + 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; ++ ++ return 0; + } + + static void +@@ -374,6 +386,8 @@ qca8k_fdb_access(struct qca8k_priv *priv + /* Check for table full violation when adding an entry */ + if (cmd == QCA8K_FDB_LOAD) { + reg = qca8k_read(priv, QCA8K_REG_ATU_FUNC); ++ if (reg < 0) ++ return reg; + if (reg & QCA8K_ATU_FUNC_FULL) + return -1; + } +@@ -388,10 +402,10 @@ qca8k_fdb_next(struct qca8k_priv *priv, + + 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); ++ if (ret < 0) ++ return ret; + +- return ret; ++ return qca8k_fdb_read(priv, fdb); + } + + static int +@@ -449,6 +463,8 @@ qca8k_vlan_access(struct qca8k_priv *pri + /* Check for table full violation when adding an entry */ + if (cmd == QCA8K_VLAN_LOAD) { + reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC1); ++ if (reg < 0) ++ return reg; + if (reg & QCA8K_VTU_FUNC1_FULL) + return -ENOMEM; + } +@@ -475,6 +491,8 @@ qca8k_vlan_add(struct qca8k_priv *priv, + goto out; + + reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC0); ++ if (reg < 0) ++ return reg; + reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN; + reg &= ~(QCA8K_VTU_FUNC0_EG_MODE_MASK << QCA8K_VTU_FUNC0_EG_MODE_S(port)); + if (untagged) +@@ -506,6 +524,8 @@ qca8k_vlan_del(struct qca8k_priv *priv, + goto out; + + reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC0); ++ if (reg < 0) ++ return reg; + reg &= ~(3 << QCA8K_VTU_FUNC0_EG_MODE_S(port)); + reg |= QCA8K_VTU_FUNC0_EG_MODE_NOT << + QCA8K_VTU_FUNC0_EG_MODE_S(port); +@@ -621,8 +641,11 @@ qca8k_mdio_read(struct qca8k_priv *priv, + QCA8K_MDIO_MASTER_BUSY)) + return -ETIMEDOUT; + +- val = (qca8k_read(priv, QCA8K_MDIO_MASTER_CTRL) & +- QCA8K_MDIO_MASTER_DATA_MASK); ++ val = qca8k_read(priv, QCA8K_MDIO_MASTER_CTRL); ++ if (val < 0) ++ return val; ++ ++ val &= QCA8K_MDIO_MASTER_DATA_MASK; + + return val; + } +@@ -978,6 +1001,8 @@ qca8k_phylink_mac_link_state(struct dsa_ + u32 reg; + + reg = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port)); ++ if (reg < 0) ++ return reg; + + state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP); + state->an_complete = state->link; +@@ -1078,18 +1103,26 @@ qca8k_get_ethtool_stats(struct dsa_switc + { + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; + const struct qca8k_mib_desc *mib; +- u32 reg, i; ++ u32 reg, i, val; + u64 hi; + + for (i = 0; i < ARRAY_SIZE(ar8327_mib); i++) { + mib = &ar8327_mib[i]; + reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset; + +- data[i] = qca8k_read(priv, reg); ++ val = qca8k_read(priv, reg); ++ if (val < 0) ++ continue; ++ + if (mib->size == 2) { + hi = qca8k_read(priv, reg + 4); +- data[i] |= hi << 32; ++ if (hi < 0) ++ continue; + } ++ ++ data[i] = val; ++ if (mib->size == 2) ++ data[i] |= hi << 32; + } + } + +@@ -1107,18 +1140,25 @@ qca8k_set_mac_eee(struct dsa_switch *ds, + { + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; + u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port); ++ int ret = 0; + u32 reg; + + mutex_lock(&priv->reg_mutex); + reg = qca8k_read(priv, QCA8K_REG_EEE_CTRL); ++ if (reg < 0) { ++ ret = reg; ++ goto exit; ++ } ++ + if (eee->eee_enabled) + reg |= lpi_en; + else + reg &= ~lpi_en; + qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg); +- mutex_unlock(&priv->reg_mutex); + +- return 0; ++exit: ++ mutex_unlock(&priv->reg_mutex); ++ return ret; + } + + static int +@@ -1456,6 +1496,9 @@ qca8k_sw_probe(struct mdio_device *mdiod + + /* read the switches ID register */ + id = qca8k_read(priv, QCA8K_REG_MASK_CTRL); ++ if (id < 0) ++ return id; ++ + id >>= QCA8K_MASK_CTRL_ID_S; + id &= QCA8K_MASK_CTRL_ID_M; + if (id != QCA8K_ID_QCA8337) diff --git a/target/linux/generic/backport-5.10/785-v5.14-06-net-dsa-qca8k-handle-error-with-qca8k_write-operatio.patch b/target/linux/generic/backport-5.10/785-v5.14-06-net-dsa-qca8k-handle-error-with-qca8k_write-operatio.patch new file mode 100644 index 0000000000..1e0e224c39 --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.14-06-net-dsa-qca8k-handle-error-with-qca8k_write-operatio.patch @@ -0,0 +1,263 @@ +From d7805757c75c76e9518fc1023a29f0c4eed5b581 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 22:59:56 +0200 +Subject: [PATCH] net: dsa: qca8k: handle error with qca8k_write operation + +qca8k_write can fail. Rework any user to handle error values and +correctly return. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 102 ++++++++++++++++++++++++++-------------- + 1 file changed, 67 insertions(+), 35 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -168,7 +168,7 @@ exit: + return val; + } + +-static void ++static int + qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val) + { + struct mii_bus *bus = priv->bus; +@@ -187,6 +187,7 @@ qca8k_write(struct qca8k_priv *priv, u32 + + exit: + mutex_unlock(&bus->mdio_lock); ++ return ret; + } + + static u32 +@@ -247,9 +248,7 @@ qca8k_regmap_write(void *ctx, uint32_t r + { + struct qca8k_priv *priv = (struct qca8k_priv *)ctx; + +- qca8k_write(priv, reg, val); +- +- return 0; ++ return qca8k_write(priv, reg, val); + } + + static const struct regmap_range qca8k_readable_ranges[] = { +@@ -367,6 +366,7 @@ static int + qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port) + { + u32 reg; ++ int ret; + + /* Set the command and FDB index */ + reg = QCA8K_ATU_FUNC_BUSY; +@@ -377,7 +377,9 @@ qca8k_fdb_access(struct qca8k_priv *priv + } + + /* Write the function register triggering the table access */ +- qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg); ++ ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg); ++ if (ret) ++ return ret; + + /* wait for completion */ + if (qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY)) +@@ -447,6 +449,7 @@ static int + qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid) + { + u32 reg; ++ int ret; + + /* Set the command and VLAN index */ + reg = QCA8K_VTU_FUNC1_BUSY; +@@ -454,7 +457,9 @@ qca8k_vlan_access(struct qca8k_priv *pri + reg |= vid << QCA8K_VTU_FUNC1_VID_S; + + /* Write the function register triggering the table access */ +- qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg); ++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg); ++ if (ret) ++ return ret; + + /* wait for completion */ + if (qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY)) +@@ -502,7 +507,9 @@ qca8k_vlan_add(struct qca8k_priv *priv, + reg |= QCA8K_VTU_FUNC0_EG_MODE_TAG << + QCA8K_VTU_FUNC0_EG_MODE_S(port); + +- qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); ++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); ++ if (ret) ++ return ret; + ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); + + out: +@@ -545,7 +552,9 @@ qca8k_vlan_del(struct qca8k_priv *priv, + if (del) { + ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid); + } else { +- qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); ++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); ++ if (ret) ++ return ret; + ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); + } + +@@ -555,15 +564,20 @@ out: + return ret; + } + +-static void ++static int + qca8k_mib_init(struct qca8k_priv *priv) + { ++ int ret; ++ + mutex_lock(&priv->reg_mutex); + qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY); + qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); + qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); +- qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB); ++ ++ ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB); ++ + mutex_unlock(&priv->reg_mutex); ++ return ret; + } + + static void +@@ -600,6 +614,7 @@ static int + qca8k_mdio_write(struct qca8k_priv *priv, int port, u32 regnum, u16 data) + { + u32 phy, val; ++ int ret; + + if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) + return -EINVAL; +@@ -613,7 +628,9 @@ qca8k_mdio_write(struct qca8k_priv *priv + QCA8K_MDIO_MASTER_REG_ADDR(regnum) | + QCA8K_MDIO_MASTER_DATA(data); + +- qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val); ++ ret = qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val); ++ if (ret) ++ return ret; + + return qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, + QCA8K_MDIO_MASTER_BUSY); +@@ -623,6 +640,7 @@ static int + qca8k_mdio_read(struct qca8k_priv *priv, int port, u32 regnum) + { + u32 phy, val; ++ int ret; + + if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) + return -EINVAL; +@@ -635,7 +653,9 @@ qca8k_mdio_read(struct qca8k_priv *priv, + QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) | + QCA8K_MDIO_MASTER_REG_ADDR(regnum); + +- qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val); ++ ret = qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val); ++ if (ret) ++ return ret; + + if (qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, + QCA8K_MDIO_MASTER_BUSY)) +@@ -766,12 +786,18 @@ qca8k_setup(struct dsa_switch *ds) + QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN); + + /* Enable MIB counters */ +- qca8k_mib_init(priv); ++ ret = qca8k_mib_init(priv); ++ if (ret) ++ dev_warn(priv->dev, "mib init failed"); + + /* Enable QCA header mode on the cpu port */ +- qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(QCA8K_CPU_PORT), +- QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_TX_S | +- QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_RX_S); ++ ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(QCA8K_CPU_PORT), ++ QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_TX_S | ++ QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_RX_S); ++ if (ret) { ++ dev_err(priv->dev, "failed enabling QCA header mode"); ++ return ret; ++ } + + /* Disable forwarding by default on all ports */ + for (i = 0; i < QCA8K_NUM_PORTS; i++) +@@ -783,11 +809,13 @@ qca8k_setup(struct dsa_switch *ds) + qca8k_port_set_status(priv, i, 0); + + /* Forward all unknown frames to CPU port for Linux processing */ +- qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1, +- BIT(0) << QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S | +- BIT(0) << QCA8K_GLOBAL_FW_CTRL1_BC_DP_S | +- BIT(0) << QCA8K_GLOBAL_FW_CTRL1_MC_DP_S | +- BIT(0) << QCA8K_GLOBAL_FW_CTRL1_UC_DP_S); ++ ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1, ++ BIT(0) << QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S | ++ BIT(0) << QCA8K_GLOBAL_FW_CTRL1_BC_DP_S | ++ BIT(0) << QCA8K_GLOBAL_FW_CTRL1_MC_DP_S | ++ BIT(0) << QCA8K_GLOBAL_FW_CTRL1_UC_DP_S); ++ if (ret) ++ return ret; + + /* Setup connection between CPU port & user ports */ + for (i = 0; i < QCA8K_NUM_PORTS; i++) { +@@ -815,16 +843,20 @@ qca8k_setup(struct dsa_switch *ds) + qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i), + 0xfff << shift, + QCA8K_PORT_VID_DEF << shift); +- qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i), +- QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) | +- QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF)); ++ ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i), ++ QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) | ++ QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF)); ++ if (ret) ++ return ret; + } + } + + /* Setup our port MTUs to match power on defaults */ + for (i = 0; i < QCA8K_NUM_PORTS; i++) + priv->port_mtu[i] = ETH_FRAME_LEN + ETH_FCS_LEN; +- qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN); ++ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN); ++ if (ret) ++ dev_warn(priv->dev, "failed setting MTU settings"); + + /* Flush the FDB table */ + qca8k_fdb_flush(priv); +@@ -1140,8 +1172,8 @@ qca8k_set_mac_eee(struct dsa_switch *ds, + { + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; + u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port); +- int ret = 0; + u32 reg; ++ int ret; + + mutex_lock(&priv->reg_mutex); + reg = qca8k_read(priv, QCA8K_REG_EEE_CTRL); +@@ -1154,7 +1186,7 @@ qca8k_set_mac_eee(struct dsa_switch *ds, + reg |= lpi_en; + else + reg &= ~lpi_en; +- qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg); ++ ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg); + + exit: + mutex_unlock(&priv->reg_mutex); +@@ -1284,9 +1316,7 @@ qca8k_port_change_mtu(struct dsa_switch + mtu = priv->port_mtu[i]; + + /* Include L2 header / FCS length */ +- qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, mtu + ETH_HLEN + ETH_FCS_LEN); +- +- return 0; ++ return qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, mtu + ETH_HLEN + ETH_FCS_LEN); + } + + static int diff --git a/target/linux/generic/backport-5.10/785-v5.14-07-net-dsa-qca8k-handle-error-with-qca8k_rmw-operation.patch b/target/linux/generic/backport-5.10/785-v5.14-07-net-dsa-qca8k-handle-error-with-qca8k_rmw-operation.patch new file mode 100644 index 0000000000..506966f1af --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.14-07-net-dsa-qca8k-handle-error-with-qca8k_rmw-operation.patch @@ -0,0 +1,226 @@ +From aaf421425cbdec4eb6fd75a29e65c2867b0b7bbd Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 22:59:57 +0200 +Subject: [PATCH] net: dsa: qca8k: handle error with qca8k_rmw operation + +qca8k_rmw can fail. Rework any user to handle error values and +correctly return. Change qca8k_rmw to return the error code or 0 instead +of the reg value. The reg returned by qca8k_rmw wasn't used anywhere, +so this doesn't cause any functional change. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 133 +++++++++++++++++++++++++--------------- + 1 file changed, 83 insertions(+), 50 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -190,12 +190,13 @@ exit: + return ret; + } + +-static u32 +-qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 val) ++static int ++qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) + { + struct mii_bus *bus = priv->bus; + u16 r1, r2, page; +- u32 ret; ++ u32 val; ++ int ret; + + qca8k_split_addr(reg, &r1, &r2, &page); + +@@ -205,10 +206,15 @@ qca8k_rmw(struct qca8k_priv *priv, u32 r + if (ret < 0) + goto exit; + +- ret = qca8k_mii_read32(bus, 0x10 | r2, r1); +- ret &= ~mask; +- ret |= val; +- qca8k_mii_write32(bus, 0x10 | r2, r1, ret); ++ val = qca8k_mii_read32(bus, 0x10 | r2, r1); ++ if (val < 0) { ++ ret = val; ++ goto exit; ++ } ++ ++ val &= ~mask; ++ val |= write_val; ++ qca8k_mii_write32(bus, 0x10 | r2, r1, val); + + exit: + mutex_unlock(&bus->mdio_lock); +@@ -216,16 +222,16 @@ exit: + return ret; + } + +-static void ++static int + qca8k_reg_set(struct qca8k_priv *priv, u32 reg, u32 val) + { +- qca8k_rmw(priv, reg, 0, val); ++ return qca8k_rmw(priv, reg, 0, val); + } + +-static void ++static int + qca8k_reg_clear(struct qca8k_priv *priv, u32 reg, u32 val) + { +- qca8k_rmw(priv, reg, val, 0); ++ return qca8k_rmw(priv, reg, val, 0); + } + + static int +@@ -570,12 +576,19 @@ qca8k_mib_init(struct qca8k_priv *priv) + int ret; + + mutex_lock(&priv->reg_mutex); +- qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY); ++ ret = qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY); ++ if (ret) ++ goto exit; ++ + qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); +- qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); ++ ++ ret = qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); ++ if (ret) ++ goto exit; + + ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB); + ++exit: + mutex_unlock(&priv->reg_mutex); + return ret; + } +@@ -747,9 +760,8 @@ qca8k_setup_mdio_bus(struct qca8k_priv * + * a dt-overlay and driver reload changed the configuration + */ + +- qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_EN); +- return 0; ++ return qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_EN); + } + + priv->ops.phy_read = qca8k_phy_read; +@@ -782,8 +794,12 @@ qca8k_setup(struct dsa_switch *ds) + return ret; + + /* Enable CPU Port */ +- qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0, +- QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN); ++ ret = qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0, ++ QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN); ++ if (ret) { ++ dev_err(priv->dev, "failed enabling CPU port"); ++ return ret; ++ } + + /* Enable MIB counters */ + ret = qca8k_mib_init(priv); +@@ -800,9 +816,12 @@ qca8k_setup(struct dsa_switch *ds) + } + + /* Disable forwarding by default on all ports */ +- for (i = 0; i < QCA8K_NUM_PORTS; i++) +- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), +- QCA8K_PORT_LOOKUP_MEMBER, 0); ++ for (i = 0; i < QCA8K_NUM_PORTS; i++) { ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), ++ QCA8K_PORT_LOOKUP_MEMBER, 0); ++ if (ret) ++ return ret; ++ } + + /* Disable MAC by default on all ports */ + for (i = 1; i < QCA8K_NUM_PORTS; i++) +@@ -821,28 +840,37 @@ qca8k_setup(struct dsa_switch *ds) + for (i = 0; i < QCA8K_NUM_PORTS; i++) { + /* CPU port gets connected to all user ports of the switch */ + if (dsa_is_cpu_port(ds, i)) { +- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(QCA8K_CPU_PORT), +- QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(QCA8K_CPU_PORT), ++ QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); ++ if (ret) ++ return ret; + } + + /* Individual user ports get connected to CPU port only */ + if (dsa_is_user_port(ds, i)) { + int shift = 16 * (i % 2); + +- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), +- QCA8K_PORT_LOOKUP_MEMBER, +- BIT(QCA8K_CPU_PORT)); ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), ++ QCA8K_PORT_LOOKUP_MEMBER, ++ BIT(QCA8K_CPU_PORT)); ++ if (ret) ++ return ret; + + /* Enable ARP Auto-learning by default */ +- qca8k_reg_set(priv, QCA8K_PORT_LOOKUP_CTRL(i), +- QCA8K_PORT_LOOKUP_LEARN); ++ ret = qca8k_reg_set(priv, QCA8K_PORT_LOOKUP_CTRL(i), ++ QCA8K_PORT_LOOKUP_LEARN); ++ if (ret) ++ return ret; + + /* For port based vlans to work we need to set the + * default egress vid + */ +- qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i), +- 0xfff << shift, +- QCA8K_PORT_VID_DEF << shift); ++ ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i), ++ 0xfff << shift, ++ QCA8K_PORT_VID_DEF << shift); ++ if (ret) ++ return ret; ++ + ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i), + QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) | + QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF)); +@@ -1234,7 +1262,7 @@ qca8k_port_bridge_join(struct dsa_switch + { + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; + int port_mask = BIT(QCA8K_CPU_PORT); +- int i; ++ int i, ret; + + for (i = 1; i < QCA8K_NUM_PORTS; i++) { + if (dsa_to_port(ds, i)->bridge_dev != br) +@@ -1242,17 +1270,20 @@ qca8k_port_bridge_join(struct dsa_switch + /* Add this port to the portvlan mask of the other ports + * in the bridge + */ +- qca8k_reg_set(priv, +- QCA8K_PORT_LOOKUP_CTRL(i), +- BIT(port)); ++ ret = qca8k_reg_set(priv, ++ QCA8K_PORT_LOOKUP_CTRL(i), ++ BIT(port)); ++ if (ret) ++ return ret; + if (i != port) + port_mask |= BIT(i); + } ++ + /* Add all other ports to this ports portvlan mask */ +- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_MEMBER, port_mask); ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_MEMBER, port_mask); + +- return 0; ++ return ret; + } + + static void diff --git a/target/linux/generic/backport-5.10/785-v5.14-08-net-dsa-qca8k-handle-error-from-qca8k_busy_wait.patch b/target/linux/generic/backport-5.10/785-v5.14-08-net-dsa-qca8k-handle-error-from-qca8k_busy_wait.patch new file mode 100644 index 0000000000..360ce1d947 --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.14-08-net-dsa-qca8k-handle-error-from-qca8k_busy_wait.patch @@ -0,0 +1,66 @@ +From b7c818d194927bdc60ed15db55bb8654496a36b7 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 22:59:58 +0200 +Subject: [PATCH] net: dsa: qca8k: handle error from qca8k_busy_wait + +Propagate errors from qca8k_busy_wait instead of hardcoding return +value. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 21 +++++++++++++-------- + 1 file changed, 13 insertions(+), 8 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -388,8 +388,9 @@ qca8k_fdb_access(struct qca8k_priv *priv + return ret; + + /* wait for completion */ +- if (qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY)) +- return -1; ++ ret = qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY); ++ if (ret) ++ return ret; + + /* Check for table full violation when adding an entry */ + if (cmd == QCA8K_FDB_LOAD) { +@@ -468,8 +469,9 @@ qca8k_vlan_access(struct qca8k_priv *pri + return ret; + + /* wait for completion */ +- if (qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY)) +- return -ETIMEDOUT; ++ ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY); ++ if (ret) ++ return ret; + + /* Check for table full violation when adding an entry */ + if (cmd == QCA8K_VLAN_LOAD) { +@@ -580,7 +582,9 @@ qca8k_mib_init(struct qca8k_priv *priv) + if (ret) + goto exit; + +- qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); ++ ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); ++ if (ret) ++ goto exit; + + ret = qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); + if (ret) +@@ -670,9 +674,10 @@ qca8k_mdio_read(struct qca8k_priv *priv, + if (ret) + return ret; + +- if (qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_BUSY)) +- return -ETIMEDOUT; ++ ret = qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_BUSY); ++ if (ret) ++ return ret; + + val = qca8k_read(priv, QCA8K_MDIO_MASTER_CTRL); + if (val < 0) diff --git a/target/linux/generic/backport-5.10/785-v5.14-09-net-dsa-qca8k-add-support-for-qca8327-switch.patch b/target/linux/generic/backport-5.10/785-v5.14-09-net-dsa-qca8k-add-support-for-qca8327-switch.patch new file mode 100644 index 0000000000..72305850ca --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.14-09-net-dsa-qca8k-add-support-for-qca8327-switch.patch @@ -0,0 +1,96 @@ +From 6e82a457e06252b59102486767539cc9c2aba60b Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 22:59:59 +0200 +Subject: [PATCH] net: dsa: qca8k: add support for qca8327 switch + +qca8327 switch is a low tier version of the more recent qca8337. +It does share the same regs used by the qca8k driver and can be +supported with minimal change. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 23 ++++++++++++++++++++--- + drivers/net/dsa/qca8k.h | 6 ++++++ + 2 files changed, 26 insertions(+), 3 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1533,6 +1533,7 @@ static const struct dsa_switch_ops qca8k + static int + qca8k_sw_probe(struct mdio_device *mdiodev) + { ++ const struct qca8k_match_data *data; + struct qca8k_priv *priv; + u32 id; + +@@ -1560,6 +1561,11 @@ qca8k_sw_probe(struct mdio_device *mdiod + gpiod_set_value_cansleep(priv->reset_gpio, 0); + } + ++ /* get the switches ID from the compatible */ ++ data = of_device_get_match_data(&mdiodev->dev); ++ if (!data) ++ return -ENODEV; ++ + /* read the switches ID register */ + id = qca8k_read(priv, QCA8K_REG_MASK_CTRL); + if (id < 0) +@@ -1567,8 +1573,10 @@ qca8k_sw_probe(struct mdio_device *mdiod + + id >>= QCA8K_MASK_CTRL_ID_S; + id &= QCA8K_MASK_CTRL_ID_M; +- if (id != QCA8K_ID_QCA8337) ++ if (id != data->id) { ++ dev_err(&mdiodev->dev, "Switch id detected %x but expected %x", id, data->id); + return -ENODEV; ++ } + + priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL); + if (!priv->ds) +@@ -1634,9 +1642,18 @@ static int qca8k_resume(struct device *d + static SIMPLE_DEV_PM_OPS(qca8k_pm_ops, + qca8k_suspend, qca8k_resume); + ++static const struct qca8k_match_data qca832x = { ++ .id = QCA8K_ID_QCA8327, ++}; ++ ++static const struct qca8k_match_data qca833x = { ++ .id = QCA8K_ID_QCA8337, ++}; ++ + static const struct of_device_id qca8k_of_match[] = { +- { .compatible = "qca,qca8334" }, +- { .compatible = "qca,qca8337" }, ++ { .compatible = "qca,qca8327", .data = &qca832x }, ++ { .compatible = "qca,qca8334", .data = &qca833x }, ++ { .compatible = "qca,qca8337", .data = &qca833x }, + { /* sentinel */ }, + }; + +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -15,6 +15,8 @@ + #define QCA8K_NUM_PORTS 7 + #define QCA8K_MAX_MTU 9000 + ++#define PHY_ID_QCA8327 0x004dd034 ++#define QCA8K_ID_QCA8327 0x12 + #define PHY_ID_QCA8337 0x004dd036 + #define QCA8K_ID_QCA8337 0x13 + +@@ -213,6 +215,10 @@ struct ar8xxx_port_status { + int enabled; + }; + ++struct qca8k_match_data { ++ u8 id; ++}; ++ + struct qca8k_priv { + struct regmap *regmap; + struct mii_bus *bus; diff --git a/target/linux/generic/backport-5.10/785-v5.14-10-devicetree-net-dsa-qca8k-Document-new-compatible-qca.patch b/target/linux/generic/backport-5.10/785-v5.14-10-devicetree-net-dsa-qca8k-Document-new-compatible-qca.patch new file mode 100644 index 0000000000..3c4a14bd0b --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.14-10-devicetree-net-dsa-qca8k-Document-new-compatible-qca.patch @@ -0,0 +1,26 @@ +From 227a9ffc1bc77037339530607fe129af3824620e Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 23:00:00 +0200 +Subject: [PATCH] devicetree: net: dsa: qca8k: Document new compatible qca8327 + +Add support for qca8327 in the compatible list. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Acked-by: Rob Herring <robh@kernel.org> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + Documentation/devicetree/bindings/net/dsa/qca8k.txt | 1 + + 1 file changed, 1 insertion(+) + +--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt ++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt +@@ -3,6 +3,7 @@ + Required properties: + + - compatible: should be one of: ++ "qca,qca8327" + "qca,qca8334" + "qca,qca8337" + diff --git a/target/linux/generic/backport-5.10/785-v5.14-11-net-dsa-qca8k-add-priority-tweak-to-qca8337-switch.patch b/target/linux/generic/backport-5.10/785-v5.14-11-net-dsa-qca8k-add-priority-tweak-to-qca8337-switch.patch new file mode 100644 index 0000000000..cd3050ef71 --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.14-11-net-dsa-qca8k-add-priority-tweak-to-qca8337-switch.patch @@ -0,0 +1,130 @@ +From 83a3ceb39b2495171aabe9446271b94c678354f3 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 23:00:01 +0200 +Subject: [PATCH] net: dsa: qca8k: add priority tweak to qca8337 switch + +The port 5 of the qca8337 have some problem in flood condition. The +original legacy driver had some specific buffer and priority settings +for the different port suggested by the QCA switch team. Add this +missing settings to improve switch stability under load condition. +The packet priority tweak is only needed for the qca8337 switch and +other qca8k switch are not affected. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 47 +++++++++++++++++++++++++++++++++++++++++ + drivers/net/dsa/qca8k.h | 25 ++++++++++++++++++++++ + 2 files changed, 72 insertions(+) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -779,6 +779,7 @@ qca8k_setup(struct dsa_switch *ds) + { + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; + int ret, i; ++ u32 mask; + + /* Make sure that port 0 is the cpu port */ + if (!dsa_is_cpu_port(ds, 0)) { +@@ -884,6 +885,51 @@ qca8k_setup(struct dsa_switch *ds) + } + } + ++ /* The port 5 of the qca8337 have some problem in flood condition. The ++ * original legacy driver had some specific buffer and priority settings ++ * for the different port suggested by the QCA switch team. Add this ++ * missing settings to improve switch stability under load condition. ++ * This problem is limited to qca8337 and other qca8k switch are not affected. ++ */ ++ if (priv->switch_id == QCA8K_ID_QCA8337) { ++ for (i = 0; i < QCA8K_NUM_PORTS; i++) { ++ switch (i) { ++ /* The 2 CPU port and port 5 requires some different ++ * priority than any other ports. ++ */ ++ case 0: ++ case 5: ++ case 6: ++ mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) | ++ QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e); ++ break; ++ default: ++ mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) | ++ QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19); ++ } ++ qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask); ++ ++ mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) | ++ QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | ++ QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | ++ QCA8K_PORT_HOL_CTRL1_WRED_EN; ++ qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i), ++ QCA8K_PORT_HOL_CTRL1_ING_BUF | ++ QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | ++ QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | ++ QCA8K_PORT_HOL_CTRL1_WRED_EN, ++ mask); ++ } ++ } ++ + /* Setup our port MTUs to match power on defaults */ + for (i = 0; i < QCA8K_NUM_PORTS; i++) + priv->port_mtu[i] = ETH_FRAME_LEN + ETH_FCS_LEN; +@@ -1578,6 +1624,7 @@ qca8k_sw_probe(struct mdio_device *mdiod + return -ENODEV; + } + ++ priv->switch_id = id; + priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL); + if (!priv->ds) + return -ENOMEM; +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -168,6 +168,30 @@ + #define QCA8K_PORT_LOOKUP_STATE GENMASK(18, 16) + #define QCA8K_PORT_LOOKUP_LEARN BIT(20) + ++#define QCA8K_REG_PORT_HOL_CTRL0(_i) (0x970 + (_i) * 0x8) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF GENMASK(3, 0) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI0(x) ((x) << 0) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF GENMASK(7, 4) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI1(x) ((x) << 4) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF GENMASK(11, 8) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI2(x) ((x) << 8) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF GENMASK(15, 12) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI3(x) ((x) << 12) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF GENMASK(19, 16) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI4(x) ((x) << 16) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF GENMASK(23, 20) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI5(x) ((x) << 20) ++#define QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF GENMASK(29, 24) ++#define QCA8K_PORT_HOL_CTRL0_EG_PORT(x) ((x) << 24) ++ ++#define QCA8K_REG_PORT_HOL_CTRL1(_i) (0x974 + (_i) * 0x8) ++#define QCA8K_PORT_HOL_CTRL1_ING_BUF GENMASK(3, 0) ++#define QCA8K_PORT_HOL_CTRL1_ING(x) ((x) << 0) ++#define QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN BIT(6) ++#define QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN BIT(7) ++#define QCA8K_PORT_HOL_CTRL1_WRED_EN BIT(8) ++#define QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN BIT(16) ++ + /* Pkt edit registers */ + #define QCA8K_EGRESS_VLAN(x) (0x0c70 + (4 * (x / 2))) + +@@ -220,6 +244,7 @@ struct qca8k_match_data { + }; + + struct qca8k_priv { ++ u8 switch_id; + struct regmap *regmap; + struct mii_bus *bus; + struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS]; diff --git a/target/linux/generic/backport-5.10/785-v5.14-12-net-dsa-qca8k-limit-port5-delay-to-qca8337.patch b/target/linux/generic/backport-5.10/785-v5.14-12-net-dsa-qca8k-limit-port5-delay-to-qca8337.patch new file mode 100644 index 0000000000..d25edbb1aa --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.14-12-net-dsa-qca8k-limit-port5-delay-to-qca8337.patch @@ -0,0 +1,31 @@ +From 5bf9ff3b9fb5ecb67a1a3517b26db3a00f2a2f11 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 23:00:02 +0200 +Subject: [PATCH] net: dsa: qca8k: limit port5 delay to qca8337 + +Limit port5 rx delay to qca8337. This is taken from the legacy QSDK code +that limits the rx delay on port5 to only this particular switch version, +on other switch only the tx and rx delay for port0 are needed. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1003,8 +1003,10 @@ qca8k_phylink_mac_config(struct dsa_swit + QCA8K_PORT_PAD_RGMII_EN | + QCA8K_PORT_PAD_RGMII_TX_DELAY(QCA8K_MAX_DELAY) | + QCA8K_PORT_PAD_RGMII_RX_DELAY(QCA8K_MAX_DELAY)); +- qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL, +- QCA8K_PORT_PAD_RGMII_RX_DELAY_EN); ++ /* QCA8337 requires to set rgmii rx delay */ ++ if (priv->switch_id == QCA8K_ID_QCA8337) ++ qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL, ++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN); + break; + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: diff --git a/target/linux/generic/backport-5.10/785-v5.14-13-net-dsa-qca8k-add-GLOBAL_FC-settings-needed-for-qca8.patch b/target/linux/generic/backport-5.10/785-v5.14-13-net-dsa-qca8k-add-GLOBAL_FC-settings-needed-for-qca8.patch new file mode 100644 index 0000000000..2b393d242a --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.14-13-net-dsa-qca8k-add-GLOBAL_FC-settings-needed-for-qca8.patch @@ -0,0 +1,48 @@ +From 0fc57e4b5e39461fc0a54aae0afe4241363a7267 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 23:00:03 +0200 +Subject: [PATCH] net: dsa: qca8k: add GLOBAL_FC settings needed for qca8327 + +Switch qca8327 needs special settings for the GLOBAL_FC_THRES regs. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 10 ++++++++++ + drivers/net/dsa/qca8k.h | 6 ++++++ + 2 files changed, 16 insertions(+) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -930,6 +930,16 @@ qca8k_setup(struct dsa_switch *ds) + } + } + ++ /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */ ++ if (priv->switch_id == QCA8K_ID_QCA8327) { ++ mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) | ++ QCA8K_GLOBAL_FC_GOL_XOFF_THRES(496); ++ qca8k_rmw(priv, QCA8K_REG_GLOBAL_FC_THRESH, ++ QCA8K_GLOBAL_FC_GOL_XON_THRES_S | ++ QCA8K_GLOBAL_FC_GOL_XOFF_THRES_S, ++ mask); ++ } ++ + /* Setup our port MTUs to match power on defaults */ + for (i = 0; i < QCA8K_NUM_PORTS; i++) + priv->port_mtu[i] = ETH_FRAME_LEN + ETH_FCS_LEN; +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -168,6 +168,12 @@ + #define QCA8K_PORT_LOOKUP_STATE GENMASK(18, 16) + #define QCA8K_PORT_LOOKUP_LEARN BIT(20) + ++#define QCA8K_REG_GLOBAL_FC_THRESH 0x800 ++#define QCA8K_GLOBAL_FC_GOL_XON_THRES(x) ((x) << 16) ++#define QCA8K_GLOBAL_FC_GOL_XON_THRES_S GENMASK(24, 16) ++#define QCA8K_GLOBAL_FC_GOL_XOFF_THRES(x) ((x) << 0) ++#define QCA8K_GLOBAL_FC_GOL_XOFF_THRES_S GENMASK(8, 0) ++ + #define QCA8K_REG_PORT_HOL_CTRL0(_i) (0x970 + (_i) * 0x8) + #define QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF GENMASK(3, 0) + #define QCA8K_PORT_HOL_CTRL0_EG_PRI0(x) ((x) << 0) diff --git a/target/linux/generic/backport-5.10/785-v5.14-14-net-dsa-qca8k-add-support-for-switch-rev.patch b/target/linux/generic/backport-5.10/785-v5.14-14-net-dsa-qca8k-add-support-for-switch-rev.patch new file mode 100644 index 0000000000..ed9b8188de --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.14-14-net-dsa-qca8k-add-support-for-switch-rev.patch @@ -0,0 +1,114 @@ +From 95ffeaf18b3bb90eeef52cbf7d79ccc9d0345ff5 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 23:00:04 +0200 +Subject: [PATCH] net: dsa: qca8k: add support for switch rev + +qca8k internal phy driver require some special debug value to be set +based on the switch revision. Rework the switch id read function to +also read the chip revision. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 53 ++++++++++++++++++++++++++--------------- + drivers/net/dsa/qca8k.h | 7 ++++-- + 2 files changed, 39 insertions(+), 21 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1588,12 +1588,40 @@ static const struct dsa_switch_ops qca8k + .phylink_mac_link_up = qca8k_phylink_mac_link_up, + }; + ++static int qca8k_read_switch_id(struct qca8k_priv *priv) ++{ ++ const struct qca8k_match_data *data; ++ u32 val; ++ u8 id; ++ ++ /* get the switches ID from the compatible */ ++ data = of_device_get_match_data(priv->dev); ++ if (!data) ++ return -ENODEV; ++ ++ val = qca8k_read(priv, QCA8K_REG_MASK_CTRL); ++ if (val < 0) ++ return -ENODEV; ++ ++ id = QCA8K_MASK_CTRL_DEVICE_ID(val & QCA8K_MASK_CTRL_DEVICE_ID_MASK); ++ if (id != data->id) { ++ dev_err(priv->dev, "Switch id detected %x but expected %x", id, data->id); ++ return -ENODEV; ++ } ++ ++ priv->switch_id = id; ++ ++ /* Save revision to communicate to the internal PHY driver */ ++ priv->switch_revision = (val & QCA8K_MASK_CTRL_REV_ID_MASK); ++ ++ return 0; ++} ++ + static int + qca8k_sw_probe(struct mdio_device *mdiodev) + { +- const struct qca8k_match_data *data; + struct qca8k_priv *priv; +- u32 id; ++ int ret; + + /* allocate the private data struct so that we can probe the switches + * ID register +@@ -1619,24 +1647,11 @@ qca8k_sw_probe(struct mdio_device *mdiod + gpiod_set_value_cansleep(priv->reset_gpio, 0); + } + +- /* get the switches ID from the compatible */ +- data = of_device_get_match_data(&mdiodev->dev); +- if (!data) +- return -ENODEV; ++ /* Check the detected switch id */ ++ ret = qca8k_read_switch_id(priv); ++ if (ret) ++ return ret; + +- /* read the switches ID register */ +- id = qca8k_read(priv, QCA8K_REG_MASK_CTRL); +- if (id < 0) +- return id; +- +- id >>= QCA8K_MASK_CTRL_ID_S; +- id &= QCA8K_MASK_CTRL_ID_M; +- if (id != data->id) { +- dev_err(&mdiodev->dev, "Switch id detected %x but expected %x", id, data->id); +- return -ENODEV; +- } +- +- priv->switch_id = id; + priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL); + if (!priv->ds) + return -ENOMEM; +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -30,8 +30,10 @@ + + /* Global control registers */ + #define QCA8K_REG_MASK_CTRL 0x000 +-#define QCA8K_MASK_CTRL_ID_M 0xff +-#define QCA8K_MASK_CTRL_ID_S 8 ++#define QCA8K_MASK_CTRL_REV_ID_MASK GENMASK(7, 0) ++#define QCA8K_MASK_CTRL_REV_ID(x) ((x) >> 0) ++#define QCA8K_MASK_CTRL_DEVICE_ID_MASK GENMASK(15, 8) ++#define QCA8K_MASK_CTRL_DEVICE_ID(x) ((x) >> 8) + #define QCA8K_REG_PORT0_PAD_CTRL 0x004 + #define QCA8K_REG_PORT5_PAD_CTRL 0x008 + #define QCA8K_REG_PORT6_PAD_CTRL 0x00c +@@ -251,6 +253,7 @@ struct qca8k_match_data { + + struct qca8k_priv { + u8 switch_id; ++ u8 switch_revision; + struct regmap *regmap; + struct mii_bus *bus; + struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS]; diff --git a/target/linux/generic/backport-5.10/785-v5.14-15-net-dsa-qca8k-add-ethernet-ports-fallback-to-setup_m.patch b/target/linux/generic/backport-5.10/785-v5.14-15-net-dsa-qca8k-add-ethernet-ports-fallback-to-setup_m.patch new file mode 100644 index 0000000000..629cb324e0 --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.14-15-net-dsa-qca8k-add-ethernet-ports-fallback-to-setup_m.patch @@ -0,0 +1,28 @@ +From 1ee0591a1093c2448642c33433483e9260275f7b Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 23:00:05 +0200 +Subject: [PATCH] net: dsa: qca8k: add ethernet-ports fallback to + setup_mdio_bus + +Dsa now also supports ethernet-ports. Add this new binding as a fallback +if the ports node can't be found. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -719,6 +719,9 @@ qca8k_setup_mdio_bus(struct qca8k_priv * + + ports = of_get_child_by_name(priv->dev->of_node, "ports"); + if (!ports) ++ ports = of_get_child_by_name(priv->dev->of_node, "ethernet-ports"); ++ ++ if (!ports) + return -EINVAL; + + for_each_available_child_of_node(ports, port) { diff --git a/target/linux/generic/backport-5.10/785-v5.14-16-net-dsa-qca8k-make-rgmii-delay-configurable.patch b/target/linux/generic/backport-5.10/785-v5.14-16-net-dsa-qca8k-make-rgmii-delay-configurable.patch new file mode 100644 index 0000000000..6dc2dc6e3e --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.14-16-net-dsa-qca8k-make-rgmii-delay-configurable.patch @@ -0,0 +1,188 @@ +From e4b9977cee1583da38a6e9118078bb728aaccf7b Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 23:00:06 +0200 +Subject: [PATCH] net: dsa: qca8k: make rgmii delay configurable + +The legacy qsdk code used a different delay instead of the max value. +Qsdk use 1 ns for rx and 2 ns for tx. Make these values configurable +using the standard rx/tx-internal-delay-ps ethernet binding and apply +qsdk values by default. The connected gmac doesn't add any delay so no +additional delay is added to tx/rx. +On this switch the delay is actually in ns so value should be in the +1000 order. Any value converted from ps to ns by dividing it by 1000 +as the switch max value for delay is 3ns. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 82 ++++++++++++++++++++++++++++++++++++++++- + drivers/net/dsa/qca8k.h | 11 +++--- + 2 files changed, 86 insertions(+), 7 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -778,6 +778,68 @@ qca8k_setup_mdio_bus(struct qca8k_priv * + } + + static int ++qca8k_setup_of_rgmii_delay(struct qca8k_priv *priv) ++{ ++ struct device_node *port_dn; ++ phy_interface_t mode; ++ struct dsa_port *dp; ++ u32 val; ++ ++ /* CPU port is already checked */ ++ dp = dsa_to_port(priv->ds, 0); ++ ++ port_dn = dp->dn; ++ ++ /* Check if port 0 is set to the correct type */ ++ of_get_phy_mode(port_dn, &mode); ++ if (mode != PHY_INTERFACE_MODE_RGMII_ID && ++ mode != PHY_INTERFACE_MODE_RGMII_RXID && ++ mode != PHY_INTERFACE_MODE_RGMII_TXID) { ++ return 0; ++ } ++ ++ switch (mode) { ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_RGMII_RXID: ++ if (of_property_read_u32(port_dn, "rx-internal-delay-ps", &val)) ++ val = 2; ++ else ++ /* Switch regs accept value in ns, convert ps to ns */ ++ val = val / 1000; ++ ++ if (val > QCA8K_MAX_DELAY) { ++ dev_err(priv->dev, "rgmii rx delay is limited to a max value of 3ns, setting to the max value"); ++ val = 3; ++ } ++ ++ priv->rgmii_rx_delay = val; ++ /* Stop here if we need to check only for rx delay */ ++ if (mode != PHY_INTERFACE_MODE_RGMII_ID) ++ break; ++ ++ fallthrough; ++ case PHY_INTERFACE_MODE_RGMII_TXID: ++ if (of_property_read_u32(port_dn, "tx-internal-delay-ps", &val)) ++ val = 1; ++ else ++ /* Switch regs accept value in ns, convert ps to ns */ ++ val = val / 1000; ++ ++ if (val > QCA8K_MAX_DELAY) { ++ dev_err(priv->dev, "rgmii tx delay is limited to a max value of 3ns, setting to the max value"); ++ val = 3; ++ } ++ ++ priv->rgmii_tx_delay = val; ++ break; ++ default: ++ return 0; ++ } ++ ++ return 0; ++} ++ ++static int + qca8k_setup(struct dsa_switch *ds) + { + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +@@ -802,6 +864,10 @@ qca8k_setup(struct dsa_switch *ds) + if (ret) + return ret; + ++ ret = qca8k_setup_of_rgmii_delay(priv); ++ if (ret) ++ return ret; ++ + /* Enable CPU Port */ + ret = qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0, + QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN); +@@ -970,6 +1036,8 @@ qca8k_phylink_mac_config(struct dsa_swit + case 0: /* 1st CPU port */ + if (state->interface != PHY_INTERFACE_MODE_RGMII && + state->interface != PHY_INTERFACE_MODE_RGMII_ID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID && + state->interface != PHY_INTERFACE_MODE_SGMII) + return; + +@@ -985,6 +1053,8 @@ qca8k_phylink_mac_config(struct dsa_swit + case 6: /* 2nd CPU port / external PHY */ + if (state->interface != PHY_INTERFACE_MODE_RGMII && + state->interface != PHY_INTERFACE_MODE_RGMII_ID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID && + state->interface != PHY_INTERFACE_MODE_SGMII && + state->interface != PHY_INTERFACE_MODE_1000BASEX) + return; +@@ -1008,14 +1078,18 @@ qca8k_phylink_mac_config(struct dsa_swit + qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN); + break; + case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_RGMII_TXID: ++ case PHY_INTERFACE_MODE_RGMII_RXID: + /* RGMII_ID needs internal delay. This is enabled through + * PORT5_PAD_CTRL for all ports, rather than individual port + * registers + */ + qca8k_write(priv, reg, + QCA8K_PORT_PAD_RGMII_EN | +- QCA8K_PORT_PAD_RGMII_TX_DELAY(QCA8K_MAX_DELAY) | +- QCA8K_PORT_PAD_RGMII_RX_DELAY(QCA8K_MAX_DELAY)); ++ QCA8K_PORT_PAD_RGMII_TX_DELAY(priv->rgmii_tx_delay) | ++ QCA8K_PORT_PAD_RGMII_RX_DELAY(priv->rgmii_rx_delay) | ++ QCA8K_PORT_PAD_RGMII_TX_DELAY_EN | ++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN); + /* QCA8337 requires to set rgmii rx delay */ + if (priv->switch_id == QCA8K_ID_QCA8337) + qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL, +@@ -1073,6 +1147,8 @@ qca8k_phylink_validate(struct dsa_switch + if (state->interface != PHY_INTERFACE_MODE_NA && + state->interface != PHY_INTERFACE_MODE_RGMII && + state->interface != PHY_INTERFACE_MODE_RGMII_ID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID && + state->interface != PHY_INTERFACE_MODE_SGMII) + goto unsupported; + break; +@@ -1090,6 +1166,8 @@ qca8k_phylink_validate(struct dsa_switch + if (state->interface != PHY_INTERFACE_MODE_NA && + state->interface != PHY_INTERFACE_MODE_RGMII && + state->interface != PHY_INTERFACE_MODE_RGMII_ID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID && + state->interface != PHY_INTERFACE_MODE_SGMII && + state->interface != PHY_INTERFACE_MODE_1000BASEX) + goto unsupported; +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -38,12 +38,11 @@ + #define QCA8K_REG_PORT5_PAD_CTRL 0x008 + #define QCA8K_REG_PORT6_PAD_CTRL 0x00c + #define QCA8K_PORT_PAD_RGMII_EN BIT(26) +-#define QCA8K_PORT_PAD_RGMII_TX_DELAY(x) \ +- ((0x8 + (x & 0x3)) << 22) +-#define QCA8K_PORT_PAD_RGMII_RX_DELAY(x) \ +- ((0x10 + (x & 0x3)) << 20) +-#define QCA8K_MAX_DELAY 3 ++#define QCA8K_PORT_PAD_RGMII_TX_DELAY(x) ((x) << 22) ++#define QCA8K_PORT_PAD_RGMII_RX_DELAY(x) ((x) << 20) ++#define QCA8K_PORT_PAD_RGMII_TX_DELAY_EN BIT(25) + #define QCA8K_PORT_PAD_RGMII_RX_DELAY_EN BIT(24) ++#define QCA8K_MAX_DELAY 3 + #define QCA8K_PORT_PAD_SGMII_EN BIT(7) + #define QCA8K_REG_PWS 0x010 + #define QCA8K_PWS_SERDES_AEN_DIS BIT(7) +@@ -254,6 +253,8 @@ struct qca8k_match_data { + struct qca8k_priv { + u8 switch_id; + u8 switch_revision; ++ u8 rgmii_tx_delay; ++ u8 rgmii_rx_delay; + struct regmap *regmap; + struct mii_bus *bus; + struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS]; diff --git a/target/linux/generic/backport-5.10/785-v5.14-17-net-dsa-qca8k-clear-MASTER_EN-after-phy-read-write.patch b/target/linux/generic/backport-5.10/785-v5.14-17-net-dsa-qca8k-clear-MASTER_EN-after-phy-read-write.patch new file mode 100644 index 0000000000..4593da032b --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.14-17-net-dsa-qca8k-clear-MASTER_EN-after-phy-read-write.patch @@ -0,0 +1,50 @@ +From 63c33bbfeb6842a956a0eb12901e28eb335bdb18 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 23:00:07 +0200 +Subject: [PATCH] net: dsa: qca8k: clear MASTER_EN after phy read/write + +Clear MDIO_MASTER_EN bit from MDIO_MASTER_CTRL after read/write +operation. The MDIO_MASTER_EN bit is not reset after read/write +operation and the next operation can be wrongly interpreted by the +switch as a mdio operation. This cause a production of wrong/garbage +data from the switch and underfined bheavior. (random port drop, +unplugged port flagged with link up, wrong port speed) +Also on driver remove the MASTER_CTRL can be left set and cause the +malfunction of any next driver using the mdio device. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 14 ++++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -649,8 +649,14 @@ qca8k_mdio_write(struct qca8k_priv *priv + if (ret) + return ret; + +- return qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_BUSY); ++ ret = qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_BUSY); ++ ++ /* even if the busy_wait timeouts try to clear the MASTER_EN */ ++ qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_EN); ++ ++ return ret; + } + + static int +@@ -685,6 +691,10 @@ qca8k_mdio_read(struct qca8k_priv *priv, + + val &= QCA8K_MDIO_MASTER_DATA_MASK; + ++ /* even if the busy_wait timeouts try to clear the MASTER_EN */ ++ qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_EN); ++ + return val; + } + diff --git a/target/linux/generic/backport-5.10/785-v5.14-18-net-dsa-qca8k-dsa-qca8k-protect-MASTER-busy_wait-wit.patch b/target/linux/generic/backport-5.10/785-v5.14-18-net-dsa-qca8k-dsa-qca8k-protect-MASTER-busy_wait-wit.patch new file mode 100644 index 0000000000..b6684d7210 --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.14-18-net-dsa-qca8k-dsa-qca8k-protect-MASTER-busy_wait-wit.patch @@ -0,0 +1,128 @@ +From 60df02b6ea4581d72eb7a3ab7204504a54059b72 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 23:00:08 +0200 +Subject: [PATCH] net: dsa: qca8k: dsa: qca8k: protect MASTER busy_wait with + mdio mutex + +MDIO_MASTER operation have a dedicated busy wait that is not protected +by the mdio mutex. This can cause situation where the MASTER operation +is done and a normal operation is executed between the MASTER read/write +and the MASTER busy_wait. Rework the qca8k_mdio_read/write function to +address this issue by binding the lock for the whole MASTER operation +and not only the mdio read/write common operation. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 68 +++++++++++++++++++++++++++++++++-------- + 1 file changed, 55 insertions(+), 13 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -628,8 +628,31 @@ qca8k_port_to_phy(int port) + } + + static int ++qca8k_mdio_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) ++{ ++ u16 r1, r2, page; ++ u32 val; ++ int ret; ++ ++ qca8k_split_addr(reg, &r1, &r2, &page); ++ ++ ret = read_poll_timeout(qca8k_mii_read32, val, !(val & mask), 0, ++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, ++ priv->bus, 0x10 | r2, r1); ++ ++ /* Check if qca8k_read has failed for a different reason ++ * before returnting -ETIMEDOUT ++ */ ++ if (ret < 0 && val < 0) ++ return val; ++ ++ return ret; ++} ++ ++static int + qca8k_mdio_write(struct qca8k_priv *priv, int port, u32 regnum, u16 data) + { ++ u16 r1, r2, page; + u32 phy, val; + int ret; + +@@ -645,12 +668,21 @@ qca8k_mdio_write(struct qca8k_priv *priv + QCA8K_MDIO_MASTER_REG_ADDR(regnum) | + QCA8K_MDIO_MASTER_DATA(data); + +- ret = qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val); ++ qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); ++ ++ mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ ret = qca8k_set_page(priv->bus, page); + if (ret) +- return ret; ++ goto exit; ++ ++ qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val); + +- ret = qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_BUSY); ++ ret = qca8k_mdio_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_BUSY); ++ ++exit: ++ mutex_unlock(&priv->bus->mdio_lock); + + /* even if the busy_wait timeouts try to clear the MASTER_EN */ + qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL, +@@ -662,6 +694,7 @@ qca8k_mdio_write(struct qca8k_priv *priv + static int + qca8k_mdio_read(struct qca8k_priv *priv, int port, u32 regnum) + { ++ u16 r1, r2, page; + u32 phy, val; + int ret; + +@@ -676,21 +709,30 @@ qca8k_mdio_read(struct qca8k_priv *priv, + QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) | + QCA8K_MDIO_MASTER_REG_ADDR(regnum); + +- ret = qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val); +- if (ret) +- return ret; ++ qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); ++ ++ mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); + +- ret = qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_BUSY); ++ ret = qca8k_set_page(priv->bus, page); + if (ret) +- return ret; ++ goto exit; + +- val = qca8k_read(priv, QCA8K_MDIO_MASTER_CTRL); +- if (val < 0) +- return val; ++ qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val); ++ ++ ret = qca8k_mdio_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_BUSY); ++ if (ret) ++ goto exit; + ++ val = qca8k_mii_read32(priv->bus, 0x10 | r2, r1); + val &= QCA8K_MDIO_MASTER_DATA_MASK; + ++exit: ++ mutex_unlock(&priv->bus->mdio_lock); ++ ++ if (val >= 0) ++ val &= QCA8K_MDIO_MASTER_DATA_MASK; ++ + /* even if the busy_wait timeouts try to clear the MASTER_EN */ + qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL, + QCA8K_MDIO_MASTER_EN); diff --git a/target/linux/generic/backport-5.10/785-v5.14-19-net-dsa-qca8k-enlarge-mdio-delay-and-timeout.patch b/target/linux/generic/backport-5.10/785-v5.14-19-net-dsa-qca8k-enlarge-mdio-delay-and-timeout.patch new file mode 100644 index 0000000000..30eeed361e --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.14-19-net-dsa-qca8k-enlarge-mdio-delay-and-timeout.patch @@ -0,0 +1,39 @@ +From 617960d72e93de0f3fa52407e2d39e8c43e73b0a Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 23:00:09 +0200 +Subject: [PATCH] net: dsa: qca8k: enlarge mdio delay and timeout + +The witch require some extra delay after setting page or the next +read/write can use still use the old page. Add a delay after the +set_page function to address this as it's done in QSDK legacy driver. +Some timeouts were notice with VLAN and phy function, enlarge the +mdio busy wait timeout to fix these problems. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 1 + + drivers/net/dsa/qca8k.h | 2 +- + 2 files changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -143,6 +143,7 @@ qca8k_set_page(struct mii_bus *bus, u16 + } + + qca8k_current_page = page; ++ usleep_range(1000, 2000); + return 0; + } + +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -20,7 +20,7 @@ + #define PHY_ID_QCA8337 0x004dd036 + #define QCA8K_ID_QCA8337 0x13 + +-#define QCA8K_BUSY_WAIT_TIMEOUT 20 ++#define QCA8K_BUSY_WAIT_TIMEOUT 2000 + + #define QCA8K_NUM_FDB_RECORDS 2048 + diff --git a/target/linux/generic/backport-5.10/785-v5.14-20-net-dsa-qca8k-add-support-for-internal-phy-and-inter.patch b/target/linux/generic/backport-5.10/785-v5.14-20-net-dsa-qca8k-add-support-for-internal-phy-and-inter.patch new file mode 100644 index 0000000000..88d3c1ef43 --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.14-20-net-dsa-qca8k-add-support-for-internal-phy-and-inter.patch @@ -0,0 +1,267 @@ +From 759bafb8a3226326ca357613bc90acf738f80c32 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 23:00:10 +0200 +Subject: [PATCH] net: dsa: qca8k: add support for internal phy and internal + mdio + +Add support to setup_mdio_bus for internal phy declaration. Introduce a +flag to use the legacy port phy mapping by default and use the direct +mapping if a mdio node is detected in the switch node. Register a +dedicated mdio internal mdio bus to address the different mapping +between port and phy if the mdio node is detected. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 112 +++++++++++++++++++++++++++++----------- + drivers/net/dsa/qca8k.h | 1 + + 2 files changed, 83 insertions(+), 30 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -11,6 +11,7 @@ + #include <linux/netdevice.h> + #include <net/dsa.h> + #include <linux/of_net.h> ++#include <linux/of_mdio.h> + #include <linux/of_platform.h> + #include <linux/if_bridge.h> + #include <linux/mdio.h> +@@ -629,7 +630,7 @@ qca8k_port_to_phy(int port) + } + + static int +-qca8k_mdio_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) ++qca8k_mdio_busy_wait(struct mii_bus *bus, u32 reg, u32 mask) + { + u16 r1, r2, page; + u32 val; +@@ -639,7 +640,7 @@ qca8k_mdio_busy_wait(struct qca8k_priv * + + ret = read_poll_timeout(qca8k_mii_read32, val, !(val & mask), 0, + QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, +- priv->bus, 0x10 | r2, r1); ++ bus, 0x10 | r2, r1); + + /* Check if qca8k_read has failed for a different reason + * before returnting -ETIMEDOUT +@@ -651,19 +652,16 @@ qca8k_mdio_busy_wait(struct qca8k_priv * + } + + static int +-qca8k_mdio_write(struct qca8k_priv *priv, int port, u32 regnum, u16 data) ++qca8k_mdio_write(struct mii_bus *salve_bus, int phy, int regnum, u16 data) + { ++ struct qca8k_priv *priv = salve_bus->priv; + u16 r1, r2, page; +- u32 phy, val; ++ u32 val; + int ret; + + if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) + return -EINVAL; + +- /* callee is responsible for not passing bad ports, +- * but we still would like to make spills impossible. +- */ +- phy = qca8k_port_to_phy(port) % PHY_MAX_ADDR; + val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | + QCA8K_MDIO_MASTER_WRITE | QCA8K_MDIO_MASTER_PHY_ADDR(phy) | + QCA8K_MDIO_MASTER_REG_ADDR(regnum) | +@@ -679,33 +677,29 @@ qca8k_mdio_write(struct qca8k_priv *priv + + qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val); + +- ret = qca8k_mdio_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, ++ ret = qca8k_mdio_busy_wait(priv->bus, QCA8K_MDIO_MASTER_CTRL, + QCA8K_MDIO_MASTER_BUSY); + + exit: +- mutex_unlock(&priv->bus->mdio_lock); +- + /* even if the busy_wait timeouts try to clear the MASTER_EN */ +- qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_EN); ++ qca8k_mii_write32(priv->bus, 0x10 | r2, r1, 0); ++ ++ mutex_unlock(&priv->bus->mdio_lock); + + return ret; + } + + static int +-qca8k_mdio_read(struct qca8k_priv *priv, int port, u32 regnum) ++qca8k_mdio_read(struct mii_bus *salve_bus, int phy, int regnum) + { ++ struct qca8k_priv *priv = salve_bus->priv; + u16 r1, r2, page; +- u32 phy, val; ++ u32 val; + int ret; + + if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) + return -EINVAL; + +- /* callee is responsible for not passing bad ports, +- * but we still would like to make spills impossible. +- */ +- phy = qca8k_port_to_phy(port) % PHY_MAX_ADDR; + val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | + QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) | + QCA8K_MDIO_MASTER_REG_ADDR(regnum); +@@ -720,24 +714,22 @@ qca8k_mdio_read(struct qca8k_priv *priv, + + qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val); + +- ret = qca8k_mdio_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, ++ ret = qca8k_mdio_busy_wait(priv->bus, QCA8K_MDIO_MASTER_CTRL, + QCA8K_MDIO_MASTER_BUSY); + if (ret) + goto exit; + + val = qca8k_mii_read32(priv->bus, 0x10 | r2, r1); +- val &= QCA8K_MDIO_MASTER_DATA_MASK; + + exit: ++ /* even if the busy_wait timeouts try to clear the MASTER_EN */ ++ qca8k_mii_write32(priv->bus, 0x10 | r2, r1, 0); ++ + mutex_unlock(&priv->bus->mdio_lock); + + if (val >= 0) + val &= QCA8K_MDIO_MASTER_DATA_MASK; + +- /* even if the busy_wait timeouts try to clear the MASTER_EN */ +- qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_EN); +- + return val; + } + +@@ -746,7 +738,14 @@ qca8k_phy_write(struct dsa_switch *ds, i + { + struct qca8k_priv *priv = ds->priv; + +- return qca8k_mdio_write(priv, port, regnum, data); ++ /* Check if the legacy mapping should be used and the ++ * port is not correctly mapped to the right PHY in the ++ * devicetree ++ */ ++ if (priv->legacy_phy_port_mapping) ++ port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; ++ ++ return qca8k_mdio_write(priv->bus, port, regnum, data); + } + + static int +@@ -755,7 +754,14 @@ qca8k_phy_read(struct dsa_switch *ds, in + struct qca8k_priv *priv = ds->priv; + int ret; + +- ret = qca8k_mdio_read(priv, port, regnum); ++ /* Check if the legacy mapping should be used and the ++ * port is not correctly mapped to the right PHY in the ++ * devicetree ++ */ ++ if (priv->legacy_phy_port_mapping) ++ port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; ++ ++ ret = qca8k_mdio_read(priv->bus, port, regnum); + + if (ret < 0) + return 0xffff; +@@ -764,10 +770,37 @@ qca8k_phy_read(struct dsa_switch *ds, in + } + + static int ++qca8k_mdio_register(struct qca8k_priv *priv, struct device_node *mdio) ++{ ++ struct dsa_switch *ds = priv->ds; ++ struct mii_bus *bus; ++ ++ bus = devm_mdiobus_alloc(ds->dev); ++ ++ if (!bus) ++ return -ENOMEM; ++ ++ bus->priv = (void *)priv; ++ bus->name = "qca8k slave mii"; ++ bus->read = qca8k_mdio_read; ++ bus->write = qca8k_mdio_write; ++ snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d", ++ ds->index); ++ ++ bus->parent = ds->dev; ++ bus->phy_mask = ~ds->phys_mii_mask; ++ ++ ds->slave_mii_bus = bus; ++ ++ return devm_of_mdiobus_register(priv->dev, bus, mdio); ++} ++ ++static int + qca8k_setup_mdio_bus(struct qca8k_priv *priv) + { + u32 internal_mdio_mask = 0, external_mdio_mask = 0, reg; +- struct device_node *ports, *port; ++ struct device_node *ports, *port, *mdio; ++ phy_interface_t mode; + int err; + + ports = of_get_child_by_name(priv->dev->of_node, "ports"); +@@ -788,7 +821,10 @@ qca8k_setup_mdio_bus(struct qca8k_priv * + if (!dsa_is_user_port(priv->ds, reg)) + continue; + +- if (of_property_read_bool(port, "phy-handle")) ++ of_get_phy_mode(port, &mode); ++ ++ if (of_property_read_bool(port, "phy-handle") && ++ mode != PHY_INTERFACE_MODE_INTERNAL) + external_mdio_mask |= BIT(reg); + else + internal_mdio_mask |= BIT(reg); +@@ -825,8 +861,23 @@ qca8k_setup_mdio_bus(struct qca8k_priv * + QCA8K_MDIO_MASTER_EN); + } + ++ /* Check if the devicetree declare the port:phy mapping */ ++ mdio = of_get_child_by_name(priv->dev->of_node, "mdio"); ++ if (of_device_is_available(mdio)) { ++ err = qca8k_mdio_register(priv, mdio); ++ if (err) ++ of_node_put(mdio); ++ ++ return err; ++ } ++ ++ /* If a mapping can't be found the legacy mapping is used, ++ * using the qca8k_port_to_phy function ++ */ ++ priv->legacy_phy_port_mapping = true; + priv->ops.phy_read = qca8k_phy_read; + priv->ops.phy_write = qca8k_phy_write; ++ + return 0; + } + +@@ -1212,7 +1263,8 @@ qca8k_phylink_validate(struct dsa_switch + case 5: + /* Internal PHY */ + if (state->interface != PHY_INTERFACE_MODE_NA && +- state->interface != PHY_INTERFACE_MODE_GMII) ++ state->interface != PHY_INTERFACE_MODE_GMII && ++ state->interface != PHY_INTERFACE_MODE_INTERNAL) + goto unsupported; + break; + case 6: /* 2nd CPU port / external PHY */ +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -255,6 +255,7 @@ struct qca8k_priv { + u8 switch_revision; + u8 rgmii_tx_delay; + u8 rgmii_rx_delay; ++ bool legacy_phy_port_mapping; + struct regmap *regmap; + struct mii_bus *bus; + struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS]; diff --git a/target/linux/generic/backport-5.10/785-v5.14-21-devicetree-bindings-dsa-qca8k-Document-internal-mdio.patch b/target/linux/generic/backport-5.10/785-v5.14-21-devicetree-bindings-dsa-qca8k-Document-internal-mdio.patch new file mode 100644 index 0000000000..6db01b4b41 --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.14-21-devicetree-bindings-dsa-qca8k-Document-internal-mdio.patch @@ -0,0 +1,93 @@ +From 0c994a28e7518f098c84a3049cb2915780db873a Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 23:00:11 +0200 +Subject: [PATCH] devicetree: bindings: dsa: qca8k: Document internal mdio + definition + +Document new way of declare mapping of internal PHY to port. +The new implementation directly declare the PHY connected to the port +by adding a node in the switch node. The driver detect this and register +an internal mdiobus using the mapping defined in the mdio node. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Rob Herring <robh@kernel.org> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + .../devicetree/bindings/net/dsa/qca8k.txt | 39 +++++++++++++++++++ + 1 file changed, 39 insertions(+) + +--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt ++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt +@@ -21,6 +21,10 @@ described in dsa/dsa.txt. If the QCA8K s + mdio-bus each subnode describing a port needs to have a valid phandle + referencing the internal PHY it is connected to. This is because there's no + N:N mapping of port and PHY id. ++To declare the internal mdio-bus configuration, declare a mdio node in the ++switch node and declare the phandle for the port referencing the internal ++PHY is connected to. In this config a internal mdio-bus is registered and ++the mdio MASTER is used as communication. + + Don't use mixed external and internal mdio-bus configurations, as this is + not supported by the hardware. +@@ -150,26 +154,61 @@ for the internal master mdio-bus configu + port@1 { + reg = <1>; + label = "lan1"; ++ phy-mode = "internal"; ++ phy-handle = <&phy_port1>; + }; + + port@2 { + reg = <2>; + label = "lan2"; ++ phy-mode = "internal"; ++ phy-handle = <&phy_port2>; + }; + + port@3 { + reg = <3>; + label = "lan3"; ++ phy-mode = "internal"; ++ phy-handle = <&phy_port3>; + }; + + port@4 { + reg = <4>; + label = "lan4"; ++ phy-mode = "internal"; ++ phy-handle = <&phy_port4>; + }; + + port@5 { + reg = <5>; + label = "wan"; ++ phy-mode = "internal"; ++ phy-handle = <&phy_port5>; ++ }; ++ }; ++ ++ mdio { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ phy_port1: phy@0 { ++ reg = <0>; ++ }; ++ ++ phy_port2: phy@1 { ++ reg = <1>; ++ }; ++ ++ phy_port3: phy@2 { ++ reg = <2>; ++ }; ++ ++ phy_port4: phy@3 { ++ reg = <3>; ++ }; ++ ++ phy_port5: phy@4 { ++ reg = <4>; + }; + }; + }; diff --git a/target/linux/generic/backport-5.10/785-v5.14-22-net-dsa-qca8k-improve-internal-mdio-read-write-bus-a.patch b/target/linux/generic/backport-5.10/785-v5.14-22-net-dsa-qca8k-improve-internal-mdio-read-write-bus-a.patch new file mode 100644 index 0000000000..da8d5b3462 --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.14-22-net-dsa-qca8k-improve-internal-mdio-read-write-bus-a.patch @@ -0,0 +1,95 @@ +From b7ebac354d54f1657bb89b7a7ca149db50203e6a Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 23:00:12 +0200 +Subject: [PATCH] net: dsa: qca8k: improve internal mdio read/write bus access + +Improve the internal mdio read/write bus access by caching the value +without accessing it for every read/write. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 28 +++++++++++++++------------- + 1 file changed, 15 insertions(+), 13 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -655,6 +655,7 @@ static int + qca8k_mdio_write(struct mii_bus *salve_bus, int phy, int regnum, u16 data) + { + struct qca8k_priv *priv = salve_bus->priv; ++ struct mii_bus *bus = priv->bus; + u16 r1, r2, page; + u32 val; + int ret; +@@ -669,22 +670,22 @@ qca8k_mdio_write(struct mii_bus *salve_b + + qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); + +- mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +- ret = qca8k_set_page(priv->bus, page); ++ ret = qca8k_set_page(bus, page); + if (ret) + goto exit; + +- qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val); ++ qca8k_mii_write32(bus, 0x10 | r2, r1, val); + +- ret = qca8k_mdio_busy_wait(priv->bus, QCA8K_MDIO_MASTER_CTRL, ++ ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL, + QCA8K_MDIO_MASTER_BUSY); + + exit: + /* even if the busy_wait timeouts try to clear the MASTER_EN */ +- qca8k_mii_write32(priv->bus, 0x10 | r2, r1, 0); ++ qca8k_mii_write32(bus, 0x10 | r2, r1, 0); + +- mutex_unlock(&priv->bus->mdio_lock); ++ mutex_unlock(&bus->mdio_lock); + + return ret; + } +@@ -693,6 +694,7 @@ static int + qca8k_mdio_read(struct mii_bus *salve_bus, int phy, int regnum) + { + struct qca8k_priv *priv = salve_bus->priv; ++ struct mii_bus *bus = priv->bus; + u16 r1, r2, page; + u32 val; + int ret; +@@ -706,26 +708,26 @@ qca8k_mdio_read(struct mii_bus *salve_bu + + qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); + +- mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +- ret = qca8k_set_page(priv->bus, page); ++ ret = qca8k_set_page(bus, page); + if (ret) + goto exit; + +- qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val); ++ qca8k_mii_write32(bus, 0x10 | r2, r1, val); + +- ret = qca8k_mdio_busy_wait(priv->bus, QCA8K_MDIO_MASTER_CTRL, ++ ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL, + QCA8K_MDIO_MASTER_BUSY); + if (ret) + goto exit; + +- val = qca8k_mii_read32(priv->bus, 0x10 | r2, r1); ++ val = qca8k_mii_read32(bus, 0x10 | r2, r1); + + exit: + /* even if the busy_wait timeouts try to clear the MASTER_EN */ +- qca8k_mii_write32(priv->bus, 0x10 | r2, r1, 0); ++ qca8k_mii_write32(bus, 0x10 | r2, r1, 0); + +- mutex_unlock(&priv->bus->mdio_lock); ++ mutex_unlock(&bus->mdio_lock); + + if (val >= 0) + val &= QCA8K_MDIO_MASTER_DATA_MASK; diff --git a/target/linux/generic/backport-5.10/785-v5.14-23-net-dsa-qca8k-pass-switch_revision-info-to-phy-dev_f.patch b/target/linux/generic/backport-5.10/785-v5.14-23-net-dsa-qca8k-pass-switch_revision-info-to-phy-dev_f.patch new file mode 100644 index 0000000000..1179cf152d --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.14-23-net-dsa-qca8k-pass-switch_revision-info-to-phy-dev_f.patch @@ -0,0 +1,48 @@ +From a46aec02bc06ac2c33f326339e4ef88c735dc30d Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 23:00:13 +0200 +Subject: [PATCH] net: dsa: qca8k: pass switch_revision info to phy dev_flags + +Define get_phy_flags to pass switch_Revision needed to tweak the +internal PHY with debug values based on the revision. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1740,6 +1740,22 @@ qca8k_port_vlan_del(struct dsa_switch *d + return ret; + } + ++static u32 qca8k_get_phy_flags(struct dsa_switch *ds, int port) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ /* Communicate to the phy internal driver the switch revision. ++ * Based on the switch revision different values needs to be ++ * set to the dbg and mmd reg on the phy. ++ * The first 2 bit are used to communicate the switch revision ++ * to the phy driver. ++ */ ++ if (port > 0 && port < 6) ++ return priv->switch_revision; ++ ++ return 0; ++} ++ + static enum dsa_tag_protocol + qca8k_get_tag_protocol(struct dsa_switch *ds, int port, + enum dsa_tag_protocol mp) +@@ -1774,6 +1790,7 @@ static const struct dsa_switch_ops qca8k + .phylink_mac_config = qca8k_phylink_mac_config, + .phylink_mac_link_down = qca8k_phylink_mac_link_down, + .phylink_mac_link_up = qca8k_phylink_mac_link_up, ++ .get_phy_flags = qca8k_get_phy_flags, + }; + + static int qca8k_read_switch_id(struct qca8k_priv *priv) diff --git a/target/linux/generic/backport-5.10/785-v5.14-25-net-phy-add-support-for-qca8k-switch-internal-PHY-in.patch b/target/linux/generic/backport-5.10/785-v5.14-25-net-phy-add-support-for-qca8k-switch-internal-PHY-in.patch new file mode 100644 index 0000000000..20325f564d --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.14-25-net-phy-add-support-for-qca8k-switch-internal-PHY-in.patch @@ -0,0 +1,229 @@ +From 272833b9b3b3969be7a91839121d86662c8c4253 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 23:00:15 +0200 +Subject: [PATCH] net: phy: add support for qca8k switch internal PHY in at803x + +Since the at803x share the same regs, it's assumed they are based on the +same implementation. Make it part of the at803x PHY driver to skip +having redudant code. +Add initial support for qca8k internal PHYs. The internal PHYs requires +special mmd and debug values to be set based on the switch revision +passwd using the dev_flags. Supports output of idle, receive and eee_wake +errors stats. +Some debug values sets can't be translated as the documentation lacks any +reference about them. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/phy/Kconfig | 5 +- + drivers/net/phy/at803x.c | 132 ++++++++++++++++++++++++++++++++++++++- + 2 files changed, 134 insertions(+), 3 deletions(-) + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -235,10 +235,11 @@ config NXP_TJA11XX_PHY + Currently supports the NXP TJA1100 and TJA1101 PHY. + + config AT803X_PHY +- tristate "Qualcomm Atheros AR803X PHYs" ++ tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs" + depends on REGULATOR + help +- Currently supports the AR8030, AR8031, AR8033 and AR8035 model ++ Currently supports the AR8030, AR8031, AR8033, AR8035 and internal ++ QCA8337(Internal qca8k PHY) model + + config QSEMI_PHY + tristate "Quality Semiconductor PHYs" +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -92,10 +92,16 @@ + #define AT803X_DEBUG_REG_5 0x05 + #define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8) + ++#define AT803X_DEBUG_REG_3C 0x3C ++ ++#define AT803X_DEBUG_REG_3D 0x3D ++ + #define AT803X_DEBUG_REG_1F 0x1F + #define AT803X_DEBUG_PLL_ON BIT(2) + #define AT803X_DEBUG_RGMII_1V8 BIT(3) + ++#define MDIO_AZ_DEBUG 0x800D ++ + /* AT803x supports either the XTAL input pad, an internal PLL or the + * DSP as clock reference for the clock output pad. The XTAL reference + * is only used for 25 MHz output, all other frequencies need the PLL. +@@ -142,10 +148,34 @@ + #define AT803X_PAGE_FIBER 0 + #define AT803X_PAGE_COPPER 1 + ++#define QCA8327_PHY_ID 0x004dd034 ++#define QCA8337_PHY_ID 0x004dd036 ++#define QCA8K_PHY_ID_MASK 0xffffffff ++ ++#define QCA8K_DEVFLAGS_REVISION_MASK GENMASK(2, 0) ++ + MODULE_DESCRIPTION("Qualcomm Atheros AR803x PHY driver"); + MODULE_AUTHOR("Matus Ujhelyi"); + MODULE_LICENSE("GPL"); + ++enum stat_access_type { ++ PHY, ++ MMD ++}; ++ ++struct at803x_hw_stat { ++ const char *string; ++ u8 reg; ++ u32 mask; ++ enum stat_access_type access_type; ++}; ++ ++static struct at803x_hw_stat at803x_hw_stats[] = { ++ { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY}, ++ { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY}, ++ { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD}, ++}; ++ + struct at803x_priv { + int flags; + #define AT803X_KEEP_PLL_ENABLED BIT(0) /* don't turn off internal PLL */ +@@ -154,6 +184,7 @@ struct at803x_priv { + struct regulator_dev *vddio_rdev; + struct regulator_dev *vddh_rdev; + struct regulator *vddio; ++ u64 stats[ARRAY_SIZE(at803x_hw_stats)]; + }; + + struct at803x_context { +@@ -165,6 +196,17 @@ struct at803x_context { + u16 led_control; + }; + ++static int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data) ++{ ++ int ret; ++ ++ ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); ++ if (ret < 0) ++ return ret; ++ ++ return phy_write(phydev, AT803X_DEBUG_DATA, data); ++} ++ + static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg) + { + int ret; +@@ -327,6 +369,53 @@ static void at803x_get_wol(struct phy_de + wol->wolopts |= WAKE_MAGIC; + } + ++static int at803x_get_sset_count(struct phy_device *phydev) ++{ ++ return ARRAY_SIZE(at803x_hw_stats); ++} ++ ++static void at803x_get_strings(struct phy_device *phydev, u8 *data) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++) { ++ strscpy(data + i * ETH_GSTRING_LEN, ++ at803x_hw_stats[i].string, ETH_GSTRING_LEN); ++ } ++} ++ ++static u64 at803x_get_stat(struct phy_device *phydev, int i) ++{ ++ struct at803x_hw_stat stat = at803x_hw_stats[i]; ++ struct at803x_priv *priv = phydev->priv; ++ int val; ++ u64 ret; ++ ++ if (stat.access_type == MMD) ++ val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat.reg); ++ else ++ val = phy_read(phydev, stat.reg); ++ ++ if (val < 0) { ++ ret = U64_MAX; ++ } else { ++ val = val & stat.mask; ++ priv->stats[i] += val; ++ ret = priv->stats[i]; ++ } ++ ++ return ret; ++} ++ ++static void at803x_get_stats(struct phy_device *phydev, ++ struct ethtool_stats *stats, u64 *data) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++) ++ data[i] = at803x_get_stat(phydev, i); ++} ++ + static int at803x_suspend(struct phy_device *phydev) + { + int value; +@@ -1102,6 +1191,34 @@ static int at803x_cable_test_start(struc + return 0; + } + ++static int qca83xx_config_init(struct phy_device *phydev) ++{ ++ u8 switch_revision; ++ ++ switch_revision = phydev->dev_flags & QCA8K_DEVFLAGS_REVISION_MASK; ++ ++ switch (switch_revision) { ++ case 1: ++ /* For 100M waveform */ ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_0, 0x02ea); ++ /* Turn on Gigabit clock */ ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3D, 0x68a0); ++ break; ++ ++ case 2: ++ phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0); ++ fallthrough; ++ case 4: ++ phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f); ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3D, 0x6860); ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_5, 0x2c46); ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000); ++ break; ++ } ++ ++ return 0; ++} ++ + static struct phy_driver at803x_driver[] = { + { + /* Qualcomm Atheros AR8035 */ +@@ -1198,7 +1315,20 @@ static struct phy_driver at803x_driver[] + .read_status = at803x_read_status, + .soft_reset = genphy_soft_reset, + .config_aneg = at803x_config_aneg, +-} }; ++}, { ++ /* QCA8337 */ ++ .phy_id = QCA8337_PHY_ID, ++ .phy_id_mask = QCA8K_PHY_ID_MASK, ++ .name = "QCA PHY 8337", ++ /* PHY_GBIT_FEATURES */ ++ .probe = at803x_probe, ++ .flags = PHY_IS_INTERNAL, ++ .config_init = qca83xx_config_init, ++ .soft_reset = genphy_soft_reset, ++ .get_sset_count = at803x_get_sset_count, ++ .get_strings = at803x_get_strings, ++ .get_stats = at803x_get_stats, ++}, }; + + module_phy_driver(at803x_driver); + |