From d45499694e77ce5a14b826ebe145f2af02f03679 Mon Sep 17 00:00:00 2001 From: Heisath Date: Mon, 4 Oct 2021 11:18:13 +0200 Subject: [PATCH] Update mvebu-edge 5.14 patches with version from http://git.armlinux.org.uk/cgit/linux-arm.git/log/?h=clearfog --- .../10-mvebu-clearfog-pcie-updates.patch | 208 +- .../mvebu-5.14/12-net-dsa-mv88e6xxx.patch | 1332 ++++ .../archive/mvebu-5.14/13-net-mvneta.patch | 5567 +++++++++++++++++ .../mvebu-5.14/20-pcie-bridge-emul.patch | 104 + .../mvebu-5.14/21-pcie-bridge-emul.patch | 145 + ...2-sfp-display-SFP-module-information.patch | 432 -- 6 files changed, 7176 insertions(+), 612 deletions(-) create mode 100644 patch/kernel/archive/mvebu-5.14/12-net-dsa-mv88e6xxx.patch create mode 100644 patch/kernel/archive/mvebu-5.14/13-net-mvneta.patch create mode 100644 patch/kernel/archive/mvebu-5.14/20-pcie-bridge-emul.patch create mode 100644 patch/kernel/archive/mvebu-5.14/21-pcie-bridge-emul.patch delete mode 100644 patch/kernel/archive/mvebu-5.14/402-sfp-display-SFP-module-information.patch diff --git a/patch/kernel/archive/mvebu-5.14/10-mvebu-clearfog-pcie-updates.patch b/patch/kernel/archive/mvebu-5.14/10-mvebu-clearfog-pcie-updates.patch index 6ee38c55de..95c2ef7eee 100644 --- a/patch/kernel/archive/mvebu-5.14/10-mvebu-clearfog-pcie-updates.patch +++ b/patch/kernel/archive/mvebu-5.14/10-mvebu-clearfog-pcie-updates.patch @@ -1,17 +1,18 @@ -From 527312a74d9d85ba9520c8cb2979004f6d23c4da Mon Sep 17 00:00:00 2001 +From 49853eacbe2c75761c34e6198e5c3eec142a8d7c Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 29 Nov 2016 10:13:46 +0000 -Subject: [PATCH] mvebu/clearfog pcie updates +Subject: mvebu/clearfog pcie updates Signed-off-by: Russell King --- - drivers/pci/controller/pci-mvebu.c | 112 ++++++++++++++++++++++++++++- - drivers/pci/pci-bridge-emul.c | 83 ++++++++++++--------- - drivers/pci/pci-bridge-emul.h | 15 ++++ + drivers/pci/controller/pci-mvebu.c | 112 ++++++++++++++++++++++++++++++++++++- + drivers/pci/pci-bridge-emul.c | 2 + drivers/pci/pcie/aspm.c | 6 ++ drivers/pci/pcie/portdrv_core.c | 2 + - 5 files changed, 184 insertions(+), 34 deletions(-) + 4 files changed, 121 insertions(+), 1 deletion(-) +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index ed13e81cd691..2dc9f457bc76 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -52,7 +52,14 @@ @@ -29,7 +30,7 @@ Signed-off-by: Russell King #define PCIE_CTRL_OFF 0x1a00 #define PCIE_CTRL_X1_MODE 0x0001 #define PCIE_STAT_OFF 0x1a04 -@@ -430,6 +437,54 @@ static void mvebu_pcie_handle_membase_ch +@@ -430,6 +437,54 @@ static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) &port->memwin); } @@ -84,7 +85,7 @@ Signed-off-by: Russell King static pci_bridge_emul_read_status_t mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, int reg, u32 *value) -@@ -475,6 +530,30 @@ mvebu_pci_bridge_emul_pcie_conf_read(str +@@ -475,6 +530,30 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, return PCI_BRIDGE_EMUL_HANDLED; } @@ -115,7 +116,7 @@ Signed-off-by: Russell King static void mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge, int reg, u32 old, u32 new, u32 mask) -@@ -492,7 +571,8 @@ mvebu_pci_bridge_emul_base_conf_write(st +@@ -492,7 +571,8 @@ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge, mvebu_pcie_handle_iobase_change(port); if ((old ^ new) & PCI_COMMAND_MEMORY) mvebu_pcie_handle_membase_change(port); @@ -125,7 +126,7 @@ Signed-off-by: Russell King break; } -@@ -515,6 +595,11 @@ mvebu_pci_bridge_emul_base_conf_write(st +@@ -515,6 +595,11 @@ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge, mvebu_pcie_handle_iobase_change(port); break; @@ -137,7 +138,7 @@ Signed-off-by: Russell King case PCI_PRIMARY_BUS: mvebu_pcie_set_local_bus_nr(port, conf->secondary_bus); break; -@@ -532,6 +617,10 @@ mvebu_pci_bridge_emul_pcie_conf_write(st +@@ -532,6 +617,10 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, switch (reg) { case PCI_EXP_DEVCTL: @@ -148,7 +149,7 @@ Signed-off-by: Russell King /* * Armada370 data says these bits must always * be zero when in root complex mode. -@@ -557,6 +646,25 @@ mvebu_pci_bridge_emul_pcie_conf_write(st +@@ -557,6 +646,25 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, case PCI_EXP_RTSTA: mvebu_writel(port, new, PCIE_RC_RTSTA); break; @@ -174,7 +175,7 @@ Signed-off-by: Russell King } } -@@ -564,6 +672,8 @@ static struct pci_bridge_emul_ops mvebu_ +@@ -564,6 +672,8 @@ static struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = { .write_base = mvebu_pci_bridge_emul_base_conf_write, .read_pcie = mvebu_pci_bridge_emul_pcie_conf_read, .write_pcie = mvebu_pci_bridge_emul_pcie_conf_write, @@ -183,9 +184,11 @@ Signed-off-by: Russell King }; /* +diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c +index fbff7da94245..50b1b48f6e0d 100644 --- a/drivers/pci/pci-bridge-emul.c +++ b/drivers/pci/pci-bridge-emul.c -@@ -151,6 +151,7 @@ static const struct pci_bridge_reg_behav +@@ -153,6 +153,7 @@ struct pci_bridge_reg_behavior pci_regs_behavior[PCI_STD_HEADER_SIZEOF / 4] = { .rw = (GENMASK(7, 0) | ((PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR | @@ -193,7 +196,7 @@ Signed-off-by: Russell King PCI_BRIDGE_CTL_ISA | PCI_BRIDGE_CTL_VGA | PCI_BRIDGE_CTL_MASTER_ABORT | -@@ -264,6 +265,7 @@ int pci_bridge_emul_init(struct pci_brid +@@ -269,6 +270,7 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge, bridge->conf.header_type = PCI_HEADER_TYPE_BRIDGE; bridge->conf.cache_line_size = 0x10; bridge->conf.status = cpu_to_le16(PCI_STATUS_CAP_LIST); @@ -201,171 +204,11 @@ Signed-off-by: Russell King bridge->pci_regs_behavior = kmemdup(pci_regs_behavior, sizeof(pci_regs_behavior), GFP_KERNEL); -@@ -323,25 +325,26 @@ int pci_bridge_emul_conf_read(struct pci - __le32 *cfgspace; - const struct pci_bridge_reg_behavior *behavior; - -- if (bridge->has_pcie && reg >= PCI_CAP_PCIE_END) { -- *value = 0; -- return PCIBIOS_SUCCESSFUL; -- } -- -- if (!bridge->has_pcie && reg >= PCI_BRIDGE_CONF_END) { -+ if (reg < PCI_CAP_PCIE_START) { -+ read_op = bridge->ops->read_base; -+ cfgspace = (__le32 *) &bridge->conf; -+ behavior = bridge->pci_regs_behavior; -+ } else if (!bridge->has_pcie) { - *value = 0; - return PCIBIOS_SUCCESSFUL; -- } -- -- if (bridge->has_pcie && reg >= PCI_CAP_PCIE_START) { -+ } else if (reg < PCI_CAP_PCIE_END) { - reg -= PCI_CAP_PCIE_START; - read_op = bridge->ops->read_pcie; - cfgspace = (__le32 *) &bridge->pcie_conf; - behavior = bridge->pcie_cap_regs_behavior; -+ } else if (reg < 0x100) { -+ *value = 0; -+ return PCIBIOS_SUCCESSFUL; - } else { -- read_op = bridge->ops->read_base; -- cfgspace = (__le32 *) &bridge->conf; -- behavior = bridge->pci_regs_behavior; -+ reg -= 0x100; -+ read_op = bridge->ops->read_ext; -+ cfgspace = NULL; -+ behavior = NULL; - } - - if (read_op) -@@ -349,15 +352,20 @@ int pci_bridge_emul_conf_read(struct pci - else - ret = PCI_BRIDGE_EMUL_NOT_HANDLED; - -- if (ret == PCI_BRIDGE_EMUL_NOT_HANDLED) -- *value = le32_to_cpu(cfgspace[reg / 4]); -+ if (ret == PCI_BRIDGE_EMUL_NOT_HANDLED) { -+ if (cfgspace) -+ *value = le32_to_cpu(cfgspace[reg / 4]); -+ else -+ *value = 0; -+ } - - /* - * Make sure we never return any reserved bit with a value - * different from 0. - */ -- *value &= behavior[reg / 4].ro | behavior[reg / 4].rw | -- behavior[reg / 4].w1c; -+ if (behavior) -+ *value &= behavior[reg / 4].ro | behavior[reg / 4].rw | -+ behavior[reg / 4].w1c; - - if (size == 1) - *value = (*value >> (8 * (where & 3))) & 0xff; -@@ -385,12 +393,6 @@ int pci_bridge_emul_conf_write(struct pc - __le32 *cfgspace; - const struct pci_bridge_reg_behavior *behavior; - -- if (bridge->has_pcie && reg >= PCI_CAP_PCIE_END) -- return PCIBIOS_SUCCESSFUL; -- -- if (!bridge->has_pcie && reg >= PCI_BRIDGE_CONF_END) -- return PCIBIOS_SUCCESSFUL; -- - shift = (where & 0x3) * 8; - - if (size == 4) -@@ -406,27 +408,42 @@ int pci_bridge_emul_conf_write(struct pc - if (ret != PCIBIOS_SUCCESSFUL) - return ret; - -- if (bridge->has_pcie && reg >= PCI_CAP_PCIE_START) { -+ if (reg < PCI_CAP_PCIE_START) { -+ write_op = bridge->ops->write_base; -+ cfgspace = (__le32 *) &bridge->conf; -+ behavior = bridge->pci_regs_behavior; -+ } else if (!bridge->has_pcie) { -+ return PCIBIOS_SUCCESSFUL; -+ } else if (reg < PCI_CAP_PCIE_END) { - reg -= PCI_CAP_PCIE_START; - write_op = bridge->ops->write_pcie; - cfgspace = (__le32 *) &bridge->pcie_conf; - behavior = bridge->pcie_cap_regs_behavior; -+ } else if (reg < 0x100) { -+ return PCIBIOS_SUCCESSFUL; - } else { -- write_op = bridge->ops->write_base; -- cfgspace = (__le32 *) &bridge->conf; -- behavior = bridge->pci_regs_behavior; -+ reg -= 0x100; -+ write_op = bridge->ops->write_ext; -+ cfgspace = NULL; -+ behavior = NULL; - } - -- /* Keep all bits, except the RW bits */ -- new = old & (~mask | ~behavior[reg / 4].rw); -+ if (behavior) { -+ /* Keep all bits, except the RW bits */ -+ new = old & (~mask | ~behavior[reg / 4].rw); - -- /* Update the value of the RW bits */ -- new |= (value << shift) & (behavior[reg / 4].rw & mask); -+ /* Update the value of the RW bits */ -+ new |= (value << shift) & (behavior[reg / 4].rw & mask); - -- /* Clear the W1C bits */ -- new &= ~((value << shift) & (behavior[reg / 4].w1c & mask)); -+ /* Clear the W1C bits */ -+ new &= ~((value << shift) & (behavior[reg / 4].w1c & mask)); -+ } else { -+ new = old & ~mask; -+ new |= (value << shift) & mask; -+ } - -- cfgspace[reg / 4] = cpu_to_le32(new); -+ if (cfgspace) -+ cfgspace[reg / 4] = cpu_to_le32(new); - - if (write_op) - write_op(bridge, reg, old, new, mask); ---- a/drivers/pci/pci-bridge-emul.h -+++ b/drivers/pci/pci-bridge-emul.h -@@ -90,6 +90,14 @@ struct pci_bridge_emul_ops { - */ - pci_bridge_emul_read_status_t (*read_pcie)(struct pci_bridge_emul *bridge, - int reg, u32 *value); -+ -+ /* -+ * Same as ->read_base(), except it is for reading from the -+ * PCIe extended capability configuration space. -+ */ -+ pci_bridge_emul_read_status_t (*read_ext)(struct pci_bridge_emul *bridge, -+ int reg, u32 *value); -+ - /* - * Called when writing to the regular PCI bridge configuration - * space. old is the current value, new is the new value being -@@ -105,6 +113,13 @@ struct pci_bridge_emul_ops { - */ - void (*write_pcie)(struct pci_bridge_emul *bridge, int reg, - u32 old, u32 new, u32 mask); -+ -+ /* -+ * Same as ->write_base(), except it is for writing from the -+ * PCIe extended capability configuration space. -+ */ -+ void (*write_ext)(struct pci_bridge_emul *bridge, int reg, -+ u32 old, u32 new, u32 mask); - }; - - struct pci_bridge_reg_behavior; +diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c +index 013a47f587ce..26ee590caec0 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c -@@ -578,6 +578,12 @@ static void pcie_aspm_cap_init(struct pc +@@ -578,6 +578,12 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) pcie_capability_read_dword(child, PCI_EXP_LNKCAP, &child_lnkcap); pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &parent_lnkctl); pcie_capability_read_word(child, PCI_EXP_LNKCTL, &child_lnkctl); @@ -378,9 +221,11 @@ Signed-off-by: Russell King /* * Setup L0s state +diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c +index e1fed6649c41..a50dac9f8d39 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c -@@ -325,6 +325,7 @@ int pcie_port_device_register(struct pci +@@ -322,6 +322,7 @@ int pcie_port_device_register(struct pci_dev *dev) /* Get and check PCI Express port services */ capabilities = get_port_device_capability(dev); @@ -388,7 +233,7 @@ Signed-off-by: Russell King if (!capabilities) return 0; -@@ -337,6 +338,7 @@ int pcie_port_device_register(struct pci +@@ -334,6 +335,7 @@ int pcie_port_device_register(struct pci_dev *dev) * if that is to be used. */ status = pcie_init_service_irqs(dev, irqs, capabilities); @@ -396,3 +241,6 @@ Signed-off-by: Russell King if (status) { capabilities &= PCIE_PORT_SERVICE_HP; if (!capabilities) +-- +cgit v1.2.3 + diff --git a/patch/kernel/archive/mvebu-5.14/12-net-dsa-mv88e6xxx.patch b/patch/kernel/archive/mvebu-5.14/12-net-dsa-mv88e6xxx.patch new file mode 100644 index 0000000000..8249bd3c2c --- /dev/null +++ b/patch/kernel/archive/mvebu-5.14/12-net-dsa-mv88e6xxx.patch @@ -0,0 +1,1332 @@ +From 3724260d6f3b5e821ce7ead6410416bf02c3fff6 Mon Sep 17 00:00:00 2001 +From: Vivien Didelot +Date: Thu, 22 Oct 2015 14:31:23 -0400 +Subject: net: dsa: mv88e6xxx: add debugfs interface + +Add a debugfs directory named mv88e6xxx.X where X is the DSA switch +index. Mount the debugfs file system with: + + # mount -t debugfs none /sys/kernel/debug + +Signed-off-by: Vivien Didelot +[Modified by rmk for current kernels.] +Signed-off-by: Russell King +--- + drivers/net/dsa/mv88e6xxx/chip.c | 7 + + drivers/net/dsa/mv88e6xxx/chip.h | 2 + + drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c | 1099 +++++++++++++++++++++++++ + 3 files changed, 1108 insertions(+) + create mode 100644 drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c + +diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c +index 272b0535d946..34b7f057a80d 100644 +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -3008,8 +3008,13 @@ static int mv88e6390_setup_errata(struct mv88e6xxx_chip *chip) + return mv88e6xxx_software_reset(chip); + } + ++#include "mv88e6xxx_debugfs.c" ++ + static void mv88e6xxx_teardown(struct dsa_switch *ds) + { ++ struct mv88e6xxx_chip *chip = ds->priv; ++ ++ mv88e6xxx_remove_debugfs(chip); + mv88e6xxx_teardown_devlink_params(ds); + dsa_devlink_resources_unregister(ds); + mv88e6xxx_teardown_devlink_regions(ds); +@@ -3128,6 +3133,8 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) + if (err) + goto unlock; + ++ mv88e6xxx_init_debugfs(chip); ++ + unlock: + mv88e6xxx_reg_unlock(chip); + +diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h +index 675b1f3e43b7..850706ed2dad 100644 +--- a/drivers/net/dsa/mv88e6xxx/chip.h ++++ b/drivers/net/dsa/mv88e6xxx/chip.h +@@ -377,6 +377,8 @@ struct mv88e6xxx_chip { + + /* devlink regions */ + struct devlink_region *regions[_MV88E6XXX_REGION_MAX]; ++ ++ struct dentry *dbgfs; + }; + + struct mv88e6xxx_bus_ops { +diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c +new file mode 100644 +index 000000000000..931e769fe9ce +--- /dev/null ++++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c +@@ -0,0 +1,1099 @@ ++#include ++#include ++ ++#define GLOBAL2_PVT_ADDR 0x0b ++#define GLOBAL2_PVT_ADDR_BUSY BIT(15) ++#define GLOBAL2_PVT_ADDR_OP_INIT_ONES ((0x01 << 12) | GLOBAL2_PVT_ADDR_BUSY) ++#define GLOBAL2_PVT_ADDR_OP_WRITE_PVLAN ((0x03 << 12) | GLOBAL2_PVT_ADDR_BUSY) ++#define GLOBAL2_PVT_ADDR_OP_READ ((0x04 << 12) | GLOBAL2_PVT_ADDR_BUSY) ++#define GLOBAL2_PVT_DATA 0x0c ++ ++#define ADDR_GLOBAL2 0x1c ++ ++static int mv88e6xxx_serdes_read(struct mv88e6xxx_chip *chip, int reg, u16 *val) ++{ ++ return mv88e6xxx_phy_page_read(chip, MV88E6352_ADDR_SERDES, ++ MV88E6352_SERDES_PAGE_FIBER, ++ reg, val); ++} ++ ++static int mv88e6xxx_serdes_write(struct mv88e6xxx_chip *chip, int reg, u16 val) ++{ ++ return mv88e6xxx_phy_page_write(chip, MV88E6352_ADDR_SERDES, ++ MV88E6352_SERDES_PAGE_FIBER, ++ reg, val); ++} ++ ++static int _mv88e6xxx_pvt_wait(struct mv88e6xxx_chip *chip) ++{ ++ return mv88e6xxx_wait_mask(chip, ADDR_GLOBAL2, GLOBAL2_PVT_ADDR, ++ GLOBAL2_PVT_ADDR_BUSY, 0); ++} ++ ++static int _mv88e6xxx_pvt_cmd(struct mv88e6xxx_chip *chip, int src_dev, ++ int src_port, u16 op) ++{ ++ u16 reg = op; ++ int err; ++ ++ /* 9-bit Cross-chip PVT pointer: with GLOBAL2_MISC_5_BIT_PORT cleared, ++ * source device is 5-bit, source port is 4-bit. ++ */ ++ reg |= (src_dev & 0x1f) << 4; ++ reg |= (src_port & 0xf); ++ ++ err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_ADDR, reg); ++ if (err) ++ return err; ++ ++ return _mv88e6xxx_pvt_wait(chip); ++} ++ ++static int _mv88e6xxx_pvt_read(struct mv88e6xxx_chip *chip, int src_dev, ++ int src_port, u16 *data) ++{ ++ int ret; ++ ++ ret = _mv88e6xxx_pvt_wait(chip); ++ if (ret < 0) ++ return ret; ++ ++ ret = _mv88e6xxx_pvt_cmd(chip, src_dev, src_port, ++ GLOBAL2_PVT_ADDR_OP_READ); ++ if (ret < 0) ++ return ret; ++ ++ return mv88e6xxx_g2_read(chip, GLOBAL2_PVT_DATA, data); ++} ++ ++static int _mv88e6xxx_pvt_write(struct mv88e6xxx_chip *chip, int src_dev, ++ int src_port, u16 data) ++{ ++ int err; ++ ++ err = _mv88e6xxx_pvt_wait(chip); ++ if (err) ++ return err; ++ ++ err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_DATA, data); ++ if (err) ++ return err; ++ ++ return _mv88e6xxx_pvt_cmd(chip, src_dev, src_port, ++ GLOBAL2_PVT_ADDR_OP_WRITE_PVLAN); ++} ++ ++static int mv88e6xxx_regs_show(struct seq_file *s, void *p) ++{ ++ struct mv88e6xxx_chip *chip = s->private; ++ int port, reg, ret; ++ u16 data; ++ ++ seq_puts(s, " GLOBAL GLOBAL2 SERDES "); ++ for (port = 0; port < mv88e6xxx_num_ports(chip); port++) ++ seq_printf(s, " %2d ", port); ++ seq_puts(s, "\n"); ++ ++ mutex_lock(&chip->reg_lock); ++ ++ for (reg = 0; reg < 32; reg++) { ++ seq_printf(s, "%2x:", reg); ++ ++ ret = mv88e6xxx_g1_read(chip, reg, &data); ++ if (ret < 0) ++ goto unlock; ++ seq_printf(s, " %4x ", data); ++ ++ ret = mv88e6xxx_g2_read(chip, reg, &data); ++ if (ret < 0) ++ goto unlock; ++ seq_printf(s, " %4x ", data); ++ ++ if (reg != MV88E6XXX_PHY_PAGE) { ++ ret = mv88e6xxx_serdes_read(chip, reg, &data); ++ if (ret < 0) ++ goto unlock; ++ } else { ++ data = 0; ++ } ++ seq_printf(s, " %4x ", data); ++ ++ /* Port regs 0x1a-0x1f are reserved in 6185 family */ ++ if (chip->info->family == MV88E6XXX_FAMILY_6185 && reg > 25) { ++ for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) ++ seq_printf(s, "%4c ", '-'); ++ seq_puts(s, "\n"); ++ continue; ++ } ++ ++ for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) { ++ ret = mv88e6xxx_port_read(chip, port, reg, &data); ++ if (ret < 0) ++ goto unlock; ++ ++ seq_printf(s, "%4x ", data); ++ } ++ ++ seq_puts(s, "\n"); ++ } ++ ++ ret = 0; ++unlock: ++ mutex_unlock(&chip->reg_lock); ++ ++ return ret; ++} ++ ++static ssize_t mv88e6xxx_regs_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct seq_file *s = file->private_data; ++ struct mv88e6xxx_chip *chip = s->private; ++ char cmd[32], name[32] = { 0 }; ++ unsigned int port, reg, val; ++ int ret; ++ ++ if (count > sizeof(name) - 1) ++ return -EINVAL; ++ ++ if (copy_from_user(cmd, buf, sizeof(cmd))) ++ return -EFAULT; ++ ++ ret = sscanf(cmd, "%s %x %x", name, ®, &val); ++ if (ret != 3) ++ return -EINVAL; ++ ++ if (reg > 0x1f || val > 0xffff) ++ return -ERANGE; ++ ++ mutex_lock(&chip->reg_lock); ++ ++ if (strcasecmp(name, "GLOBAL") == 0) ++ ret = mv88e6xxx_g1_write(chip, reg, val); ++ else if (strcasecmp(name, "GLOBAL2") == 0) ++ ret = mv88e6xxx_g2_write(chip, reg, val); ++ else if (strcasecmp(name, "SERDES") == 0) ++ ret = mv88e6xxx_serdes_write(chip, reg, val); ++ else if (kstrtouint(name, 10, &port) == 0 && port < mv88e6xxx_num_ports(chip)) ++ ret = mv88e6xxx_port_write(chip, port, reg, val); ++ else ++ ret = -EINVAL; ++ ++ mutex_unlock(&chip->reg_lock); ++ ++ return ret < 0 ? ret : count; ++} ++ ++static int mv88e6xxx_regs_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, mv88e6xxx_regs_show, inode->i_private); ++} ++ ++static const struct file_operations mv88e6xxx_regs_fops = { ++ .open = mv88e6xxx_regs_open, ++ .read = seq_read, ++ .write = mv88e6xxx_regs_write, ++ .llseek = no_llseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++ ++static int mv88e6xxx_name_show(struct seq_file *s, void *p) ++{ ++ struct mv88e6xxx_chip *chip = s->private; ++ struct dsa_switch *ds = chip->ds; ++ struct dsa_switch_tree *dst = ds->dst; ++ struct dsa_port *dp; ++ int i; ++ ++ if (!ds->cd) ++ return 0; ++ ++ seq_puts(s, " Port Name\n"); ++ ++ list_for_each_entry(dp, &dst->ports, list) { ++ if (dp->ds != ds) ++ continue; ++ ++ i = dp->index; ++ if (!ds->cd->port_names[i]) ++ continue; ++ ++ seq_printf(s, "%4d %s", i, ds->cd->port_names[i]); ++ ++ if (dp->slave) ++ seq_printf(s, " (%s)", netdev_name(dp->slave)); ++ ++ seq_puts(s, "\n"); ++ } ++ ++ return 0; ++} ++ ++static int mv88e6xxx_name_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, mv88e6xxx_name_show, inode->i_private); ++} ++ ++static const struct file_operations mv88e6xxx_name_fops = { ++ .open = mv88e6xxx_name_open, ++ .read = seq_read, ++ .llseek = no_llseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++ ++static int mv88e6xxx_atu_show(struct seq_file *s, void *p) ++{ ++ struct mv88e6xxx_chip *chip = s->private; ++ struct mv88e6xxx_atu_entry addr; ++ const char *state; ++ int fid, i, err; ++ ++ seq_puts(s, " FID MAC Addr State Trunk? DPV/Trunk ID\n"); ++ ++ for (fid = 0; fid < mv88e6xxx_num_databases(chip); ++fid) { ++ addr.state = 0; ++ eth_broadcast_addr(addr.mac); ++ ++ do { ++ mutex_lock(&chip->reg_lock); ++ err = mv88e6xxx_g1_atu_getnext(chip, fid, &addr); ++ mutex_unlock(&chip->reg_lock); ++ if (err) ++ return err; ++ ++ if (addr.state == 0) ++ break; ++ ++ /* print ATU entry */ ++ seq_printf(s, "%4d %pM", fid, addr.mac); ++ ++ if (is_multicast_ether_addr(addr.mac)) { ++ switch (addr.state) { ++ case MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_PO: ++ state = "MC_STATIC_PO"; ++ break; ++ case MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_DA_MGMT_PO: ++ state = "MC_STATIC_MGMT_PO"; ++ break; ++ case MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_AVB_NRL_PO: ++ state = "MC_STATIC_NRL_PO"; ++ break; ++ case MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_POLICY_PO: ++ state = "MC_STATIC_POLICY_PO"; ++ break; ++ case MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC: ++ state = "MC_STATIC"; ++ break; ++ case MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_DA_MGMT: ++ state = "MC_STATIC_MGMT"; ++ break; ++ case MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_AVB_NRL: ++ state = "MC_STATIC_NRL"; ++ break; ++ case MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_POLICY: ++ state = "MC_STATIC_POLICY"; ++ break; ++ case 0xb: case 0xa: case 0x9: case 0x8: ++ /* Reserved for future use */ ++ case 0x3: case 0x2: case 0x1: ++ /* Reserved for future use */ ++ case MV88E6XXX_G1_ATU_DATA_STATE_MC_UNUSED: ++ default: ++ state = "???"; ++ break; ++ } ++ } else { ++ switch (addr.state) { ++ case MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_PO: ++ state = "UC_STATIC_PO"; ++ break; ++ case MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC: ++ state = "UC_STATIC"; ++ break; ++ case MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_DA_MGMT_PO: ++ state = "UC_STATIC_MGMT_PO"; ++ break; ++ case MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_DA_MGMT: ++ state = "UC_STATIC_MGMT"; ++ break; ++ case MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_AVB_NRL_PO: ++ state = "UC_STATIC_NRL_PO"; ++ break; ++ case MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_AVB_NRL: ++ state = "UC_STATIC_NRL"; ++ break; ++ case MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_POLICY_PO: ++ state = "UC_STATIC_POLICY_PO"; ++ break; ++ case MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_POLICY: ++ state = "UC_STATIC_POLICY"; ++ break; ++ case MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_7_NEWEST: ++ state = "Age 7 (newest)"; ++ break; ++ case MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_6: ++ state = "Age 6"; ++ break; ++ case MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_5: ++ state = "Age 5"; ++ break; ++ case MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_4: ++ state = "Age 4"; ++ break; ++ case MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_3: ++ state = "Age 3"; ++ break; ++ case MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_2: ++ state = "Age 2"; ++ break; ++ case MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_1_OLDEST: ++ state = "Age 1 (oldest)"; ++ break; ++ case MV88E6XXX_G1_ATU_DATA_STATE_UC_UNUSED: ++ default: ++ state = "???"; ++ break; ++ } ++ } ++ ++ seq_printf(s, " %19s", state); ++ ++ if (addr.trunk) { ++ seq_printf(s, " y %d", ++ addr.portvec); ++ } else { ++ seq_puts(s, " n "); ++ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) ++ seq_printf(s, " %c", ++ addr.portvec & BIT(i) ? ++ 48 + i : '-'); ++ } ++ ++ seq_puts(s, "\n"); ++ } while (!is_broadcast_ether_addr(addr.mac)); ++ } ++ ++ return 0; ++} ++ ++static ssize_t mv88e6xxx_atu_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct seq_file *s = file->private_data; ++ struct mv88e6xxx_chip *chip = s->private; ++ char cmd[64]; ++ unsigned int fid; ++ int ret; ++ ++ if (copy_from_user(cmd, buf, sizeof(cmd))) ++ return -EFAULT; ++ ++ ret = sscanf(cmd, "%u", &fid); ++ if (ret != 1) ++ return -EINVAL; ++ ++ if (fid >= mv88e6xxx_num_databases(chip)) ++ return -ERANGE; ++ ++ mutex_lock(&chip->reg_lock); ++ ret = mv88e6xxx_g1_atu_flush(chip, fid, true); ++ mutex_unlock(&chip->reg_lock); ++ ++ return ret < 0 ? ret : count; ++} ++ ++static int mv88e6xxx_atu_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, mv88e6xxx_atu_show, inode->i_private); ++} ++ ++static const struct file_operations mv88e6xxx_atu_fops = { ++ .open = mv88e6xxx_atu_open, ++ .read = seq_read, ++ .write = mv88e6xxx_atu_write, ++ .llseek = no_llseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++ ++static int mv88e6xxx_default_vid_show(struct seq_file *s, void *p) ++{ ++ struct mv88e6xxx_chip *chip = s->private; ++ u16 pvid; ++ int i, err; ++ ++ seq_puts(s, " Port DefaultVID\n"); ++ ++ mutex_lock(&chip->reg_lock); ++ ++ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { ++ err = mv88e6xxx_port_get_pvid(chip, i, &pvid); ++ if (err) ++ break; ++ ++ seq_printf(s, "%4d %d\n", i, pvid); ++ } ++ ++ mutex_unlock(&chip->reg_lock); ++ ++ return err; ++} ++ ++static ssize_t mv88e6xxx_default_vid_write(struct file *file, ++ const char __user *buf, size_t count, ++ loff_t *ppos) ++{ ++ struct seq_file *s = file->private_data; ++ struct mv88e6xxx_chip *chip = s->private; ++ char cmd[32]; ++ unsigned int port, pvid; ++ int ret; ++ ++ if (copy_from_user(cmd, buf, sizeof(cmd))) ++ return -EFAULT; ++ ++ ret = sscanf(cmd, "%u %u", &port, &pvid); ++ if (ret != 2) ++ return -EINVAL; ++ ++ if (port >= mv88e6xxx_num_ports(chip) || pvid > 0xfff) ++ return -ERANGE; ++ ++ mutex_lock(&chip->reg_lock); ++ ret = mv88e6xxx_port_set_pvid(chip, port, pvid); ++ mutex_unlock(&chip->reg_lock); ++ ++ return ret < 0 ? ret : count; ++} ++ ++static int mv88e6xxx_default_vid_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, mv88e6xxx_default_vid_show, inode->i_private); ++} ++ ++static const struct file_operations mv88e6xxx_default_vid_fops = { ++ .open = mv88e6xxx_default_vid_open, ++ .read = seq_read, ++ .write = mv88e6xxx_default_vid_write, ++ .llseek = no_llseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++ ++static int mv88e6xxx_fid_show(struct seq_file *s, void *p) ++{ ++ struct mv88e6xxx_chip *chip = s->private; ++ u16 fid; ++ int i, err; ++ ++ seq_puts(s, " Port FID\n"); ++ ++ mutex_lock(&chip->reg_lock); ++ ++ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { ++ err = mv88e6xxx_port_get_fid(chip, i, &fid); ++ if (err) ++ break; ++ ++ seq_printf(s, "%4d %d\n", i, fid); ++ } ++ ++ mutex_unlock(&chip->reg_lock); ++ ++ return err; ++} ++ ++static int mv88e6xxx_fid_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, mv88e6xxx_fid_show, inode->i_private); ++} ++ ++static const struct file_operations mv88e6xxx_fid_fops = { ++ .open = mv88e6xxx_fid_open, ++ .read = seq_read, ++ .llseek = no_llseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++ ++static const char * const mv88e6xxx_port_state_names[] = { ++ [MV88E6XXX_PORT_CTL0_STATE_DISABLED] = "Disabled", ++ [MV88E6XXX_PORT_CTL0_STATE_BLOCKING] = "Blocking/Listening", ++ [MV88E6XXX_PORT_CTL0_STATE_LEARNING] = "Learning", ++ [MV88E6XXX_PORT_CTL0_STATE_FORWARDING] = "Forwarding", ++}; ++ ++static int mv88e6xxx_state_show(struct seq_file *s, void *p) ++{ ++ struct mv88e6xxx_chip *chip = s->private; ++ int i, ret; ++ u16 data; ++ ++ /* header */ ++ seq_puts(s, " Port Mode\n"); ++ ++ mutex_lock(&chip->reg_lock); ++ ++ /* One line per input port */ ++ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { ++ seq_printf(s, "%4d ", i); ++ ++ ret = mv88e6xxx_port_read(chip, i, MV88E6XXX_PORT_CTL0, &data); ++ if (ret < 0) ++ goto unlock; ++ ++ data &= MV88E6XXX_PORT_CTL0_STATE_MASK; ++ seq_printf(s, " %s\n", mv88e6xxx_port_state_names[data]); ++ ret = 0; ++ } ++ ++unlock: ++ mutex_unlock(&chip->reg_lock); ++ ++ return ret; ++} ++ ++static int mv88e6xxx_state_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, mv88e6xxx_state_show, inode->i_private); ++} ++ ++static const struct file_operations mv88e6xxx_state_fops = { ++ .open = mv88e6xxx_state_open, ++ .read = seq_read, ++ .llseek = no_llseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++ ++static const char * const mv88e6xxx_port_8021q_mode_names[] = { ++ [MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED] = "Disabled", ++ [MV88E6XXX_PORT_CTL2_8021Q_MODE_FALLBACK] = "Fallback", ++ [MV88E6XXX_PORT_CTL2_8021Q_MODE_CHECK] = "Check", ++ [MV88E6XXX_PORT_CTL2_8021Q_MODE_SECURE] = "Secure", ++}; ++ ++static int mv88e6xxx_8021q_mode_show(struct seq_file *s, void *p) ++{ ++ struct mv88e6xxx_chip *chip = s->private; ++ int i, ret; ++ u16 data; ++ ++ /* header */ ++ seq_puts(s, " Port Mode\n"); ++ ++ mutex_lock(&chip->reg_lock); ++ ++ /* One line per input port */ ++ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { ++ seq_printf(s, "%4d ", i); ++ ++ ret = mv88e6xxx_port_read(chip, i, MV88E6XXX_PORT_CTL2, &data); ++ if (ret < 0) ++ goto unlock; ++ ++ data &= MV88E6XXX_PORT_CTL2_8021Q_MODE_MASK; ++ seq_printf(s, " %s\n", mv88e6xxx_port_8021q_mode_names[data]); ++ ret = 0; ++ } ++ ++unlock: ++ mutex_unlock(&chip->reg_lock); ++ ++ return ret; ++} ++ ++static int mv88e6xxx_8021q_mode_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, mv88e6xxx_8021q_mode_show, inode->i_private); ++} ++ ++static const struct file_operations mv88e6xxx_8021q_mode_fops = { ++ .open = mv88e6xxx_8021q_mode_open, ++ .read = seq_read, ++ .llseek = no_llseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++ ++static int mv88e6xxx_vlan_table_show(struct seq_file *s, void *p) ++{ ++ struct mv88e6xxx_chip *chip = s->private; ++ int i, j, ret; ++ u16 data; ++ ++ /* header */ ++ seq_puts(s, " Port"); ++ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) ++ seq_printf(s, " %2d", i); ++ seq_puts(s, "\n"); ++ ++ mutex_lock(&chip->reg_lock); ++ ++ /* One line per input port */ ++ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { ++ seq_printf(s, "%4d ", i); ++ ++ ret = mv88e6xxx_port_read(chip, i, MV88E6XXX_PORT_BASE_VLAN, &data); ++ if (ret < 0) ++ goto unlock; ++ ++ /* One column per output port */ ++ for (j = 0; j < mv88e6xxx_num_ports(chip); ++j) ++ seq_printf(s, " %c", data & BIT(j) ? '*' : '-'); ++ seq_puts(s, "\n"); ++ } ++ ++ ret = 0; ++unlock: ++ mutex_unlock(&chip->reg_lock); ++ ++ return ret; ++} ++ ++static int mv88e6xxx_vlan_table_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, mv88e6xxx_vlan_table_show, inode->i_private); ++} ++ ++static const struct file_operations mv88e6xxx_vlan_table_fops = { ++ .open = mv88e6xxx_vlan_table_open, ++ .read = seq_read, ++ .llseek = no_llseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++ ++static int mv88e6xxx_pvt_show(struct seq_file *s, void *p) ++{ ++ struct mv88e6xxx_chip *chip = s->private; ++ struct dsa_switch_tree *dst = chip->ds->dst; ++ int port, src_dev, src_port; ++ u16 pvlan; ++ int err = 0; ++ ++ if (chip->info->family == MV88E6XXX_FAMILY_6185) ++ return -ENODEV; ++ ++ /* header */ ++ seq_puts(s, " Dev Port PVLAN"); ++ for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) ++ seq_printf(s, " %2d", port); ++ seq_puts(s, "\n"); ++ ++ mutex_lock(&chip->reg_lock); ++ ++ /* One line per external port */ ++ for (src_dev = 0; src_dev < DSA_MAX_SWITCHES; ++src_dev) { ++ if (!dst->ds[src_dev]) ++ break; ++ ++ if (src_dev == chip->ds->index) ++ continue; ++ ++ seq_puts(s, "\n"); ++ for (src_port = 0; src_port < 16; ++src_port) { ++ if (src_port >= DSA_MAX_PORTS) ++ break; ++ ++ err = _mv88e6xxx_pvt_read(chip, src_dev, src_port, ++ &pvlan); ++ if (err) ++ goto unlock; ++ ++ seq_printf(s, " %d %2d %03hhx ", src_dev, src_port, ++ pvlan); ++ ++ /* One column per internal output port */ ++ for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) ++ seq_printf(s, " %c", ++ pvlan & BIT(port) ? '*' : '-'); ++ seq_puts(s, "\n"); ++ } ++ } ++ ++unlock: ++ mutex_unlock(&chip->reg_lock); ++ ++ return err; ++} ++ ++static ssize_t mv88e6xxx_pvt_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct seq_file *s = file->private_data; ++ struct mv88e6xxx_chip *chip = s->private; ++ const u16 mask = (1 << mv88e6xxx_num_ports(chip)) - 1; ++ char cmd[32]; ++ unsigned int src_dev, src_port, pvlan; ++ int ret; ++ ++ if (copy_from_user(cmd, buf, sizeof(cmd))) ++ return -EFAULT; ++ ++ if (sscanf(cmd, "%d %d %x", &src_dev, &src_port, &pvlan) != 3) ++ return -EINVAL; ++ ++ if (src_dev >= 32 || src_port >= 16 || pvlan & ~mask) ++ return -ERANGE; ++ ++ mutex_lock(&chip->reg_lock); ++ ret = _mv88e6xxx_pvt_write(chip, src_dev, src_port, pvlan); ++ mutex_unlock(&chip->reg_lock); ++ ++ return ret < 0 ? ret : count; ++} ++ ++static int mv88e6xxx_pvt_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, mv88e6xxx_pvt_show, inode->i_private); ++} ++ ++static const struct file_operations mv88e6xxx_pvt_fops = { ++ .open = mv88e6xxx_pvt_open, ++ .read = seq_read, ++ .write = mv88e6xxx_pvt_write, ++ .llseek = no_llseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++ ++static int mv88e6xxx_vtu_show(struct seq_file *s, void *p) ++{ ++ struct mv88e6xxx_chip *chip = s->private; ++ struct mv88e6xxx_vtu_entry next = { 0 }; ++ int port, ret = 0; ++ ++ seq_puts(s, " VID FID SID"); ++ for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) ++ seq_printf(s, " %2d", port); ++ seq_puts(s, "\n"); ++ ++ if (!chip->info->ops->vtu_getnext) ++ return 0; ++ ++ next.vid = chip->info->max_vid; /* first or lowest VID */ ++ ++ do { ++ mutex_lock(&chip->reg_lock); ++ ret = chip->info->ops->vtu_getnext(chip, &next); ++ mutex_unlock(&chip->reg_lock); ++ if (ret < 0) ++ break; ++ ++ if (!next.valid) ++ break; ++ ++ seq_printf(s, "%4d %4d %2d", next.vid, next.fid, next.sid); ++ for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) { ++ switch (next.member[port]) { ++ case MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNMODIFIED: ++ seq_puts(s, " ="); ++ break; ++ case MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNTAGGED: ++ seq_puts(s, " u"); ++ break; ++ case MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_TAGGED: ++ seq_puts(s, " t"); ++ break; ++ case MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER: ++ seq_puts(s, " x"); ++ break; ++ default: ++ seq_puts(s, " ??"); ++ break; ++ } ++ } ++ seq_puts(s, "\n"); ++ } while (next.vid < chip->info->max_vid); ++ ++ return ret; ++} ++ ++static ssize_t mv88e6xxx_vtu_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct seq_file *s = file->private_data; ++ struct mv88e6xxx_chip *chip = s->private; ++ struct mv88e6xxx_vtu_entry entry = { 0 }; ++ bool valid = true; ++ char cmd[64], tags[12]; /* DSA_MAX_PORTS */ ++ int vid, fid, sid, port, ret; ++ ++ if (!chip->info->ops->vtu_loadpurge) ++ return -EOPNOTSUPP; ++ ++ if (copy_from_user(cmd, buf, sizeof(cmd))) ++ return -EFAULT; ++ ++ /* scan 12 chars instead of num_ports to avoid dynamic scanning... */ ++ ret = sscanf(cmd, "%d %d %d %c %c %c %c %c %c %c %c %c %c %c %c", &vid, ++ &fid, &sid, &tags[0], &tags[1], &tags[2], &tags[3], ++ &tags[4], &tags[5], &tags[6], &tags[7], &tags[8], &tags[9], ++ &tags[10], &tags[11]); ++ if (ret == 1) ++ valid = false; ++ else if (ret != 3 + mv88e6xxx_num_ports(chip)) ++ return -EINVAL; ++ ++ entry.vid = vid; ++ entry.valid = valid; ++ ++ if (valid) { ++ entry.fid = fid; ++ entry.sid = sid; ++ /* Note: The VTU entry pointed by VID will be loaded but not ++ * considered valid until the STU entry pointed by SID is valid. ++ */ ++ ++ for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) { ++ u8 tag; ++ ++ switch (tags[port]) { ++ case 'u': ++ tag = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNTAGGED; ++ break; ++ case 't': ++ tag = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_TAGGED; ++ break; ++ case 'x': ++ tag = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER; ++ break; ++ case '=': ++ tag = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNMODIFIED; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ entry.member[port] = tag; ++ } ++ } ++ ++ mutex_lock(&chip->reg_lock); ++ ret = chip->info->ops->vtu_loadpurge(chip, &entry); ++ mutex_unlock(&chip->reg_lock); ++ ++ return ret < 0 ? ret : count; ++} ++ ++static int mv88e6xxx_vtu_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, mv88e6xxx_vtu_show, inode->i_private); ++} ++ ++static const struct file_operations mv88e6xxx_vtu_fops = { ++ .open = mv88e6xxx_vtu_open, ++ .read = seq_read, ++ .write = mv88e6xxx_vtu_write, ++ .llseek = no_llseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++#if 0 ++static int mv88e6xxx_stats_show(struct seq_file *s, void *p) ++{ ++ struct mv88e6xxx_chip *chip = s->private; ++ char *strs; ++ u64 *stats; ++ int stat, port, num_stats, num_ports; ++ int err = 0; ++ ++ num_stats = mv88e6xxx_get_sset_count(chip->ds); ++ if (num_stats == 0) ++ return 0; ++ ++ num_ports = mv88e6xxx_num_ports(chip); ++ ++ strs = kcalloc(num_stats, ETH_GSTRING_LEN, GFP_KERNEL); ++ stats = kcalloc(num_stats, num_ports * sizeof(*stats), GFP_KERNEL); ++ if (!strs || !strs) { ++ kfree(strs); ++ kfree(stats); ++ return -ENOMEM; ++ } ++ ++ mv88e6xxx_get_strings(chip->ds, 0, strs); ++ ++ for (port = 0; port < num_ports; port++) ++ mv88e6xxx_get_ethtool_stats(chip->ds, port, stats + (port * num_stats)); ++ ++ seq_puts(s, " Statistic "); ++ for (port = 0; port < mv88e6xxx_num_ports(chip); port++) ++ seq_printf(s, " Port %2d ", port); ++ seq_puts(s, "\n"); ++ ++ for (stat = 0; stat < num_stats; stat++) { ++ seq_printf(s, "%19s: ", strs + stat * ETH_GSTRING_LEN); ++ for (port = 0 ; port < num_ports; port++) { ++ u64 value = stats[stat + port * num_stats]; ++ ++ seq_printf(s, "%8llu ", value); ++ } ++ seq_puts(s, "\n"); ++ } ++ ++ kfree(stats); ++ kfree(strs); ++ ++ return err; ++} ++ ++static int mv88e6xxx_stats_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, mv88e6xxx_stats_show, inode->i_private); ++} ++ ++static const struct file_operations mv88e6xxx_stats_fops = { ++ .open = mv88e6xxx_stats_open, ++ .read = seq_read, ++ .llseek = no_llseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++#endif ++static int mv88e6xxx_device_map_show(struct seq_file *s, void *p) ++{ ++ struct mv88e6xxx_chip *chip = s->private; ++ int target, ret; ++ u16 data, port_mask; ++ ++ seq_puts(s, "Target Port\n"); ++ ++ /* FIXME */ ++ port_mask = MV88E6390_G2_DEVICE_MAPPING_PORT_MASK; ++ ++ mutex_lock(&chip->reg_lock); ++ for (target = 0; target < 32; target++) { ++ ret = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_DEVICE_MAPPING, ++ target << 8 /* MV88E6XXX_G2_DEVICE_MAPPING_DEV_MASK */); ++ if (ret < 0) ++ goto out; ++ ret = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_DEVICE_MAPPING, &data); ++ if (ret < 0) ++ goto out; ++ seq_printf(s, " %2d %2d\n", target, data & port_mask); ++ } ++out: ++ mutex_unlock(&chip->reg_lock); ++ ++ return 0; ++} ++ ++static int mv88e6xxx_device_map_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, mv88e6xxx_device_map_show, inode->i_private); ++} ++ ++static const struct file_operations mv88e6xxx_device_map_fops = { ++ .open = mv88e6xxx_device_map_open, ++ .read = seq_read, ++ .llseek = no_llseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++ ++/* Must be called with SMI lock held */ ++static int _mv88e6xxx_scratch_wait(struct mv88e6xxx_chip *chip) ++{ ++ return mv88e6xxx_wait_mask(chip, ADDR_GLOBAL2, ++ MV88E6XXX_G2_SCRATCH_MISC_MISC, ++ MV88E6XXX_G2_SCRATCH_MISC_UPDATE, 0); ++} ++ ++static int mv88e6xxx_scratch_show(struct seq_file *s, void *p) ++{ ++ struct mv88e6xxx_chip *chip = s->private; ++ int reg, ret; ++ u16 data; ++ ++ seq_puts(s, "Register Value\n"); ++ ++ mutex_lock(&chip->reg_lock); ++ for (reg = 0; reg < 0x80; reg++) { ++ ret = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SCRATCH_MISC_MISC, ++ reg << 8 /* MV88E6XXX_G2_SCRATCH_MISC_PTR_MASK */); ++ if (ret < 0) ++ goto out; ++ ++ ret = _mv88e6xxx_scratch_wait(chip); ++ if (ret < 0) ++ goto out; ++ ++ ret = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SCRATCH_MISC_MISC, &data); ++ seq_printf(s, " %2x %2x\n", reg, ++ data & MV88E6XXX_G2_SCRATCH_MISC_DATA_MASK); ++ } ++out: ++ mutex_unlock(&chip->reg_lock); ++ ++ return 0; ++} ++ ++static int mv88e6xxx_scratch_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, mv88e6xxx_scratch_show, inode->i_private); ++} ++ ++static const struct file_operations mv88e6xxx_scratch_fops = { ++ .open = mv88e6xxx_scratch_open, ++ .read = seq_read, ++ .llseek = no_llseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++ ++static void mv88e6xxx_init_debugfs(struct mv88e6xxx_chip *chip) ++{ ++ char *name; ++ ++ name = kasprintf(GFP_KERNEL, "mv88e6xxx.%d", chip->ds->index); ++ chip->dbgfs = debugfs_create_dir(name, NULL); ++ ++ kfree(name); ++ ++ debugfs_create_file("regs", S_IRUGO | S_IWUSR, chip->dbgfs, chip, ++ &mv88e6xxx_regs_fops); ++ ++ debugfs_create_file("name", S_IRUGO, chip->dbgfs, chip, ++ &mv88e6xxx_name_fops); ++ ++ debugfs_create_file("atu", S_IRUGO | S_IWUSR, chip->dbgfs, chip, ++ &mv88e6xxx_atu_fops); ++ ++ debugfs_create_file("default_vid", S_IRUGO | S_IWUSR, chip->dbgfs, chip, ++ &mv88e6xxx_default_vid_fops); ++ ++ debugfs_create_file("fid", S_IRUGO, chip->dbgfs, chip, &mv88e6xxx_fid_fops); ++ ++ debugfs_create_file("state", S_IRUGO, chip->dbgfs, chip, ++ &mv88e6xxx_state_fops); ++ ++ debugfs_create_file("8021q_mode", S_IRUGO, chip->dbgfs, chip, ++ &mv88e6xxx_8021q_mode_fops); ++ ++ debugfs_create_file("vlan_table", S_IRUGO, chip->dbgfs, chip, ++ &mv88e6xxx_vlan_table_fops); ++ ++ debugfs_create_file("pvt", S_IRUGO | S_IWUSR, chip->dbgfs, chip, ++ &mv88e6xxx_pvt_fops); ++ ++ debugfs_create_file("vtu", S_IRUGO | S_IWUSR, chip->dbgfs, chip, ++ &mv88e6xxx_vtu_fops); ++#if 0 ++ debugfs_create_file("stats", S_IRUGO, chip->dbgfs, chip, ++ &mv88e6xxx_stats_fops); ++#endif ++ debugfs_create_file("device_map", S_IRUGO, chip->dbgfs, chip, ++ &mv88e6xxx_device_map_fops); ++ ++ debugfs_create_file("scratch", S_IRUGO, chip->dbgfs, chip, ++ &mv88e6xxx_scratch_fops); ++} ++ ++static void mv88e6xxx_remove_debugfs(struct mv88e6xxx_chip *chip) ++{ ++ debugfs_remove_recursive(chip->dbgfs); ++} +-- +cgit v1.2.3 + + +From ee71c167800c79ed367a6cb8d0efb4e2cfffabf7 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Mon, 27 Jan 2020 14:00:12 +0000 +Subject: net: dsa: mv88e6xxx: debugfs hacks to fix the compile + +This is the problem with out-of-tree maintained patches; they break, +sometimes requiring substantial rework. It's all very well promising +to publish new versions as that happens, but it causes pain when they +aren't published in a timely manner. Hence this hack. + +Signed-off-by: Russell King +--- + drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c +index 931e769fe9ce..4005a4760884 100644 +--- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c ++++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c +@@ -668,6 +668,7 @@ static const struct file_operations mv88e6xxx_vlan_table_fops = { + + static int mv88e6xxx_pvt_show(struct seq_file *s, void *p) + { ++#if 0 + struct mv88e6xxx_chip *chip = s->private; + struct dsa_switch_tree *dst = chip->ds->dst; + int port, src_dev, src_port; +@@ -716,8 +717,10 @@ static int mv88e6xxx_pvt_show(struct seq_file *s, void *p) + + unlock: + mutex_unlock(&chip->reg_lock); +- + return err; ++#else ++ return 0; ++#endif + } + + static ssize_t mv88e6xxx_pvt_write(struct file *file, const char __user *buf, +-- +cgit v1.2.3 + + +From 04bcd6e51433052a79feb37fe6d02c9c06980d62 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Thu, 28 Sep 2017 12:09:56 +0100 +Subject: Revert "net: dsa: mv88e6xxx: remove LED control register" + +This reverts commit c56a71a92114e3198e249593841cb744abaadcb7. +--- + drivers/net/dsa/mv88e6xxx/port.h | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h +index b10e5aebacf6..bfc37d152f4d 100644 +--- a/drivers/net/dsa/mv88e6xxx/port.h ++++ b/drivers/net/dsa/mv88e6xxx/port.h +@@ -282,6 +282,9 @@ + /* Offset 0x13: OutFiltered Counter */ + #define MV88E6XXX_PORT_OUT_FILTERED 0x13 + ++/* Offset 0x16: LED Control */ ++#define MV88E6XXX_PORT_LED_CONTROL 0x16 ++ + /* Offset 0x18: IEEE Priority Mapping Table */ + #define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE 0x18 + #define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_UPDATE 0x8000 +-- +cgit v1.2.3 + + +From e43ea2d687d58c0df5ca0334fe99385664b38431 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Sat, 7 Jan 2017 20:47:36 +0000 +Subject: net: dsa: program 6176 LED registers + +Signed-off-by: Russell King +--- + drivers/net/dsa/mv88e6xxx/chip.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c +index 34b7f057a80d..ff4d9cf2fbd1 100644 +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -2720,6 +2720,20 @@ static int mv88e6xxx_setup_upstream_port(struct mv88e6xxx_chip *chip, int port) + return 0; + } + ++static int mv88e6xxx_setup_led(struct mv88e6xxx_chip *chip, int port) ++{ ++ int err; ++ ++ /* LED0 = link/activity, LED1 = 10/100 */ ++ err = mv88e6xxx_wait_bit(chip, chip->info->port_base_addr + port, ++ MV88E6XXX_PORT_LED_CONTROL, 15, 0); ++ if (err) ++ return err; ++ ++ return mv88e6xxx_write(chip, chip->info->port_base_addr + port, ++ MV88E6XXX_PORT_LED_CONTROL, 0x80b3); ++} ++ + static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) + { + struct dsa_switch *ds = chip->ds; +@@ -2775,6 +2789,12 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) + if (err) + return err; + ++ if (chip->info->num_gpio) { ++ err = mv88e6xxx_setup_led(chip, port); ++ if (err) ++ return err; ++ } ++ + /* Port Control 2: don't force a good FCS, set the maximum frame size to + * 10240 bytes, disable 802.1q tags checking, don't discard tagged or + * untagged frames on this port, do a destination address lookup on all +-- +cgit v1.2.3 + + +From 0732f844ba9e295c78a911016fec9f00b14a8f59 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Wed, 8 Jul 2020 12:31:01 +0100 +Subject: net: dsa/mv88e6xxx: add support for rate-matching PHYs + +Add basic support for rate-matching 10G PHYs for mv88e6xxx - if we are +in RXAUI, XAUI or 10GBASE-R mode, the link speed is 10G, even if the +media is running at a slower speed. + +Signed-off-by: Russell King +--- + drivers/net/dsa/mv88e6xxx/chip.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c +index ff4d9cf2fbd1..5925d5402306 100644 +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -784,6 +784,18 @@ static void mv88e6xxx_mac_link_up(struct dsa_switch *ds, int port, + if (err) + goto error; + ++ /* The link parameters passed in are the media side parameters. ++ * If in RXAUI, XAUI or 10GBASE-R with a rate matching PHY, we ++ * need to operate our link at 10G. Only full duplex is ++ * supported at this speed. ++ */ ++ if (interface == PHY_INTERFACE_MODE_RXAUI || ++ interface == PHY_INTERFACE_MODE_XAUI || ++ interface == PHY_INTERFACE_MODE_10GBASER) { ++ speed = SPEED_10000; ++ duplex = DUPLEX_FULL; ++ } ++ + if (ops->port_set_speed_duplex) { + err = ops->port_set_speed_duplex(chip, port, + speed, duplex); +-- +cgit v1.2.3 + diff --git a/patch/kernel/archive/mvebu-5.14/13-net-mvneta.patch b/patch/kernel/archive/mvebu-5.14/13-net-mvneta.patch new file mode 100644 index 0000000000..6b36f20d61 --- /dev/null +++ b/patch/kernel/archive/mvebu-5.14/13-net-mvneta.patch @@ -0,0 +1,5567 @@ +From b823238b0d4a57a7d1ba73fe52cf668a5a47749e Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Fri, 17 Sep 2021 10:40:04 +0100 +Subject: net: phylink: don't call netif_carrier_off() with NULL netdev + +Dan Carpenter points out that we have a code path that permits a NULL +netdev pointer to be passed to netif_carrier_off(), which will cause +a kernel oops. In any case, we need to set pl->old_link_state to false +to have the desired effect when there is no netdev present. + +Fixes: f97493657c63 ("net: phylink: add suspend/resume support") +Reported-by: Dan Carpenter +Signed-off-by: Russell King (Oracle) +--- + drivers/net/phy/phylink.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index 42e5a681183f..3f24deea367d 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1332,7 +1332,10 @@ void phylink_suspend(struct phylink *pl, bool mac_wol) + * but one would hope all packets have been sent. This + * also means phylink_resolve() will do nothing. + */ +- netif_carrier_off(pl->netdev); ++ if (pl->netdev) ++ netif_carrier_off(pl->netdev); ++ else ++ pl->old_link_state = false; + + /* We do not call mac_link_down() here as we want the + * link to remain up to receive the WoL packets. +-- +cgit v1.2.3 + + +From 7e7049505ce1354295c5d543e06dfb417e997583 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Fri, 29 Nov 2019 21:41:56 +0000 +Subject: net: phy: marvell10g: add downshift tunable support + +Add support for the downshift tunable for the Marvell 88x3310 PHY. +Downshift is only usable with firmware 0.3.5.0 and later. + +Signed-off-by: Russell King +--- + drivers/net/phy/marvell10g.c | 101 ++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 100 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c +index 53a433442803..e1ccb2acae84 100644 +--- a/drivers/net/phy/marvell10g.c ++++ b/drivers/net/phy/marvell10g.c +@@ -22,6 +22,7 @@ + * If both the fiber and copper ports are connected, the first to gain + * link takes priority and the other port is completely locked out. + */ ++#include + #include + #include + #include +@@ -32,6 +33,8 @@ + #define MV_PHY_ALASKA_NBT_QUIRK_MASK 0xfffffffe + #define MV_PHY_ALASKA_NBT_QUIRK_REV (MARVELL_PHY_ID_88X3310 | 0xa) + ++#define MV_VERSION(a,b,c,d) ((a) << 24 | (b) << 16 | (c) << 8 | (d)) ++ + enum { + MV_PMA_FW_VER0 = 0xc011, + MV_PMA_FW_VER1 = 0xc012, +@@ -61,6 +64,15 @@ enum { + MV_PCS_CSCR1_MDIX_MDIX = 0x0020, + MV_PCS_CSCR1_MDIX_AUTO = 0x0060, + ++ MV_PCS_DSC1 = 0x8003, ++ MV_PCS_DSC1_ENABLE = BIT(9), ++ MV_PCS_DSC1_10GBT = 0x01c0, ++ MV_PCS_DSC1_1GBR = 0x0038, ++ MV_PCS_DSC1_100BTX = 0x0007, ++ MV_PCS_DSC2 = 0x8004, ++ MV_PCS_DSC2_2P5G = 0xf000, ++ MV_PCS_DSC2_5G = 0x0f00, ++ + MV_PCS_CSSR1 = 0x8008, + MV_PCS_CSSR1_SPD1_MASK = 0xc000, + MV_PCS_CSSR1_SPD1_SPD2 = 0xc000, +@@ -114,6 +126,7 @@ enum { + }; + + struct mv3310_chip { ++ bool (*has_downshift)(struct phy_device *phydev); + void (*init_supported_interfaces)(unsigned long *mask); + int (*get_mactype)(struct phy_device *phydev); + int (*init_interface)(struct phy_device *phydev, int mactype); +@@ -127,6 +140,7 @@ struct mv3310_priv { + DECLARE_BITMAP(supported_interfaces, PHY_INTERFACE_MODE_MAX); + + u32 firmware_ver; ++ bool has_downshift; + bool rate_match; + phy_interface_t const_interface; + +@@ -319,6 +333,65 @@ static int mv3310_reset(struct phy_device *phydev, u32 unit) + 5000, 100000, true); + } + ++static int mv3310_get_downshift(struct phy_device *phydev, u8 *ds) ++{ ++ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); ++ int val; ++ ++ if (!priv->has_downshift) ++ return -EOPNOTSUPP; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_DSC1); ++ if (val < 0) ++ return val; ++ ++ if (val & MV_PCS_DSC1_ENABLE) ++ /* assume that all fields are the same */ ++ *ds = 1 + FIELD_GET(MV_PCS_DSC1_10GBT, (u16)val); ++ else ++ *ds = DOWNSHIFT_DEV_DISABLE; ++ ++ return 0; ++} ++ ++static int mv3310_set_downshift(struct phy_device *phydev, u8 ds) ++{ ++ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); ++ u16 val; ++ int err; ++ ++ if (!priv->has_downshift) ++ return -EOPNOTSUPP; ++ ++ if (ds == DOWNSHIFT_DEV_DISABLE) ++ return phy_clear_bits_mmd(phydev, MDIO_MMD_PCS, MV_PCS_DSC1, ++ MV_PCS_DSC1_ENABLE); ++ ++ /* FIXME: The default is disabled, so should we disable? */ ++ if (ds == DOWNSHIFT_DEV_DEFAULT_COUNT) ++ ds = 2; ++ ++ if (ds > 8) ++ return -E2BIG; ++ ++ ds -= 1; ++ val = FIELD_PREP(MV_PCS_DSC2_2P5G, ds); ++ val |= FIELD_PREP(MV_PCS_DSC2_5G, ds); ++ err = phy_modify_mmd(phydev, MDIO_MMD_PCS, MV_PCS_DSC2, ++ MV_PCS_DSC2_2P5G | MV_PCS_DSC2_5G, val); ++ if (err < 0) ++ return err; ++ ++ val = MV_PCS_DSC1_ENABLE; ++ val |= FIELD_PREP(MV_PCS_DSC1_10GBT, ds); ++ val |= FIELD_PREP(MV_PCS_DSC1_1GBR, ds); ++ val |= FIELD_PREP(MV_PCS_DSC1_100BTX, ds); ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_PCS, MV_PCS_DSC1, ++ MV_PCS_DSC1_ENABLE | MV_PCS_DSC1_10GBT | ++ MV_PCS_DSC1_1GBR | MV_PCS_DSC1_100BTX, val); ++} ++ + static int mv3310_get_edpd(struct phy_device *phydev, u16 *edpd) + { + int val; +@@ -437,6 +510,9 @@ static int mv3310_probe(struct phy_device *phydev) + priv->firmware_ver >> 24, (priv->firmware_ver >> 16) & 255, + (priv->firmware_ver >> 8) & 255, priv->firmware_ver & 255); + ++ if (chip->has_downshift) ++ priv->has_downshift = chip->has_downshift(phydev); ++ + /* Powering down the port when not in use saves about 600mW */ + ret = mv3310_power_down(phydev); + if (ret) +@@ -605,7 +681,16 @@ static int mv3310_config_init(struct phy_device *phydev) + } + + /* Enable EDPD mode - saving 600mW */ +- return mv3310_set_edpd(phydev, ETHTOOL_PHY_EDPD_DFLT_TX_MSECS); ++ err = mv3310_set_edpd(phydev, ETHTOOL_PHY_EDPD_DFLT_TX_MSECS); ++ if (err) ++ return err; ++ ++ /* Allow downshift */ ++ err = mv3310_set_downshift(phydev, DOWNSHIFT_DEV_DEFAULT_COUNT); ++ if (err && err != -EOPNOTSUPP) ++ return err; ++ ++ return 0; + } + + static int mv3310_get_features(struct phy_device *phydev) +@@ -875,6 +960,8 @@ static int mv3310_get_tunable(struct phy_device *phydev, + struct ethtool_tunable *tuna, void *data) + { + switch (tuna->id) { ++ case ETHTOOL_PHY_DOWNSHIFT: ++ return mv3310_get_downshift(phydev, data); + case ETHTOOL_PHY_EDPD: + return mv3310_get_edpd(phydev, data); + default: +@@ -886,6 +973,8 @@ static int mv3310_set_tunable(struct phy_device *phydev, + struct ethtool_tunable *tuna, const void *data) + { + switch (tuna->id) { ++ case ETHTOOL_PHY_DOWNSHIFT: ++ return mv3310_set_downshift(phydev, *(u8 *)data); + case ETHTOOL_PHY_EDPD: + return mv3310_set_edpd(phydev, *(u16 *)data); + default: +@@ -893,6 +982,14 @@ static int mv3310_set_tunable(struct phy_device *phydev, + } + } + ++static bool mv3310_has_downshift(struct phy_device *phydev) ++{ ++ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); ++ ++ /* Fails to downshift with firmware older than v0.3.5.0 */ ++ return priv->firmware_ver >= MV_VERSION(0,3,5,0); ++} ++ + static void mv3310_init_supported_interfaces(unsigned long *mask) + { + __set_bit(PHY_INTERFACE_MODE_SGMII, mask); +@@ -932,6 +1029,7 @@ static void mv2111_init_supported_interfaces(unsigned long *mask) + } + + static const struct mv3310_chip mv3310_type = { ++ .has_downshift = mv3310_has_downshift, + .init_supported_interfaces = mv3310_init_supported_interfaces, + .get_mactype = mv3310_get_mactype, + .init_interface = mv3310_init_interface, +@@ -942,6 +1040,7 @@ static const struct mv3310_chip mv3310_type = { + }; + + static const struct mv3310_chip mv3340_type = { ++ .has_downshift = mv3310_has_downshift, + .init_supported_interfaces = mv3340_init_supported_interfaces, + .get_mactype = mv3310_get_mactype, + .init_interface = mv3340_init_interface, +-- +cgit v1.2.3 + + +From cb91cf67515d694cdbb3253f3a96121d40efe774 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Thu, 30 Jan 2020 22:42:38 +0000 +Subject: net: dpaa2-mac: add support for more ethtool 10G link modes +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Phylink documentation says: + Note that the PHY may be able to transform from one connection + technology to another, so, eg, don't clear 1000BaseX just + because the MAC is unable to BaseX mode. This is more about + clearing unsupported speeds and duplex settings. The port modes + should not be cleared; phylink_set_port_modes() will help with this. + +So add the missing 10G modes. + +Signed-off-by: Russell King +Acked-by: Marek Behún +Acked-by: Ioana Ciornei +--- + drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +index ae6d382d8735..a882f7e6639a 100644 +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +@@ -140,6 +140,12 @@ static void dpaa2_mac_validate(struct phylink_config *config, + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_USXGMII: + phylink_set(mask, 10000baseT_Full); ++ phylink_set(mask, 10000baseKR_Full); ++ phylink_set(mask, 10000baseCR_Full); ++ phylink_set(mask, 10000baseSR_Full); ++ phylink_set(mask, 10000baseLR_Full); ++ phylink_set(mask, 10000baseLRM_Full); ++ phylink_set(mask, 10000baseER_Full); + if (state->interface == PHY_INTERFACE_MODE_10GBASER) + break; + phylink_set(mask, 5000baseT_Full); +-- +cgit v1.2.3 + + +From 382d7f7a824efb3db405f94c55769122622b887f Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 20 Jul 2021 15:15:40 +0100 +Subject: net: phylink: add phylink_set_10g_modes() helper + +Add a helper for setting 10Gigabit modes, so we have one central +place that sets all appropriate 10G modes for a driver. + +Signed-off-by: Russell King (Oracle) +--- + drivers/net/phy/phylink.c | 11 +++++++++++ + include/linux/phylink.h | 1 + + 2 files changed, 12 insertions(+) + +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index 3f24deea367d..3bf8865e9e82 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -132,6 +132,17 @@ void phylink_set_port_modes(unsigned long *mask) + } + EXPORT_SYMBOL_GPL(phylink_set_port_modes); + ++void phylink_set_10g_modes(unsigned long *mask) ++{ ++ phylink_set(mask, 10000baseT_Full); ++ phylink_set(mask, 10000baseCR_Full); ++ phylink_set(mask, 10000baseSR_Full); ++ phylink_set(mask, 10000baseLR_Full); ++ phylink_set(mask, 10000baseLRM_Full); ++ phylink_set(mask, 10000baseER_Full); ++} ++EXPORT_SYMBOL_GPL(phylink_set_10g_modes); ++ + static int phylink_is_empty_linkmode(const unsigned long *linkmode) + { + __ETHTOOL_DECLARE_LINK_MODE_MASK(tmp) = { 0, }; +diff --git a/include/linux/phylink.h b/include/linux/phylink.h +index 237291196ce2..f7b5ed06a815 100644 +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -484,6 +484,7 @@ int phylink_speed_up(struct phylink *pl); + #define phylink_test(bm, mode) __phylink_do_bit(test_bit, bm, mode) + + void phylink_set_port_modes(unsigned long *bits); ++void phylink_set_10g_modes(unsigned long *mask); + void phylink_helper_basex_speed(struct phylink_link_state *state); + + void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs, +-- +cgit v1.2.3 + + +From 4cad7d702c5de9f47243e9cd9975df86fc39f607 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 20 Jul 2021 15:20:28 +0100 +Subject: net: ethernet: use phylink_set_10g_modes() + +Update three drivers to use the new phylink_set_10g_modes() helper: +Cadence macb, Freescale DPAA2 and Marvell PP2. + +Signed-off-by: Russell King (Oracle) +--- + drivers/net/ethernet/cadence/macb_main.c | 7 +------ + drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 7 +------ + drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 7 +------ + 3 files changed, 3 insertions(+), 18 deletions(-) + +diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c +index 7d2fe13a52f8..15b55ade29bc 100644 +--- a/drivers/net/ethernet/cadence/macb_main.c ++++ b/drivers/net/ethernet/cadence/macb_main.c +@@ -547,13 +547,8 @@ static void macb_validate(struct phylink_config *config, + if (bp->caps & MACB_CAPS_GIGABIT_MODE_AVAILABLE && + (state->interface == PHY_INTERFACE_MODE_NA || + state->interface == PHY_INTERFACE_MODE_10GBASER)) { +- phylink_set(mask, 10000baseCR_Full); +- phylink_set(mask, 10000baseER_Full); ++ phylink_set_10g_modes(mask); + phylink_set(mask, 10000baseKR_Full); +- phylink_set(mask, 10000baseLR_Full); +- phylink_set(mask, 10000baseLRM_Full); +- phylink_set(mask, 10000baseSR_Full); +- phylink_set(mask, 10000baseT_Full); + if (state->interface != PHY_INTERFACE_MODE_NA) + goto out; + } +diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +index a882f7e6639a..3faf45c22c19 100644 +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +@@ -139,13 +139,8 @@ static void dpaa2_mac_validate(struct phylink_config *config, + case PHY_INTERFACE_MODE_NA: + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_USXGMII: +- phylink_set(mask, 10000baseT_Full); ++ phylink_set_10g_modes(mask); + phylink_set(mask, 10000baseKR_Full); +- phylink_set(mask, 10000baseCR_Full); +- phylink_set(mask, 10000baseSR_Full); +- phylink_set(mask, 10000baseLR_Full); +- phylink_set(mask, 10000baseLRM_Full); +- phylink_set(mask, 10000baseER_Full); + if (state->interface == PHY_INTERFACE_MODE_10GBASER) + break; + phylink_set(mask, 5000baseT_Full); +diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +index 3229bafa2a2c..76753db2be48 100644 +--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c ++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +@@ -6286,12 +6286,7 @@ static void mvpp2_phylink_validate(struct phylink_config *config, + case PHY_INTERFACE_MODE_XAUI: + case PHY_INTERFACE_MODE_NA: + if (mvpp2_port_supports_xlg(port)) { +- phylink_set(mask, 10000baseT_Full); +- phylink_set(mask, 10000baseCR_Full); +- phylink_set(mask, 10000baseSR_Full); +- phylink_set(mask, 10000baseLR_Full); +- phylink_set(mask, 10000baseLRM_Full); +- phylink_set(mask, 10000baseER_Full); ++ phylink_set_10g_modes(mask); + phylink_set(mask, 10000baseKR_Full); + } + if (state->interface != PHY_INTERFACE_MODE_NA) +-- +cgit v1.2.3 + + +From 3ab4988c8312433944800964545fdaa63bd5ba90 Mon Sep 17 00:00:00 2001 +From: Robert Hancock +Date: Wed, 30 Jun 2021 11:49:27 -0600 +Subject: net: phylink: Support disabling autonegotiation for PCS + +The auto-negotiation state in the PCS as set by +phylink_mii_c22_pcs_config was previously always enabled when the +driver is configured for in-band autonegotiation, even if +autonegotiation was disabled on the interface with ethtool. Update the +code to set the BMCR_ANENABLE bit based on the interface's +autonegotiation enabled state. + +Update phylink_mii_c22_pcs_get_state to not check +autonegotiation-related fields when autonegotiation is disabled. + +Update phylink_mac_pcs_get_state to initialize the state based on the +interface's configured speed, duplex and pause parameters rather than +to unknown when autonegotiation is disabled, before calling the +driver's pcs_get_state functions, as they are not likely to provide +meaningful data for these fields when autonegotiation is disabled. In +this case the driver is really just filling in the link state field. + +Note that in cases where there is a downstream PHY connected, such as +with SGMII and a copper PHY, the configuration set by ethtool is +handled by phy_ethtool_ksettings_set and not propagated to the PCS. +This is correct since SGMII or 1000Base-X autonegotiation with the PCS +should normally still be used even if the copper side has disabled it. + +Signed-off-by: Robert Hancock +Signed-off-by: Russell King (Oracle) +--- + drivers/net/phy/phylink.c | 21 ++++++++++++++++----- + 1 file changed, 16 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index 3bf8865e9e82..1f3f5ca2e5cf 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -551,9 +551,15 @@ static void phylink_mac_pcs_get_state(struct phylink *pl, + linkmode_zero(state->lp_advertising); + state->interface = pl->link_config.interface; + state->an_enabled = pl->link_config.an_enabled; +- state->speed = SPEED_UNKNOWN; +- state->duplex = DUPLEX_UNKNOWN; +- state->pause = MLO_PAUSE_NONE; ++ if (state->an_enabled) { ++ state->speed = SPEED_UNKNOWN; ++ state->duplex = DUPLEX_UNKNOWN; ++ state->pause = MLO_PAUSE_NONE; ++ } else { ++ state->speed = pl->link_config.speed; ++ state->duplex = pl->link_config.duplex; ++ state->pause = pl->link_config.pause; ++ } + state->an_complete = 0; + state->link = 1; + +@@ -2518,7 +2524,10 @@ void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs, + + state->link = !!(bmsr & BMSR_LSTATUS); + state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE); +- if (!state->link) ++ /* If there is no link or autonegotiation is disabled, the LP advertisement ++ * data is not meaningful, so don't go any further. ++ */ ++ if (!state->link || !state->an_enabled) + return; + + switch (state->interface) { +@@ -2641,7 +2650,9 @@ int phylink_mii_c22_pcs_config(struct mdio_device *pcs, unsigned int mode, + changed = ret > 0; + + /* Ensure ISOLATE bit is disabled */ +- bmcr = mode == MLO_AN_INBAND ? BMCR_ANENABLE : 0; ++ bmcr = (mode == MLO_AN_INBAND && ++ linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, advertising)) ? ++ BMCR_ANENABLE : 0; + ret = mdiobus_modify(pcs->bus, pcs->addr, MII_BMCR, + BMCR_ANENABLE | BMCR_ISOLATE, bmcr); + if (ret < 0) +-- +cgit v1.2.3 + + +From 1a6300b528fb4507da11af3173e40dcae0d53e76 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 8 Jun 2021 15:53:31 +0100 +Subject: net: phylink: add phy change pause mode debug + +Augment the phy link debug prints with the pause state. + +Signed-off-by: Russell King (Oracle) +--- + drivers/net/phy/phylink.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index 1f3f5ca2e5cf..876c1a461d6b 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -960,10 +960,11 @@ static void phylink_phy_change(struct phy_device *phydev, bool up) + + phylink_run_resolve(pl); + +- phylink_dbg(pl, "phy link %s %s/%s/%s\n", up ? "up" : "down", ++ phylink_dbg(pl, "phy link %s %s/%s/%s/%s\n", up ? "up" : "down", + phy_modes(phydev->interface), + phy_speed_to_str(phydev->speed), +- phy_duplex_to_str(phydev->duplex)); ++ phy_duplex_to_str(phydev->duplex), ++ phylink_pause_to_str(pl->phy_state.pause)); + } + + static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, +-- +cgit v1.2.3 + + +From 2b5d092a5727f4d0931b3c3823e5013f74024180 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 8 Jun 2021 15:57:30 +0100 +Subject: net: phylink: cleanup ksettings_set + +We only need to fiddle about with the supported mask after we have +validated the user's requested parameters. Simplify and streamline the +code by moving the linkmode copy and update of the autoneg bit after +validating the user's request. + +Signed-off-by: Russell King (Oracle) +--- + drivers/net/phy/phylink.c | 16 +++++++++------- + 1 file changed, 9 insertions(+), 7 deletions(-) + +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index 876c1a461d6b..b5b9342b49d7 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1560,15 +1560,11 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, + return phy_ethtool_ksettings_set(pl->phydev, kset); + } + +- linkmode_copy(support, pl->supported); + config = pl->link_config; +- config.an_enabled = kset->base.autoneg == AUTONEG_ENABLE; + +- /* Mask out unsupported advertisements, and force the autoneg bit */ ++ /* Mask out unsupported advertisements */ + linkmode_and(config.advertising, kset->link_modes.advertising, +- support); +- linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising, +- config.an_enabled); ++ pl->supported); + + /* FIXME: should we reject autoneg if phy/mac does not support it? */ + switch (kset->base.autoneg) { +@@ -1577,7 +1573,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, + * duplex. + */ + s = phy_lookup_setting(kset->base.speed, kset->base.duplex, +- support, false); ++ pl->supported, false); + if (!s) + return -EINVAL; + +@@ -1618,6 +1614,12 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, + /* We have ruled out the case with a PHY attached, and the + * fixed-link cases. All that is left are in-band links. + */ ++ config.an_enabled = kset->base.autoneg == AUTONEG_ENABLE; ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising, ++ config.an_enabled); ++ ++ /* Validate without changing the current supported mask. */ ++ linkmode_copy(support, pl->supported); + if (phylink_validate(pl, support, &config)) + return -EINVAL; + +-- +cgit v1.2.3 + + +From f1d717845dc84420883e9853a588a5bd60ba93b2 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 8 Jun 2021 14:43:54 +0100 +Subject: net: phy: add phy_interface_t bitmap support + +Add support for a bitmap for phy interface modes, which includes: +- a macro to declare the interface bitmap +- an inline helper to zero the interface bitmap +- an inline helper to detect an empty interface bitmap +- an inline helper to do a bitwise AND operation on two interface + bitmaps + +Signed-off-by: Russell King (Oracle) +--- + include/linux/phy.h | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/include/linux/phy.h b/include/linux/phy.h +index 3b80dc3ed68b..1472657a8136 100644 +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -155,6 +155,26 @@ typedef enum { + PHY_INTERFACE_MODE_MAX, + } phy_interface_t; + ++/* PHY interface mode bitmap handling */ ++#define DECLARE_PHY_INTERFACE_MASK(name) \ ++ DECLARE_BITMAP(name, PHY_INTERFACE_MODE_MAX) ++ ++static inline void phy_interface_zero(unsigned long *intf) ++{ ++ bitmap_zero(intf, PHY_INTERFACE_MODE_MAX); ++} ++ ++static inline bool phy_interface_empty(const unsigned long *intf) ++{ ++ return bitmap_empty(intf, PHY_INTERFACE_MODE_MAX); ++} ++ ++static inline void phy_interface_and(unsigned long *dst, const unsigned long *a, ++ const unsigned long *b) ++{ ++ bitmap_and(dst, a, b, PHY_INTERFACE_MODE_MAX); ++} ++ + /* + * phy_supported_speeds - return all speeds currently supported by a PHY device + */ +-- +cgit v1.2.3 + + +From 7072ef48e86ddb063bf8f509a89c54090d84c1b3 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Tue, 3 Mar 2020 11:57:39 +0000 +Subject: net: sfp: augment SFP parsing with phy_interface_t bitmap + +We currently parse the SFP EEPROM to a bitmap of ethtool link modes, +and then attempt to convert the link modes to a PHY interface mode. +While this works at present, there are cases where this is sub-optimal. +For example, where a module can operate with several different PHY +interface modes. + +To start addressing this, arrange for the SFP EEPROM parsing to also +provide a bitmap of the possible PHY interface modes. + +Signed-off-by: Russell King +--- + drivers/net/phy/marvell-88x2222.c | 3 +- + drivers/net/phy/marvell10g.c | 3 +- + drivers/net/phy/phylink.c | 4 ++- + drivers/net/phy/sfp-bus.c | 76 +++++++++++++++++++++++++++++---------- + include/linux/sfp.h | 5 +-- + 5 files changed, 67 insertions(+), 24 deletions(-) + +diff --git a/drivers/net/phy/marvell-88x2222.c b/drivers/net/phy/marvell-88x2222.c +index d8b31d4d2a73..ae285e4225d6 100644 +--- a/drivers/net/phy/marvell-88x2222.c ++++ b/drivers/net/phy/marvell-88x2222.c +@@ -478,6 +478,7 @@ static int mv2222_config_init(struct phy_device *phydev) + + static int mv2222_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) + { ++ DECLARE_PHY_INTERFACE_MASK(interfaces); + struct phy_device *phydev = upstream; + phy_interface_t sfp_interface; + struct mv2222_data *priv; +@@ -489,7 +490,7 @@ static int mv2222_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) + priv = (struct mv2222_data *)phydev->priv; + dev = &phydev->mdio.dev; + +- sfp_parse_support(phydev->sfp_bus, id, sfp_supported); ++ sfp_parse_support(phydev->sfp_bus, id, sfp_supported, interfaces); + sfp_interface = sfp_select_interface(phydev->sfp_bus, sfp_supported); + + dev_info(dev, "%s SFP module inserted\n", phy_modes(sfp_interface)); +diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c +index e1ccb2acae84..eff980b94c95 100644 +--- a/drivers/net/phy/marvell10g.c ++++ b/drivers/net/phy/marvell10g.c +@@ -449,9 +449,10 @@ static int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) + { + struct phy_device *phydev = upstream; + __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, }; ++ DECLARE_PHY_INTERFACE_MASK(interfaces); + phy_interface_t iface; + +- sfp_parse_support(phydev->sfp_bus, id, support); ++ sfp_parse_support(phydev->sfp_bus, id, support, interfaces); + iface = sfp_select_interface(phydev->sfp_bus, support); + + if (iface != PHY_INTERFACE_MODE_10GBASER) { +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index b5b9342b49d7..a0a21cf530f4 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -77,6 +77,7 @@ struct phylink { + + struct sfp_bus *sfp_bus; + bool sfp_may_have_phy; ++ DECLARE_PHY_INTERFACE_MASK(sfp_interfaces); + __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); + u8 sfp_port; + }; +@@ -2242,7 +2243,8 @@ static int phylink_sfp_module_insert(void *upstream, + ASSERT_RTNL(); + + linkmode_zero(support); +- sfp_parse_support(pl->sfp_bus, id, support); ++ phy_interface_zero(pl->sfp_interfaces); ++ sfp_parse_support(pl->sfp_bus, id, support, pl->sfp_interfaces); + pl->sfp_port = sfp_parse_port(pl->sfp_bus, id, support); + + /* If this module may have a PHY connecting later, defer until later */ +diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c +index 7362f8c3271c..21b5f8badfeb 100644 +--- a/drivers/net/phy/sfp-bus.c ++++ b/drivers/net/phy/sfp-bus.c +@@ -13,7 +13,8 @@ + struct sfp_quirk { + const char *vendor; + const char *part; +- void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes); ++ void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes, ++ unsigned long *interfaces); + }; + + /** +@@ -39,13 +40,15 @@ struct sfp_bus { + }; + + static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, +- unsigned long *modes) ++ unsigned long *modes, unsigned long *interfaces) + { + phylink_set(modes, 2500baseX_Full); ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces); + } + + static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id, +- unsigned long *modes) ++ unsigned long *modes, ++ unsigned long *interfaces) + { + /* Ubiquiti U-Fiber Instant module claims that support all transceiver + * types including 10G Ethernet which is not truth. So clear all claimed +@@ -226,12 +229,14 @@ EXPORT_SYMBOL_GPL(sfp_may_have_phy); + * @bus: a pointer to the &struct sfp_bus structure for the sfp module + * @id: a pointer to the module's &struct sfp_eeprom_id + * @support: pointer to an array of unsigned long for the ethtool support mask ++ * @interfaces: pointer to an array of unsigned long for phy interface modes ++ * mask + * + * Parse the EEPROM identification information and derive the supported + * ethtool link modes for the module. + */ + void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, +- unsigned long *support) ++ unsigned long *support, unsigned long *interfaces) + { + unsigned int br_min, br_nom, br_max; + __ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, }; +@@ -258,27 +263,41 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, + } + + /* Set ethtool support from the compliance fields. */ +- if (id->base.e10g_base_sr) ++ if (id->base.e10g_base_sr) { + phylink_set(modes, 10000baseSR_Full); +- if (id->base.e10g_base_lr) ++ __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); ++ } ++ if (id->base.e10g_base_lr) { + phylink_set(modes, 10000baseLR_Full); +- if (id->base.e10g_base_lrm) ++ __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); ++ } ++ if (id->base.e10g_base_lrm) { + phylink_set(modes, 10000baseLRM_Full); +- if (id->base.e10g_base_er) ++ __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); ++ } ++ if (id->base.e10g_base_er) { + phylink_set(modes, 10000baseER_Full); ++ __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); ++ } + if (id->base.e1000_base_sx || + id->base.e1000_base_lx || +- id->base.e1000_base_cx) ++ id->base.e1000_base_cx) { + phylink_set(modes, 1000baseX_Full); ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces); ++ } + if (id->base.e1000_base_t) { + phylink_set(modes, 1000baseT_Half); + phylink_set(modes, 1000baseT_Full); ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces); ++ __set_bit(PHY_INTERFACE_MODE_SGMII, interfaces); + } + + /* 1000Base-PX or 1000Base-BX10 */ + if ((id->base.e_base_px || id->base.e_base_bx10) && +- br_min <= 1300 && br_max >= 1200) ++ br_min <= 1300 && br_max >= 1200) { + phylink_set(modes, 1000baseX_Full); ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces); ++ } + + /* 100Base-FX, 100Base-LX, 100Base-PX, 100Base-BX10 */ + if (id->base.e100_base_fx || id->base.e100_base_lx) +@@ -291,21 +310,30 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, + */ + if ((id->base.sfp_ct_passive || id->base.sfp_ct_active) && br_nom) { + /* This may look odd, but some manufacturers use 12000MBd */ +- if (br_min <= 12000 && br_max >= 10300) ++ if (br_min <= 12000 && br_max >= 10300) { + phylink_set(modes, 10000baseCR_Full); +- if (br_min <= 3200 && br_max >= 3100) ++ __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); ++ } ++ if (br_min <= 3200 && br_max >= 3100) { + phylink_set(modes, 2500baseX_Full); +- if (br_min <= 1300 && br_max >= 1200) ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces); ++ } ++ if (br_min <= 1300 && br_max >= 1200) { + phylink_set(modes, 1000baseX_Full); ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces); ++ } + } + if (id->base.sfp_ct_passive) { +- if (id->base.passive.sff8431_app_e) ++ if (id->base.passive.sff8431_app_e) { + phylink_set(modes, 10000baseCR_Full); ++ __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); ++ } + } + if (id->base.sfp_ct_active) { + if (id->base.active.sff8431_app_e || + id->base.active.sff8431_lim) { + phylink_set(modes, 10000baseCR_Full); ++ __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); + } + } + +@@ -330,12 +358,14 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, + case SFF8024_ECC_10GBASE_T_SFI: + case SFF8024_ECC_10GBASE_T_SR: + phylink_set(modes, 10000baseT_Full); ++ __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); + break; + case SFF8024_ECC_5GBASE_T: + phylink_set(modes, 5000baseT_Full); + break; + case SFF8024_ECC_2_5GBASE_T: + phylink_set(modes, 2500baseT_Full); ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces); + break; + default: + dev_warn(bus->sfp_dev, +@@ -348,10 +378,14 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, + if (id->base.fc_speed_100 || + id->base.fc_speed_200 || + id->base.fc_speed_400) { +- if (id->base.br_nominal >= 31) ++ if (id->base.br_nominal >= 31) { + phylink_set(modes, 2500baseX_Full); +- if (id->base.br_nominal >= 12) ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces); ++ } ++ if (id->base.br_nominal >= 12) { + phylink_set(modes, 1000baseX_Full); ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces); ++ } + } + + /* If we haven't discovered any modes that this module supports, try +@@ -364,14 +398,18 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, + * 2500BASE-X, so we allow some slack here. + */ + if (bitmap_empty(modes, __ETHTOOL_LINK_MODE_MASK_NBITS) && br_nom) { +- if (br_min <= 1300 && br_max >= 1200) ++ if (br_min <= 1300 && br_max >= 1200) { + phylink_set(modes, 1000baseX_Full); +- if (br_min <= 3200 && br_max >= 2500) ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces); ++ } ++ if (br_min <= 3200 && br_max >= 2500) { + phylink_set(modes, 2500baseX_Full); ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces); ++ } + } + + if (bus->sfp_quirk) +- bus->sfp_quirk->modes(id, modes); ++ bus->sfp_quirk->modes(id, modes, interfaces); + + bitmap_or(support, support, modes, __ETHTOOL_LINK_MODE_MASK_NBITS); + +diff --git a/include/linux/sfp.h b/include/linux/sfp.h +index 302094b855fb..d1f343853b6c 100644 +--- a/include/linux/sfp.h ++++ b/include/linux/sfp.h +@@ -535,7 +535,7 @@ int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id, + unsigned long *support); + bool sfp_may_have_phy(struct sfp_bus *bus, const struct sfp_eeprom_id *id); + void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, +- unsigned long *support); ++ unsigned long *support, unsigned long *interfaces); + phy_interface_t sfp_select_interface(struct sfp_bus *bus, + unsigned long *link_modes); + +@@ -568,7 +568,8 @@ static inline bool sfp_may_have_phy(struct sfp_bus *bus, + + static inline void sfp_parse_support(struct sfp_bus *bus, + const struct sfp_eeprom_id *id, +- unsigned long *support) ++ unsigned long *support, ++ unsigned long *interfaces) + { + } + +-- +cgit v1.2.3 + + +From 03a6c744372a8682f913dff44a874c2c2d01cfd6 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Tue, 3 Mar 2020 12:12:43 +0000 +Subject: net: phylink: add mac PHY interface bitmap + +Add a PHY interface bitmap for the MAC driver to specify which PHY +interfaces are supported to phylink. + +Signed-off-by: Russell King +--- + include/linux/phylink.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/include/linux/phylink.h b/include/linux/phylink.h +index f7b5ed06a815..bc4b866cd99b 100644 +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -76,6 +76,7 @@ struct phylink_config { + bool ovr_an_inband; + void (*get_fixed_state)(struct phylink_config *config, + struct phylink_link_state *state); ++ DECLARE_PHY_INTERFACE_MASK(supported_interfaces); + }; + + /** +-- +cgit v1.2.3 + + +From b1f7bec7a2c8b3bd05258fd2f8fd739d36f758d2 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Tue, 3 Mar 2020 12:12:43 +0000 +Subject: net: phylink: use phy interface mode bitmaps for optical modules + +Where a MAC provides the PHY interface mode capabilities, use the PHY +interface mode bitmaps to select the operating interface mode for +optical SFP modules, rather than using the linkmode bitmaps. + +Signed-off-by: Russell King +--- + drivers/net/phy/phylink.c | 56 +++++++++++++++++++++++++++++++++++++++++------ + 1 file changed, 49 insertions(+), 7 deletions(-) + +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index a0a21cf530f4..c721ef6dda2e 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -2152,6 +2152,41 @@ static void phylink_sfp_detach(void *upstream, struct sfp_bus *bus) + pl->netdev->sfp_bus = NULL; + } + ++static const phy_interface_t phylink_sfp_interface_preference[] = { ++ PHY_INTERFACE_MODE_USXGMII, ++ PHY_INTERFACE_MODE_10GBASER, ++ PHY_INTERFACE_MODE_10GKR, ++ PHY_INTERFACE_MODE_2500BASEX, ++ PHY_INTERFACE_MODE_SGMII, ++ PHY_INTERFACE_MODE_1000BASEX, ++}; ++ ++static phy_interface_t phylink_select_interface(struct phylink *pl, ++ const unsigned long *intf, ++ const char *intf_name) ++{ ++ DECLARE_PHY_INTERFACE_MASK(u); ++ phy_interface_t interface; ++ size_t i; ++ ++ phy_interface_and(u, intf, pl->config->supported_interfaces); ++ ++ interface = PHY_INTERFACE_MODE_NA; ++ for (i = 0; i < ARRAY_SIZE(phylink_sfp_interface_preference); i++) ++ if (test_bit(phylink_sfp_interface_preference[i], u)) { ++ interface = phylink_sfp_interface_preference[i]; ++ break; ++ } ++ ++ phylink_info(pl, "interfaces=[mac=%*pbl %s=%*pbl] selected %d (%s)\n", ++ (int)PHY_INTERFACE_MODE_MAX, ++ pl->config->supported_interfaces, ++ intf_name, (int)PHY_INTERFACE_MODE_MAX, intf, ++ interface, phy_modes(interface)); ++ ++ return interface; ++} ++ + static int phylink_sfp_config(struct phylink *pl, u8 mode, + const unsigned long *supported, + const unsigned long *advertising) +@@ -2234,25 +2269,33 @@ static int phylink_sfp_config(struct phylink *pl, u8 mode, + return ret; + } + ++static int phylink_sfp_config_nophy(struct phylink *pl) ++{ ++ if (!phy_interface_empty(pl->config->supported_interfaces)) ++ phylink_select_interface(pl, pl->sfp_interfaces, "sfp"); ++ ++ return phylink_sfp_config(pl, MLO_AN_INBAND, ++ pl->sfp_support, pl->sfp_support); ++} ++ + static int phylink_sfp_module_insert(void *upstream, + const struct sfp_eeprom_id *id) + { + struct phylink *pl = upstream; +- unsigned long *support = pl->sfp_support; + + ASSERT_RTNL(); + +- linkmode_zero(support); ++ linkmode_zero(pl->sfp_support); + phy_interface_zero(pl->sfp_interfaces); +- sfp_parse_support(pl->sfp_bus, id, support, pl->sfp_interfaces); +- pl->sfp_port = sfp_parse_port(pl->sfp_bus, id, support); ++ sfp_parse_support(pl->sfp_bus, id, pl->sfp_support, pl->sfp_interfaces); ++ pl->sfp_port = sfp_parse_port(pl->sfp_bus, id, pl->sfp_support); + + /* If this module may have a PHY connecting later, defer until later */ + pl->sfp_may_have_phy = sfp_may_have_phy(pl->sfp_bus, id); + if (pl->sfp_may_have_phy) + return 0; + +- return phylink_sfp_config(pl, MLO_AN_INBAND, support, support); ++ return phylink_sfp_config_nophy(pl); + } + + static int phylink_sfp_module_start(void *upstream) +@@ -2271,8 +2314,7 @@ static int phylink_sfp_module_start(void *upstream) + if (!pl->sfp_may_have_phy) + return 0; + +- return phylink_sfp_config(pl, MLO_AN_INBAND, +- pl->sfp_support, pl->sfp_support); ++ return phylink_sfp_config_nophy(pl); + } + + static void phylink_sfp_module_stop(void *upstream) +-- +cgit v1.2.3 + + +From 696b4e39df4792ab9638c87972167fdf8376bdda Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Tue, 3 Mar 2020 12:15:38 +0000 +Subject: net: mvneta: fill in phy interface mode bitmap + +Fill in the phy interface mode bitmap for the Marvell mvneta driver, so +phylink can know which interfaces are supported by the MAC. + +Signed-off-by: Russell King +--- + drivers/net/ethernet/marvell/mvneta.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c +index de32e5b49053..697207b88444 100644 +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -5172,6 +5172,22 @@ static int mvneta_probe(struct platform_device *pdev) + + pp->phylink_config.dev = &dev->dev; + pp->phylink_config.type = PHYLINK_NETDEV; ++ __set_bit(PHY_INTERFACE_MODE_SGMII, ++ pp->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_RGMII, ++ pp->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_RGMII_ID, ++ pp->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_RGMII_RXID, ++ pp->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_RGMII_TXID, ++ pp->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_QSGMII, ++ pp->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, ++ pp->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, ++ pp->phylink_config.supported_interfaces); + + phylink = phylink_create(&pp->phylink_config, pdev->dev.fwnode, + phy_mode, &mvneta_phylink_ops); +-- +cgit v1.2.3 + + +From b096813a94db41df365a1b9a20e4de7176b185ff Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Tue, 3 Mar 2020 12:15:38 +0000 +Subject: net: mvpp2: fill in phy interface mode bitmap + +Fill in the phy interface mode bitmap for the Marvell mvpp2 driver, so +phylink can know which interfaces are supported by the MAC. + +Signed-off-by: Russell King +--- + drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 23 +++++++++++++++++++++++ + 1 file changed, 23 insertions(+) + +diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +index 76753db2be48..dd7e2c17a5be 100644 +--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c ++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +@@ -6923,6 +6923,29 @@ static int mvpp2_port_probe(struct platform_device *pdev, + port->phylink_config.dev = &dev->dev; + port->phylink_config.type = PHYLINK_NETDEV; + ++ if (mvpp2_port_supports_xlg(port)) { ++ __set_bit(PHY_INTERFACE_MODE_10GBASER, ++ port->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_XAUI, ++ port->phylink_config.supported_interfaces); ++ } ++ if (mvpp2_port_supports_rgmii(port)) { ++ __set_bit(PHY_INTERFACE_MODE_RGMII, ++ port->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_RGMII_ID, ++ port->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_RGMII_TXID, ++ port->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_RGMII_RXID, ++ port->phylink_config.supported_interfaces); ++ } ++ __set_bit(PHY_INTERFACE_MODE_SGMII, ++ port->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, ++ port->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, ++ port->phylink_config.supported_interfaces); ++ + phylink = phylink_create(&port->phylink_config, port_fwnode, + phy_mode, &mvpp2_phylink_ops); + if (IS_ERR(phylink)) { +-- +cgit v1.2.3 + + +From e77daa70521c36402efe3f87951852358182bc08 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= +Date: Tue, 20 Oct 2020 19:09:11 +0200 +Subject: net: dsa: fill phylink's config supported_interfaces member +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add a new DSA switch operation, phylink_get_interfaces, which should +fill in which PHY_INTERFACE_MODE_* are supported by given port. + +Use this before phylink_create to fill phylink's config +supported_interfaces member. + +This allows for phylink to determine which PHY_INTERFACE_MODE to use +with SFP modules. + +Signed-off-by: Marek Behún +Signed-off-by: Russell King +--- + include/net/dsa.h | 2 ++ + net/dsa/slave.c | 4 ++++ + 2 files changed, 6 insertions(+) + +diff --git a/include/net/dsa.h b/include/net/dsa.h +index 33f40c1ec379..07b29d4b4336 100644 +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -592,6 +592,8 @@ struct dsa_switch_ops { + /* + * PHYLINK integration + */ ++ void (*phylink_get_interfaces)(struct dsa_switch *ds, int port, ++ unsigned long *supported_interfaces); + void (*phylink_validate)(struct dsa_switch *ds, int port, + unsigned long *supported, + struct phylink_link_state *state); +diff --git a/net/dsa/slave.c b/net/dsa/slave.c +index 23be8e01026b..64005bfecd23 100644 +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -1773,6 +1773,10 @@ static int dsa_slave_phy_setup(struct net_device *slave_dev) + dp->pl_config.poll_fixed_state = true; + } + ++ if (ds->ops->phylink_get_interfaces) ++ ds->ops->phylink_get_interfaces(ds, dp->index, ++ dp->pl_config.supported_interfaces); ++ + dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn), mode, + &dsa_port_phylink_mac_ops); + if (IS_ERR(dp->pl)) { +-- +cgit v1.2.3 + + +From 58339cd013b7e37739c54d06b2e9f226d99d2f50 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= +Date: Tue, 20 Oct 2020 19:09:12 +0200 +Subject: net: dsa: mv88e6xxx: implement .phylink_get_interfaces operation +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Implement the .phylink_get_interfaces method for mv88e6xxx driver. + +We are currently only interested in SGMII, 1000base-x and 2500base-x +modes (for the SFP code). USXGMII and 10gbase-r can be added later for +Amethyst. XAUI and RXAUI are irrelevant for SFP (but maybe not for +QSFP?). + +Signed-off-by: Marek Behún +Signed-off-by: Russell King +--- + drivers/net/dsa/mv88e6xxx/chip.c | 57 ++++++++++++++++++++++++++++++++++++++++ + drivers/net/dsa/mv88e6xxx/chip.h | 2 ++ + 2 files changed, 59 insertions(+) + +diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c +index 272b0535d946..c088915f1feb 100644 +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -683,6 +683,50 @@ static void mv88e6xxx_validate(struct dsa_switch *ds, int port, + phylink_helper_basex_speed(state); + } + ++static void mv88e6352_phylink_get_interfaces(struct mv88e6xxx_chip *chip, ++ int port, ++ unsigned long *supported) ++{ ++ if (mv88e6xxx_serdes_get_lane(chip, port)) { ++ /* FIXME: does code for 6352 family support changing between ++ * SGMII and 1000base-x? ++ */ ++ __set_bit(PHY_INTERFACE_MODE_SGMII, supported); ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); ++ } ++} ++ ++static void mv88e6341_phylink_get_interfaces(struct mv88e6xxx_chip *chip, ++ int port, ++ unsigned long *supported) ++{ ++ if (port == 5) { ++ __set_bit(PHY_INTERFACE_MODE_SGMII, supported); ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported); ++ } ++} ++ ++static void mv88e6390_phylink_get_interfaces(struct mv88e6xxx_chip *chip, ++ int port, ++ unsigned long *supported) ++{ ++ if (port == 9 || port == 10) { ++ __set_bit(PHY_INTERFACE_MODE_SGMII, supported); ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported); ++ } ++} ++ ++static void mv88e6xxx_get_interfaces(struct dsa_switch *ds, int port, ++ unsigned long *supported) ++{ ++ struct mv88e6xxx_chip *chip = ds->priv; ++ ++ if (chip->info->ops->phylink_get_interfaces) ++ chip->info->ops->phylink_get_interfaces(chip, port, supported); ++} ++ + static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port, + unsigned int mode, + const struct phylink_link_state *state) +@@ -3628,6 +3672,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { + .serdes_get_stats = mv88e6390_serdes_get_stats, + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, ++ .phylink_get_interfaces = mv88e6341_phylink_get_interfaces, + .phylink_validate = mv88e6341_phylink_validate, + }; + +@@ -3803,6 +3848,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { + .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, + .serdes_get_regs = mv88e6352_serdes_get_regs, + .gpio_ops = &mv88e6352_gpio_ops, ++ .phylink_get_interfaces = mv88e6352_phylink_get_interfaces, + .phylink_validate = mv88e6352_phylink_validate, + }; + +@@ -3903,6 +3949,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { + .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, + .serdes_get_regs = mv88e6352_serdes_get_regs, + .gpio_ops = &mv88e6352_gpio_ops, ++ .phylink_get_interfaces = mv88e6352_phylink_get_interfaces, + .phylink_validate = mv88e6352_phylink_validate, + }; + +@@ -4004,6 +4051,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, + .gpio_ops = &mv88e6352_gpio_ops, ++ .phylink_get_interfaces = mv88e6390_phylink_get_interfaces, + .phylink_validate = mv88e6390_phylink_validate, + }; + +@@ -4065,6 +4113,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, + .gpio_ops = &mv88e6352_gpio_ops, ++ .phylink_get_interfaces = mv88e6390_phylink_get_interfaces, + .phylink_validate = mv88e6390x_phylink_validate, + }; + +@@ -4125,6 +4174,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { + .serdes_get_regs = mv88e6390_serdes_get_regs, + .avb_ops = &mv88e6390_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, ++ .phylink_get_interfaces = mv88e6390_phylink_get_interfaces, + .phylink_validate = mv88e6390_phylink_validate, + }; + +@@ -4185,6 +4235,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { + .gpio_ops = &mv88e6352_gpio_ops, + .avb_ops = &mv88e6352_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, ++ .phylink_get_interfaces = mv88e6352_phylink_get_interfaces, + .phylink_validate = mv88e6352_phylink_validate, + }; + +@@ -4287,6 +4338,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { + .gpio_ops = &mv88e6352_gpio_ops, + .avb_ops = &mv88e6390_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, ++ .phylink_get_interfaces = mv88e6390_phylink_get_interfaces, + .phylink_validate = mv88e6390_phylink_validate, + }; + +@@ -4439,6 +4491,7 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { + .serdes_get_stats = mv88e6390_serdes_get_stats, + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, ++ .phylink_get_interfaces = mv88e6341_phylink_get_interfaces, + .phylink_validate = mv88e6341_phylink_validate, + }; + +@@ -4588,6 +4641,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { + .serdes_get_stats = mv88e6352_serdes_get_stats, + .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, + .serdes_get_regs = mv88e6352_serdes_get_regs, ++ .phylink_get_interfaces = mv88e6352_phylink_get_interfaces, + .phylink_validate = mv88e6352_phylink_validate, + }; + +@@ -4653,6 +4707,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { + .serdes_get_stats = mv88e6390_serdes_get_stats, + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, ++ .phylink_get_interfaces = mv88e6390_phylink_get_interfaces, + .phylink_validate = mv88e6390_phylink_validate, + }; + +@@ -4717,6 +4772,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { + .gpio_ops = &mv88e6352_gpio_ops, + .avb_ops = &mv88e6390_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, ++ .phylink_get_interfaces = mv88e6390_phylink_get_interfaces, + .phylink_validate = mv88e6390x_phylink_validate, + }; + +@@ -6072,6 +6128,7 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { + .change_tag_protocol = mv88e6xxx_change_tag_protocol, + .setup = mv88e6xxx_setup, + .teardown = mv88e6xxx_teardown, ++ .phylink_get_interfaces = mv88e6xxx_get_interfaces, + .port_setup = mv88e6xxx_port_setup, + .port_teardown = mv88e6xxx_port_teardown, + .phylink_validate = mv88e6xxx_validate, +diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h +index 675b1f3e43b7..601c995cf4c7 100644 +--- a/drivers/net/dsa/mv88e6xxx/chip.h ++++ b/drivers/net/dsa/mv88e6xxx/chip.h +@@ -599,6 +599,8 @@ struct mv88e6xxx_ops { + const struct mv88e6xxx_ptp_ops *ptp_ops; + + /* Phylink */ ++ void (*phylink_get_interfaces)(struct mv88e6xxx_chip *chip, int port, ++ unsigned long *supported); + void (*phylink_validate)(struct mv88e6xxx_chip *chip, int port, + unsigned long *mask, + struct phylink_link_state *state); +-- +cgit v1.2.3 + + +From b020661fc4a29c39f0051cd22493e26cc1541b17 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Fri, 13 Mar 2020 10:48:32 +0000 +Subject: net: mtk_eth_soc: use resolved link config for PCS PHY + +The SGMII PCS PHY needs to be updated with the link configuration in +the mac_link_up() call rather than in mac_config(). However, +mtk_sgmii_setup_mode_force() programs the SGMII block during +mac_config() when using 802.3z interface modes with the link +configuration. + +Split that functionality from mtk_sgmii_setup_mode_force(), moving it +to a new mtk_sgmii_link_up() function, and call it from mac_link_up(). + +This does not look correct to me: 802.3z modes operate at a fixed +speed. The contents of mtk_sgmii_link_up() look more appropriate for +SGMII mode, but the original code definitely did not call +mtk_sgmii_setup_mode_force() for SGMII mode but only 802.3z mode. + +Signed-off-by: Russell King +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 11 ++++++++- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 3 ++- + drivers/net/ethernet/mediatek/mtk_sgmii.c | 37 ++++++++++++++++++++--------- + 3 files changed, 38 insertions(+), 13 deletions(-) + +diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +index 64adfd24e134..82e4e9de3846 100644 +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -337,7 +337,7 @@ static void mtk_mac_config(struct phylink_config *config, unsigned int mode, + /* Setup SGMIISYS with the determined property */ + if (state->interface != PHY_INTERFACE_MODE_SGMII) + err = mtk_sgmii_setup_mode_force(eth->sgmii, sid, +- state); ++ state->interface); + else if (phylink_autoneg_inband(mode)) + err = mtk_sgmii_setup_mode_an(eth->sgmii, sid); + +@@ -434,6 +434,15 @@ static void mtk_mac_link_up(struct phylink_config *config, + phylink_config); + u32 mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); + ++ if (phy_interface_mode_is_8023z(interface)) { ++ struct mtk_eth *eth = mac->hw; ++ ++ /* Decide how GMAC and SGMIISYS be mapped */ ++ int sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ? ++ 0 : mac->id; ++ mtk_sgmii_link_up(eth->sgmii, sid, speed, duplex); ++ } ++ + mcr &= ~(MAC_MCR_SPEED_100 | MAC_MCR_SPEED_1000 | + MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_TX_FC | + MAC_MCR_FORCE_RX_FC); +diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +index 5ef70dd8b49c..c4771c29312a 100644 +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -1004,7 +1004,8 @@ int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *np, + u32 ana_rgc3); + int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id); + int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id, +- const struct phylink_link_state *state); ++ phy_interface_t interface); ++void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex); + void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id); + + int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id); +diff --git a/drivers/net/ethernet/mediatek/mtk_sgmii.c b/drivers/net/ethernet/mediatek/mtk_sgmii.c +index 32d83421226a..372c85c830b5 100644 +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -60,7 +60,7 @@ int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id) + } + + int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id, +- const struct phylink_link_state *state) ++ phy_interface_t interface) + { + unsigned int val; + +@@ -69,7 +69,7 @@ int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id, + + regmap_read(ss->regmap[id], ss->ana_rgc3, &val); + val &= ~RG_PHY_SPEED_MASK; +- if (state->interface == PHY_INTERFACE_MODE_2500BASEX) ++ if (interface == PHY_INTERFACE_MODE_2500BASEX) + val |= RG_PHY_SPEED_3_125G; + regmap_write(ss->regmap[id], ss->ana_rgc3, val); + +@@ -78,11 +78,33 @@ int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id, + val &= ~SGMII_AN_ENABLE; + regmap_write(ss->regmap[id], SGMSYS_PCS_CONTROL_1, val); + ++ if (interface == PHY_INTERFACE_MODE_1000BASEX || ++ interface == PHY_INTERFACE_MODE_2500BASEX) { ++ /* SGMII force mode setting */ ++ regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val); ++ val &= ~SGMII_IF_MODE_MASK; ++ val |= SGMII_SPEED_1000; ++ val |= SGMII_DUPLEX_FULL; ++ regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val); ++ } ++ ++ /* Release PHYA power down state */ ++ regmap_read(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, &val); ++ val &= ~SGMII_PHYA_PWD; ++ regmap_write(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, val); ++ ++ return 0; ++} ++ ++void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex) ++{ ++ unsigned int val; ++ + /* SGMII force mode setting */ + regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val); + val &= ~SGMII_IF_MODE_MASK; + +- switch (state->speed) { ++ switch (speed) { + case SPEED_10: + val |= SGMII_SPEED_10; + break; +@@ -95,17 +117,10 @@ int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id, + break; + } + +- if (state->duplex == DUPLEX_FULL) ++ if (duplex == DUPLEX_FULL) + val |= SGMII_DUPLEX_FULL; + + regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val); +- +- /* Release PHYA power down state */ +- regmap_read(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, &val); +- val &= ~SGMII_PHYA_PWD; +- regmap_write(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, val); +- +- return 0; + } + + void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id) +-- +cgit v1.2.3 + + +From 8312e4eac6092eea69fba34906b972ec1c6927c9 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Sun, 21 Jun 2020 14:30:20 +0100 +Subject: net: phy: at803x: simplify custom phy id matching + +The at803x driver contains a function, at803x_match_phy_id(), which +tests whether the PHY ID matches the value passed, comparing phy_id +with phydev->phy_id and testing all bits that have a "one" in the +mask, phydev->drv->phy_id_mask. + +This is the same test that is used to match the driver, with phy_id +replaced with the driver specified ID, phydev->drv->phy_id. + +Hence, we already know the value of the bits being tested if we look +at phydev->drv->phy_id directly, and we do not require a complicated +test to check them. Test directly against phydev->drv->phy_id instead. + +Signed-off-by: Russell King +--- + drivers/net/phy/at803x.c | 18 ++++++------------ + 1 file changed, 6 insertions(+), 12 deletions(-) + +diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c +index 5d62b85a4024..bdac087058b2 100644 +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -532,12 +532,6 @@ static int at8031_register_regulators(struct phy_device *phydev) + return 0; + } + +-static bool at803x_match_phy_id(struct phy_device *phydev, u32 phy_id) +-{ +- return (phydev->phy_id & phydev->drv->phy_id_mask) +- == (phy_id & phydev->drv->phy_id_mask); +-} +- + static int at803x_parse_dt(struct phy_device *phydev) + { + struct device_node *node = phydev->mdio.dev.of_node; +@@ -602,8 +596,8 @@ static int at803x_parse_dt(struct phy_device *phydev) + * to the AR8030 so there might be a good chance it works on + * the AR8030 too. + */ +- if (at803x_match_phy_id(phydev, ATH8030_PHY_ID) || +- at803x_match_phy_id(phydev, ATH8035_PHY_ID)) { ++ if (phydev->drv->phy_id == ATH8030_PHY_ID || ++ phydev->drv->phy_id == ATH8035_PHY_ID) { + priv->clk_25m_reg &= AT8035_CLK_OUT_MASK; + priv->clk_25m_mask &= AT8035_CLK_OUT_MASK; + } +@@ -631,7 +625,7 @@ static int at803x_parse_dt(struct phy_device *phydev) + /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping + * options. + */ +- if (at803x_match_phy_id(phydev, ATH8031_PHY_ID)) { ++ if (phydev->drv->phy_id == ATH8031_PHY_ID) { + if (of_property_read_bool(node, "qca,keep-pll-enabled")) + priv->flags |= AT803X_KEEP_PLL_ENABLED; + +@@ -676,7 +670,7 @@ static int at803x_probe(struct phy_device *phydev) + * Switch to the copper page, as otherwise we read + * the PHY capabilities from the fiber side. + */ +- if (at803x_match_phy_id(phydev, ATH8031_PHY_ID)) { ++ if (phydev->drv->phy_id == ATH8031_PHY_ID) { + phy_lock_mdio_bus(phydev); + ret = at803x_write_page(phydev, AT803X_PAGE_COPPER); + phy_unlock_mdio_bus(phydev); +@@ -709,7 +703,7 @@ static int at803x_get_features(struct phy_device *phydev) + if (err) + return err; + +- if (!at803x_match_phy_id(phydev, ATH8031_PHY_ID)) ++ if (phydev->drv->phy_id != ATH8031_PHY_ID) + return 0; + + /* AR8031/AR8033 have different status registers +@@ -820,7 +814,7 @@ static int at803x_config_init(struct phy_device *phydev) + if (ret < 0) + return ret; + +- if (at803x_match_phy_id(phydev, ATH8031_PHY_ID)) { ++ if (phydev->drv->phy_id == ATH8031_PHY_ID) { + ret = at8031_pll_config(phydev); + if (ret < 0) + return ret; +-- +cgit v1.2.3 + + +From 62e364e44a89c28faf0bb8a5500d830b43cec9ae Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Fri, 29 Nov 2019 00:31:03 +0000 +Subject: net: phy: add helpers for comparing phy IDs + +There are several places which open code comparing PHY IDs. Provide a +couple of helpers to assist with this, using a slightly simpler test +than the original: + +- phy_id_compare() compares two arbitary PHY IDs and a mask of the + significant bits in the ID. +- phydev_id_compare() compares the bound phydev with the specified + PHY ID, using the bound driver's mask. + +Signed-off-by: Russell King +--- + drivers/net/phy/phy_device.c | 16 +++++++--------- + drivers/net/phy/phylink.c | 4 ++-- + include/linux/phy.h | 28 ++++++++++++++++++++++++++++ + 3 files changed, 37 insertions(+), 11 deletions(-) + +diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c +index 5d5f9a9ee768..67593cf47bf0 100644 +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -388,8 +388,7 @@ int phy_unregister_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask) + fixup = list_entry(pos, struct phy_fixup, list); + + if ((!strcmp(fixup->bus_id, bus_id)) && +- ((fixup->phy_uid & phy_uid_mask) == +- (phy_uid & phy_uid_mask))) { ++ phy_id_compare(fixup->phy_uid, phy_uid, phy_uid_mask)) { + list_del(&fixup->list); + kfree(fixup); + ret = 0; +@@ -425,8 +424,8 @@ static int phy_needs_fixup(struct phy_device *phydev, struct phy_fixup *fixup) + if (strcmp(fixup->bus_id, PHY_ANY_ID) != 0) + return 0; + +- if ((fixup->phy_uid & fixup->phy_uid_mask) != +- (phydev->phy_id & fixup->phy_uid_mask)) ++ if (!phy_id_compare(phydev->phy_id, fixup->phy_uid, ++ fixup->phy_uid_mask)) + if (fixup->phy_uid != PHY_ANY_UID) + return 0; + +@@ -473,15 +472,14 @@ static int phy_bus_match(struct device *dev, struct device_driver *drv) + if (phydev->c45_ids.device_ids[i] == 0xffffffff) + continue; + +- if ((phydrv->phy_id & phydrv->phy_id_mask) == +- (phydev->c45_ids.device_ids[i] & +- phydrv->phy_id_mask)) ++ if (phy_id_compare(phydev->c45_ids.device_ids[i], ++ phydrv->phy_id, phydrv->phy_id_mask)) + return 1; + } + return 0; + } else { +- return (phydrv->phy_id & phydrv->phy_id_mask) == +- (phydev->phy_id & phydrv->phy_id_mask); ++ return phy_id_compare(phydev->phy_id, phydrv->phy_id, ++ phydrv->phy_id_mask); + } + } + +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index c721ef6dda2e..1454e8d199de 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -2350,8 +2350,8 @@ static void phylink_sfp_link_up(void *upstream) + */ + static bool phylink_phy_no_inband(struct phy_device *phy) + { +- return phy->is_c45 && +- (phy->c45_ids.device_ids[1] & 0xfffffff0) == 0xae025150; ++ return phy->is_c45 && phy_id_compare(phy->c45_ids.device_ids[1], ++ 0xae025150, 0xfffffff0); + } + + static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) +diff --git a/include/linux/phy.h b/include/linux/phy.h +index 1472657a8136..a9d22716b16b 100644 +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -928,6 +928,34 @@ struct phy_driver { + #define PHY_ID_MATCH_MODEL(id) .phy_id = (id), .phy_id_mask = GENMASK(31, 4) + #define PHY_ID_MATCH_VENDOR(id) .phy_id = (id), .phy_id_mask = GENMASK(31, 10) + ++/** ++ * phy_id_compare - compare @id1 with @id2 taking account of @mask ++ * @id1: first PHY ID ++ * @id2: second PHY ID ++ * @mask: the PHY ID mask, set bits are significant in matching ++ * ++ * Return true if the bits from @id1 and @id2 specified by @mask match. ++ * This uses an equivalent test to (@id & @mask) == (@phy_id & @mask). ++ */ ++static inline bool phy_id_compare(u32 id1, u32 id2, u32 mask) ++{ ++ return !((id1 ^ id2) & mask); ++} ++ ++/** ++ * phydev_id_compare - compare @id with the PHY's Clause 22 ID ++ * @phydev: the PHY device ++ * @id: the PHY ID to be matched ++ * ++ * Compare the @phydev clause 22 ID with the provided @id and return true or ++ * false depending whether it matches, using the bound driver mask. The ++ * @phydev must be bound to a driver. ++ */ ++static inline bool phydev_id_compare(struct phy_device *phydev, u32 id) ++{ ++ return phy_id_compare(id, phydev->phy_id, phydev->drv->phy_id_mask); ++} ++ + /* A Structure for boards to register fixups with the PHY Lib */ + struct phy_fixup { + struct list_head list; +-- +cgit v1.2.3 + + +From 85cb27e836df0c37ca6e5fab7b20ab1106478956 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Tue, 17 Dec 2019 15:21:50 +0000 +Subject: dt-bindings: net: add dt bindings for marvell10g driver + +Add a DT bindings document for the Marvell 10G driver, which will +augment the generic ethernet PHY binding by having LED mode +configuration. + +Signed-off-by: Russell King +--- + .../devicetree/bindings/net/marvell,10g.yaml | 35 ++++++++++++++++++++++ + 1 file changed, 35 insertions(+) + create mode 100644 Documentation/devicetree/bindings/net/marvell,10g.yaml + +diff --git a/Documentation/devicetree/bindings/net/marvell,10g.yaml b/Documentation/devicetree/bindings/net/marvell,10g.yaml +new file mode 100644 +index 000000000000..93d2c8d7e759 +--- /dev/null ++++ b/Documentation/devicetree/bindings/net/marvell,10g.yaml +@@ -0,0 +1,35 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/net/marvell,10g.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Marvell Alaska X family Ethernet PHYs ++ ++maintainers: ++ - Russell King ++ ++allOf: ++ - $ref: ethernet-phy.yaml# ++ ++properties: ++ marvell,led-mode: ++ description: | ++ An array of one to four 16-bit integers to write to the PHY LED ++ configuration registers. ++ allOf: ++ - $ref: /schemas/types.yaml#/definitions/uint16-array ++ - minItems: 1 ++ maxItems: 4 ++ ++examples: ++ - | ++ mdio { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ethernet-phy@0 { ++ reg = <0>; ++ compatible = "ethernet-phy-ieee802.3-c45"; ++ marvell,led-mode = /bits/ 16 <0x0129 0x095d 0x0855>; ++ }; ++ }; +-- +cgit v1.2.3 + + +From 9b8ec077ca9d8bbb83c7e4c19f631596433f3cdf Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Sun, 1 Dec 2019 15:33:39 +0000 +Subject: net: phy: marvell10g: add support for configuring LEDs + +Add support for configuring the LEDs. Macchiatobin has an oddity in +that the left LED goes out when the cable is connected, and flashes +when there's link activity. This is because the reset default for +the LED outputs assume that the LED is connected to supply, not to +ground. Add support for configuring the LED modes and polarities. + +Signed-off-by: Russell King +--- + drivers/net/phy/marvell10g.c | 62 +++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 55 insertions(+), 7 deletions(-) + +diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c +index eff980b94c95..9eece4f5fb4a 100644 +--- a/drivers/net/phy/marvell10g.c ++++ b/drivers/net/phy/marvell10g.c +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -146,6 +147,8 @@ struct mv3310_priv { + + struct device *hwmon_dev; + char *hwmon_name; ++ u8 num_leds; ++ u16 led_mode[4]; + }; + + static const struct mv3310_chip *to_mv3310_chip(struct phy_device *phydev) +@@ -468,6 +471,43 @@ static const struct sfp_upstream_ops mv3310_sfp_ops = { + .module_insert = mv3310_sfp_insert, + }; + ++static int mv3310_leds_write(struct phy_device *phydev) ++{ ++ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); ++ int i, ret; ++ ++ for (i = 0; i < priv->num_leds; i++) { ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, 0xf020 + i, ++ priv->led_mode[i]); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int mv3310_fw_config(struct phy_device *phydev) ++{ ++ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); ++ struct device_node *node; ++ int ret; ++ ++ node = phydev->mdio.dev.of_node; ++ if (!node) ++ return 0; ++ ++ ret = of_property_read_variable_u16_array(node, "marvell,led-mode", ++ priv->led_mode, 1, ARRAY_SIZE(priv->led_mode)); ++ if (ret == -EINVAL) ++ ret = 0; ++ if (ret < 0) ++ return ret; ++ ++ priv->num_leds = ret; ++ ++ return 0; ++} ++ + static int mv3310_probe(struct phy_device *phydev) + { + const struct mv3310_chip *chip = to_mv3310_chip(phydev); +@@ -479,6 +519,20 @@ static int mv3310_probe(struct phy_device *phydev) + (phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask) + return -ENODEV; + ++ priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ dev_set_drvdata(&phydev->mdio.dev, priv); ++ ++ ret = mv3310_fw_config(phydev); ++ if (ret < 0) ++ return ret; ++ ++ ret = mv3310_leds_write(phydev); ++ if (ret < 0) ++ return ret; ++ + ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_BOOT); + if (ret < 0) + return ret; +@@ -489,12 +543,6 @@ static int mv3310_probe(struct phy_device *phydev) + return -ENODEV; + } + +- priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- dev_set_drvdata(&phydev->mdio.dev, priv); +- + ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_FW_VER0); + if (ret < 0) + return ret; +@@ -691,7 +739,7 @@ static int mv3310_config_init(struct phy_device *phydev) + if (err && err != -EOPNOTSUPP) + return err; + +- return 0; ++ return mv3310_leds_write(phydev); + } + + static int mv3310_get_features(struct phy_device *phydev) +-- +cgit v1.2.3 + + +From 6e562bcf785d136cf57cdb996c591a058ca58fd9 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Sun, 1 Dec 2019 15:35:08 +0000 +Subject: arm64: dts: configure Macchiatobin 10G PHY LED modes + +Configure the Macchiatobin 10G PHY LED modes to correct their polarity. +We keep the existing LED behaviours, but switch their polarity to +reflect how they are connected. Tweak the LED modes as well to be: + +left: off = no link + solid green = RJ45 link up (not SFP+ cage) + flash green = traffic +right: off = no link + solid green = 10G + solid yellow = 1G + flash green = 100M + flash yellow = 10M + +Signed-off-by: Russell King +--- + arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts b/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts +index 1766cf58101b..68c27f22ff57 100644 +--- a/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts ++++ b/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts +@@ -21,12 +21,14 @@ + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <0>; + sfp = <&sfp_eth0>; ++ marvell,led-mode = /bits/ 16 <0x0129 0x095d 0x0855>; + }; + + phy8: ethernet-phy@8 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <8>; + sfp = <&sfp_eth1>; ++ marvell,led-mode = /bits/ 16 <0x0129 0x095d 0x0855>; + }; + }; + +-- +cgit v1.2.3 + + +From d9598632d7bbea77eeaa2854f2410e332b1a2845 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Fri, 3 Jan 2020 23:13:28 +0000 +Subject: net: phy: add resolved pause support + +Allow phylib drivers to pass the hardware-resolved pause state to MAC +drivers, rather than using the software-based pause resolution code. + +Signed-off-by: Russell King +--- + drivers/net/phy/phy_device.c | 6 ++++++ + include/linux/phy.h | 9 +++++++++ + 2 files changed, 15 insertions(+) + +diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c +index 67593cf47bf0..7317539a78da 100644 +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -2785,6 +2785,12 @@ void phy_get_pause(struct phy_device *phydev, bool *tx_pause, bool *rx_pause) + return; + } + ++ if (phydev->resolved_pause_valid) { ++ *tx_pause = phydev->resolved_tx_pause; ++ *rx_pause = phydev->resolved_rx_pause; ++ return; ++ } ++ + return linkmode_resolve_pause(phydev->advertising, + phydev->lp_advertising, + tx_pause, rx_pause); +diff --git a/include/linux/phy.h b/include/linux/phy.h +index a9d22716b16b..8e5306ce68a1 100644 +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -630,6 +630,15 @@ struct phy_device { + u8 master_slave_set; + u8 master_slave_state; + ++ /* ++ * private to phylib: the resolved pause state - only valid if ++ * resolved_pause_valid is true. only phy drivers and phylib ++ * should touch this. ++ */ ++ bool resolved_pause_valid; ++ bool resolved_tx_pause; ++ bool resolved_rx_pause; ++ + /* Union of PHY and Attached devices' supported link modes */ + /* See ethtool.h for more info */ + __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); +-- +cgit v1.2.3 + + +From f1526441e47d215ab14b594efd60a7f547b93a79 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Sat, 4 Jan 2020 00:41:35 +0000 +Subject: net: phy: marvell*: add support for hw resolved pause modes + +Support reporting the hardware resolved pause enablement states via +phylib, overriding our software implementation. + +Signed-off-by: Russell King +--- + drivers/net/phy/marvell.c | 45 ++++++++++++++++++++++++++++++++++++++------ + drivers/net/phy/marvell10g.c | 6 ++++++ + 2 files changed, 45 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c +index 3de93c9f2744..4509b5b3ff91 100644 +--- a/drivers/net/phy/marvell.c ++++ b/drivers/net/phy/marvell.c +@@ -169,6 +169,10 @@ + #define MII_M1011_PHY_STATUS_FULLDUPLEX 0x2000 + #define MII_M1011_PHY_STATUS_RESOLVED 0x0800 + #define MII_M1011_PHY_STATUS_LINK 0x0400 ++#define MII_M1111_PHY_STATUS_TX_PAUSE 0x0008 ++#define MII_M1111_PHY_STATUS_RX_PAUSE 0x0004 ++#define MII_88E151X_PHY_STATUS_TX_PAUSE 0x0200 ++#define MII_88E151X_PHY_STATUS_RX_PAUSE 0x0100 + + #define MII_88E3016_PHY_SPEC_CTRL 0x10 + #define MII_88E3016_DISABLE_SCRAMBLER 0x0200 +@@ -294,6 +298,8 @@ struct marvell_priv { + u32 last; + u32 step; + s8 pair; ++ u16 tx_pause_mask; ++ u16 rx_pause_mask; + }; + + static int marvell_read_page(struct phy_device *phydev) +@@ -1509,6 +1515,7 @@ static void fiber_lpa_mod_linkmode_lpa_t(unsigned long *advertising, u32 lpa) + static int marvell_read_status_page_an(struct phy_device *phydev, + int fiber, int status) + { ++ struct marvell_priv *priv = phydev->priv; + int lpa; + int err; + +@@ -1564,6 +1571,11 @@ static int marvell_read_status_page_an(struct phy_device *phydev, + } + } + ++ phydev->resolved_tx_pause = !!(status & priv->tx_pause_mask); ++ phydev->resolved_rx_pause = !!(status & priv->rx_pause_mask); ++ phydev->resolved_pause_valid = !fiber && priv->tx_pause_mask && ++ priv->rx_pause_mask; ++ + return 0; + } + +@@ -1607,6 +1619,7 @@ static int marvell_read_status_page(struct phy_device *phydev, int page) + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + phydev->port = fiber ? PORT_FIBRE : PORT_TP; ++ phydev->resolved_pause_valid = false; + + if (phydev->autoneg == AUTONEG_ENABLE) + err = marvell_read_status_page_an(phydev, fiber, status); +@@ -2688,7 +2701,8 @@ static int marvell_hwmon_probe(struct phy_device *phydev) + } + #endif + +-static int marvell_probe(struct phy_device *phydev) ++static int marvell_probe_pause(struct phy_device *phydev, u16 tx_pause_mask, ++ u16 rx_pause_mask) + { + struct marvell_priv *priv; + +@@ -2696,11 +2710,30 @@ static int marvell_probe(struct phy_device *phydev) + if (!priv) + return -ENOMEM; + ++ priv->tx_pause_mask = tx_pause_mask; ++ priv->rx_pause_mask = rx_pause_mask; + phydev->priv = priv; + + return marvell_hwmon_probe(phydev); + } + ++static int marvell_probe(struct phy_device *phydev) ++{ ++ return marvell_probe_pause(phydev, 0, 0); ++} ++ ++static int m88e1111_probe(struct phy_device *phydev) ++{ ++ return marvell_probe_pause(phydev, MII_M1111_PHY_STATUS_TX_PAUSE, ++ MII_M1111_PHY_STATUS_RX_PAUSE); ++} ++ ++static int m88e1510_probe(struct phy_device *phydev) ++{ ++ return marvell_probe_pause(phydev, MII_88E151X_PHY_STATUS_TX_PAUSE, ++ MII_88E151X_PHY_STATUS_RX_PAUSE); ++} ++ + static struct phy_driver marvell_drivers[] = { + { + .phy_id = MARVELL_PHY_ID_88E1101, +@@ -2745,7 +2778,7 @@ static struct phy_driver marvell_drivers[] = { + .phy_id_mask = MARVELL_PHY_ID_MASK, + .name = "Marvell 88E1111", + /* PHY_GBIT_FEATURES */ +- .probe = marvell_probe, ++ .probe = m88e1111_probe, + .config_init = m88e1111gbe_config_init, + .config_aneg = m88e1111_config_aneg, + .read_status = marvell_read_status, +@@ -2927,7 +2960,7 @@ static struct phy_driver marvell_drivers[] = { + .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops), + .features = PHY_GBIT_FIBRE_FEATURES, + .flags = PHY_POLL_CABLE_TEST, +- .probe = marvell_probe, ++ .probe = m88e1510_probe, + .config_init = m88e1510_config_init, + .config_aneg = m88e1510_config_aneg, + .read_status = marvell_read_status, +@@ -2956,7 +2989,7 @@ static struct phy_driver marvell_drivers[] = { + .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops), + /* PHY_GBIT_FEATURES */ + .flags = PHY_POLL_CABLE_TEST, +- .probe = marvell_probe, ++ .probe = m88e1510_probe, + .config_init = marvell_1011gbe_config_init, + .config_aneg = m88e1510_config_aneg, + .read_status = marvell_read_status, +@@ -2980,7 +3013,7 @@ static struct phy_driver marvell_drivers[] = { + .phy_id_mask = MARVELL_PHY_ID_MASK, + .name = "Marvell 88E1545", + .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops), +- .probe = marvell_probe, ++ .probe = m88e1510_probe, + /* PHY_GBIT_FEATURES */ + .flags = PHY_POLL_CABLE_TEST, + .config_init = marvell_1011gbe_config_init, +@@ -3027,7 +3060,7 @@ static struct phy_driver marvell_drivers[] = { + .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops), + /* PHY_GBIT_FEATURES */ + .flags = PHY_POLL_CABLE_TEST, +- .probe = marvell_probe, ++ .probe = m88e1510_probe, + .config_init = marvell_1011gbe_config_init, + .config_aneg = m88e6390_config_aneg, + .read_status = marvell_read_status, +diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c +index 9eece4f5fb4a..6fd63044daf5 100644 +--- a/drivers/net/phy/marvell10g.c ++++ b/drivers/net/phy/marvell10g.c +@@ -82,6 +82,8 @@ enum { + MV_PCS_CSSR1_SPD1_10 = 0x0000, + MV_PCS_CSSR1_DUPLEX_FULL= BIT(13), + MV_PCS_CSSR1_RESOLVED = BIT(11), ++ MV_PCS_CSSR1_TX_PAUSE = BIT(9), ++ MV_PCS_CSSR1_RX_PAUSE = BIT(8), + MV_PCS_CSSR1_MDIX = BIT(6), + MV_PCS_CSSR1_SPD2_MASK = 0x000c, + MV_PCS_CSSR1_SPD2_5000 = 0x0008, +@@ -957,6 +959,10 @@ static int mv3310_read_status_copper(struct phy_device *phydev) + phydev->mdix = cssr1 & MV_PCS_CSSR1_MDIX ? + ETH_TP_MDI_X : ETH_TP_MDI; + ++ phydev->resolved_tx_pause = !!(cssr1 & MV_PCS_CSSR1_TX_PAUSE); ++ phydev->resolved_rx_pause = !!(cssr1 & MV_PCS_CSSR1_RX_PAUSE); ++ phydev->resolved_pause_valid = true; ++ + if (val & MDIO_AN_STAT1_COMPLETE) { + val = genphy_c45_read_lpa(phydev); + if (val < 0) +-- +cgit v1.2.3 + + +From b6fcbbc99c55ca450563af6d24826d1aaca0fddf Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Mon, 26 Aug 2019 12:19:35 +0100 +Subject: net: phy: provide phy driver start/stop hooks + +Provide phy driver start/stop hooks so that the PHY driver knows when +the network driver is starting or stopping. This will be used for the +Marvell 10G driver so that we can sanely refuse to start if the PHYs +firmware is not present, and also so that we can sanely support SFPs +behind the PHY. + +Signed-off-by: Russell King +--- + drivers/net/phy/phy.c | 5 +++++ + include/linux/phy.h | 3 +++ + 2 files changed, 8 insertions(+) + +diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c +index 8eeb26d8aeb7..abf58a152fc3 100644 +--- a/drivers/net/phy/phy.c ++++ b/drivers/net/phy/phy.c +@@ -1024,6 +1024,8 @@ void phy_stop(struct phy_device *phydev) + sfp_upstream_stop(phydev->sfp_bus); + + phydev->state = PHY_HALTED; ++ if (phydev->drv->stop) ++ phydev->drv->stop(phydev); + + mutex_unlock(&phydev->lock); + +@@ -1057,6 +1059,9 @@ void phy_start(struct phy_device *phydev) + goto out; + } + ++ if (phydev->drv->start && phydev->drv->start(phydev)) ++ goto out; ++ + if (phydev->sfp_bus) + sfp_upstream_start(phydev->sfp_bus); + +diff --git a/include/linux/phy.h b/include/linux/phy.h +index 8e5306ce68a1..92e174a35fde 100644 +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -788,6 +788,9 @@ struct phy_driver { + /** @resume: Resume the hardware, restoring state if needed */ + int (*resume)(struct phy_device *phydev); + ++ int (*start)(struct phy_device *phydev); ++ void (*stop)(struct phy_device *phydev); ++ + /** + * @config_aneg: Configures the advertisement and resets + * autonegotiation if phydev->autoneg is on, +-- +cgit v1.2.3 + + +From f896f17eb7d719e4420d0f9f79a9c091b04e7b46 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Wed, 5 Jun 2019 11:44:55 +0100 +Subject: net: phy: marvell10g: allow PHY to probe without firmware + +Allow the PHY to probe when there is no firmware, but do not allow the +link to come up by forcing the PHY state to PHY_HALTED in a similar way +to phy_error(). + +Signed-off-by: Russell King +--- + drivers/net/phy/marvell10g.c | 18 +++++++++++++++++- + 1 file changed, 17 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c +index 6fd63044daf5..57ba639c7a2f 100644 +--- a/drivers/net/phy/marvell10g.c ++++ b/drivers/net/phy/marvell10g.c +@@ -146,6 +146,7 @@ struct mv3310_priv { + bool has_downshift; + bool rate_match; + phy_interface_t const_interface; ++ bool firmware_failed; + + struct device *hwmon_dev; + char *hwmon_name; +@@ -542,7 +543,7 @@ static int mv3310_probe(struct phy_device *phydev) + if (ret & MV_PMA_BOOT_FATAL) { + dev_warn(&phydev->mdio.dev, + "PHY failed to boot firmware, status=%04x\n", ret); +- return -ENODEV; ++ priv->firmware_failed = true; + } + + ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_FW_VER0); +@@ -599,6 +600,19 @@ static int mv3310_resume(struct phy_device *phydev) + return mv3310_hwmon_config(phydev, true); + } + ++static int mv3310_start(struct phy_device *phydev) ++{ ++ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); ++ ++ if (priv->firmware_failed) { ++ dev_warn(&phydev->mdio.dev, ++ "PHY firmware failure: PHY not starting"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ + /* Some PHYs in the Alaska family such as the 88X3310 and the 88E2010 + * don't set bit 14 in PMA Extended Abilities (1.11), although they do + * support 2.5GBASET and 5GBASET. For these models, we can still read their +@@ -1205,6 +1219,7 @@ static struct phy_driver mv3310_drivers[] = { + .probe = mv3310_probe, + .suspend = mv3310_suspend, + .resume = mv3310_resume, ++ .start = mv3310_start, + .config_aneg = mv3310_config_aneg, + .aneg_done = mv3310_aneg_done, + .read_status = mv3310_read_status, +@@ -1240,6 +1255,7 @@ static struct phy_driver mv3310_drivers[] = { + .probe = mv3310_probe, + .suspend = mv3310_suspend, + .resume = mv3310_resume, ++ .start = mv3310_start, + .config_init = mv3310_config_init, + .config_aneg = mv3310_config_aneg, + .aneg_done = mv3310_aneg_done, +-- +cgit v1.2.3 + + +From a8ba15f31356f0044f264d677d7f2a56b8599f8e Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Tue, 5 Nov 2019 10:34:39 +0000 +Subject: net: phy: make phy_error() report which PHY has failed + +phy_error() is called from phy_interrupt() or phy_state_machine(), and +uses WARN_ON() to print a backtrace. The backtrace is not useful when +reporting a PHY error. + +However, a system may contain multiple ethernet PHYs, and phy_error() +gives no clue which one caused the problem. + +Replace WARN_ON() with a call to phydev_err() so that we can see which +PHY had an error, and also inform the user that we are halting the PHY. + +Signed-off-by: Russell King +--- + drivers/net/phy/phy.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c +index abf58a152fc3..db42a330f099 100644 +--- a/drivers/net/phy/phy.c ++++ b/drivers/net/phy/phy.c +@@ -912,7 +912,7 @@ void phy_stop_machine(struct phy_device *phydev) + */ + void phy_error(struct phy_device *phydev) + { +- WARN_ON(1); ++ phydev_err(phydev, "Error detected, halting PHY\n"); + + mutex_lock(&phydev->lock); + phydev->state = PHY_HALTED; +-- +cgit v1.2.3 + + +From 135897c1f6ce9b35bb564036f6741313976ddd8f Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Fri, 18 Oct 2019 10:37:44 +0100 +Subject: net: sfp: add support for cooled SFP+ transceivers + +Cooled SFP+ transceivers need a longer initialisation (startup) time. +Select the initialisation time depending on the cooled option bit. + +Signed-off-by: Russell King +--- + drivers/net/phy/sfp.c | 15 +++++++++------ + 1 file changed, 9 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c +index 34e90216bd2c..21aefefd19a1 100644 +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -168,6 +168,7 @@ static const enum gpiod_flags gpio_flags[] = { + #define T_WAIT msecs_to_jiffies(50) + #define T_START_UP msecs_to_jiffies(300) + #define T_START_UP_BAD_GPON msecs_to_jiffies(60000) ++#define T_START_UP_COOLED msecs_to_jiffies(90000) + + /* t_reset is the time required to assert the TX_DISABLE signal to reset + * an indicated TX_FAULT. +@@ -1925,6 +1926,8 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) + if (!memcmp(id.base.vendor_name, "ALCATELLUCENT ", 16) && + !memcmp(id.base.vendor_pn, "3FE46541AA ", 16)) + sfp->module_t_start_up = T_START_UP_BAD_GPON; ++ else if (id.ext.options & cpu_to_be16(SFP_OPTIONS_COOLED_XCVR)) ++ sfp->module_t_start_up = T_START_UP_COOLED; + else + sfp->module_t_start_up = T_START_UP; + +@@ -2131,10 +2134,10 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event) + break; + + if (sfp->state & SFP_F_TX_FAULT) { +- /* Wait up to t_init (SFF-8472) or t_start_up (SFF-8431) +- * from the TX_DISABLE deassertion for the module to +- * initialise, which is indicated by TX_FAULT +- * deasserting. ++ /* Wait up to t_init (SFF-8472), t_start_up (SFF-8431), ++ * or t_start_up_cooled (SFF-8431) from the TX_DISABLE ++ * deassertion for the module to initialise, which is ++ * indicated by TX_FAULT deasserting. + */ + timeout = sfp->module_t_start_up; + if (timeout > T_WAIT) +@@ -2153,8 +2156,8 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event) + + case SFP_S_INIT: + if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) { +- /* TX_FAULT is still asserted after t_init +- * or t_start_up, so assume there is a fault. ++ /* TX_FAULT is still asserted after t_init, t_start_up ++ * or t_start_up_cooled, so assume there is a fault. + */ + sfp_sm_fault(sfp, SFP_S_INIT_TX_FAULT, + sfp->sm_fault_retries == N_FAULT_INIT); +-- +cgit v1.2.3 + + +From 1fa01bc40869fae288b98ed21ec4cf25e9d70e87 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Fri, 14 Apr 2017 20:17:13 +0100 +Subject: net: sfp: add sfp+ compatible [*not for mainline*] + +Add a compatible for SFP+ cages. SFP+ cages are backwards compatible, +but the ethernet device behind them may not support the slower speeds +of SFP modules. + +Signed-off-by: Russell King +--- + drivers/net/phy/sfp.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c +index 21aefefd19a1..f2484197cbbd 100644 +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -304,6 +304,7 @@ static const struct sff_data sfp_data = { + static const struct of_device_id sfp_of_match[] = { + { .compatible = "sff,sff", .data = &sff_data, }, + { .compatible = "sff,sfp", .data = &sfp_data, }, ++ { .compatible = "sff,sfp+", .data = &sfp_data, }, + { }, + }; + MODULE_DEVICE_TABLE(of, sfp_of_match); +-- +cgit v1.2.3 + + +From 74505eaba6278e9e554e9d7fd42e7bd720477b35 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Sun, 13 Sep 2015 01:06:31 +0100 +Subject: net: sfp: display SFP module information [*not for mainline*] + +Display SFP module information verbosely, splitting the generic parts +into a separate file. + +Signed-off-by: Russell King +--- + drivers/net/phy/Makefile | 2 +- + drivers/net/phy/sff.c | 114 ++++++++++++++++++++++++ + drivers/net/phy/sff.h | 16 ++++ + drivers/net/phy/sfp.c | 228 ++++++++++++++++++++++++++++++++++++++++++++--- + 4 files changed, 349 insertions(+), 11 deletions(-) + create mode 100644 drivers/net/phy/sff.c + create mode 100644 drivers/net/phy/sff.h + +diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile +index 172bb193ae6a..93428ab6d5f3 100644 +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -26,7 +26,7 @@ obj-$(CONFIG_PHYLIB) += libphy.o + + obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += mii_timestamper.o + +-obj-$(CONFIG_SFP) += sfp.o ++obj-$(CONFIG_SFP) += sff.o sfp.o + sfp-obj-$(CONFIG_SFP) += sfp-bus.o + obj-y += $(sfp-obj-y) $(sfp-obj-m) + +diff --git a/drivers/net/phy/sff.c b/drivers/net/phy/sff.c +new file mode 100644 +index 000000000000..a2eb56118dd4 +--- /dev/null ++++ b/drivers/net/phy/sff.c +@@ -0,0 +1,114 @@ ++#include ++#include ++#include "sff.h" ++ ++const char *sff_link_len(char *buf, size_t size, unsigned int length, ++ unsigned int multiplier) ++{ ++ if (length == 0) ++ return "unsupported/unspecified"; ++ ++ if (length == 255) { ++ *buf++ = '>'; ++ size -= 1; ++ length -= 1; ++ } ++ ++ length *= multiplier; ++ ++ if (length >= 1000) ++ snprintf(buf, size, "%u.%0*ukm", ++ length / 1000, ++ multiplier > 100 ? 1 : ++ multiplier > 10 ? 2 : 3, ++ length % 1000); ++ else ++ snprintf(buf, size, "%um", length); ++ ++ return buf; ++} ++EXPORT_SYMBOL_GPL(sff_link_len); ++ ++const char *sff_bitfield(char *buf, size_t size, ++ const struct sff_bitfield *bits, unsigned int val) ++{ ++ char *p = buf; ++ int n; ++ ++ *p = '\0'; ++ while (bits->mask) { ++ if ((val & bits->mask) == bits->val) { ++ n = snprintf(p, size, "%s%s", ++ buf != p ? ", " : "", ++ bits->str); ++ if (n == size) ++ break; ++ p += n; ++ size -= n; ++ } ++ bits++; ++ } ++ ++ return buf; ++} ++EXPORT_SYMBOL_GPL(sff_bitfield); ++ ++const char *sff_connector(unsigned int connector) ++{ ++ switch (connector) { ++ case SFF8024_CONNECTOR_UNSPEC: ++ return "unknown/unspecified"; ++ case SFF8024_CONNECTOR_SC: ++ return "SC"; ++ case SFF8024_CONNECTOR_FIBERJACK: ++ return "Fiberjack"; ++ case SFF8024_CONNECTOR_LC: ++ return "LC"; ++ case SFF8024_CONNECTOR_MT_RJ: ++ return "MT-RJ"; ++ case SFF8024_CONNECTOR_MU: ++ return "MU"; ++ case SFF8024_CONNECTOR_SG: ++ return "SG"; ++ case SFF8024_CONNECTOR_OPTICAL_PIGTAIL: ++ return "Optical pigtail"; ++ case SFF8024_CONNECTOR_MPO_1X12: ++ return "MPO 1X12"; ++ case SFF8024_CONNECTOR_MPO_2X16: ++ return "MPO 2X16"; ++ case SFF8024_CONNECTOR_HSSDC_II: ++ return "HSSDC II"; ++ case SFF8024_CONNECTOR_COPPER_PIGTAIL: ++ return "Copper pigtail"; ++ case SFF8024_CONNECTOR_RJ45: ++ return "RJ45"; ++ case SFF8024_CONNECTOR_MXC_2X16: ++ return "MXC 2X16"; ++ default: ++ return "unknown"; ++ } ++} ++EXPORT_SYMBOL_GPL(sff_connector); ++ ++const char *sff_encoding(unsigned int encoding) ++{ ++ switch (encoding) { ++ case SFF8024_ENCODING_UNSPEC: ++ return "unspecified"; ++ case SFF8024_ENCODING_8472_64B66B: ++ return "64b66b"; ++ case SFF8024_ENCODING_8B10B: ++ return "8b10b"; ++ case SFF8024_ENCODING_4B5B: ++ return "4b5b"; ++ case SFF8024_ENCODING_NRZ: ++ return "NRZ"; ++ case SFF8024_ENCODING_8472_MANCHESTER: ++ return "MANCHESTER"; ++ default: ++ return "unknown"; ++ } ++} ++EXPORT_SYMBOL_GPL(sff_encoding); ++ ++MODULE_LICENSE("GPL"); +diff --git a/drivers/net/phy/sff.h b/drivers/net/phy/sff.h +new file mode 100644 +index 000000000000..cd7bb7c7ae4a +--- /dev/null ++++ b/drivers/net/phy/sff.h +@@ -0,0 +1,16 @@ ++#ifndef SFF_H ++#define SFF_H ++ ++struct sff_bitfield { ++ unsigned int mask; ++ unsigned int val; ++ const char *str; ++}; ++ ++const char *sff_link_len(char *buf, size_t size, unsigned int length, ++ unsigned int multiplier); ++const char *sff_bitfield(char *buf, size_t size, ++ const struct sff_bitfield *bits, unsigned int val); ++const char *sff_connector(unsigned int connector); ++const char *sff_encoding(unsigned int encoding); ++#endif +diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c +index f2484197cbbd..4637e3769ab2 100644 +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -18,6 +18,7 @@ + #include + #include + ++#include "sff.h" + #include "sfp.h" + #include "swphy.h" + +@@ -1398,6 +1399,114 @@ static void sfp_hwmon_exit(struct sfp *sfp) + } + #endif + ++static const struct sff_bitfield sfp_options[] = { ++ { ++ .mask = SFP_OPTIONS_HIGH_POWER_LEVEL, ++ .val = SFP_OPTIONS_HIGH_POWER_LEVEL, ++ .str = "hpl", ++ }, { ++ .mask = SFP_OPTIONS_PAGING_A2, ++ .val = SFP_OPTIONS_PAGING_A2, ++ .str = "paginga2", ++ }, { ++ .mask = SFP_OPTIONS_RETIMER, ++ .val = SFP_OPTIONS_RETIMER, ++ .str = "retimer", ++ }, { ++ .mask = SFP_OPTIONS_COOLED_XCVR, ++ .val = SFP_OPTIONS_COOLED_XCVR, ++ .str = "cooled", ++ }, { ++ .mask = SFP_OPTIONS_POWER_DECL, ++ .val = SFP_OPTIONS_POWER_DECL, ++ .str = "powerdecl", ++ }, { ++ .mask = SFP_OPTIONS_RX_LINEAR_OUT, ++ .val = SFP_OPTIONS_RX_LINEAR_OUT, ++ .str = "rxlinear", ++ }, { ++ .mask = SFP_OPTIONS_RX_DECISION_THRESH, ++ .val = SFP_OPTIONS_RX_DECISION_THRESH, ++ .str = "rxthresh", ++ }, { ++ .mask = SFP_OPTIONS_TUNABLE_TX, ++ .val = SFP_OPTIONS_TUNABLE_TX, ++ .str = "tunabletx", ++ }, { ++ .mask = SFP_OPTIONS_RATE_SELECT, ++ .val = SFP_OPTIONS_RATE_SELECT, ++ .str = "ratesel", ++ }, { ++ .mask = SFP_OPTIONS_TX_DISABLE, ++ .val = SFP_OPTIONS_TX_DISABLE, ++ .str = "txdisable", ++ }, { ++ .mask = SFP_OPTIONS_TX_FAULT, ++ .val = SFP_OPTIONS_TX_FAULT, ++ .str = "txfault", ++ }, { ++ .mask = SFP_OPTIONS_LOS_INVERTED, ++ .val = SFP_OPTIONS_LOS_INVERTED, ++ .str = "los-", ++ }, { ++ .mask = SFP_OPTIONS_LOS_NORMAL, ++ .val = SFP_OPTIONS_LOS_NORMAL, ++ .str = "los+", ++ }, { } ++}; ++ ++static const struct sff_bitfield diagmon[] = { ++ { ++ .mask = SFP_DIAGMON_DDM, ++ .val = SFP_DIAGMON_DDM, ++ .str = "ddm", ++ }, { ++ .mask = SFP_DIAGMON_INT_CAL, ++ .val = SFP_DIAGMON_INT_CAL, ++ .str = "intcal", ++ }, { ++ .mask = SFP_DIAGMON_EXT_CAL, ++ .val = SFP_DIAGMON_EXT_CAL, ++ .str = "extcal", ++ }, { ++ .mask = SFP_DIAGMON_RXPWR_AVG, ++ .val = SFP_DIAGMON_RXPWR_AVG, ++ .str = "rxpwravg", ++ }, { } ++}; ++ ++static const struct sff_bitfield sfp_enhopts[] = { ++ { ++ .mask = SFP_ENHOPTS_ALARMWARN, ++ .val = SFP_ENHOPTS_ALARMWARN, ++ .str = "alarmwarn", ++ }, { ++ .mask = SFP_ENHOPTS_SOFT_TX_DISABLE, ++ .val = SFP_ENHOPTS_SOFT_TX_DISABLE, ++ .str = "soft_tx_dis", ++ }, { ++ .mask = SFP_ENHOPTS_SOFT_TX_FAULT, ++ .val = SFP_ENHOPTS_SOFT_TX_FAULT, ++ .str = "soft_tx_fault", ++ }, { ++ .mask = SFP_ENHOPTS_SOFT_RX_LOS, ++ .val = SFP_ENHOPTS_SOFT_RX_LOS, ++ .str = "soft_rx_los", ++ }, { ++ .mask = SFP_ENHOPTS_SOFT_RATE_SELECT, ++ .val = SFP_ENHOPTS_SOFT_RATE_SELECT, ++ .str = "soft_rs", ++ }, { ++ .mask = SFP_ENHOPTS_APP_SELECT_SFF8079, ++ .val = SFP_ENHOPTS_APP_SELECT_SFF8079, ++ .str = "app_sel", ++ }, { ++ .mask = SFP_ENHOPTS_SOFT_RATE_SFF8431, ++ .val = SFP_ENHOPTS_SOFT_RATE_SFF8431, ++ .str = "soft_r8431", ++ }, { } ++}; ++ + /* Helpers */ + static void sfp_module_tx_disable(struct sfp *sfp) + { +@@ -1781,6 +1890,110 @@ static int sfp_cotsworks_fixup_check(struct sfp *sfp, struct sfp_eeprom_id *id) + return 0; + } + ++static void sfp_print_module_info(struct sfp *sfp, const struct sfp_eeprom_id *id, bool cotsworks) ++{ ++ unsigned int br_nom, br_min, br_max; ++ char date[9]; ++ char options[80]; ++ ++ /* Cotsworks also gets the date code wrong. */ ++ date[0] = id->ext.datecode[4 - 2 * cotsworks]; ++ date[1] = id->ext.datecode[5 - 2 * cotsworks]; ++ date[2] = '-'; ++ date[3] = id->ext.datecode[2 + 2 * cotsworks]; ++ date[4] = id->ext.datecode[3 + 2 * cotsworks]; ++ date[5] = '-'; ++ date[6] = id->ext.datecode[0]; ++ date[7] = id->ext.datecode[1]; ++ date[8] = '\0'; ++ ++ if (id->base.br_nominal == 0) { ++ br_min = br_nom = br_max = 0; ++ } else if (id->base.br_nominal == 255) { ++ br_nom = 250 * id->ext.br_max; ++ br_max = br_nom + br_nom * id->ext.br_min / 100; ++ br_min = br_nom - br_nom * id->ext.br_min / 100; ++ } else { ++ br_nom = id->base.br_nominal * 100; ++ br_min = br_nom - id->base.br_nominal * id->ext.br_min; ++ br_max = br_nom + id->base.br_nominal * id->ext.br_max; ++ } ++ ++ dev_info(sfp->dev, "module %.*s %.*s rev %.*s sn %.*s dc %s\n", ++ (int)sizeof(id->base.vendor_name), id->base.vendor_name, ++ (int)sizeof(id->base.vendor_pn), id->base.vendor_pn, ++ (int)sizeof(id->base.vendor_rev), id->base.vendor_rev, ++ (int)sizeof(id->ext.vendor_sn), id->ext.vendor_sn, date); ++ dev_info(sfp->dev, " %s connector, encoding %s, bitrate %u.%03u (%u.%03u-%u.%03u) Gbps\n", ++ sff_connector(id->base.connector), ++ sff_encoding(id->base.encoding), ++ br_nom / 1000, br_nom % 1000, ++ br_min / 1000, br_min % 1000, br_max / 1000, br_max % 1000); ++ dev_info(sfp->dev, " 1000BaseSX%c 1000BaseLX%c 1000BaseCX%c 1000BaseT%c 100BaseLX%c 100BaseFX%c BaseBX10%c BasePX%c\n", ++ id->base.e1000_base_sx ? '+' : '-', ++ id->base.e1000_base_lx ? '+' : '-', ++ id->base.e1000_base_cx ? '+' : '-', ++ id->base.e1000_base_t ? '+' : '-', ++ id->base.e100_base_lx ? '+' : '-', ++ id->base.e100_base_fx ? '+' : '-', ++ id->base.e_base_bx10 ? '+' : '-', ++ id->base.e_base_px ? '+' : '-'); ++ dev_info(sfp->dev, " 10GBaseSR%c 10GBaseLR%c 10GBaseLRM%c 10GBaseER%c\n", ++ id->base.e10g_base_sr ? '+' : '-', ++ id->base.e10g_base_lr ? '+' : '-', ++ id->base.e10g_base_lrm ? '+' : '-', ++ id->base.e10g_base_er ? '+' : '-'); ++ ++ if (!id->base.sfp_ct_passive && !id->base.sfp_ct_active && ++ !id->base.e1000_base_t) { ++ char len_9um[16], len_om[16]; ++ ++ dev_info(sfp->dev, " Wavelength %unm, fiber lengths:\n", ++ be16_to_cpup(&id->base.optical_wavelength)); ++ ++ if (id->base.link_len[0] == 255) ++ strcpy(len_9um, ">254km"); ++ else if (id->base.link_len[1] && id->base.link_len[1] != 255) ++ sprintf(len_9um, "%um", ++ id->base.link_len[1] * 100); ++ else if (id->base.link_len[0]) ++ sprintf(len_9um, "%ukm", id->base.link_len[0]); ++ else if (id->base.link_len[1] == 255) ++ strcpy(len_9um, ">25.4km"); ++ else ++ strcpy(len_9um, "unsupported"); ++ ++ dev_info(sfp->dev, " 9µm SM : %s\n", len_9um); ++ dev_info(sfp->dev, " 62.5µm MM OM1: %s\n", ++ sff_link_len(len_om, sizeof(len_om), ++ id->base.link_len[3], 10)); ++ dev_info(sfp->dev, " 50µm MM OM2: %s\n", ++ sff_link_len(len_om, sizeof(len_om), ++ id->base.link_len[2], 10)); ++ dev_info(sfp->dev, " 50µm MM OM3: %s\n", ++ sff_link_len(len_om, sizeof(len_om), ++ id->base.link_len[5], 10)); ++ dev_info(sfp->dev, " 50µm MM OM4: %s\n", ++ sff_link_len(len_om, sizeof(len_om), ++ id->base.link_len[4], 10)); ++ } else { ++ char len[16]; ++ dev_info(sfp->dev, " Copper length: %s\n", ++ sff_link_len(len, sizeof(len), ++ id->base.link_len[4], 1)); ++ } ++ ++ dev_info(sfp->dev, " Options: %s\n", ++ sff_bitfield(options, sizeof(options), sfp_options, ++ be16_to_cpu(id->ext.options))); ++ dev_info(sfp->dev, " Diagnostics: %s\n", ++ sff_bitfield(options, sizeof(options), diagmon, ++ id->ext.diagmon)); ++ dev_info(sfp->dev, " EnhOpts: %s\n", ++ sff_bitfield(options, sizeof(options), sfp_enhopts, ++ id->ext.enhopts)); ++} ++ + static int sfp_sm_mod_probe(struct sfp *sfp, bool report) + { + /* SFP module inserted - read I2C data */ +@@ -1835,9 +2048,9 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) + } + } + +- /* Cotsworks do not seem to update the checksums when they +- * do the final programming with the final module part number, +- * serial number and date code. ++ /* Cotsworks do not seem to update the checksums when they update the ++ * module part number, serial number and date code. They also format ++ * the date code incorrectly. + */ + cotsworks = !memcmp(id.base.vendor_name, "COTSWORKS ", 16); + cotsworks_sfbg = !memcmp(id.base.vendor_pn, "SFBG", 4); +@@ -1897,14 +2110,9 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) + } + } + +- sfp->id = id; ++ sfp_print_module_info(sfp, &id, cotsworks); + +- dev_info(sfp->dev, "module %.*s %.*s rev %.*s sn %.*s dc %.*s\n", +- (int)sizeof(id.base.vendor_name), id.base.vendor_name, +- (int)sizeof(id.base.vendor_pn), id.base.vendor_pn, +- (int)sizeof(id.base.vendor_rev), id.base.vendor_rev, +- (int)sizeof(id.ext.vendor_sn), id.ext.vendor_sn, +- (int)sizeof(id.ext.datecode), id.ext.datecode); ++ sfp->id = id; + + /* Check whether we support this module */ + if (!sfp->type->module_supported(&id)) { +-- +cgit v1.2.3 + + +From e16986770e91289be92da37245e9a4bf668f24bd Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Sun, 16 Aug 2020 09:32:18 +0100 +Subject: net: phy: pass supported PHY interface types to phylib + +Pass the supported PHY interface types to phylib so that PHY drivers +can select an appropriate host configuration mode for their interface +according to the host capabilities. + +This is only done for SFP modules presently. + +Signed-off-by: Russell King +--- + drivers/net/phy/phylink.c | 18 ++++++++++++++++++ + include/linux/phy.h | 3 +++ + 2 files changed, 21 insertions(+) + +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index 1454e8d199de..7479040ce98a 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -2152,6 +2152,8 @@ static void phylink_sfp_detach(void *upstream, struct sfp_bus *bus) + pl->netdev->sfp_bus = NULL; + } + ++static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces); ++ + static const phy_interface_t phylink_sfp_interface_preference[] = { + PHY_INTERFACE_MODE_USXGMII, + PHY_INTERFACE_MODE_10GBASER, +@@ -2375,6 +2377,10 @@ static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) + else + mode = MLO_AN_INBAND; + ++ /* Set the PHY's host supported interfaces */ ++ phy_interface_and(phy->host_interfaces, phylink_sfp_interfaces, ++ pl->config->supported_interfaces); ++ + /* Do the initial configuration */ + ret = phylink_sfp_config(pl, mode, phy->supported, phy->advertising); + if (ret < 0) +@@ -2763,4 +2769,16 @@ void phylink_mii_c45_pcs_get_state(struct mdio_device *pcs, + } + EXPORT_SYMBOL_GPL(phylink_mii_c45_pcs_get_state); + ++static int __init phylink_init(void) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(phylink_sfp_interface_preference); i++) ++ set_bit(phylink_sfp_interface_preference[i], ++ phylink_sfp_interfaces); ++ ++ return 0; ++} ++module_init(phylink_init); ++ + MODULE_LICENSE("GPL v2"); +diff --git a/include/linux/phy.h b/include/linux/phy.h +index 92e174a35fde..ef3d16e91ae1 100644 +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -647,6 +647,9 @@ struct phy_device { + /* used with phy_speed_down */ + __ETHTOOL_DECLARE_LINK_MODE_MASK(adv_old); + ++ /* supported PHY interface types */ ++ DECLARE_PHY_INTERFACE_MASK(host_interfaces); ++ + /* Energy efficient ethernet modes which should be prohibited */ + u32 eee_broken_modes; + +-- +cgit v1.2.3 + + +From 5f39ed83ac1970d7c644cd7d31dba77315b30d38 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Sun, 16 Aug 2020 09:32:18 +0100 +Subject: net: phy: marvell10g: select host interface configuration + +Select the host interface configuration according to the capabilities +of the host; this allows the kernel to support SFP modules using the +88x3310. + +Signed-off-by: Russell King +--- + drivers/net/phy/marvell10g.c | 52 +++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 47 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c +index 57ba639c7a2f..e476cd33ee30 100644 +--- a/drivers/net/phy/marvell10g.c ++++ b/drivers/net/phy/marvell10g.c +@@ -718,15 +718,41 @@ static int mv3340_init_interface(struct phy_device *phydev, int mactype) + return err; + } + ++static int mv3310_select_mode(struct phy_device *phydev, ++ unsigned long *host_interfaces) ++{ ++ int mac_type = -1; ++ ++ if (test_bit(PHY_INTERFACE_MODE_USXGMII, host_interfaces)) ++ mac_type = 7; ++ else if (test_bit(PHY_INTERFACE_MODE_SGMII, host_interfaces) && ++ test_bit(PHY_INTERFACE_MODE_10GBASER, host_interfaces)) ++ mac_type = 4; ++ else if (test_bit(PHY_INTERFACE_MODE_SGMII, host_interfaces) && ++ test_bit(PHY_INTERFACE_MODE_RXAUI, host_interfaces)) ++ mac_type = 0; ++ else if (test_bit(PHY_INTERFACE_MODE_10GBASER, host_interfaces)) ++ mac_type = 6; ++ else if (test_bit(PHY_INTERFACE_MODE_RXAUI, host_interfaces)) ++ mac_type = 2; ++ else if (test_bit(PHY_INTERFACE_MODE_SGMII, host_interfaces)) ++ mac_type = 4; ++ ++ return mac_type; ++} ++ + static int mv3310_config_init(struct phy_device *phydev) + { + struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); + const struct mv3310_chip *chip = to_mv3310_chip(phydev); +- int err, mactype; ++ int ret, err, mactype = -1; + + /* Check that the PHY interface type is compatible */ +- if (!test_bit(phydev->interface, priv->supported_interfaces)) ++ if (!phy_interface_empty(phydev->host_interfaces)) { ++ mactype = mv3310_select_mode(phydev, phydev->host_interfaces); ++ } else if (!test_bit(phydev->interface, priv->supported_interfaces)) { + return -ENODEV; ++ } + + phydev->mdix_ctrl = ETH_TP_MDI_AUTO; + +@@ -735,9 +761,25 @@ static int mv3310_config_init(struct phy_device *phydev) + if (err) + return err; + +- mactype = chip->get_mactype(phydev); +- if (mactype < 0) +- return mactype; ++ if (mactype == -1) { ++ mactype = chip->get_mactype(phydev); ++ if (mactype < 0) ++ return mactype; ++ } else { ++ /* FIXME For 88x2210 */ ++ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2, ++ MV_V2_PORT_CTRL, ++ MV_V2_33X0_PORT_CTRL_MACTYPE_MASK, ++ mactype); ++ if (ret > 0) ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, ++ MV_V2_PORT_CTRL, ++ MV_V2_33X0_PORT_CTRL_SWRST, ++ MV_V2_33X0_PORT_CTRL_SWRST); ++ ++ if (ret < 0) ++ return ret; ++ } + + err = chip->init_interface(phydev, mactype); + if (err) { +-- +cgit v1.2.3 + + +From 896c0894fd55f5c8f4953e9968329418fb073e12 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Mon, 23 Dec 2019 23:24:17 +0000 +Subject: net: phy: add supported_interfaces to phylib + +Add a supported_interfaces member to phylib so we know which +interfaces a PHY supports. Currently, set any unconverted driver +to indicate all interfaces are supported. + +Signed-off-by: Russell King +--- + include/linux/phy.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/include/linux/phy.h b/include/linux/phy.h +index ef3d16e91ae1..66c062a8e69d 100644 +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -649,6 +649,7 @@ struct phy_device { + + /* supported PHY interface types */ + DECLARE_PHY_INTERFACE_MASK(host_interfaces); ++ DECLARE_PHY_INTERFACE_MASK(supported_interfaces); + + /* Energy efficient ethernet modes which should be prohibited */ + u32 eee_broken_modes; +-- +cgit v1.2.3 + + +From 7ed98071c97472d39b9dd0c3b995098948262050 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Mon, 23 Dec 2019 23:25:35 +0000 +Subject: net: phy: add supported_interfaces to bcm84881 + +Signed-off-by: Russell King +--- + drivers/net/phy/bcm84881.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/drivers/net/phy/bcm84881.c b/drivers/net/phy/bcm84881.c +index 9717a1626f3f..cba3ffc0708f 100644 +--- a/drivers/net/phy/bcm84881.c ++++ b/drivers/net/phy/bcm84881.c +@@ -51,6 +51,10 @@ static int bcm84881_probe(struct phy_device *phydev) + (phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask) + return -ENODEV; + ++ __set_bit(PHY_INTERFACE_MODE_SGMII, phydev->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, phydev->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_10GBASER, phydev->supported_interfaces); ++ + return 0; + } + +-- +cgit v1.2.3 + + +From 387177f36f60556f41939fd43552f1c0189c9e96 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Mon, 23 Dec 2019 23:25:49 +0000 +Subject: net: phy: add supported_interfaces to marvell PHYs + +Signed-off-by: Russell King +--- + drivers/net/phy/marvell.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c +index 4509b5b3ff91..568f6f48463f 100644 +--- a/drivers/net/phy/marvell.c ++++ b/drivers/net/phy/marvell.c +@@ -2706,6 +2706,15 @@ static int marvell_probe_pause(struct phy_device *phydev, u16 tx_pause_mask, + { + struct marvell_priv *priv; + ++ __set_bit(PHY_INTERFACE_MODE_GMII, phydev->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_SGMII, phydev->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_TBI, phydev->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_RGMII, phydev->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_RGMII_ID, phydev->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_RGMII_RXID, phydev->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_RGMII_TXID, phydev->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_RTBI, phydev->supported_interfaces); ++ + priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; +-- +cgit v1.2.3 + + +From c2c3b299a410ad0c21b9eb2a7c3d50d1210354b0 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Mon, 23 Dec 2019 23:26:04 +0000 +Subject: net: phy: add supported_interfaces to marvell10g PHYs + +Signed-off-by: Russell King +--- + drivers/net/phy/marvell10g.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c +index e476cd33ee30..f88f7fcfcc48 100644 +--- a/drivers/net/phy/marvell10g.c ++++ b/drivers/net/phy/marvell10g.c +@@ -522,6 +522,10 @@ static int mv3310_probe(struct phy_device *phydev) + (phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask) + return -ENODEV; + ++ __set_bit(PHY_INTERFACE_MODE_SGMII, phydev->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, phydev->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_10GBASER, phydev->supported_interfaces); ++ + priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; +-- +cgit v1.2.3 + + +From 230e9094761227dbf3ea50f49f2182b39ee25e2e Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Tue, 3 Mar 2020 12:12:43 +0000 +Subject: net: phylink: use phy interface mode bitmaps + +Use the phy interface mode bitmaps for SFP modules and PHYs to select +the operating interface for SFPs and PHYs with SFPs. + +Signed-off-by: Russell King +--- + drivers/net/phy/phylink.c | 57 ++++++++++++++++++++++++++++++++++++++--------- + 1 file changed, 46 insertions(+), 11 deletions(-) + +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index 7479040ce98a..a2859dd7bde0 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -2381,19 +2381,54 @@ static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) + phy_interface_and(phy->host_interfaces, phylink_sfp_interfaces, + pl->config->supported_interfaces); + +- /* Do the initial configuration */ +- ret = phylink_sfp_config(pl, mode, phy->supported, phy->advertising); +- if (ret < 0) +- return ret; ++ if (!phy_interface_empty(phy->supported_interfaces) && ++ !phy_interface_empty(pl->config->supported_interfaces)) { ++ interface = phylink_select_interface(pl, ++ phy->supported_interfaces, ++ "phy"); ++ if (interface == PHY_INTERFACE_MODE_NA) { ++ phylink_err(pl, ++ "selection of interface for PHY failed\n"); ++ return -EINVAL; ++ } + +- interface = pl->link_config.interface; +- ret = phylink_attach_phy(pl, phy, interface); +- if (ret < 0) +- return ret; ++ if (pl->cur_link_an_mode != mode || ++ pl->link_config.interface != interface) { ++ pl->link_config.interface = interface; ++ pl->cur_link_an_mode = mode; + +- ret = phylink_bringup_phy(pl, phy, interface); +- if (ret) +- phy_detach(phy); ++ phylink_info(pl, "switched to %s/%s link mode\n", ++ phylink_an_mode_str(mode), ++ phy_modes(interface)); ++ } ++ ++ ret = phylink_attach_phy(pl, phy, interface); ++ if (ret < 0) ++ return ret; ++ ++ ret = phylink_bringup_phy(pl, phy, interface); ++ if (ret) ++ phy_detach(phy); ++ ++ if (!test_bit(PHYLINK_DISABLE_STOPPED, ++ &pl->phylink_disable_state)) ++ phylink_mac_initial_config(pl, false); ++ } else { ++ /* Do the initial configuration */ ++ ret = phylink_sfp_config(pl, mode, phy->supported, ++ phy->advertising); ++ if (ret < 0) ++ return ret; ++ ++ interface = pl->link_config.interface; ++ ret = phylink_attach_phy(pl, phy, interface); ++ if (ret < 0) ++ return ret; ++ ++ ret = phylink_bringup_phy(pl, phy, interface); ++ if (ret) ++ phy_detach(phy); ++ } + + return ret; + } +-- +cgit v1.2.3 + + +From 8e691e5bcf4ad1b789fbe84afe15b213469587b7 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Mon, 22 Jun 2020 01:05:59 +0100 +Subject: net: dsa/bcm_sf2: fix pause mode validation + +The implementation appears not to support pause modes on anything +but RGMII, RGMII_TXID, MII and REVMII interface modes. Let phylink +know that detail. + +(This may not be correct; particularly see the FIXMEs in this patch.) + +Signed-off-by: Russell King +--- + drivers/net/dsa/bcm_sf2.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c +index 3b018fcf4412..08a723729fce 100644 +--- a/drivers/net/dsa/bcm_sf2.c ++++ b/drivers/net/dsa/bcm_sf2.c +@@ -677,6 +677,7 @@ static void bcm_sf2_sw_validate(struct dsa_switch *ds, int port, + struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + ++ /* FIXME: Are RGMII_RXID and RGMII_ID actually supported? */ + if (!phy_interface_mode_is_rgmii(state->interface) && + state->interface != PHY_INTERFACE_MODE_MII && + state->interface != PHY_INTERFACE_MODE_REVMII && +@@ -694,8 +695,13 @@ static void bcm_sf2_sw_validate(struct dsa_switch *ds, int port, + /* Allow all the expected bits */ + phylink_set(mask, Autoneg); + phylink_set_port_modes(mask); +- phylink_set(mask, Pause); +- phylink_set(mask, Asym_Pause); ++ if (state->interface == PHY_INTERFACE_MODE_RGMII || ++ state->interface == PHY_INTERFACE_MODE_RGMII_TXID || ++ state->interface == PHY_INTERFACE_MODE_MII || ++ state->interface == PHY_INTERFACE_MODE_REVMII) { ++ phylink_set(mask, Pause); ++ phylink_set(mask, Asym_Pause); ++ } + + /* With the exclusion of MII and Reverse MII, we support Gigabit, + * including Half duplex +@@ -730,6 +736,7 @@ static void bcm_sf2_sw_mac_config(struct dsa_switch *ds, int port, + return; + + switch (state->interface) { ++ /* FIXME: Are RGMII_RXID and RGMII_ID actually supported? */ + case PHY_INTERFACE_MODE_RGMII: + id_mode_dis = 1; + fallthrough; +@@ -830,6 +837,7 @@ static void bcm_sf2_sw_mac_link_up(struct dsa_switch *ds, int port, + else + offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port); + ++ /* FIXME: Are RGMII_RXID and RGMII_ID actually supported? */ + if (interface == PHY_INTERFACE_MODE_RGMII || + interface == PHY_INTERFACE_MODE_RGMII_TXID || + interface == PHY_INTERFACE_MODE_MII || +-- +cgit v1.2.3 + + +From e591f03bc0d6e177cbe48893d3983dc7e64af892 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Mon, 29 Jun 2020 23:16:21 +0100 +Subject: phy: armada-38x: further augmentation of setup + +Further augmentation of the comphy setup. + +Signed-off-by: Russell King +--- + arch/arm/boot/dts/armada-38x.dtsi | 5 +- + drivers/phy/marvell/phy-armada38x-comphy.c | 147 +++++++++++++++++++++++++---- + 2 files changed, 132 insertions(+), 20 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-38x.dtsi b/arch/arm/boot/dts/armada-38x.dtsi +index 9b1a24cc5e91..404943b5cdac 100644 +--- a/arch/arm/boot/dts/armada-38x.dtsi ++++ b/arch/arm/boot/dts/armada-38x.dtsi +@@ -342,8 +342,9 @@ + + comphy: phy@18300 { + compatible = "marvell,armada-380-comphy"; +- reg-names = "comphy", "conf"; +- reg = <0x18300 0x100>, <0x18460 4>; ++ reg-names = "comphy", "pipe", "conf"; ++ reg = <0x18300 0x100>, <0xa0000 0x3000>, ++ <0x18460 4>; + #address-cells = <1>; + #size-cells = <0>; + +diff --git a/drivers/phy/marvell/phy-armada38x-comphy.c b/drivers/phy/marvell/phy-armada38x-comphy.c +index 0fe408964334..0e86f484269c 100644 +--- a/drivers/phy/marvell/phy-armada38x-comphy.c ++++ b/drivers/phy/marvell/phy-armada38x-comphy.c +@@ -15,32 +15,49 @@ + #define MAX_A38X_COMPHY 6 + #define MAX_A38X_PORTS 3 + ++/* Common PHY registers */ ++#define COMPHY_REG_SIZE 0x28 + #define COMPHY_CFG1 0x00 ++#define COMPHY_CFG1_RX_INIT BIT(30) + #define COMPHY_CFG1_GEN_TX(x) ((x) << 26) + #define COMPHY_CFG1_GEN_TX_MSK COMPHY_CFG1_GEN_TX(15) + #define COMPHY_CFG1_GEN_RX(x) ((x) << 22) + #define COMPHY_CFG1_GEN_RX_MSK COMPHY_CFG1_GEN_RX(15) ++#define COMPHY_CFG1_POWER_UP_TX BIT(18) ++#define COMPHY_CFG1_POWER_UP_RX BIT(17) ++#define COMPHY_CFG1_POWER_UP_PLL BIT(16) + #define GEN_SGMII_1_25GBPS 6 + #define GEN_SGMII_3_125GBPS 8 + + #define COMPHY_STAT1 0x18 + #define COMPHY_STAT1_PLL_RDY_TX BIT(3) + #define COMPHY_STAT1_PLL_RDY_RX BIT(2) ++#define COMPHY_STAT1_RX_INIT_DONE BIT(0) + + #define COMPHY_SELECTOR 0xfc + ++/* Common PHY and Pipe Registers */ ++#define PIPE_REG_SIZE 0x800 ++#define PIPE_POWER_CTRL 0x148 ++#define PIPE_POWER_CTRL_SFT_RST_NO_REG BIT(10) ++/* u-boot sets bit 0 during comphy initialisation, but it is undocumented */ ++#define PIPE_POWER_CTRL_BIT0 BIT(0) ++ + struct a38x_comphy; + + struct a38x_comphy_lane { + void __iomem *base; ++ void __iomem *pipe; + struct a38x_comphy *priv; + unsigned int n; + + int port; ++ u8 gen; + }; + + struct a38x_comphy { + void __iomem *base; ++ void __iomem *pipe; + void __iomem *conf; + struct device *dev; + struct a38x_comphy_lane lane[MAX_A38X_COMPHY]; +@@ -88,6 +105,7 @@ static void a38x_comphy_set_speed(struct a38x_comphy_lane *lane, + COMPHY_CFG1_GEN_RX(gen_rx)); + } + ++/* Poll every 1ms for 150ms for a status */ + static int a38x_comphy_poll(struct a38x_comphy_lane *lane, + unsigned int offset, u32 mask, u32 value) + { +@@ -105,6 +123,97 @@ static int a38x_comphy_poll(struct a38x_comphy_lane *lane, + return ret; + } + ++static int a38x_comphy_power_up(struct a38x_comphy_lane *lane) ++{ ++ /* Power up TX, RX and PLL */ ++ a38x_comphy_set_reg(lane, COMPHY_CFG1, 0, ++ COMPHY_CFG1_POWER_UP_TX | COMPHY_CFG1_POWER_UP_RX | ++ COMPHY_CFG1_POWER_UP_PLL); ++ ++ /* Wait for power up */ ++ return a38x_comphy_poll(lane, COMPHY_STAT1, ++ COMPHY_STAT1_PLL_RDY_TX | ++ COMPHY_STAT1_PLL_RDY_RX, ++ COMPHY_STAT1_PLL_RDY_TX | ++ COMPHY_STAT1_PLL_RDY_RX); ++} ++ ++static int a38x_comphy_rx_init(struct a38x_comphy_lane *lane) ++{ ++ int ret; ++ ++ /* Perform RX init */ ++ a38x_comphy_set_reg(lane, COMPHY_CFG1, 0, COMPHY_CFG1_RX_INIT); ++ ++ /* Wait for RX init done */ ++ ret = a38x_comphy_poll(lane, COMPHY_STAT1, COMPHY_STAT1_RX_INIT_DONE, ++ COMPHY_STAT1_RX_INIT_DONE); ++ ++ /* Clear RX init */ ++ a38x_comphy_set_reg(lane, COMPHY_CFG1, COMPHY_CFG1_RX_INIT, 0); ++ ++ return ret; ++} ++ ++static int a38x_comphy_power_off(struct phy *phy) ++{ ++ struct a38x_comphy_lane *lane = phy_get_drvdata(phy); ++ ++ a38x_set_conf(lane, false); ++ ++ /* Soft reset */ ++ if (lane->pipe) { ++ u32 rst = PIPE_POWER_CTRL_SFT_RST_NO_REG | PIPE_POWER_CTRL_BIT0; ++ u32 val; ++ ++ val = readl_relaxed(lane->pipe + PIPE_POWER_CTRL); ++ writel(val | rst, lane->pipe + PIPE_POWER_CTRL); ++ } ++ ++ a38x_comphy_set_reg(lane, COMPHY_CFG1, ++ COMPHY_CFG1_POWER_UP_TX | COMPHY_CFG1_POWER_UP_RX | ++ COMPHY_CFG1_POWER_UP_PLL, 0); ++ ++ return 0; ++} ++ ++static int a38x_comphy_power_on(struct phy *phy) ++{ ++ struct a38x_comphy_lane *lane = phy_get_drvdata(phy); ++ int ret; ++ ++ /* Program the GEN settings */ ++ a38x_comphy_set_speed(lane, lane->gen, lane->gen); ++ ++ /* Soft reset */ ++ if (lane->pipe) { ++ u32 rst = PIPE_POWER_CTRL_SFT_RST_NO_REG | PIPE_POWER_CTRL_BIT0; ++ u32 val; ++ ++ val = readl_relaxed(lane->pipe + PIPE_POWER_CTRL); ++ writel(val | rst, lane->pipe + PIPE_POWER_CTRL); ++ writel(val & ~rst, lane->pipe + PIPE_POWER_CTRL); ++ } ++ ++ /* Power up TX, RX and PLL and wait */ ++ ret = a38x_comphy_power_up(lane); ++ if (ret) ++ goto fail; ++ ++ /* Perform RX init */ ++ ret = a38x_comphy_rx_init(lane); ++ if (ret) ++ goto fail; ++ ++ a38x_set_conf(lane, true); ++ ++ return 0; ++ ++fail: ++ a38x_comphy_power_off(phy); ++ return ret; ++} ++ + /* + * We only support changing the speed for comphys configured for GBE. + * Since that is all we do, we only poll for PLL ready status. +@@ -113,7 +222,6 @@ static int a38x_comphy_set_mode(struct phy *phy, enum phy_mode mode, int sub) + { + struct a38x_comphy_lane *lane = phy_get_drvdata(phy); + unsigned int gen; +- int ret; + + if (mode != PHY_MODE_ETHERNET) + return -EINVAL; +@@ -132,23 +240,14 @@ static int a38x_comphy_set_mode(struct phy *phy, enum phy_mode mode, int sub) + return -EINVAL; + } + +- a38x_set_conf(lane, false); +- +- a38x_comphy_set_speed(lane, gen, gen); +- +- ret = a38x_comphy_poll(lane, COMPHY_STAT1, +- COMPHY_STAT1_PLL_RDY_TX | +- COMPHY_STAT1_PLL_RDY_RX, +- COMPHY_STAT1_PLL_RDY_TX | +- COMPHY_STAT1_PLL_RDY_RX); +- +- if (ret == 0) +- a38x_set_conf(lane, true); ++ lane->gen = gen; + +- return ret; ++ return 0; + } + + static const struct phy_ops a38x_comphy_ops = { ++ .power_on = a38x_comphy_power_on, ++ .power_off = a38x_comphy_power_off, + .set_mode = a38x_comphy_set_mode, + .owner = THIS_MODULE, + }; +@@ -193,6 +292,7 @@ static int a38x_comphy_probe(struct platform_device *pdev) + struct a38x_comphy *priv; + struct resource *res; + void __iomem *base; ++ void __iomem *pipe = NULL; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) +@@ -202,8 +302,16 @@ static int a38x_comphy_probe(struct platform_device *pdev) + if (IS_ERR(base)) + return PTR_ERR(base); + ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pipe"); ++ if (res) { ++ pipe = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ } ++ + priv->dev = &pdev->dev; + priv->base = base; ++ priv->pipe = pipe; + + /* Optional */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "conf"); +@@ -230,16 +338,19 @@ static int a38x_comphy_probe(struct platform_device *pdev) + continue; + } + ++ priv->lane[val].base = base + COMPHY_REG_SIZE * val; ++ if (pipe) ++ priv->lane[val].pipe = pipe + PIPE_REG_SIZE * val; ++ priv->lane[val].priv = priv; ++ priv->lane[val].n = val; ++ priv->lane[val].port = -1; ++ + phy = devm_phy_create(&pdev->dev, child, &a38x_comphy_ops); + if (IS_ERR(phy)) { + of_node_put(child); + return PTR_ERR(phy); + } + +- priv->lane[val].base = base + 0x28 * val; +- priv->lane[val].priv = priv; +- priv->lane[val].n = val; +- priv->lane[val].port = -1; + phy_set_drvdata(phy, &priv->lane[val]); + } + +-- +cgit v1.2.3 + + +From cb633af31a0858f6e801f92a67d3929a2f1989e0 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Wed, 8 Jul 2020 11:37:25 +0100 +Subject: net: mvneta: program 1ms autonegotiation clock divisor + +Program the 1ms autonegotiation clock divisor according to the clocking +rate of neta - without this, the 1ms clock ticks at about 660us on +Armada 38x configured for 250MHz. Bring this into correct specification. + +Signed-off-by: Russell King +--- + drivers/net/ethernet/marvell/mvneta.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c +index 697207b88444..c9a95c10392d 100644 +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -3962,7 +3962,7 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, + } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { + /* SGMII mode receives the state from the PHY */ + new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE; +- new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE; ++ new_clk = MVNETA_GMAC_1MS_CLOCK_ENABLE; + new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN | + MVNETA_GMAC_FORCE_LINK_PASS | + MVNETA_GMAC_CONFIG_MII_SPEED | +@@ -3974,7 +3974,7 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, + } else { + /* 802.3z negotiation - only 1000base-X */ + new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X; +- new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE; ++ new_clk = MVNETA_GMAC_1MS_CLOCK_ENABLE; + new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN | + MVNETA_GMAC_FORCE_LINK_PASS | + MVNETA_GMAC_CONFIG_MII_SPEED)) | +@@ -3987,6 +3987,10 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, + new_an |= MVNETA_GMAC_AN_FLOW_CTRL_EN; + } + ++ /* Set the 1ms clock divisor */ ++ if (new_clk == MVNETA_GMAC_1MS_CLOCK_ENABLE) ++ new_clk |= clk_get_rate(pp->clk) / 1000; ++ + /* Armada 370 documentation says we can only change the port mode + * and in-band enable when the link is down, so force it down + * while making these changes. We also do this for GMAC_CTRL2 +-- +cgit v1.2.3 + + +From 76bebe694b25b49f7f1369efbafdb77456f2c4d8 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Sun, 28 Jun 2020 13:03:47 +0100 +Subject: net: mvneta: convert to use mac_prepare()/mac_finish() + +Convert mvneta to use the mac_prepare() and mac_finish() methods in +preparation to converting mvneta to split-PCS support. + +Signed-off-by: Russell King +--- + drivers/net/ethernet/marvell/mvneta.c | 78 ++++++++++++++++++++++++----------- + 1 file changed, 53 insertions(+), 25 deletions(-) + +diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c +index c9a95c10392d..7a1aca759ff2 100644 +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -3919,6 +3919,32 @@ static void mvneta_mac_an_restart(struct phylink_config *config) + gmac_an & ~MVNETA_GMAC_INBAND_RESTART_AN); + } + ++static int mvneta_mac_prepare(struct phylink_config *config, unsigned int mode, ++ phy_interface_t interface) ++{ ++ struct net_device *ndev = to_net_dev(config->dev); ++ struct mvneta_port *pp = netdev_priv(ndev); ++ u32 val; ++ ++ if (pp->phy_interface != interface || ++ phylink_autoneg_inband(mode)) { ++ /* Force the link down when changing the interface or if in ++ * in-band mode. According to Armada 370 documentation, we ++ * can only change the port mode and in-band enable when the ++ * link is down. ++ */ ++ val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); ++ val &= ~MVNETA_GMAC_FORCE_LINK_PASS; ++ val |= MVNETA_GMAC_FORCE_LINK_DOWN; ++ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); ++ } ++ ++ if (pp->phy_interface != interface) ++ WARN_ON(phy_power_off(pp->comphy)); ++ ++ return 0; ++} ++ + static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) + { +@@ -3963,9 +3989,7 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, + /* SGMII mode receives the state from the PHY */ + new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE; + new_clk = MVNETA_GMAC_1MS_CLOCK_ENABLE; +- new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN | +- MVNETA_GMAC_FORCE_LINK_PASS | +- MVNETA_GMAC_CONFIG_MII_SPEED | ++ new_an = (new_an & ~(MVNETA_GMAC_CONFIG_MII_SPEED | + MVNETA_GMAC_CONFIG_GMII_SPEED | + MVNETA_GMAC_CONFIG_FULL_DUPLEX)) | + MVNETA_GMAC_INBAND_AN_ENABLE | +@@ -3975,9 +3999,7 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, + /* 802.3z negotiation - only 1000base-X */ + new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X; + new_clk = MVNETA_GMAC_1MS_CLOCK_ENABLE; +- new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN | +- MVNETA_GMAC_FORCE_LINK_PASS | +- MVNETA_GMAC_CONFIG_MII_SPEED)) | ++ new_an = (new_an & ~MVNETA_GMAC_CONFIG_MII_SPEED) | + MVNETA_GMAC_INBAND_AN_ENABLE | + MVNETA_GMAC_CONFIG_GMII_SPEED | + /* The MAC only supports FD mode */ +@@ -3991,31 +4013,12 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, + if (new_clk == MVNETA_GMAC_1MS_CLOCK_ENABLE) + new_clk |= clk_get_rate(pp->clk) / 1000; + +- /* Armada 370 documentation says we can only change the port mode +- * and in-band enable when the link is down, so force it down +- * while making these changes. We also do this for GMAC_CTRL2 +- */ +- if ((new_ctrl0 ^ gmac_ctrl0) & MVNETA_GMAC0_PORT_1000BASE_X || +- (new_ctrl2 ^ gmac_ctrl2) & MVNETA_GMAC2_INBAND_AN_ENABLE || +- (new_an ^ gmac_an) & MVNETA_GMAC_INBAND_AN_ENABLE) { +- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, +- (gmac_an & ~MVNETA_GMAC_FORCE_LINK_PASS) | +- MVNETA_GMAC_FORCE_LINK_DOWN); +- } +- +- + /* When at 2.5G, the link partner can send frames with shortened + * preambles. + */ + if (state->interface == PHY_INTERFACE_MODE_2500BASEX) + new_ctrl4 |= MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE; + +- if (pp->phy_interface != state->interface) { +- if (pp->comphy) +- WARN_ON(phy_power_off(pp->comphy)); +- WARN_ON(mvneta_config_interface(pp, state->interface)); +- } +- + if (new_ctrl0 != gmac_ctrl0) + mvreg_write(pp, MVNETA_GMAC_CTRL_0, new_ctrl0); + if (new_ctrl2 != gmac_ctrl2) +@@ -4034,6 +4037,29 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, + } + } + ++static int mvneta_mac_finish(struct phylink_config *config, unsigned int mode, ++ phy_interface_t interface) ++{ ++ struct net_device *ndev = to_net_dev(config->dev); ++ struct mvneta_port *pp = netdev_priv(ndev); ++ u32 val; ++ ++ if (pp->phy_interface != interface) ++ /* Enable the Serdes PHY */ ++ WARN_ON(mvneta_config_interface(pp, interface)); ++ ++ /* Allow the link to come up if in in-band mode, otherwise the ++ * link is forced via mac_link_down()/mac_link_up() ++ */ ++ if (phylink_autoneg_inband(mode)) { ++ val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); ++ val &= ~MVNETA_GMAC_FORCE_LINK_DOWN; ++ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); ++ } ++ ++ return 0; ++} ++ + static void mvneta_set_eee(struct mvneta_port *pp, bool enable) + { + u32 lpi_ctl1; +@@ -4123,7 +4149,9 @@ static const struct phylink_mac_ops mvneta_phylink_ops = { + .validate = mvneta_validate, + .mac_pcs_get_state = mvneta_mac_pcs_get_state, + .mac_an_restart = mvneta_mac_an_restart, ++ .mac_prepare = mvneta_mac_prepare, + .mac_config = mvneta_mac_config, ++ .mac_finish = mvneta_mac_finish, + .mac_link_down = mvneta_mac_link_down, + .mac_link_up = mvneta_mac_link_up, + }; +-- +cgit v1.2.3 + + +From d987a2494717ac88fbdc6695fd6c669ac70206b7 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Wed, 8 Jul 2020 11:44:36 +0100 +Subject: net: mvneta: move 1ms clock control into mac_prepare/mac_finish + +Move the 1ms clock control out of mac_config() into mac_prepare() and +mac_finish(), which simplifies the mac_config() code. + +Signed-off-by: Russell King +--- + drivers/net/ethernet/marvell/mvneta.c | 27 ++++++++++++++++----------- + 1 file changed, 16 insertions(+), 11 deletions(-) + +diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c +index 7a1aca759ff2..341fbec93bdb 100644 +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -3942,6 +3942,14 @@ static int mvneta_mac_prepare(struct phylink_config *config, unsigned int mode, + if (pp->phy_interface != interface) + WARN_ON(phy_power_off(pp->comphy)); + ++ /* Enable the 1ms clock */ ++ if (phylink_autoneg_inband(mode)) { ++ unsigned long rate = clk_get_rate(pp->clk); ++ ++ mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, ++ MVNETA_GMAC_1MS_CLOCK_ENABLE | (rate / 1000)); ++ } ++ + return 0; + } + +@@ -3953,14 +3961,12 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, + u32 new_ctrl0, gmac_ctrl0 = mvreg_read(pp, MVNETA_GMAC_CTRL_0); + u32 new_ctrl2, gmac_ctrl2 = mvreg_read(pp, MVNETA_GMAC_CTRL_2); + u32 new_ctrl4, gmac_ctrl4 = mvreg_read(pp, MVNETA_GMAC_CTRL_4); +- u32 new_clk, gmac_clk = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER); + u32 new_an, gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); + + new_ctrl0 = gmac_ctrl0 & ~MVNETA_GMAC0_PORT_1000BASE_X; + new_ctrl2 = gmac_ctrl2 & ~(MVNETA_GMAC2_INBAND_AN_ENABLE | + MVNETA_GMAC2_PORT_RESET); + new_ctrl4 = gmac_ctrl4 & ~(MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE); +- new_clk = gmac_clk & ~MVNETA_GMAC_1MS_CLOCK_ENABLE; + new_an = gmac_an & ~(MVNETA_GMAC_INBAND_AN_ENABLE | + MVNETA_GMAC_INBAND_RESTART_AN | + MVNETA_GMAC_AN_SPEED_EN | +@@ -3988,7 +3994,6 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, + } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { + /* SGMII mode receives the state from the PHY */ + new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE; +- new_clk = MVNETA_GMAC_1MS_CLOCK_ENABLE; + new_an = (new_an & ~(MVNETA_GMAC_CONFIG_MII_SPEED | + MVNETA_GMAC_CONFIG_GMII_SPEED | + MVNETA_GMAC_CONFIG_FULL_DUPLEX)) | +@@ -3998,7 +4003,6 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, + } else { + /* 802.3z negotiation - only 1000base-X */ + new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X; +- new_clk = MVNETA_GMAC_1MS_CLOCK_ENABLE; + new_an = (new_an & ~MVNETA_GMAC_CONFIG_MII_SPEED) | + MVNETA_GMAC_INBAND_AN_ENABLE | + MVNETA_GMAC_CONFIG_GMII_SPEED | +@@ -4009,10 +4013,6 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, + new_an |= MVNETA_GMAC_AN_FLOW_CTRL_EN; + } + +- /* Set the 1ms clock divisor */ +- if (new_clk == MVNETA_GMAC_1MS_CLOCK_ENABLE) +- new_clk |= clk_get_rate(pp->clk) / 1000; +- + /* When at 2.5G, the link partner can send frames with shortened + * preambles. + */ +@@ -4025,8 +4025,6 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, + mvreg_write(pp, MVNETA_GMAC_CTRL_2, new_ctrl2); + if (new_ctrl4 != gmac_ctrl4) + mvreg_write(pp, MVNETA_GMAC_CTRL_4, new_ctrl4); +- if (new_clk != gmac_clk) +- mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, new_clk); + if (new_an != gmac_an) + mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, new_an); + +@@ -4042,7 +4040,14 @@ static int mvneta_mac_finish(struct phylink_config *config, unsigned int mode, + { + struct net_device *ndev = to_net_dev(config->dev); + struct mvneta_port *pp = netdev_priv(ndev); +- u32 val; ++ u32 val, clk; ++ ++ /* Disable 1ms clock if not in in-band mode */ ++ if (!phylink_autoneg_inband(mode)) { ++ clk = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER); ++ clk &= ~MVNETA_GMAC_1MS_CLOCK_ENABLE; ++ mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, clk); ++ } + + if (pp->phy_interface != interface) + /* Enable the Serdes PHY */ +-- +cgit v1.2.3 + + +From c8eb55edd9e8b00863f2dd8cafb7937b0b3dea02 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Mon, 22 Jun 2020 17:21:43 +0100 +Subject: net: mvneta: convert to phylink pcs operations + +An initial stab at converting mvneta to PCS operations. There's a few +FIXMEs to be solved. + +Signed-off-by: Russell King +--- + drivers/net/ethernet/marvell/mvneta.c | 178 +++++++++++++++++++++------------- + 1 file changed, 109 insertions(+), 69 deletions(-) + +diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c +index 341fbec93bdb..c9bad557f8f9 100644 +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -499,6 +499,7 @@ struct mvneta_port { + unsigned int tx_csum_limit; + struct phylink *phylink; + struct phylink_config phylink_config; ++ struct phylink_pcs phylink_pcs; + struct phy *comphy; + + struct mvneta_bm *bm_priv; +@@ -3824,6 +3825,111 @@ static int mvneta_set_mac_addr(struct net_device *dev, void *addr) + return 0; + } + ++static struct mvneta_port *mvneta_pcs_to_port(struct phylink_pcs *pcs) ++{ ++ return container_of(pcs, struct mvneta_port, phylink_pcs); ++} ++ ++static void mvneta_pcs_get_state(struct phylink_pcs *pcs, ++ struct phylink_link_state *state) ++{ ++ struct mvneta_port *pp = mvneta_pcs_to_port(pcs); ++ u32 gmac_stat; ++ ++ gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS); ++ ++ if (gmac_stat & MVNETA_GMAC_SPEED_1000) ++ state->speed = ++ state->interface == PHY_INTERFACE_MODE_2500BASEX ? ++ SPEED_2500 : SPEED_1000; ++ else if (gmac_stat & MVNETA_GMAC_SPEED_100) ++ state->speed = SPEED_100; ++ else ++ state->speed = SPEED_10; ++ ++ state->an_complete = !!(gmac_stat & MVNETA_GMAC_AN_COMPLETE); ++ state->link = !!(gmac_stat & MVNETA_GMAC_LINK_UP); ++ state->duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX); ++ ++ if (gmac_stat & MVNETA_GMAC_RX_FLOW_CTRL_ENABLE) ++ state->pause |= MLO_PAUSE_RX; ++ if (gmac_stat & MVNETA_GMAC_TX_FLOW_CTRL_ENABLE) ++ state->pause |= MLO_PAUSE_TX; ++} ++ ++static int mvneta_pcs_config(struct phylink_pcs *pcs, ++ unsigned int mode, phy_interface_t interface, ++ const unsigned long *advertising, ++ bool permit_pause_to_mac) ++{ ++ struct mvneta_port *pp = mvneta_pcs_to_port(pcs); ++ u32 mask, val, an, old_an, changed; ++ ++ mask = MVNETA_GMAC_INBAND_AN_ENABLE | ++ MVNETA_GMAC_INBAND_RESTART_AN | ++ MVNETA_GMAC_AN_SPEED_EN | ++ MVNETA_GMAC_AN_FLOW_CTRL_EN | ++ MVNETA_GMAC_AN_DUPLEX_EN; ++ ++ if (phylink_autoneg_inband(mode)) { ++ mask |= MVNETA_GMAC_CONFIG_MII_SPEED | ++ MVNETA_GMAC_CONFIG_GMII_SPEED | ++ MVNETA_GMAC_CONFIG_FULL_DUPLEX; ++ val = MVNETA_GMAC_INBAND_AN_ENABLE; ++ ++ if (state->interface == PHY_INTERFACE_MODE_SGMII) { ++ /* SGMII mode receives the speed and duplex from PHY */ ++ val |= MVNETA_GMAC_AN_SPEED_EN | ++ MVNETA_GMAC_AN_DUPLEX_EN; ++ } else { ++ /* 802.3z mode has fixed speed and duplex */ ++ val |= MVNETA_GMAC_CONFIG_GMII_SPEED | ++ MVNETA_GMAC_CONFIG_FULL_DUPLEX; ++ ++ /* The FLOW_CTRL_EN bit selects either the hardware ++ * automatically or the CONFIG_FLOW_CTRL manually ++ * controls the GMAC pause mode. ++ */ ++ if (permit_pause_to_mac) ++ val |= MVNETA_GMAC_AN_FLOW_CTRL_EN; ++ ++ /* Update the advertisement bits */ ++ mask |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL; ++ if (phylink_test(advertising, Pause)) ++ val |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL; ++ } ++ } else { ++ /* Phy or fixed speed - disable in-band AN modes */ ++ val = 0; ++ } ++ ++ old_an = an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); ++ an = (an & ~mask) | val; ++ changed = old_an ^ an; ++ if (changed) ++ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, an); ++ ++ /* We are only interested in the advertisement bits changing */ ++ return !!(changed & MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL); ++} ++ ++static void mvneta_pcs_an_restart(struct phylink_pcs *pcs) ++{ ++ struct mvneta_port *pp = mvneta_pcs_to_port(pcs); ++ u32 gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); ++ ++ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, ++ gmac_an | MVNETA_GMAC_INBAND_RESTART_AN); ++ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, ++ gmac_an & ~MVNETA_GMAC_INBAND_RESTART_AN); ++} ++ ++static const struct phylink_pcs_ops mvneta_phylink_pcs_ops = { ++ .pcs_get_state = mvneta_pcs_get_state, ++ .pcs_config = mvneta_pcs_config, ++ .pcs_an_restart = mvneta_pcs_an_restart, ++}; ++ + static void mvneta_validate(struct phylink_config *config, + unsigned long *supported, + struct phylink_link_state *state) +@@ -3878,47 +3984,6 @@ static void mvneta_validate(struct phylink_config *config, + phylink_helper_basex_speed(state); + } + +-static void mvneta_mac_pcs_get_state(struct phylink_config *config, +- struct phylink_link_state *state) +-{ +- struct net_device *ndev = to_net_dev(config->dev); +- struct mvneta_port *pp = netdev_priv(ndev); +- u32 gmac_stat; +- +- gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS); +- +- if (gmac_stat & MVNETA_GMAC_SPEED_1000) +- state->speed = +- state->interface == PHY_INTERFACE_MODE_2500BASEX ? +- SPEED_2500 : SPEED_1000; +- else if (gmac_stat & MVNETA_GMAC_SPEED_100) +- state->speed = SPEED_100; +- else +- state->speed = SPEED_10; +- +- state->an_complete = !!(gmac_stat & MVNETA_GMAC_AN_COMPLETE); +- state->link = !!(gmac_stat & MVNETA_GMAC_LINK_UP); +- state->duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX); +- +- state->pause = 0; +- if (gmac_stat & MVNETA_GMAC_RX_FLOW_CTRL_ENABLE) +- state->pause |= MLO_PAUSE_RX; +- if (gmac_stat & MVNETA_GMAC_TX_FLOW_CTRL_ENABLE) +- state->pause |= MLO_PAUSE_TX; +-} +- +-static void mvneta_mac_an_restart(struct phylink_config *config) +-{ +- struct net_device *ndev = to_net_dev(config->dev); +- struct mvneta_port *pp = netdev_priv(ndev); +- u32 gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); +- +- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, +- gmac_an | MVNETA_GMAC_INBAND_RESTART_AN); +- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, +- gmac_an & ~MVNETA_GMAC_INBAND_RESTART_AN); +-} +- + static int mvneta_mac_prepare(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) + { +@@ -3961,18 +4026,11 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, + u32 new_ctrl0, gmac_ctrl0 = mvreg_read(pp, MVNETA_GMAC_CTRL_0); + u32 new_ctrl2, gmac_ctrl2 = mvreg_read(pp, MVNETA_GMAC_CTRL_2); + u32 new_ctrl4, gmac_ctrl4 = mvreg_read(pp, MVNETA_GMAC_CTRL_4); +- u32 new_an, gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); + + new_ctrl0 = gmac_ctrl0 & ~MVNETA_GMAC0_PORT_1000BASE_X; + new_ctrl2 = gmac_ctrl2 & ~(MVNETA_GMAC2_INBAND_AN_ENABLE | + MVNETA_GMAC2_PORT_RESET); + new_ctrl4 = gmac_ctrl4 & ~(MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE); +- new_an = gmac_an & ~(MVNETA_GMAC_INBAND_AN_ENABLE | +- MVNETA_GMAC_INBAND_RESTART_AN | +- MVNETA_GMAC_AN_SPEED_EN | +- MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL | +- MVNETA_GMAC_AN_FLOW_CTRL_EN | +- MVNETA_GMAC_AN_DUPLEX_EN); + + /* Even though it might look weird, when we're configured in + * SGMII or QSGMII mode, the RGMII bit needs to be set. +@@ -3984,9 +4042,6 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, + phy_interface_mode_is_8023z(state->interface)) + new_ctrl2 |= MVNETA_GMAC2_PCS_ENABLE; + +- if (phylink_test(state->advertising, Pause)) +- new_an |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL; +- + if (!phylink_autoneg_inband(mode)) { + /* Phy or fixed speed - nothing to do, leave the + * configured speed, duplex and flow control as-is. +@@ -3994,23 +4049,9 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, + } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { + /* SGMII mode receives the state from the PHY */ + new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE; +- new_an = (new_an & ~(MVNETA_GMAC_CONFIG_MII_SPEED | +- MVNETA_GMAC_CONFIG_GMII_SPEED | +- MVNETA_GMAC_CONFIG_FULL_DUPLEX)) | +- MVNETA_GMAC_INBAND_AN_ENABLE | +- MVNETA_GMAC_AN_SPEED_EN | +- MVNETA_GMAC_AN_DUPLEX_EN; + } else { + /* 802.3z negotiation - only 1000base-X */ + new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X; +- new_an = (new_an & ~MVNETA_GMAC_CONFIG_MII_SPEED) | +- MVNETA_GMAC_INBAND_AN_ENABLE | +- MVNETA_GMAC_CONFIG_GMII_SPEED | +- /* The MAC only supports FD mode */ +- MVNETA_GMAC_CONFIG_FULL_DUPLEX; +- +- if (state->pause & MLO_PAUSE_AN && state->an_enabled) +- new_an |= MVNETA_GMAC_AN_FLOW_CTRL_EN; + } + + /* When at 2.5G, the link partner can send frames with shortened +@@ -4025,8 +4066,6 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, + mvreg_write(pp, MVNETA_GMAC_CTRL_2, new_ctrl2); + if (new_ctrl4 != gmac_ctrl4) + mvreg_write(pp, MVNETA_GMAC_CTRL_4, new_ctrl4); +- if (new_an != gmac_an) +- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, new_an); + + if (gmac_ctrl2 & MVNETA_GMAC2_PORT_RESET) { + while ((mvreg_read(pp, MVNETA_GMAC_CTRL_2) & +@@ -4152,8 +4191,6 @@ static void mvneta_mac_link_up(struct phylink_config *config, + + static const struct phylink_mac_ops mvneta_phylink_ops = { + .validate = mvneta_validate, +- .mac_pcs_get_state = mvneta_mac_pcs_get_state, +- .mac_an_restart = mvneta_mac_an_restart, + .mac_prepare = mvneta_mac_prepare, + .mac_config = mvneta_mac_config, + .mac_finish = mvneta_mac_finish, +@@ -5233,6 +5270,9 @@ static int mvneta_probe(struct platform_device *pdev) + goto err_free_irq; + } + ++ pp->phylink_pcs.ops = &mvneta_phylink_pcs_ops; ++ phylink_set_pcs(phylink, &pp->phylink_pcs); ++ + dev->tx_queue_len = MVNETA_MAX_TXD; + dev->watchdog_timeo = 5 * HZ; + dev->netdev_ops = &mvneta_netdev_ops; +-- +cgit v1.2.3 + + +From 72c827679c96ef310428ee26600366ae50896022 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Thu, 22 Dec 2016 16:03:15 +0000 +Subject: net: mvneta: split out GMAC + +Split out the code handling the GMAC from the rest of the driver. This +block appears to be shared amongst several revisions of the IP. + +Signed-off-by: Russell King +--- + drivers/net/ethernet/marvell/Kconfig | 4 + + drivers/net/ethernet/marvell/Makefile | 1 + + drivers/net/ethernet/marvell/mvgmac.c | 356 ++++++++++++++++++++++++++++++++++ + drivers/net/ethernet/marvell/mvgmac.h | 43 ++++ + drivers/net/ethernet/marvell/mvneta.c | 305 ++++------------------------- + 5 files changed, 438 insertions(+), 271 deletions(-) + create mode 100644 drivers/net/ethernet/marvell/mvgmac.c + create mode 100644 drivers/net/ethernet/marvell/mvgmac.h + +diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig +index fe0989c0fc25..e9149e6fab10 100644 +--- a/drivers/net/ethernet/marvell/Kconfig ++++ b/drivers/net/ethernet/marvell/Kconfig +@@ -59,6 +59,7 @@ config MVNETA_BM_ENABLE + config MVNETA + tristate "Marvell Armada 370/38x/XP/37xx network interface support" + depends on ARCH_MVEBU || COMPILE_TEST ++ select MVGMAC + select MVMDIO + select PHYLINK + select PAGE_POOL +@@ -71,6 +72,9 @@ config MVNETA + driver, which should be used for the older Marvell SoCs + (Dove, Orion, Discovery, Kirkwood). + ++config MVGMAC ++ tristate ++ + config MVNETA_BM + tristate + depends on !64BIT +diff --git a/drivers/net/ethernet/marvell/Makefile b/drivers/net/ethernet/marvell/Makefile +index 9f88fe822555..b1442ff46abf 100644 +--- a/drivers/net/ethernet/marvell/Makefile ++++ b/drivers/net/ethernet/marvell/Makefile +@@ -7,6 +7,7 @@ obj-$(CONFIG_MVMDIO) += mvmdio.o + obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o + obj-$(CONFIG_MVNETA_BM) += mvneta_bm.o + obj-$(CONFIG_MVNETA) += mvneta.o ++obj-$(CONFIG_MVGMAC) += mvgmac.o + obj-$(CONFIG_MVPP2) += mvpp2/ + obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o + obj-$(CONFIG_SKGE) += skge.o +diff --git a/drivers/net/ethernet/marvell/mvgmac.c b/drivers/net/ethernet/marvell/mvgmac.c +new file mode 100644 +index 000000000000..6c0b7c5c0b24 +--- /dev/null ++++ b/drivers/net/ethernet/marvell/mvgmac.c +@@ -0,0 +1,356 @@ ++/* ++ * GMAC driver for Marvell network interfaces on Armada SoCs. ++ * ++ * Copyright (C) 2012 Marvell ++ * ++ * Rami Rosen ++ * Thomas Petazzoni ++ * ++ * Split from mvneta and mvpp2 by Russell King. ++ * ++ * This file is licensed under the terms of the GNU General Public ++ * License version 2. This program is licensed "as is" without any ++ * warranty of any kind, whether express or implied. ++ */ ++#include ++#include ++#include ++ ++#include "mvgmac.h" ++ ++enum { ++ GMAC_CTRL0_REG = 0x00, ++ GMAC_CTRL0_PORT_ENABLE = BIT(0), ++ GMAC_CTRL0_PORT_1000BASE_X = BIT(1), ++ GMAC_CTRL0_MAX_RX_SIZE_SHIFT = 2, ++ GMAC_CTRL0_MAX_RX_SIZE_MASK = 0x1fff << GMAC_CTRL0_MAX_RX_SIZE_SHIFT, ++ GMAC_CTRL0_MIB_CNTR_ENABLE = BIT(15), ++ ++ GMAC_CTRL2_REG = 0x08, ++ GMAC_CTRL2_INBAND_AN_SGMII = BIT(0), ++ GMAC_CTRL2_PCS_ENABLE = BIT(3), ++ GMAC_CTRL2_PORT_RGMII = BIT(4), ++ GMAC_CTRL2_PORT_RESET = BIT(6), ++ ++ GMAC_ANEG_REG = 0x0c, ++ GMAC_ANEG_FORCE_LINK_DOWN = BIT(0), ++ GMAC_ANEG_FORCE_LINK_PASS = BIT(1), ++ GMAC_ANEG_INBAND_AN_ENABLE = BIT(2), ++ GMAC_ANEG_AN_BYPASS_ENABLE = BIT(3), ++ GMAC_ANEG_INBAND_RESTART_AN = BIT(4), ++ GMAC_ANEG_MII_SPEED = BIT(5), ++ GMAC_ANEG_GMII_SPEED = BIT(6), ++ GMAC_ANEG_AN_SPEED_ENABLE = BIT(7), ++ GMAC_ANEG_CONFIG_FLOW_CTRL = BIT(8), ++ GMAC_ANEG_ADVERT_SYM_FLOW_CTRL = BIT(9), ++ GMAC_ANEG_AN_FLOW_CTRL_ENABLE = BIT(11), ++ GMAC_ANEG_FULL_DUPLEX = BIT(12), ++ GMAC_ANEG_AN_DUPLEX_ENABLE = BIT(13), ++ ++ GMAC_STATUS_REG = 0x10, ++ MVGMAC_LINK_UP = BIT(0), ++ MVGMAC_SPEED_1000 = BIT(1), ++ MVGMAC_SPEED_100 = BIT(2), ++ MVGMAC_FULL_DUPLEX = BIT(3), ++ MVGMAC_RX_FLOW_CTRL_ENABLE = BIT(4), ++ MVGMAC_TX_FLOW_CTRL_ENABLE = BIT(5), ++ MVGMAC_RX_FLOW_CTRL_ACTIVE = BIT(6), ++ MVGMAC_TX_FLOW_CTRL_ACTIVE = BIT(7), ++ MVGMAC_AN_COMPLETE = BIT(11), ++ MVGMAC_SYNC_OK = BIT(14), ++ ++ GMAC_CTRL4_REG = 0x90, ++ GMAC_CTRL4_SHORT_PREAMBLE_ENABLE = BIT(1), ++ ++ GMAC_LPI_CTRL0_REG = 0xc0, ++ GMAC_LPI_CTRL0_TS = 0xff << 8, ++ GMAC_LPI_CTRL1_REG = 0xc4, ++ GMAC_LPI_CTRL1_REQ_EN = BIT(0), ++ GMAC_LPI_CTRL2_REG = 0xc8, ++ GMAC_LPI_STATUS_REG = 0xcc, ++ GMAC_LPI_CNTR_REG = 0xd0, ++}; ++ ++#define insert(var, mask, val) ({ \ ++ u32 __mask = mask; \ ++ ((var) & ~(__mask)) | (((val) << __ffs(__mask)) & (__mask)); \ ++}) ++ ++/* Change maximum receive size of the port. */ ++void mvgmac_set_max_rx_size(struct mvgmac *gmac, size_t max_rx_size) ++{ ++ int size = (max_rx_size - MARVELL_HEADER_SIZE) / 2; ++ u32 val; ++ ++ val = readl_relaxed(gmac->base + GMAC_CTRL0_REG); ++ val = insert(val, GMAC_CTRL0_MAX_RX_SIZE_MASK, size); ++ writel_relaxed(val, gmac->base + GMAC_CTRL0_REG); ++} ++EXPORT_SYMBOL_GPL(mvgmac_set_max_rx_size); ++ ++/* Enable the port by setting the port enable bit of the MAC control register */ ++void mvgmac_enable(struct mvgmac *gmac) ++{ ++ u32 val; ++ ++ val = readl_relaxed(gmac->base + GMAC_CTRL0_REG); ++ val |= GMAC_CTRL0_PORT_ENABLE; ++ val |= GMAC_CTRL0_MIB_CNTR_ENABLE; ++ writel_relaxed(val, gmac->base + GMAC_CTRL0_REG); ++} ++EXPORT_SYMBOL_GPL(mvgmac_enable); ++ ++/* Disable the port */ ++void mvgmac_disable(struct mvgmac *gmac) ++{ ++ u32 val; ++ ++ val = readl_relaxed(gmac->base + GMAC_CTRL0_REG); ++ val &= ~GMAC_CTRL0_PORT_ENABLE; ++ writel_relaxed(val, gmac->base + GMAC_CTRL0_REG); ++} ++EXPORT_SYMBOL_GPL(mvgmac_disable); ++ ++void mvgmac_link_unforce(struct mvgmac *gmac) ++{ ++ u32 val; ++ ++ val = readl_relaxed(gmac->base + GMAC_ANEG_REG); ++ val &= ~(GMAC_ANEG_FORCE_LINK_PASS | GMAC_ANEG_FORCE_LINK_DOWN); ++ writel_relaxed(val, gmac->base + GMAC_ANEG_REG); ++} ++EXPORT_SYMBOL_GPL(mvgmac_link_unforce); ++ ++void mvgmac_link_force_down(struct mvgmac *gmac) ++{ ++ u32 val; ++ ++ val = readl_relaxed(gmac->base + GMAC_ANEG_REG); ++ val &= ~GMAC_ANEG_FORCE_LINK_PASS; ++ val |= GMAC_ANEG_FORCE_LINK_DOWN; ++ writel_relaxed(val, gmac->base + GMAC_ANEG_REG); ++} ++EXPORT_SYMBOL_GPL(mvgmac_link_force_down); ++ ++void mvgmac_link_down(struct mvgmac *gmac, int mode) ++{ ++ if (!phylink_autoneg_inband(mode)) ++ mvgmac_link_force_down(gmac); ++} ++EXPORT_SYMBOL_GPL(mvgmac_link_down); ++ ++void mvgmac_link_up(struct mvgmac *gmac, int mode, int speed, int duplex, ++ bool tx_pause, bool rx_pause) ++{ ++ u32 val; ++ ++ val = readl_relaxed(gmac->base + GMAC_ANEG_REG); ++ val &= ~GMAC_ANEG_CONFIG_FLOW_CTRL; ++ ++ if (!phylink_autoneg_inband(mode)) { ++ val &= ~(GMAC_ANEG_FORCE_LINK_DOWN | ++ GMAC_ANEG_MII_SPEED | ++ GMAC_ANEG_GMII_SPEED | ++ GMAC_ANEG_FULL_DUPLEX); ++ val |= GMAC_ANEG_FORCE_LINK_PASS; ++ ++ if (speed == SPEED_1000 || speed == SPEED_2500) ++ val |= GMAC_ANEG_GMII_SPEED; ++ else if (speed == SPEED_100) ++ val |= GMAC_ANEG_MII_SPEED; ++ ++ if (duplex == DUPLEX_FULL) ++ val |= GMAC_ANEG_FULL_DUPLEX; ++ } ++ ++ if (tx_pause || rx_pause) ++ val |= GMAC_ANEG_CONFIG_FLOW_CTRL; ++ ++ writel_relaxed(val, gmac->base + GMAC_ANEG_REG); ++} ++EXPORT_SYMBOL_GPL(mvgmac_link_up); ++ ++bool mvgmac_link_is_up(struct mvgmac *gmac) ++{ ++ u32 gmac_stat = readl_relaxed(gmac->base + GMAC_STATUS_REG); ++ ++ return !!(gmac_stat & MVGMAC_LINK_UP); ++} ++EXPORT_SYMBOL_GPL(mvgmac_link_is_up); ++ ++void mvgmac_pcs_get_state(struct mvgmac *gmac, struct phylink_link_state *state) ++{ ++ u32 gmac_stat = readl_relaxed(gmac->base + GMAC_STATUS_REG); ++ ++ if (gmac_stat & MVGMAC_SPEED_1000) ++ state->speed = ++ state->interface == PHY_INTERFACE_MODE_2500BASEX ? ++ SPEED_2500 : SPEED_1000; ++ else if (gmac_stat & MVGMAC_SPEED_100) ++ state->speed = SPEED_100; ++ else ++ state->speed = SPEED_10; ++ ++ state->an_complete = !!(gmac_stat & MVGMAC_AN_COMPLETE); ++ state->link = !!(gmac_stat & MVGMAC_LINK_UP); ++ state->duplex = !!(gmac_stat & MVGMAC_FULL_DUPLEX); ++ ++ if (gmac_stat & MVGMAC_RX_FLOW_CTRL_ENABLE) ++ state->pause |= MLO_PAUSE_RX; ++ if (gmac_stat & MVGMAC_TX_FLOW_CTRL_ENABLE) ++ state->pause |= MLO_PAUSE_TX; ++} ++EXPORT_SYMBOL_GPL(mvgmac_pcs_get_state); ++ ++void mvgmac_pcs_an_restart(struct mvgmac *gmac) ++{ ++ u32 gmac_an = readl_relaxed(gmac->base + GMAC_ANEG_REG); ++ ++ writel_relaxed(gmac_an | GMAC_ANEG_INBAND_RESTART_AN, ++ gmac->base + GMAC_ANEG_REG); ++ writel_relaxed(gmac_an & ~GMAC_ANEG_INBAND_RESTART_AN, ++ gmac->base + GMAC_ANEG_REG); ++} ++EXPORT_SYMBOL_GPL(mvgmac_pcs_an_restart); ++ ++int mvgmac_pcs_config(struct mvgmac *gmac, unsigned int mode, ++ phy_interface_t interface, ++ const unsigned long *advertising, ++ bool permit_pause_to_mac) ++{ ++ u32 mask, val, an, old_an, changed; ++ ++ mask = GMAC_ANEG_INBAND_AN_ENABLE | ++ GMAC_ANEG_INBAND_RESTART_AN | ++ GMAC_ANEG_AN_SPEED_ENABLE | ++ GMAC_ANEG_AN_FLOW_CTRL_ENABLE | ++ GMAC_ANEG_AN_DUPLEX_ENABLE; ++ ++ if (phylink_autoneg_inband(mode)) { ++ mask |= GMAC_ANEG_MII_SPEED | ++ GMAC_ANEG_GMII_SPEED | ++ GMAC_ANEG_FULL_DUPLEX; ++ val = GMAC_ANEG_INBAND_AN_ENABLE; ++ ++ if (interface == PHY_INTERFACE_MODE_SGMII) { ++ /* SGMII mode receives the speed and duplex from PHY */ ++ val |= GMAC_ANEG_AN_SPEED_ENABLE | ++ GMAC_ANEG_AN_DUPLEX_ENABLE; ++ } else { ++ /* 802.3z mode has fixed speed and duplex */ ++ val |= GMAC_ANEG_GMII_SPEED | ++ GMAC_ANEG_FULL_DUPLEX; ++ ++ /* The FLOW_CTRL_ENABLE bit selects either the hardware ++ * automatically or the GMAC_ANEG_FLOW_CTRL manually ++ * controls the GMAC pause mode. ++ */ ++ if (permit_pause_to_mac) ++ val |= GMAC_ANEG_AN_FLOW_CTRL_ENABLE; ++ ++ /* Update the advertisement bits */ ++ mask |= GMAC_ANEG_ADVERT_SYM_FLOW_CTRL; ++ if (phylink_test(advertising, Pause)) ++ val |= GMAC_ANEG_ADVERT_SYM_FLOW_CTRL; ++ } ++ } else { ++ /* Phy or fixed speed - disable in-band AN modes */ ++ val = 0; ++ } ++ ++ old_an = an = readl_relaxed(gmac->base + GMAC_ANEG_REG); ++ an = (an & ~mask) | val; ++ changed = old_an ^ an; ++ if (changed) ++ writel_relaxed(an, gmac->base + GMAC_ANEG_REG); ++ ++ /* We are only interested in the advertisement bits changing */ ++ return !!(changed & GMAC_ANEG_ADVERT_SYM_FLOW_CTRL); ++} ++EXPORT_SYMBOL_GPL(mvgmac_pcs_config); ++ ++void mvgmac_config_mac(struct mvgmac *gmac, unsigned int mode, ++ const struct phylink_link_state *state) ++{ ++ u32 new_ctrl0, gmac_ctrl0 = readl_relaxed(gmac->base + GMAC_CTRL0_REG); ++ u32 new_ctrl2, gmac_ctrl2 = readl_relaxed(gmac->base + GMAC_CTRL2_REG); ++ u32 new_ctrl4, gmac_ctrl4 = readl_relaxed(gmac->base + GMAC_CTRL4_REG); ++ ++ new_ctrl0 = gmac_ctrl0 & ~GMAC_CTRL0_PORT_1000BASE_X; ++ new_ctrl2 = gmac_ctrl2 & ~(GMAC_CTRL2_INBAND_AN_SGMII | ++ GMAC_CTRL2_PORT_RESET); ++ new_ctrl4 = gmac_ctrl4 & ~GMAC_CTRL4_SHORT_PREAMBLE_ENABLE; ++ ++ /* Even though it might look weird, when we're configured in ++ * SGMII or QSGMII mode, the RGMII bit needs to be set. ++ */ ++ new_ctrl2 |= GMAC_CTRL2_PORT_RGMII; ++ ++ if (state->interface == PHY_INTERFACE_MODE_QSGMII || ++ state->interface == PHY_INTERFACE_MODE_SGMII || ++ phy_interface_mode_is_8023z(state->interface)) ++ new_ctrl2 |= GMAC_CTRL2_PCS_ENABLE; ++ ++ if (!phylink_autoneg_inband(mode)) { ++ /* Phy or fixed speed - nothing to do, leave the ++ * configured speed, duplex and flow control as-is. ++ */ ++ } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { ++ /* SGMII mode receives the state from the PHY */ ++ new_ctrl2 |= GMAC_CTRL2_INBAND_AN_SGMII; ++ } else { ++ /* 802.3z negotiation - 1000BaseX */ ++ new_ctrl0 |= GMAC_CTRL0_PORT_1000BASE_X; ++ } ++ ++ /* When at 2.5G, the link partner can send frames with shortened ++ * preambles. ++ */ ++ if (state->interface == PHY_INTERFACE_MODE_2500BASEX) ++ new_ctrl4 |= GMAC_CTRL4_SHORT_PREAMBLE_ENABLE; ++ ++ if (new_ctrl0 != gmac_ctrl0) ++ writel_relaxed(new_ctrl0, gmac->base + GMAC_CTRL0_REG); ++ if (new_ctrl2 != gmac_ctrl2) ++ writel_relaxed(new_ctrl2, gmac->base + GMAC_CTRL2_REG); ++ if (new_ctrl4 != gmac_ctrl4) ++ writel_relaxed(new_ctrl4, gmac->base + GMAC_CTRL4_REG); ++ ++ if (gmac_ctrl2 & GMAC_CTRL2_PORT_RESET) { ++ while ((readl_relaxed(gmac->base + GMAC_CTRL2_REG) & ++ GMAC_CTRL2_PORT_RESET) != 0) ++ continue; ++ } ++} ++EXPORT_SYMBOL_GPL(mvgmac_config_mac); ++ ++int mvgmac_set_lpi_ts(struct mvgmac *gmac, unsigned int ts) ++{ ++ u32 val; ++ ++ if (!(readl_relaxed(gmac->base + GMAC_STATUS_REG) & MVGMAC_SPEED_1000)) ++ ts = DIV_ROUND_UP(ts, 10); ++ ++ if (ts > 255) ++ ts = 255; ++ ++ val = readl_relaxed(gmac->base + GMAC_LPI_CTRL0_REG); ++ val = insert(val, GMAC_LPI_CTRL0_TS, ts); ++ writel_relaxed(val, gmac->base + GMAC_LPI_CTRL0_REG); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mvgmac_set_lpi_ts); ++ ++void mvgmac_set_eee(struct mvgmac *gmac, bool enable) ++{ ++ u32 val; ++ ++ val = readl_relaxed(gmac->base + GMAC_LPI_CTRL1_REG); ++ val = insert(val, GMAC_LPI_CTRL1_REQ_EN, enable); ++ writel_relaxed(val, gmac->base + GMAC_LPI_CTRL1_REG); ++} ++EXPORT_SYMBOL_GPL(mvgmac_set_eee); ++ ++MODULE_DESCRIPTION("Marvell GMAC driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/net/ethernet/marvell/mvgmac.h b/drivers/net/ethernet/marvell/mvgmac.h +new file mode 100644 +index 000000000000..ceccc6777237 +--- /dev/null ++++ b/drivers/net/ethernet/marvell/mvgmac.h +@@ -0,0 +1,43 @@ ++#ifndef MVGMAC_H ++#define MVGMAC_H ++ ++#include ++ ++struct phylink_link_state; ++ ++/* The two bytes Marvell header. Either contains a special value used by ++ * Marvell switches when a specific hardware mode is enabled (not supported ++ * by this driver) or is filled automatically by zeroes on the RX side. ++ * Those two bytes being at the front of the Ethernet header, they allow ++ * to have the IP header aligned on a 4 bytes boundary automatically: the ++ * hardware skips those two bytes on its own. ++ */ ++#define MARVELL_HEADER_SIZE 2 ++ ++struct mvgmac { ++ void __iomem *base; ++}; ++ ++void mvgmac_set_max_rx_size(struct mvgmac *gmac, size_t max_rx_size); ++void mvgmac_enable(struct mvgmac *gmac); ++void mvgmac_disable(struct mvgmac *gmac); ++void mvgmac_link_unforce(struct mvgmac *gmac); ++void mvgmac_link_force_down(struct mvgmac *gmac); ++void mvgmac_link_down(struct mvgmac *gmac, int mode); ++void mvgmac_link_up(struct mvgmac *gmac, int mode, int speed, int duplex, ++ bool tx_pause, bool rx_pause); ++bool mvgmac_link_is_up(struct mvgmac *gmac); ++void mvgmac_pcs_get_state(struct mvgmac *gmac, ++ struct phylink_link_state *state); ++void mvgmac_pcs_an_restart(struct mvgmac *gmac); ++int mvgmac_pcs_config(struct mvgmac *gmac, unsigned int mode, ++ phy_interface_t interface, ++ const unsigned long *advertising, ++ bool permit_pause_to_mac); ++void mvgmac_config_mac(struct mvgmac *gmac, unsigned int mode, ++ const struct phylink_link_state *state); ++ ++int mvgmac_set_lpi_ts(struct mvgmac *gmac, unsigned int ts); ++void mvgmac_set_eee(struct mvgmac *gmac, bool enable); ++ ++#endif +diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c +index c9bad557f8f9..750915bb2b28 100644 +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -34,6 +34,7 @@ + #include + #include + #include "mvneta_bm.h" ++#include "mvgmac.h" + #include + #include + #include +@@ -193,43 +194,7 @@ + #define MVNETA_RXQ_ENABLE_MASK 0x000000ff + #define MVETH_TXQ_TOKEN_COUNT_REG(q) (0x2700 + ((q) << 4)) + #define MVETH_TXQ_TOKEN_CFG_REG(q) (0x2704 + ((q) << 4)) +-#define MVNETA_GMAC_CTRL_0 0x2c00 +-#define MVNETA_GMAC_MAX_RX_SIZE_SHIFT 2 +-#define MVNETA_GMAC_MAX_RX_SIZE_MASK 0x7ffc +-#define MVNETA_GMAC0_PORT_1000BASE_X BIT(1) +-#define MVNETA_GMAC0_PORT_ENABLE BIT(0) +-#define MVNETA_GMAC_CTRL_2 0x2c08 +-#define MVNETA_GMAC2_INBAND_AN_ENABLE BIT(0) +-#define MVNETA_GMAC2_PCS_ENABLE BIT(3) +-#define MVNETA_GMAC2_PORT_RGMII BIT(4) +-#define MVNETA_GMAC2_PORT_RESET BIT(6) +-#define MVNETA_GMAC_STATUS 0x2c10 +-#define MVNETA_GMAC_LINK_UP BIT(0) +-#define MVNETA_GMAC_SPEED_1000 BIT(1) +-#define MVNETA_GMAC_SPEED_100 BIT(2) +-#define MVNETA_GMAC_FULL_DUPLEX BIT(3) +-#define MVNETA_GMAC_RX_FLOW_CTRL_ENABLE BIT(4) +-#define MVNETA_GMAC_TX_FLOW_CTRL_ENABLE BIT(5) +-#define MVNETA_GMAC_RX_FLOW_CTRL_ACTIVE BIT(6) +-#define MVNETA_GMAC_TX_FLOW_CTRL_ACTIVE BIT(7) +-#define MVNETA_GMAC_AN_COMPLETE BIT(11) +-#define MVNETA_GMAC_SYNC_OK BIT(14) +-#define MVNETA_GMAC_AUTONEG_CONFIG 0x2c0c +-#define MVNETA_GMAC_FORCE_LINK_DOWN BIT(0) +-#define MVNETA_GMAC_FORCE_LINK_PASS BIT(1) +-#define MVNETA_GMAC_INBAND_AN_ENABLE BIT(2) +-#define MVNETA_GMAC_AN_BYPASS_ENABLE BIT(3) +-#define MVNETA_GMAC_INBAND_RESTART_AN BIT(4) +-#define MVNETA_GMAC_CONFIG_MII_SPEED BIT(5) +-#define MVNETA_GMAC_CONFIG_GMII_SPEED BIT(6) +-#define MVNETA_GMAC_AN_SPEED_EN BIT(7) +-#define MVNETA_GMAC_CONFIG_FLOW_CTRL BIT(8) +-#define MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL BIT(9) +-#define MVNETA_GMAC_AN_FLOW_CTRL_EN BIT(11) +-#define MVNETA_GMAC_CONFIG_FULL_DUPLEX BIT(12) +-#define MVNETA_GMAC_AN_DUPLEX_EN BIT(13) +-#define MVNETA_GMAC_CTRL_4 0x2c90 +-#define MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE BIT(1) ++#define MVNETA_GMAC_BASE 0x2c00 + #define MVNETA_MIB_COUNTERS_BASE 0x3000 + #define MVNETA_MIB_LATE_COLLISION 0x7c + #define MVNETA_DA_FILT_SPEC_MCAST 0x3400 +@@ -253,12 +218,6 @@ + #define MVNETA_TXQ_TOKEN_SIZE_REG(q) (0x3e40 + ((q) << 2)) + #define MVNETA_TXQ_TOKEN_SIZE_MAX 0x7fffffff + +-#define MVNETA_LPI_CTRL_0 0x2cc0 +-#define MVNETA_LPI_CTRL_1 0x2cc4 +-#define MVNETA_LPI_REQUEST_ENABLE BIT(0) +-#define MVNETA_LPI_CTRL_2 0x2cc8 +-#define MVNETA_LPI_STATUS 0x2ccc +- + #define MVNETA_CAUSE_TXQ_SENT_DESC_ALL_MASK 0xff + + /* Descriptor ring Macros */ +@@ -280,7 +239,7 @@ + * boundary automatically: the hardware skips those two bytes on its + * own. + */ +-#define MVNETA_MH_SIZE 2 ++#define MVNETA_MH_SIZE MARVELL_HEADER_SIZE + + #define MVNETA_VLAN_TAG_LEN 4 + +@@ -502,6 +461,7 @@ struct mvneta_port { + struct phylink_pcs phylink_pcs; + struct phy *comphy; + ++ struct mvgmac gmac; + struct mvneta_bm *bm_priv; + struct mvneta_bm_pool *pool_long; + struct mvneta_bm_pool *pool_short; +@@ -510,6 +470,7 @@ struct mvneta_port { + bool eee_enabled; + bool eee_active; + bool tx_lpi_enabled; ++ u32 tx_lpi_timer; + + u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)]; + +@@ -892,19 +853,6 @@ mvneta_rxq_next_desc_get(struct mvneta_rx_queue *rxq) + return rxq->descs + rx_desc; + } + +-/* Change maximum receive size of the port. */ +-static void mvneta_max_rx_size_set(struct mvneta_port *pp, int max_rx_size) +-{ +- u32 val; +- +- val = mvreg_read(pp, MVNETA_GMAC_CTRL_0); +- val &= ~MVNETA_GMAC_MAX_RX_SIZE_MASK; +- val |= ((max_rx_size - MVNETA_MH_SIZE) / 2) << +- MVNETA_GMAC_MAX_RX_SIZE_SHIFT; +- mvreg_write(pp, MVNETA_GMAC_CTRL_0, val); +-} +- +- + /* Set rx queue offset */ + static void mvneta_rxq_offset_set(struct mvneta_port *pp, + struct mvneta_rx_queue *rxq, +@@ -1312,23 +1260,13 @@ static void mvneta_port_down(struct mvneta_port *pp) + /* Enable the port by setting the port enable bit of the MAC control register */ + static void mvneta_port_enable(struct mvneta_port *pp) + { +- u32 val; +- +- /* Enable port */ +- val = mvreg_read(pp, MVNETA_GMAC_CTRL_0); +- val |= MVNETA_GMAC0_PORT_ENABLE; +- mvreg_write(pp, MVNETA_GMAC_CTRL_0, val); ++ mvgmac_enable(&pp->gmac); + } + + /* Disable the port and wait for about 200 usec before retuning */ + static void mvneta_port_disable(struct mvneta_port *pp) + { +- u32 val; +- +- /* Reset the Enable bit in the Serial Control Register */ +- val = mvreg_read(pp, MVNETA_GMAC_CTRL_0); +- val &= ~MVNETA_GMAC0_PORT_ENABLE; +- mvreg_write(pp, MVNETA_GMAC_CTRL_0, val); ++ mvgmac_disable(&pp->gmac); + + udelay(200); + } +@@ -3129,9 +3067,9 @@ static irqreturn_t mvneta_percpu_isr(int irq, void *dev_id) + + static void mvneta_link_change(struct mvneta_port *pp) + { +- u32 gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS); ++ bool link_is_up = mvgmac_link_is_up(&pp->gmac); + +- phylink_mac_change(pp->phylink, !!(gmac_stat & MVNETA_GMAC_LINK_UP)); ++ phylink_mac_change(pp->phylink, link_is_up); + } + + /* NAPI handler +@@ -3623,7 +3561,7 @@ static void mvneta_start_dev(struct mvneta_port *pp) + + WARN_ON(mvneta_config_interface(pp, pp->phy_interface)); + +- mvneta_max_rx_size_set(pp, pp->pkt_size); ++ mvgmac_set_max_rx_size(&pp->gmac, pp->pkt_size); + mvneta_txq_max_tx_size_set(pp, pp->pkt_size); + + /* start the Rx/Tx activity */ +@@ -3834,27 +3772,8 @@ static void mvneta_pcs_get_state(struct phylink_pcs *pcs, + struct phylink_link_state *state) + { + struct mvneta_port *pp = mvneta_pcs_to_port(pcs); +- u32 gmac_stat; +- +- gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS); + +- if (gmac_stat & MVNETA_GMAC_SPEED_1000) +- state->speed = +- state->interface == PHY_INTERFACE_MODE_2500BASEX ? +- SPEED_2500 : SPEED_1000; +- else if (gmac_stat & MVNETA_GMAC_SPEED_100) +- state->speed = SPEED_100; +- else +- state->speed = SPEED_10; +- +- state->an_complete = !!(gmac_stat & MVNETA_GMAC_AN_COMPLETE); +- state->link = !!(gmac_stat & MVNETA_GMAC_LINK_UP); +- state->duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX); +- +- if (gmac_stat & MVNETA_GMAC_RX_FLOW_CTRL_ENABLE) +- state->pause |= MLO_PAUSE_RX; +- if (gmac_stat & MVNETA_GMAC_TX_FLOW_CTRL_ENABLE) +- state->pause |= MLO_PAUSE_TX; ++ mvgmac_pcs_get_state(&pp->gmac, state); + } + + static int mvneta_pcs_config(struct phylink_pcs *pcs, +@@ -3863,65 +3782,19 @@ static int mvneta_pcs_config(struct phylink_pcs *pcs, + bool permit_pause_to_mac) + { + struct mvneta_port *pp = mvneta_pcs_to_port(pcs); +- u32 mask, val, an, old_an, changed; + +- mask = MVNETA_GMAC_INBAND_AN_ENABLE | +- MVNETA_GMAC_INBAND_RESTART_AN | +- MVNETA_GMAC_AN_SPEED_EN | +- MVNETA_GMAC_AN_FLOW_CTRL_EN | +- MVNETA_GMAC_AN_DUPLEX_EN; ++ /* We should never see Asym_Pause set */ ++ WARN_ON(phylink_test(advertising, Asym_Pause)); + +- if (phylink_autoneg_inband(mode)) { +- mask |= MVNETA_GMAC_CONFIG_MII_SPEED | +- MVNETA_GMAC_CONFIG_GMII_SPEED | +- MVNETA_GMAC_CONFIG_FULL_DUPLEX; +- val = MVNETA_GMAC_INBAND_AN_ENABLE; +- +- if (state->interface == PHY_INTERFACE_MODE_SGMII) { +- /* SGMII mode receives the speed and duplex from PHY */ +- val |= MVNETA_GMAC_AN_SPEED_EN | +- MVNETA_GMAC_AN_DUPLEX_EN; +- } else { +- /* 802.3z mode has fixed speed and duplex */ +- val |= MVNETA_GMAC_CONFIG_GMII_SPEED | +- MVNETA_GMAC_CONFIG_FULL_DUPLEX; +- +- /* The FLOW_CTRL_EN bit selects either the hardware +- * automatically or the CONFIG_FLOW_CTRL manually +- * controls the GMAC pause mode. +- */ +- if (permit_pause_to_mac) +- val |= MVNETA_GMAC_AN_FLOW_CTRL_EN; +- +- /* Update the advertisement bits */ +- mask |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL; +- if (phylink_test(advertising, Pause)) +- val |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL; +- } +- } else { +- /* Phy or fixed speed - disable in-band AN modes */ +- val = 0; +- } +- +- old_an = an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); +- an = (an & ~mask) | val; +- changed = old_an ^ an; +- if (changed) +- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, an); +- +- /* We are only interested in the advertisement bits changing */ +- return !!(changed & MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL); ++ return mvgmac_pcs_config(&pp->gmac, mode, interface, advertising, ++ permit_pause_to_mac); + } + + static void mvneta_pcs_an_restart(struct phylink_pcs *pcs) + { + struct mvneta_port *pp = mvneta_pcs_to_port(pcs); +- u32 gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); + +- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, +- gmac_an | MVNETA_GMAC_INBAND_RESTART_AN); +- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, +- gmac_an & ~MVNETA_GMAC_INBAND_RESTART_AN); ++ mvgmac_pcs_an_restart(&pp->gmac); + } + + static const struct phylink_pcs_ops mvneta_phylink_pcs_ops = { +@@ -3989,7 +3862,6 @@ static int mvneta_mac_prepare(struct phylink_config *config, unsigned int mode, + { + struct net_device *ndev = to_net_dev(config->dev); + struct mvneta_port *pp = netdev_priv(ndev); +- u32 val; + + if (pp->phy_interface != interface || + phylink_autoneg_inband(mode)) { +@@ -3998,10 +3870,7 @@ static int mvneta_mac_prepare(struct phylink_config *config, unsigned int mode, + * can only change the port mode and in-band enable when the + * link is down. + */ +- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); +- val &= ~MVNETA_GMAC_FORCE_LINK_PASS; +- val |= MVNETA_GMAC_FORCE_LINK_DOWN; +- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); ++ mvgmac_link_force_down(&pp->gmac); + } + + if (pp->phy_interface != interface) +@@ -4023,55 +3892,8 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, + { + struct net_device *ndev = to_net_dev(config->dev); + struct mvneta_port *pp = netdev_priv(ndev); +- u32 new_ctrl0, gmac_ctrl0 = mvreg_read(pp, MVNETA_GMAC_CTRL_0); +- u32 new_ctrl2, gmac_ctrl2 = mvreg_read(pp, MVNETA_GMAC_CTRL_2); +- u32 new_ctrl4, gmac_ctrl4 = mvreg_read(pp, MVNETA_GMAC_CTRL_4); +- +- new_ctrl0 = gmac_ctrl0 & ~MVNETA_GMAC0_PORT_1000BASE_X; +- new_ctrl2 = gmac_ctrl2 & ~(MVNETA_GMAC2_INBAND_AN_ENABLE | +- MVNETA_GMAC2_PORT_RESET); +- new_ctrl4 = gmac_ctrl4 & ~(MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE); +- +- /* Even though it might look weird, when we're configured in +- * SGMII or QSGMII mode, the RGMII bit needs to be set. +- */ +- new_ctrl2 |= MVNETA_GMAC2_PORT_RGMII; +- +- if (state->interface == PHY_INTERFACE_MODE_QSGMII || +- state->interface == PHY_INTERFACE_MODE_SGMII || +- phy_interface_mode_is_8023z(state->interface)) +- new_ctrl2 |= MVNETA_GMAC2_PCS_ENABLE; + +- if (!phylink_autoneg_inband(mode)) { +- /* Phy or fixed speed - nothing to do, leave the +- * configured speed, duplex and flow control as-is. +- */ +- } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { +- /* SGMII mode receives the state from the PHY */ +- new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE; +- } else { +- /* 802.3z negotiation - only 1000base-X */ +- new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X; +- } +- +- /* When at 2.5G, the link partner can send frames with shortened +- * preambles. +- */ +- if (state->interface == PHY_INTERFACE_MODE_2500BASEX) +- new_ctrl4 |= MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE; +- +- if (new_ctrl0 != gmac_ctrl0) +- mvreg_write(pp, MVNETA_GMAC_CTRL_0, new_ctrl0); +- if (new_ctrl2 != gmac_ctrl2) +- mvreg_write(pp, MVNETA_GMAC_CTRL_2, new_ctrl2); +- if (new_ctrl4 != gmac_ctrl4) +- mvreg_write(pp, MVNETA_GMAC_CTRL_4, new_ctrl4); +- +- if (gmac_ctrl2 & MVNETA_GMAC2_PORT_RESET) { +- while ((mvreg_read(pp, MVNETA_GMAC_CTRL_2) & +- MVNETA_GMAC2_PORT_RESET) != 0) +- continue; +- } ++ mvgmac_config_mac(&pp->gmac, mode, state); + } + + static int mvneta_mac_finish(struct phylink_config *config, unsigned int mode, +@@ -4079,7 +3901,7 @@ static int mvneta_mac_finish(struct phylink_config *config, unsigned int mode, + { + struct net_device *ndev = to_net_dev(config->dev); + struct mvneta_port *pp = netdev_priv(ndev); +- u32 val, clk; ++ u32 clk; + + /* Disable 1ms clock if not in in-band mode */ + if (!phylink_autoneg_inband(mode)) { +@@ -4095,45 +3917,23 @@ static int mvneta_mac_finish(struct phylink_config *config, unsigned int mode, + /* Allow the link to come up if in in-band mode, otherwise the + * link is forced via mac_link_down()/mac_link_up() + */ +- if (phylink_autoneg_inband(mode)) { +- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); +- val &= ~MVNETA_GMAC_FORCE_LINK_DOWN; +- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); +- } ++ if (phylink_autoneg_inband(mode)) ++ mvgmac_link_unforce(&pp->gmac); + + return 0; + } + +-static void mvneta_set_eee(struct mvneta_port *pp, bool enable) +-{ +- u32 lpi_ctl1; +- +- lpi_ctl1 = mvreg_read(pp, MVNETA_LPI_CTRL_1); +- if (enable) +- lpi_ctl1 |= MVNETA_LPI_REQUEST_ENABLE; +- else +- lpi_ctl1 &= ~MVNETA_LPI_REQUEST_ENABLE; +- mvreg_write(pp, MVNETA_LPI_CTRL_1, lpi_ctl1); +-} +- + static void mvneta_mac_link_down(struct phylink_config *config, + unsigned int mode, phy_interface_t interface) + { + struct net_device *ndev = to_net_dev(config->dev); + struct mvneta_port *pp = netdev_priv(ndev); +- u32 val; + + mvneta_port_down(pp); +- +- if (!phylink_autoneg_inband(mode)) { +- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); +- val &= ~MVNETA_GMAC_FORCE_LINK_PASS; +- val |= MVNETA_GMAC_FORCE_LINK_DOWN; +- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); +- } ++ mvgmac_link_down(&pp->gmac, mode); + + pp->eee_active = false; +- mvneta_set_eee(pp, false); ++ mvgmac_set_eee(&pp->gmac, false); + } + + static void mvneta_mac_link_up(struct phylink_config *config, +@@ -4144,48 +3944,14 @@ static void mvneta_mac_link_up(struct phylink_config *config, + { + struct net_device *ndev = to_net_dev(config->dev); + struct mvneta_port *pp = netdev_priv(ndev); +- u32 val; +- +- if (!phylink_autoneg_inband(mode)) { +- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); +- val &= ~(MVNETA_GMAC_FORCE_LINK_DOWN | +- MVNETA_GMAC_CONFIG_MII_SPEED | +- MVNETA_GMAC_CONFIG_GMII_SPEED | +- MVNETA_GMAC_CONFIG_FLOW_CTRL | +- MVNETA_GMAC_CONFIG_FULL_DUPLEX); +- val |= MVNETA_GMAC_FORCE_LINK_PASS; +- +- if (speed == SPEED_1000 || speed == SPEED_2500) +- val |= MVNETA_GMAC_CONFIG_GMII_SPEED; +- else if (speed == SPEED_100) +- val |= MVNETA_GMAC_CONFIG_MII_SPEED; +- +- if (duplex == DUPLEX_FULL) +- val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX; +- +- if (tx_pause || rx_pause) +- val |= MVNETA_GMAC_CONFIG_FLOW_CTRL; +- +- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); +- } else { +- /* When inband doesn't cover flow control or flow control is +- * disabled, we need to manually configure it. This bit will +- * only have effect if MVNETA_GMAC_AN_FLOW_CTRL_EN is unset. +- */ +- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); +- val &= ~MVNETA_GMAC_CONFIG_FLOW_CTRL; +- +- if (tx_pause || rx_pause) +- val |= MVNETA_GMAC_CONFIG_FLOW_CTRL; +- +- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); +- } + ++ mvgmac_link_up(&pp->gmac, mode, speed, duplex, tx_pause, rx_pause); + mvneta_port_up(pp); + + if (phy && pp->eee_enabled) { + pp->eee_active = phy_init_eee(phy, 0) >= 0; +- mvneta_set_eee(pp, pp->eee_active && pp->tx_lpi_enabled); ++ mvgmac_set_lpi_ts(&pp->gmac, pp->tx_lpi_timer); ++ mvgmac_set_eee(&pp->gmac, pp->eee_active && pp->tx_lpi_enabled); + } + } + +@@ -4957,14 +4723,11 @@ static int mvneta_ethtool_get_eee(struct net_device *dev, + struct ethtool_eee *eee) + { + struct mvneta_port *pp = netdev_priv(dev); +- u32 lpi_ctl0; +- +- lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0); + + eee->eee_enabled = pp->eee_enabled; + eee->eee_active = pp->eee_active; + eee->tx_lpi_enabled = pp->tx_lpi_enabled; +- eee->tx_lpi_timer = (lpi_ctl0) >> 8; // * scale; ++ eee->tx_lpi_timer = pp->tx_lpi_timer; + + return phylink_ethtool_get_eee(pp->phylink, eee); + } +@@ -4973,7 +4736,6 @@ static int mvneta_ethtool_set_eee(struct net_device *dev, + struct ethtool_eee *eee) + { + struct mvneta_port *pp = netdev_priv(dev); +- u32 lpi_ctl0; + + /* The Armada 37x documents do not give limits for this other than + * it being an 8-bit register. +@@ -4981,15 +4743,13 @@ static int mvneta_ethtool_set_eee(struct net_device *dev, + if (eee->tx_lpi_enabled && eee->tx_lpi_timer > 255) + return -EINVAL; + +- lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0); +- lpi_ctl0 &= ~(0xff << 8); +- lpi_ctl0 |= eee->tx_lpi_timer << 8; +- mvreg_write(pp, MVNETA_LPI_CTRL_0, lpi_ctl0); +- + pp->eee_enabled = eee->eee_enabled; + pp->tx_lpi_enabled = eee->tx_lpi_enabled; ++ pp->tx_lpi_timer = eee->tx_lpi_timer; + +- mvneta_set_eee(pp, eee->tx_lpi_enabled && eee->eee_enabled); ++ mvgmac_set_eee(&pp->gmac, false); ++ mvgmac_set_lpi_ts(&pp->gmac, eee->tx_lpi_timer); ++ mvgmac_set_eee(&pp->gmac, pp->eee_active && pp->tx_lpi_enabled); + + return phylink_ethtool_set_eee(pp->phylink, eee); + } +@@ -5311,6 +5071,9 @@ static int mvneta_probe(struct platform_device *pdev) + goto err_clk; + } + ++ pp->gmac.base = pp->base + MVNETA_GMAC_BASE; ++ pp->tx_lpi_timer = 16; ++ + /* Alloc per-cpu port structure */ + pp->ports = alloc_percpu(struct mvneta_pcpu_port); + if (!pp->ports) { +-- +cgit v1.2.3 + + +From d4888ddce77f3cc2717dcf0f69e07514313b3ac3 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Fri, 23 Dec 2016 01:09:44 +0000 +Subject: net: mvgmac: support different hw versions + +Signed-off-by: Russell King +--- + drivers/net/ethernet/marvell/mvgmac.c | 108 ++++++++++++++++++++++++++++++---- + drivers/net/ethernet/marvell/mvgmac.h | 9 +++ + drivers/net/ethernet/marvell/mvneta.c | 1 + + 3 files changed, 108 insertions(+), 10 deletions(-) + +diff --git a/drivers/net/ethernet/marvell/mvgmac.c b/drivers/net/ethernet/marvell/mvgmac.c +index 6c0b7c5c0b24..e776d4e85d3f 100644 +--- a/drivers/net/ethernet/marvell/mvgmac.c ++++ b/drivers/net/ethernet/marvell/mvgmac.c +@@ -19,6 +19,8 @@ + #include "mvgmac.h" + + enum { ++ /* N = Neta, 21 = PPV2.1, 22 = PPV2.2 */ ++ /* N: 0-14 21: 0,2-15 22: 0-14 */ + GMAC_CTRL0_REG = 0x00, + GMAC_CTRL0_PORT_ENABLE = BIT(0), + GMAC_CTRL0_PORT_1000BASE_X = BIT(1), +@@ -26,12 +28,21 @@ enum { + GMAC_CTRL0_MAX_RX_SIZE_MASK = 0x1fff << GMAC_CTRL0_MAX_RX_SIZE_SHIFT, + GMAC_CTRL0_MIB_CNTR_ENABLE = BIT(15), + ++ /* N: 21: 1,5,6 22: */ ++ GMAC_CTRL1_REG = 0x04, ++ GMAC_CTRL1_PERIODIC_XON_ENABLE = BIT(1), ++ GMAC_CTRL1_GMII_LB_ENABLE = BIT(5), ++ GMAC_CTRL1_PCS_LB_ENABLE = BIT(6), ++ ++ /* ALL: 0,3,4,6 */ + GMAC_CTRL2_REG = 0x08, + GMAC_CTRL2_INBAND_AN_SGMII = BIT(0), + GMAC_CTRL2_PCS_ENABLE = BIT(3), + GMAC_CTRL2_PORT_RGMII = BIT(4), + GMAC_CTRL2_PORT_RESET = BIT(6), + ++ /* N:0-9,11-13 21:0,1,5-7,9,12,13 22:0-7,9-15 */ ++ /* 22 bit 2 - EN_PCS_AN */ + GMAC_ANEG_REG = 0x0c, + GMAC_ANEG_FORCE_LINK_DOWN = BIT(0), + GMAC_ANEG_FORCE_LINK_PASS = BIT(1), +@@ -43,9 +54,12 @@ enum { + GMAC_ANEG_AN_SPEED_ENABLE = BIT(7), + GMAC_ANEG_CONFIG_FLOW_CTRL = BIT(8), + GMAC_ANEG_ADVERT_SYM_FLOW_CTRL = BIT(9), ++ GMAC_ANEG_ADVERT_ASYM_FLOW_CTRL = BIT(10), + GMAC_ANEG_AN_FLOW_CTRL_ENABLE = BIT(11), + GMAC_ANEG_FULL_DUPLEX = BIT(12), + GMAC_ANEG_AN_DUPLEX_ENABLE = BIT(13), ++ /* pp22: bit 14 - phy mode */ ++ /* pp22: bit 15 - choose sample tx config */ + + GMAC_STATUS_REG = 0x10, + MVGMAC_LINK_UP = BIT(0), +@@ -59,8 +73,21 @@ enum { + MVGMAC_AN_COMPLETE = BIT(11), + MVGMAC_SYNC_OK = BIT(14), + ++ /* N: 21:6-13 22: */ ++ GMAC_FIFO_CFG1_REG = 0x1c, ++ GMAC_FIFO_CFG1_TX_MIN_TH_SHIFT = 6, ++ GMAC_FIFO_CFG1_TX_MIN_TH_MASK = 0x7f << ++ GMAC_FIFO_CFG1_TX_MIN_TH_SHIFT, ++ ++ /* N:1 21: 22:0,3-7 */ + GMAC_CTRL4_REG = 0x90, ++ GMAC_CTRL4_EXT_PIN_GMII_SEL = BIT(0), + GMAC_CTRL4_SHORT_PREAMBLE_ENABLE = BIT(1), ++ GMAC_CTRL4_FC_RX_ENABLE = BIT(3), ++ GMAC_CTRL4_FC_TX_ENABLE = BIT(4), ++ GMAC_CTRL4_DP_CLK_SEL = BIT(5), ++ GMAC_CTRL4_SYNC_BYPASS = BIT(6), ++ GMAC_CTRL4_QSGMII_BYPASS = BIT(7), + + GMAC_LPI_CTRL0_REG = 0xc0, + GMAC_LPI_CTRL0_TS = 0xff << 8, +@@ -111,6 +138,47 @@ void mvgmac_disable(struct mvgmac *gmac) + } + EXPORT_SYMBOL_GPL(mvgmac_disable); + ++int mvgmac_configure(struct mvgmac *gmac, phy_interface_t phy_mode) ++{ ++ bool ext_pin_gmii; ++ u32 val; ++ ++ switch (phy_mode) { ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_SGMII: ++ ext_pin_gmii = false; ++ break; ++ ++ case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_RGMII_RXID: ++ case PHY_INTERFACE_MODE_RGMII_TXID: ++ ext_pin_gmii = true; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ if (gmac->version == MVGMAC_PP21) { ++ /* Min. TX threshold must be less than minimum packet length */ ++ val = readl_relaxed(gmac->base + GMAC_FIFO_CFG1_REG); ++ val = insert(val, GMAC_FIFO_CFG1_TX_MIN_TH_MASK, 64 - 4 - 2); ++ writel_relaxed(val, gmac->base + GMAC_FIFO_CFG1_REG); ++ } else if (gmac->version == MVGMAC_PP22) { ++ val = readl_relaxed(gmac->base + GMAC_CTRL4_REG); ++ val &= ~GMAC_CTRL4_DP_CLK_SEL; ++ val |= GMAC_CTRL4_SYNC_BYPASS; ++ val = insert(val, GMAC_CTRL4_QSGMII_BYPASS, ++ phy_mode != PHY_INTERFACE_MODE_QSGMII); ++ val = insert(val, GMAC_CTRL4_EXT_PIN_GMII_SEL, ext_pin_gmii); ++ writel_relaxed(val, gmac->base + GMAC_CTRL4_REG); ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mvgmac_configure); ++ + void mvgmac_link_unforce(struct mvgmac *gmac) + { + u32 val; +@@ -142,7 +210,7 @@ EXPORT_SYMBOL_GPL(mvgmac_link_down); + void mvgmac_link_up(struct mvgmac *gmac, int mode, int speed, int duplex, + bool tx_pause, bool rx_pause) + { +- u32 val; ++ u32 val, ctrl4; + + val = readl_relaxed(gmac->base + GMAC_ANEG_REG); + val &= ~GMAC_ANEG_CONFIG_FLOW_CTRL; +@@ -163,8 +231,19 @@ void mvgmac_link_up(struct mvgmac *gmac, int mode, int speed, int duplex, + val |= GMAC_ANEG_FULL_DUPLEX; + } + +- if (tx_pause || rx_pause) +- val |= GMAC_ANEG_CONFIG_FLOW_CTRL; ++ switch (gmac->version) { ++ case MVGMAC_NETA: ++ val = insert(val, GMAC_ANEG_CONFIG_FLOW_CTRL, ++ tx_pause || rx_pause); ++ break; ++ ++ case MVGMAC_PP22: ++ ctrl4 = readl_relaxed(gmac->base + GMAC_CTRL4_REG); ++ ctrl4 = insert(ctrl4, GMAC_CTRL4_FC_TX_ENABLE, tx_pause); ++ ctrl4 = insert(ctrl4, GMAC_CTRL4_FC_RX_ENABLE, rx_pause); ++ writel_relaxed(ctrl4, gmac->base + GMAC_CTRL4_REG); ++ break; ++ } + + writel_relaxed(val, gmac->base + GMAC_ANEG_REG); + } +@@ -252,6 +331,11 @@ int mvgmac_pcs_config(struct mvgmac *gmac, unsigned int mode, + mask |= GMAC_ANEG_ADVERT_SYM_FLOW_CTRL; + if (phylink_test(advertising, Pause)) + val |= GMAC_ANEG_ADVERT_SYM_FLOW_CTRL; ++ if (gmac->version == MVGMAC_PP22) { ++ mask |= GMAC_ANEG_ADVERT_ASYM_FLOW_CTRL; ++ if (phylink_test(advertising, Asym_Pause)) ++ val |= GMAC_ANEG_ADVERT_ASYM_FLOW_CTRL; ++ } + } + } else { + /* Phy or fixed speed - disable in-band AN modes */ +@@ -265,7 +349,8 @@ int mvgmac_pcs_config(struct mvgmac *gmac, unsigned int mode, + writel_relaxed(an, gmac->base + GMAC_ANEG_REG); + + /* We are only interested in the advertisement bits changing */ +- return !!(changed & GMAC_ANEG_ADVERT_SYM_FLOW_CTRL); ++ return !!(changed & (GMAC_ANEG_ADVERT_SYM_FLOW_CTRL | ++ GMAC_ANEG_ADVERT_ASYM_FLOW_CTRL)); + } + EXPORT_SYMBOL_GPL(mvgmac_pcs_config); + +@@ -279,7 +364,7 @@ void mvgmac_config_mac(struct mvgmac *gmac, unsigned int mode, + new_ctrl0 = gmac_ctrl0 & ~GMAC_CTRL0_PORT_1000BASE_X; + new_ctrl2 = gmac_ctrl2 & ~(GMAC_CTRL2_INBAND_AN_SGMII | + GMAC_CTRL2_PORT_RESET); +- new_ctrl4 = gmac_ctrl4 & ~GMAC_CTRL4_SHORT_PREAMBLE_ENABLE; ++ new_ctrl4 = gmac_ctrl4; + + /* Even though it might look weird, when we're configured in + * SGMII or QSGMII mode, the RGMII bit needs to be set. +@@ -303,11 +388,14 @@ void mvgmac_config_mac(struct mvgmac *gmac, unsigned int mode, + new_ctrl0 |= GMAC_CTRL0_PORT_1000BASE_X; + } + +- /* When at 2.5G, the link partner can send frames with shortened +- * preambles. +- */ +- if (state->interface == PHY_INTERFACE_MODE_2500BASEX) +- new_ctrl4 |= GMAC_CTRL4_SHORT_PREAMBLE_ENABLE; ++ if (gmac->version == MVGMAC_NETA) { ++ /* When at 2.5G, the link partner can send frames with ++ * shortened preambles. ++ */ ++ new_ctrl4 &= ~GMAC_CTRL4_SHORT_PREAMBLE_ENABLE; ++ if (state->interface == PHY_INTERFACE_MODE_2500BASEX) ++ new_ctrl4 |= GMAC_CTRL4_SHORT_PREAMBLE_ENABLE; ++ } + + if (new_ctrl0 != gmac_ctrl0) + writel_relaxed(new_ctrl0, gmac->base + GMAC_CTRL0_REG); +diff --git a/drivers/net/ethernet/marvell/mvgmac.h b/drivers/net/ethernet/marvell/mvgmac.h +index ceccc6777237..f4111fba7258 100644 +--- a/drivers/net/ethernet/marvell/mvgmac.h ++++ b/drivers/net/ethernet/marvell/mvgmac.h +@@ -14,13 +14,22 @@ struct phylink_link_state; + */ + #define MARVELL_HEADER_SIZE 2 + ++enum { ++ /* GMAC version */ ++ MVGMAC_NETA, ++ MVGMAC_PP21, ++ MVGMAC_PP22, ++}; ++ + struct mvgmac { + void __iomem *base; ++ unsigned int version; + }; + + void mvgmac_set_max_rx_size(struct mvgmac *gmac, size_t max_rx_size); + void mvgmac_enable(struct mvgmac *gmac); + void mvgmac_disable(struct mvgmac *gmac); ++int mvgmac_configure(struct mvgmac *gmac, phy_interface_t phy_mode); + void mvgmac_link_unforce(struct mvgmac *gmac); + void mvgmac_link_force_down(struct mvgmac *gmac); + void mvgmac_link_down(struct mvgmac *gmac, int mode); +diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c +index 750915bb2b28..968005a27553 100644 +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -5072,6 +5072,7 @@ static int mvneta_probe(struct platform_device *pdev) + } + + pp->gmac.base = pp->base + MVNETA_GMAC_BASE; ++ pp->gmac.version = MVGMAC_NETA; + pp->tx_lpi_timer = 16; + + /* Alloc per-cpu port structure */ +-- +cgit v1.2.3 + + +From 9092b4d7a5b31489cabf279759771ad85e450a2d Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Thu, 1 Jul 2021 17:10:11 +0100 +Subject: net: mvneta: deny disabling autoneg for 802.3z modes +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The documentation for Armada 38x says: + + Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ... + When = 1 (1000BASE-X) this field must be set to 1. + +We presently ignore whether userspace requests autonegotiation or not +through the ethtool ksettings interface. However, we have some network +interfaces that wish to do this. To offer a consistent API across +network interfaces, deny the ability to disable autonegotiation on +mvneta hardware when in 1000BASE-X and 2500BASE-X. + +This means the only way to switch between 2500BASE-X and 1000BASE-X +on SFPs that support this will be: + + # ethtool -s ethX advertise 0x20000002000 # 1000BASE-X Pause + # ethtool -s ethX advertise 0xa000 # 2500BASE-X Pause + +Signed-off-by: Russell King (Oracle) +Acked-by: Marek Behún +--- + drivers/net/ethernet/marvell/mvneta.c | 20 ++++++++++++++------ + 1 file changed, 14 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c +index 968005a27553..0703c2e796a6 100644 +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -3811,12 +3811,20 @@ static void mvneta_validate(struct phylink_config *config, + struct mvneta_port *pp = netdev_priv(ndev); + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + +- /* We only support QSGMII, SGMII, 802.3z and RGMII modes */ +- if (state->interface != PHY_INTERFACE_MODE_NA && +- state->interface != PHY_INTERFACE_MODE_QSGMII && +- state->interface != PHY_INTERFACE_MODE_SGMII && +- !phy_interface_mode_is_8023z(state->interface) && +- !phy_interface_mode_is_rgmii(state->interface)) { ++ /* We only support QSGMII, SGMII, 802.3z and RGMII modes. ++ * When in 802.3z mode, we must have AN enabled: ++ * "Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ... ++ * When = 1 (1000BASE-X) this field must be set to 1." ++ */ ++ if (phy_interface_mode_is_8023z(state->interface)) { ++ if (!phylink_test(state->advertising, Autoneg)) { ++ bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ return; ++ } ++ } else if (state->interface != PHY_INTERFACE_MODE_NA && ++ state->interface != PHY_INTERFACE_MODE_QSGMII && ++ state->interface != PHY_INTERFACE_MODE_SGMII && ++ !phy_interface_mode_is_rgmii(state->interface)) { + bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + return; + } +-- +cgit v1.2.3 + diff --git a/patch/kernel/archive/mvebu-5.14/20-pcie-bridge-emul.patch b/patch/kernel/archive/mvebu-5.14/20-pcie-bridge-emul.patch new file mode 100644 index 0000000000..7bff82bff6 --- /dev/null +++ b/patch/kernel/archive/mvebu-5.14/20-pcie-bridge-emul.patch @@ -0,0 +1,104 @@ +From a99411d3be343e87de6e89df7b96913d723b8b17 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Tue, 2 Feb 2021 13:45:28 +0000 +Subject: PCI: pci-bridge-emul: re-arrange register tests + +Re-arrange the tests for which sets of registers are being accessed +so that it is easier to add further regions later. No functional +change. + +Signed-off-by: Russell King +--- + drivers/pci/pci-bridge-emul.c | 53 ++++++++++++++++++++++--------------------- + 1 file changed, 27 insertions(+), 26 deletions(-) + +diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c +index fdaf86a888b7..9988078e7b0e 100644 +--- a/drivers/pci/pci-bridge-emul.c ++++ b/drivers/pci/pci-bridge-emul.c +@@ -328,25 +328,25 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, + __le32 *cfgspace; + const struct pci_bridge_reg_behavior *behavior; + +- if (bridge->has_pcie && reg >= PCI_CAP_PCIE_END) { +- *value = 0; +- return PCIBIOS_SUCCESSFUL; +- } +- +- if (!bridge->has_pcie && reg >= PCI_BRIDGE_CONF_END) { ++ if (reg < PCI_BRIDGE_CONF_END) { ++ /* Emulated PCI space */ ++ read_op = bridge->ops->read_base; ++ cfgspace = (__le32 *) &bridge->conf; ++ behavior = bridge->pci_regs_behavior; ++ } else if (!bridge->has_pcie) { ++ /* PCIe space is not implemented, and no PCI capabilities */ + *value = 0; + return PCIBIOS_SUCCESSFUL; +- } +- +- if (bridge->has_pcie && reg >= PCI_CAP_PCIE_START) { ++ } else if (reg < PCI_CAP_PCIE_END) { ++ /* Our emulated PCIe capability */ + reg -= PCI_CAP_PCIE_START; + read_op = bridge->ops->read_pcie; + cfgspace = (__le32 *) &bridge->pcie_conf; + behavior = bridge->pcie_cap_regs_behavior; + } else { +- read_op = bridge->ops->read_base; +- cfgspace = (__le32 *) &bridge->conf; +- behavior = bridge->pci_regs_behavior; ++ /* Beyond our PCIe space */ ++ *value = 0; ++ return PCIBIOS_SUCCESSFUL; + } + + if (read_op) +@@ -390,11 +390,23 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, + __le32 *cfgspace; + const struct pci_bridge_reg_behavior *behavior; + +- if (bridge->has_pcie && reg >= PCI_CAP_PCIE_END) ++ if (reg < PCI_BRIDGE_CONF_END) { ++ /* Emulated PCI space */ ++ write_op = bridge->ops->write_base; ++ cfgspace = (__le32 *) &bridge->conf; ++ behavior = bridge->pci_regs_behavior; ++ } else if (!bridge->has_pcie) { ++ /* PCIe space is not implemented, and no PCI capabilities */ + return PCIBIOS_SUCCESSFUL; +- +- if (!bridge->has_pcie && reg >= PCI_BRIDGE_CONF_END) ++ } else if (reg < PCI_CAP_PCIE_END) { ++ /* Our emulated PCIe capability */ ++ reg -= PCI_CAP_PCIE_START; ++ write_op = bridge->ops->write_pcie; ++ cfgspace = (__le32 *) &bridge->pcie_conf; ++ behavior = bridge->pcie_cap_regs_behavior; ++ } else { + return PCIBIOS_SUCCESSFUL; ++ } + + shift = (where & 0x3) * 8; + +@@ -411,17 +423,6 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, + if (ret != PCIBIOS_SUCCESSFUL) + return ret; + +- if (bridge->has_pcie && reg >= PCI_CAP_PCIE_START) { +- reg -= PCI_CAP_PCIE_START; +- write_op = bridge->ops->write_pcie; +- cfgspace = (__le32 *) &bridge->pcie_conf; +- behavior = bridge->pcie_cap_regs_behavior; +- } else { +- write_op = bridge->ops->write_base; +- cfgspace = (__le32 *) &bridge->conf; +- behavior = bridge->pci_regs_behavior; +- } +- + /* Keep all bits, except the RW bits */ + new = old & (~mask | ~behavior[reg / 4].rw); + +-- +cgit v1.2.3 + diff --git a/patch/kernel/archive/mvebu-5.14/21-pcie-bridge-emul.patch b/patch/kernel/archive/mvebu-5.14/21-pcie-bridge-emul.patch new file mode 100644 index 0000000000..9551ced617 --- /dev/null +++ b/patch/kernel/archive/mvebu-5.14/21-pcie-bridge-emul.patch @@ -0,0 +1,145 @@ +From bab91927c8694e5f3a7e834343d2ec7163da4988 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Tue, 2 Feb 2021 13:57:04 +0000 +Subject: PCI: pci-bridge-emul: add support for PCIe extended capabilities + +Add support for PCIe extended capabilities, which we just redirect to +the emulating driver. + +Signed-off-by: Russell King +--- + drivers/pci/pci-bridge-emul.c | 52 +++++++++++++++++++++++++++++++------------ + drivers/pci/pci-bridge-emul.h | 15 +++++++++++++ + 2 files changed, 53 insertions(+), 14 deletions(-) + +diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c +index 9988078e7b0e..fbff7da94245 100644 +--- a/drivers/pci/pci-bridge-emul.c ++++ b/drivers/pci/pci-bridge-emul.c +@@ -343,10 +343,16 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, + read_op = bridge->ops->read_pcie; + cfgspace = (__le32 *) &bridge->pcie_conf; + behavior = bridge->pcie_cap_regs_behavior; +- } else { +- /* Beyond our PCIe space */ ++ } else if (reg < PCI_CFG_SPACE_SIZE) { ++ /* Rest of PCI space not implemented */ + *value = 0; + return PCIBIOS_SUCCESSFUL; ++ } else { ++ /* PCIe extended capability space */ ++ reg -= PCI_CFG_SPACE_SIZE; ++ read_op = bridge->ops->read_ext; ++ cfgspace = NULL; ++ behavior = NULL; + } + + if (read_op) +@@ -354,15 +360,20 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, + else + ret = PCI_BRIDGE_EMUL_NOT_HANDLED; + +- if (ret == PCI_BRIDGE_EMUL_NOT_HANDLED) +- *value = le32_to_cpu(cfgspace[reg / 4]); ++ if (ret == PCI_BRIDGE_EMUL_NOT_HANDLED) { ++ if (cfgspace) ++ *value = le32_to_cpu(cfgspace[reg / 4]); ++ else ++ *value = 0; ++ } + + /* + * Make sure we never return any reserved bit with a value + * different from 0. + */ +- *value &= behavior[reg / 4].ro | behavior[reg / 4].rw | +- behavior[reg / 4].w1c; ++ if (behavior) ++ *value &= behavior[reg / 4].ro | behavior[reg / 4].rw | ++ behavior[reg / 4].w1c; + + if (size == 1) + *value = (*value >> (8 * (where & 3))) & 0xff; +@@ -404,8 +415,15 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, + write_op = bridge->ops->write_pcie; + cfgspace = (__le32 *) &bridge->pcie_conf; + behavior = bridge->pcie_cap_regs_behavior; +- } else { ++ } else if (reg < PCI_CFG_SPACE_SIZE) { ++ /* Rest of PCI space not implemented */ + return PCIBIOS_SUCCESSFUL; ++ } else { ++ /* PCIe extended capability space */ ++ reg -= PCI_CFG_SPACE_SIZE; ++ write_op = bridge->ops->write_ext; ++ cfgspace = NULL; ++ behavior = NULL; + } + + shift = (where & 0x3) * 8; +@@ -423,16 +441,22 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, + if (ret != PCIBIOS_SUCCESSFUL) + return ret; + +- /* Keep all bits, except the RW bits */ +- new = old & (~mask | ~behavior[reg / 4].rw); ++ if (behavior) { ++ /* Keep all bits, except the RW bits */ ++ new = old & (~mask | ~behavior[reg / 4].rw); + +- /* Update the value of the RW bits */ +- new |= (value << shift) & (behavior[reg / 4].rw & mask); ++ /* Update the value of the RW bits */ ++ new |= (value << shift) & (behavior[reg / 4].rw & mask); + +- /* Clear the W1C bits */ +- new &= ~((value << shift) & (behavior[reg / 4].w1c & mask)); ++ /* Clear the W1C bits */ ++ new &= ~((value << shift) & (behavior[reg / 4].w1c & mask)); ++ } else { ++ new = old & ~mask; ++ new |= (value << shift) & mask; ++ } + +- cfgspace[reg / 4] = cpu_to_le32(new); ++ if (cfgspace) ++ cfgspace[reg / 4] = cpu_to_le32(new); + + if (write_op) + write_op(bridge, reg, old, new, mask); +diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h +index b31883022a8e..5f64560aaa26 100644 +--- a/drivers/pci/pci-bridge-emul.h ++++ b/drivers/pci/pci-bridge-emul.h +@@ -90,6 +90,14 @@ struct pci_bridge_emul_ops { + */ + pci_bridge_emul_read_status_t (*read_pcie)(struct pci_bridge_emul *bridge, + int reg, u32 *value); ++ ++ /* ++ * Same as ->read_base(), except it is for reading from the ++ * PCIe extended capability configuration space. ++ */ ++ pci_bridge_emul_read_status_t (*read_ext)(struct pci_bridge_emul *bridge, ++ int reg, u32 *value); ++ + /* + * Called when writing to the regular PCI bridge configuration + * space. old is the current value, new is the new value being +@@ -105,6 +113,13 @@ struct pci_bridge_emul_ops { + */ + void (*write_pcie)(struct pci_bridge_emul *bridge, int reg, + u32 old, u32 new, u32 mask); ++ ++ /* ++ * Same as ->write_base(), except it is for writing from the ++ * PCIe extended capability configuration space. ++ */ ++ void (*write_ext)(struct pci_bridge_emul *bridge, int reg, ++ u32 old, u32 new, u32 mask); + }; + + struct pci_bridge_reg_behavior; +-- +cgit v1.2.3 + diff --git a/patch/kernel/archive/mvebu-5.14/402-sfp-display-SFP-module-information.patch b/patch/kernel/archive/mvebu-5.14/402-sfp-display-SFP-module-information.patch deleted file mode 100644 index cc5d2fa393..0000000000 --- a/patch/kernel/archive/mvebu-5.14/402-sfp-display-SFP-module-information.patch +++ /dev/null @@ -1,432 +0,0 @@ -From 88e942a0b703fe54dad925f27f033869e4f10fba Mon Sep 17 00:00:00 2001 -From: Russell King -Date: Sun, 13 Sep 2015 01:06:31 +0100 -Subject: [PATCH] net: sfp: display SFP module information [*not for - mainline*] - -Display SFP module information verbosely, splitting the generic parts -into a separate file. - -Signed-off-by: Russell King ---- - drivers/net/phy/Makefile | 2 +- - drivers/net/phy/sff.c | 114 ++++++++++++++++++++ - drivers/net/phy/sff.h | 16 +++ - drivers/net/phy/sfp.c | 228 +++++++++++++++++++++++++++++++++++++-- - 4 files changed, 349 insertions(+), 11 deletions(-) - create mode 100644 drivers/net/phy/sff.c - create mode 100644 drivers/net/phy/sff.h - ---- a/drivers/net/phy/Makefile -+++ b/drivers/net/phy/Makefile -@@ -26,7 +26,7 @@ obj-$(CONFIG_PHYLIB) += libphy.o - - obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += mii_timestamper.o - --obj-$(CONFIG_SFP) += sfp.o -+obj-$(CONFIG_SFP) += sff.o sfp.o - sfp-obj-$(CONFIG_SFP) += sfp-bus.o - obj-y += $(sfp-obj-y) $(sfp-obj-m) - ---- /dev/null -+++ b/drivers/net/phy/sff.c -@@ -0,0 +1,114 @@ -+#include -+#include -+#include "sff.h" -+ -+const char *sff_link_len(char *buf, size_t size, unsigned int length, -+ unsigned int multiplier) -+{ -+ if (length == 0) -+ return "unsupported/unspecified"; -+ -+ if (length == 255) { -+ *buf++ = '>'; -+ size -= 1; -+ length -= 1; -+ } -+ -+ length *= multiplier; -+ -+ if (length >= 1000) -+ snprintf(buf, size, "%u.%0*ukm", -+ length / 1000, -+ multiplier > 100 ? 1 : -+ multiplier > 10 ? 2 : 3, -+ length % 1000); -+ else -+ snprintf(buf, size, "%um", length); -+ -+ return buf; -+} -+EXPORT_SYMBOL_GPL(sff_link_len); -+ -+const char *sff_bitfield(char *buf, size_t size, -+ const struct sff_bitfield *bits, unsigned int val) -+{ -+ char *p = buf; -+ int n; -+ -+ *p = '\0'; -+ while (bits->mask) { -+ if ((val & bits->mask) == bits->val) { -+ n = snprintf(p, size, "%s%s", -+ buf != p ? ", " : "", -+ bits->str); -+ if (n == size) -+ break; -+ p += n; -+ size -= n; -+ } -+ bits++; -+ } -+ -+ return buf; -+} -+EXPORT_SYMBOL_GPL(sff_bitfield); -+ -+const char *sff_connector(unsigned int connector) -+{ -+ switch (connector) { -+ case SFF8024_CONNECTOR_UNSPEC: -+ return "unknown/unspecified"; -+ case SFF8024_CONNECTOR_SC: -+ return "SC"; -+ case SFF8024_CONNECTOR_FIBERJACK: -+ return "Fiberjack"; -+ case SFF8024_CONNECTOR_LC: -+ return "LC"; -+ case SFF8024_CONNECTOR_MT_RJ: -+ return "MT-RJ"; -+ case SFF8024_CONNECTOR_MU: -+ return "MU"; -+ case SFF8024_CONNECTOR_SG: -+ return "SG"; -+ case SFF8024_CONNECTOR_OPTICAL_PIGTAIL: -+ return "Optical pigtail"; -+ case SFF8024_CONNECTOR_MPO_1X12: -+ return "MPO 1X12"; -+ case SFF8024_CONNECTOR_MPO_2X16: -+ return "MPO 2X16"; -+ case SFF8024_CONNECTOR_HSSDC_II: -+ return "HSSDC II"; -+ case SFF8024_CONNECTOR_COPPER_PIGTAIL: -+ return "Copper pigtail"; -+ case SFF8024_CONNECTOR_RJ45: -+ return "RJ45"; -+ case SFF8024_CONNECTOR_MXC_2X16: -+ return "MXC 2X16"; -+ default: -+ return "unknown"; -+ } -+} -+EXPORT_SYMBOL_GPL(sff_connector); -+ -+const char *sff_encoding(unsigned int encoding) -+{ -+ switch (encoding) { -+ case SFF8024_ENCODING_UNSPEC: -+ return "unspecified"; -+ case SFF8024_ENCODING_8472_64B66B: -+ return "64b66b"; -+ case SFF8024_ENCODING_8B10B: -+ return "8b10b"; -+ case SFF8024_ENCODING_4B5B: -+ return "4b5b"; -+ case SFF8024_ENCODING_NRZ: -+ return "NRZ"; -+ case SFF8024_ENCODING_8472_MANCHESTER: -+ return "MANCHESTER"; -+ default: -+ return "unknown"; -+ } -+} -+EXPORT_SYMBOL_GPL(sff_encoding); -+ -+MODULE_LICENSE("GPL"); ---- /dev/null -+++ b/drivers/net/phy/sff.h -@@ -0,0 +1,16 @@ -+#ifndef SFF_H -+#define SFF_H -+ -+struct sff_bitfield { -+ unsigned int mask; -+ unsigned int val; -+ const char *str; -+}; -+ -+const char *sff_link_len(char *buf, size_t size, unsigned int length, -+ unsigned int multiplier); -+const char *sff_bitfield(char *buf, size_t size, -+ const struct sff_bitfield *bits, unsigned int val); -+const char *sff_connector(unsigned int connector); -+const char *sff_encoding(unsigned int encoding); -+#endif ---- a/drivers/net/phy/sfp.c -+++ b/drivers/net/phy/sfp.c -@@ -17,6 +17,7 @@ - #include - #include - -+#include "sff.h" - #include "sfp.h" - #include "swphy.h" - -@@ -1363,6 +1364,114 @@ static void sfp_hwmon_exit(struct sfp *s - } - #endif - -+static const struct sff_bitfield sfp_options[] = { -+ { -+ .mask = SFP_OPTIONS_HIGH_POWER_LEVEL, -+ .val = SFP_OPTIONS_HIGH_POWER_LEVEL, -+ .str = "hpl", -+ }, { -+ .mask = SFP_OPTIONS_PAGING_A2, -+ .val = SFP_OPTIONS_PAGING_A2, -+ .str = "paginga2", -+ }, { -+ .mask = SFP_OPTIONS_RETIMER, -+ .val = SFP_OPTIONS_RETIMER, -+ .str = "retimer", -+ }, { -+ .mask = SFP_OPTIONS_COOLED_XCVR, -+ .val = SFP_OPTIONS_COOLED_XCVR, -+ .str = "cooled", -+ }, { -+ .mask = SFP_OPTIONS_POWER_DECL, -+ .val = SFP_OPTIONS_POWER_DECL, -+ .str = "powerdecl", -+ }, { -+ .mask = SFP_OPTIONS_RX_LINEAR_OUT, -+ .val = SFP_OPTIONS_RX_LINEAR_OUT, -+ .str = "rxlinear", -+ }, { -+ .mask = SFP_OPTIONS_RX_DECISION_THRESH, -+ .val = SFP_OPTIONS_RX_DECISION_THRESH, -+ .str = "rxthresh", -+ }, { -+ .mask = SFP_OPTIONS_TUNABLE_TX, -+ .val = SFP_OPTIONS_TUNABLE_TX, -+ .str = "tunabletx", -+ }, { -+ .mask = SFP_OPTIONS_RATE_SELECT, -+ .val = SFP_OPTIONS_RATE_SELECT, -+ .str = "ratesel", -+ }, { -+ .mask = SFP_OPTIONS_TX_DISABLE, -+ .val = SFP_OPTIONS_TX_DISABLE, -+ .str = "txdisable", -+ }, { -+ .mask = SFP_OPTIONS_TX_FAULT, -+ .val = SFP_OPTIONS_TX_FAULT, -+ .str = "txfault", -+ }, { -+ .mask = SFP_OPTIONS_LOS_INVERTED, -+ .val = SFP_OPTIONS_LOS_INVERTED, -+ .str = "los-", -+ }, { -+ .mask = SFP_OPTIONS_LOS_NORMAL, -+ .val = SFP_OPTIONS_LOS_NORMAL, -+ .str = "los+", -+ }, { } -+}; -+ -+static const struct sff_bitfield diagmon[] = { -+ { -+ .mask = SFP_DIAGMON_DDM, -+ .val = SFP_DIAGMON_DDM, -+ .str = "ddm", -+ }, { -+ .mask = SFP_DIAGMON_INT_CAL, -+ .val = SFP_DIAGMON_INT_CAL, -+ .str = "intcal", -+ }, { -+ .mask = SFP_DIAGMON_EXT_CAL, -+ .val = SFP_DIAGMON_EXT_CAL, -+ .str = "extcal", -+ }, { -+ .mask = SFP_DIAGMON_RXPWR_AVG, -+ .val = SFP_DIAGMON_RXPWR_AVG, -+ .str = "rxpwravg", -+ }, { } -+}; -+ -+static const struct sff_bitfield sfp_enhopts[] = { -+ { -+ .mask = SFP_ENHOPTS_ALARMWARN, -+ .val = SFP_ENHOPTS_ALARMWARN, -+ .str = "alarmwarn", -+ }, { -+ .mask = SFP_ENHOPTS_SOFT_TX_DISABLE, -+ .val = SFP_ENHOPTS_SOFT_TX_DISABLE, -+ .str = "soft_tx_dis", -+ }, { -+ .mask = SFP_ENHOPTS_SOFT_TX_FAULT, -+ .val = SFP_ENHOPTS_SOFT_TX_FAULT, -+ .str = "soft_tx_fault", -+ }, { -+ .mask = SFP_ENHOPTS_SOFT_RX_LOS, -+ .val = SFP_ENHOPTS_SOFT_RX_LOS, -+ .str = "soft_rx_los", -+ }, { -+ .mask = SFP_ENHOPTS_SOFT_RATE_SELECT, -+ .val = SFP_ENHOPTS_SOFT_RATE_SELECT, -+ .str = "soft_rs", -+ }, { -+ .mask = SFP_ENHOPTS_APP_SELECT_SFF8079, -+ .val = SFP_ENHOPTS_APP_SELECT_SFF8079, -+ .str = "app_sel", -+ }, { -+ .mask = SFP_ENHOPTS_SOFT_RATE_SFF8431, -+ .val = SFP_ENHOPTS_SOFT_RATE_SFF8431, -+ .str = "soft_r8431", -+ }, { } -+}; -+ - /* Helpers */ - static void sfp_module_tx_disable(struct sfp *sfp) - { -@@ -1664,6 +1773,110 @@ static int sfp_cotsworks_fixup_check(str - return 0; - } - -+static void sfp_print_module_info(struct sfp *sfp, const struct sfp_eeprom_id *id, bool cotsworks) -+{ -+ unsigned int br_nom, br_min, br_max; -+ char date[9]; -+ char options[80]; -+ -+ /* Cotsworks also gets the date code wrong. */ -+ date[0] = id->ext.datecode[4 - 2 * cotsworks]; -+ date[1] = id->ext.datecode[5 - 2 * cotsworks]; -+ date[2] = '-'; -+ date[3] = id->ext.datecode[2 + 2 * cotsworks]; -+ date[4] = id->ext.datecode[3 + 2 * cotsworks]; -+ date[5] = '-'; -+ date[6] = id->ext.datecode[0]; -+ date[7] = id->ext.datecode[1]; -+ date[8] = '\0'; -+ -+ if (id->base.br_nominal == 0) { -+ br_min = br_nom = br_max = 0; -+ } else if (id->base.br_nominal == 255) { -+ br_nom = 250 * id->ext.br_max; -+ br_max = br_nom + br_nom * id->ext.br_min / 100; -+ br_min = br_nom - br_nom * id->ext.br_min / 100; -+ } else { -+ br_nom = id->base.br_nominal * 100; -+ br_min = br_nom - id->base.br_nominal * id->ext.br_min; -+ br_max = br_nom + id->base.br_nominal * id->ext.br_max; -+ } -+ -+ dev_info(sfp->dev, "module %.*s %.*s rev %.*s sn %.*s dc %s\n", -+ (int)sizeof(id->base.vendor_name), id->base.vendor_name, -+ (int)sizeof(id->base.vendor_pn), id->base.vendor_pn, -+ (int)sizeof(id->base.vendor_rev), id->base.vendor_rev, -+ (int)sizeof(id->ext.vendor_sn), id->ext.vendor_sn, date); -+ dev_info(sfp->dev, " %s connector, encoding %s, bitrate %u.%03u (%u.%03u-%u.%03u) Gbps\n", -+ sff_connector(id->base.connector), -+ sff_encoding(id->base.encoding), -+ br_nom / 1000, br_nom % 1000, -+ br_min / 1000, br_min % 1000, br_max / 1000, br_max % 1000); -+ dev_info(sfp->dev, " 1000BaseSX%c 1000BaseLX%c 1000BaseCX%c 1000BaseT%c 100BaseLX%c 100BaseFX%c BaseBX10%c BasePX%c\n", -+ id->base.e1000_base_sx ? '+' : '-', -+ id->base.e1000_base_lx ? '+' : '-', -+ id->base.e1000_base_cx ? '+' : '-', -+ id->base.e1000_base_t ? '+' : '-', -+ id->base.e100_base_lx ? '+' : '-', -+ id->base.e100_base_fx ? '+' : '-', -+ id->base.e_base_bx10 ? '+' : '-', -+ id->base.e_base_px ? '+' : '-'); -+ dev_info(sfp->dev, " 10GBaseSR%c 10GBaseLR%c 10GBaseLRM%c 10GBaseER%c\n", -+ id->base.e10g_base_sr ? '+' : '-', -+ id->base.e10g_base_lr ? '+' : '-', -+ id->base.e10g_base_lrm ? '+' : '-', -+ id->base.e10g_base_er ? '+' : '-'); -+ -+ if (!id->base.sfp_ct_passive && !id->base.sfp_ct_active && -+ !id->base.e1000_base_t) { -+ char len_9um[16], len_om[16]; -+ -+ dev_info(sfp->dev, " Wavelength %unm, fiber lengths:\n", -+ be16_to_cpup(&id->base.optical_wavelength)); -+ -+ if (id->base.link_len[0] == 255) -+ strcpy(len_9um, ">254km"); -+ else if (id->base.link_len[1] && id->base.link_len[1] != 255) -+ sprintf(len_9um, "%um", -+ id->base.link_len[1] * 100); -+ else if (id->base.link_len[0]) -+ sprintf(len_9um, "%ukm", id->base.link_len[0]); -+ else if (id->base.link_len[1] == 255) -+ strcpy(len_9um, ">25.4km"); -+ else -+ strcpy(len_9um, "unsupported"); -+ -+ dev_info(sfp->dev, " 9µm SM : %s\n", len_9um); -+ dev_info(sfp->dev, " 62.5µm MM OM1: %s\n", -+ sff_link_len(len_om, sizeof(len_om), -+ id->base.link_len[3], 10)); -+ dev_info(sfp->dev, " 50µm MM OM2: %s\n", -+ sff_link_len(len_om, sizeof(len_om), -+ id->base.link_len[2], 10)); -+ dev_info(sfp->dev, " 50µm MM OM3: %s\n", -+ sff_link_len(len_om, sizeof(len_om), -+ id->base.link_len[5], 10)); -+ dev_info(sfp->dev, " 50µm MM OM4: %s\n", -+ sff_link_len(len_om, sizeof(len_om), -+ id->base.link_len[4], 10)); -+ } else { -+ char len[16]; -+ dev_info(sfp->dev, " Copper length: %s\n", -+ sff_link_len(len, sizeof(len), -+ id->base.link_len[4], 1)); -+ } -+ -+ dev_info(sfp->dev, " Options: %s\n", -+ sff_bitfield(options, sizeof(options), sfp_options, -+ be16_to_cpu(id->ext.options))); -+ dev_info(sfp->dev, " Diagnostics: %s\n", -+ sff_bitfield(options, sizeof(options), diagmon, -+ id->ext.diagmon)); -+ dev_info(sfp->dev, " EnhOpts: %s\n", -+ sff_bitfield(options, sizeof(options), sfp_enhopts, -+ id->ext.enhopts)); -+} -+ - static int sfp_sm_mod_probe(struct sfp *sfp, bool report) - { - /* SFP module inserted - read I2C data */ -@@ -1685,9 +1898,9 @@ static int sfp_sm_mod_probe(struct sfp * - return -EAGAIN; - } - -- /* Cotsworks do not seem to update the checksums when they -- * do the final programming with the final module part number, -- * serial number and date code. -+ /* Cotsworks do not seem to update the checksums when they update the -+ * module part number, serial number and date code. They also format -+ * the date code incorrectly. - */ - cotsworks = !memcmp(id.base.vendor_name, "COTSWORKS ", 16); - cotsworks_sfbg = !memcmp(id.base.vendor_pn, "SFBG", 4); -@@ -1735,14 +1948,9 @@ static int sfp_sm_mod_probe(struct sfp * - } - } - -- sfp->id = id; -+ sfp_print_module_info(sfp, &id, cotsworks); - -- dev_info(sfp->dev, "module %.*s %.*s rev %.*s sn %.*s dc %.*s\n", -- (int)sizeof(id.base.vendor_name), id.base.vendor_name, -- (int)sizeof(id.base.vendor_pn), id.base.vendor_pn, -- (int)sizeof(id.base.vendor_rev), id.base.vendor_rev, -- (int)sizeof(id.ext.vendor_sn), id.ext.vendor_sn, -- (int)sizeof(id.ext.datecode), id.ext.datecode); -+ sfp->id = id; - - /* Check whether we support this module */ - if (!sfp->type->module_supported(&id)) {