From f70575805708cabdedea7498aaa3f710fde4d920 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Wed, 31 Jan 2024 03:29:01 +0000 Subject: [PATCH] add lvds1024*800 --- kernel/drivers/net/dsa/mv88e6xxx/port.c | 454 +++++++++++++++++++++++++++++++++++++------------------- 1 files changed, 300 insertions(+), 154 deletions(-) diff --git a/kernel/drivers/net/dsa/mv88e6xxx/port.c b/kernel/drivers/net/dsa/mv88e6xxx/port.c index 2f16a31..dfd9e82 100644 --- a/kernel/drivers/net/dsa/mv88e6xxx/port.c +++ b/kernel/drivers/net/dsa/mv88e6xxx/port.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Marvell 88E6xxx Switch Port Registers support * @@ -5,11 +6,6 @@ * * Copyright (c) 2016-2017 Savoir-faire Linux Inc. * Vivien Didelot <vivien.didelot@savoirfairelinux.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/bitfield.h> @@ -166,46 +162,9 @@ return 0; } -int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup) -{ - u16 reg; - int err; - - err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, ®); - if (err) - return err; - - reg &= ~(MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX | - MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL); - - switch (dup) { - case DUPLEX_HALF: - reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX; - break; - case DUPLEX_FULL: - reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX | - MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL; - break; - case DUPLEX_UNFORCED: - /* normal duplex detection */ - break; - default: - return -EOPNOTSUPP; - } - - err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg); - if (err) - return err; - - dev_dbg(chip->dev, "p%d: %s %s duplex\n", port, - reg & MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX ? "Force" : "Unforce", - reg & MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL ? "full" : "half"); - - return 0; -} - -static int mv88e6xxx_port_set_speed(struct mv88e6xxx_chip *chip, int port, - int speed, bool alt_bit, bool force_bit) +static int mv88e6xxx_port_set_speed_duplex(struct mv88e6xxx_chip *chip, + int port, int speed, bool alt_bit, + bool force_bit, int duplex) { u16 reg, ctrl; int err; @@ -243,11 +202,29 @@ return -EOPNOTSUPP; } + switch (duplex) { + case DUPLEX_HALF: + ctrl |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX; + break; + case DUPLEX_FULL: + ctrl |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX | + MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL; + break; + case DUPLEX_UNFORCED: + /* normal duplex detection */ + break; + default: + return -EOPNOTSUPP; + } + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, ®); if (err) return err; - reg &= ~MV88E6XXX_PORT_MAC_CTL_SPEED_MASK; + reg &= ~(MV88E6XXX_PORT_MAC_CTL_SPEED_MASK | + MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX | + MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL); + if (alt_bit) reg &= ~MV88E6390_PORT_MAC_CTL_ALTSPEED; if (force_bit) { @@ -265,12 +242,16 @@ dev_dbg(chip->dev, "p%d: Speed set to %d Mbps\n", port, speed); else dev_dbg(chip->dev, "p%d: Speed unforced\n", port); + dev_dbg(chip->dev, "p%d: %s %s duplex\n", port, + reg & MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX ? "Force" : "Unforce", + reg & MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL ? "full" : "half"); return 0; } /* Support 10, 100, 200 Mbps (e.g. 88E6065 family) */ -int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +int mv88e6065_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex) { if (speed == SPEED_MAX) speed = 200; @@ -279,11 +260,13 @@ return -EOPNOTSUPP; /* Setting 200 Mbps on port 0 to 3 selects 100 Mbps */ - return mv88e6xxx_port_set_speed(chip, port, speed, false, false); + return mv88e6xxx_port_set_speed_duplex(chip, port, speed, false, false, + duplex); } /* Support 10, 100, 1000 Mbps (e.g. 88E6185 family) */ -int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +int mv88e6185_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex) { if (speed == SPEED_MAX) speed = 1000; @@ -291,11 +274,27 @@ if (speed == 200 || speed > 1000) return -EOPNOTSUPP; - return mv88e6xxx_port_set_speed(chip, port, speed, false, false); + return mv88e6xxx_port_set_speed_duplex(chip, port, speed, false, false, + duplex); +} + +/* Support 10, 100 Mbps (e.g. 88E6250 family) */ +int mv88e6250_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex) +{ + if (speed == SPEED_MAX) + speed = 100; + + if (speed > 100) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_speed_duplex(chip, port, speed, false, false, + duplex); } /* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6341) */ -int mv88e6341_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +int mv88e6341_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex) { if (speed == SPEED_MAX) speed = port < 5 ? 1000 : 2500; @@ -309,11 +308,21 @@ if (speed == 2500 && port < 5) return -EOPNOTSUPP; - return mv88e6xxx_port_set_speed(chip, port, speed, !port, true); + return mv88e6xxx_port_set_speed_duplex(chip, port, speed, !port, true, + duplex); +} + +phy_interface_t mv88e6341_port_max_speed_mode(int port) +{ + if (port == 5) + return PHY_INTERFACE_MODE_2500BASEX; + + return PHY_INTERFACE_MODE_NA; } /* Support 10, 100, 200, 1000 Mbps (e.g. 88E6352 family) */ -int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +int mv88e6352_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex) { if (speed == SPEED_MAX) speed = 1000; @@ -324,11 +333,13 @@ if (speed == 200 && port < 5) return -EOPNOTSUPP; - return mv88e6xxx_port_set_speed(chip, port, speed, true, false); + return mv88e6xxx_port_set_speed_duplex(chip, port, speed, true, false, + duplex); } /* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6390) */ -int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +int mv88e6390_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex) { if (speed == SPEED_MAX) speed = port < 9 ? 1000 : 2500; @@ -342,11 +353,21 @@ if (speed == 2500 && port < 9) return -EOPNOTSUPP; - return mv88e6xxx_port_set_speed(chip, port, speed, true, true); + return mv88e6xxx_port_set_speed_duplex(chip, port, speed, true, true, + duplex); +} + +phy_interface_t mv88e6390_port_max_speed_mode(int port) +{ + if (port == 9 || port == 10) + return PHY_INTERFACE_MODE_2500BASEX; + + return PHY_INTERFACE_MODE_NA; } /* Support 10, 100, 200, 1000, 2500, 10000 Mbps (e.g. 88E6190X) */ -int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +int mv88e6390x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex) { if (speed == SPEED_MAX) speed = port < 9 ? 1000 : 10000; @@ -357,26 +378,35 @@ if (speed >= 2500 && port < 9) return -EOPNOTSUPP; - return mv88e6xxx_port_set_speed(chip, port, speed, true, true); + return mv88e6xxx_port_set_speed_duplex(chip, port, speed, true, true, + duplex); } -int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, - phy_interface_t mode) +phy_interface_t mv88e6390x_port_max_speed_mode(int port) { - int lane; + if (port == 9 || port == 10) + return PHY_INTERFACE_MODE_XAUI; + + return PHY_INTERFACE_MODE_NA; +} + +static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode, bool force) +{ + u8 lane; u16 cmode; u16 reg; int err; + /* Default to a slow mode, so freeing up SERDES interfaces for + * other ports which might use them for SFPs. + */ if (mode == PHY_INTERFACE_MODE_NA) - return 0; - - if (port != 9 && port != 10) - return -EOPNOTSUPP; + mode = PHY_INTERFACE_MODE_1000BASEX; switch (mode) { case PHY_INTERFACE_MODE_1000BASEX: - cmode = MV88E6XXX_PORT_STS_CMODE_1000BASE_X; + cmode = MV88E6XXX_PORT_STS_CMODE_1000BASEX; break; case PHY_INTERFACE_MODE_SGMII: cmode = MV88E6XXX_PORT_STS_CMODE_SGMII; @@ -395,22 +425,19 @@ cmode = 0; } - /* cmode doesn't change, nothing to do for us */ - if (cmode == chip->ports[port].cmode) + /* cmode doesn't change, nothing to do for us unless forced */ + if (cmode == chip->ports[port].cmode && !force) return 0; - lane = mv88e6390x_serdes_get_lane(chip, port); - if (lane < 0 && lane != -ENODEV) - return lane; - - if (lane >= 0) { + lane = mv88e6xxx_serdes_get_lane(chip, port); + if (lane) { if (chip->ports[port].serdes_irq) { - err = mv88e6390_serdes_irq_disable(chip, port, lane); + err = mv88e6xxx_serdes_irq_disable(chip, port, lane); if (err) return err; } - err = mv88e6390x_serdes_power(chip, port, false); + err = mv88e6xxx_serdes_power_down(chip, port, lane); if (err) return err; } @@ -431,22 +458,102 @@ chip->ports[port].cmode = cmode; - lane = mv88e6390x_serdes_get_lane(chip, port); - if (lane < 0) - return lane; + lane = mv88e6xxx_serdes_get_lane(chip, port); + if (!lane) + return -ENODEV; - err = mv88e6390x_serdes_power(chip, port, true); + err = mv88e6xxx_serdes_power_up(chip, port, lane); if (err) return err; if (chip->ports[port].serdes_irq) { - err = mv88e6390_serdes_irq_enable(chip, port, lane); + err = mv88e6xxx_serdes_irq_enable(chip, port, lane); if (err) return err; } } return 0; +} + +int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode) +{ + if (port != 9 && port != 10) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_cmode(chip, port, mode, false); +} + +int mv88e6390_port_set_cmode(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode) +{ + if (port != 9 && port != 10) + return -EOPNOTSUPP; + + switch (mode) { + case PHY_INTERFACE_MODE_NA: + return 0; + case PHY_INTERFACE_MODE_XGMII: + case PHY_INTERFACE_MODE_XAUI: + case PHY_INTERFACE_MODE_RXAUI: + return -EINVAL; + default: + break; + } + + return mv88e6xxx_port_set_cmode(chip, port, mode, false); +} + +static int mv88e6341_port_set_cmode_writable(struct mv88e6xxx_chip *chip, + int port) +{ + int err, addr; + u16 reg, bits; + + if (port != 5) + return -EOPNOTSUPP; + + addr = chip->info->port_base_addr + port; + + err = mv88e6xxx_port_hidden_read(chip, 0x7, addr, 0, ®); + if (err) + return err; + + bits = MV88E6341_PORT_RESERVED_1A_FORCE_CMODE | + MV88E6341_PORT_RESERVED_1A_SGMII_AN; + + if ((reg & bits) == bits) + return 0; + + reg |= bits; + return mv88e6xxx_port_hidden_write(chip, 0x7, addr, 0, reg); +} + +int mv88e6341_port_set_cmode(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode) +{ + int err; + + if (port != 5) + return -EOPNOTSUPP; + + switch (mode) { + case PHY_INTERFACE_MODE_NA: + return 0; + case PHY_INTERFACE_MODE_XGMII: + case PHY_INTERFACE_MODE_XAUI: + case PHY_INTERFACE_MODE_RXAUI: + return -EINVAL; + default: + break; + } + + err = mv88e6341_port_set_cmode_writable(chip, port); + if (err) + return err; + + return mv88e6xxx_port_set_cmode(chip, port, mode, true); } int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode) @@ -475,80 +582,6 @@ *cmode = reg & MV88E6XXX_PORT_STS_CMODE_MASK; return 0; -} - -int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port, - struct phylink_link_state *state) -{ - int err; - u16 reg; - - err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); - if (err) - return err; - - switch (reg & MV88E6XXX_PORT_STS_SPEED_MASK) { - case MV88E6XXX_PORT_STS_SPEED_10: - state->speed = SPEED_10; - break; - case MV88E6XXX_PORT_STS_SPEED_100: - state->speed = SPEED_100; - break; - case MV88E6XXX_PORT_STS_SPEED_1000: - state->speed = SPEED_1000; - break; - case MV88E6XXX_PORT_STS_SPEED_10000: - if ((reg & MV88E6XXX_PORT_STS_CMODE_MASK) == - MV88E6XXX_PORT_STS_CMODE_2500BASEX) - state->speed = SPEED_2500; - else - state->speed = SPEED_10000; - break; - } - - state->duplex = reg & MV88E6XXX_PORT_STS_DUPLEX ? - DUPLEX_FULL : DUPLEX_HALF; - state->link = !!(reg & MV88E6XXX_PORT_STS_LINK); - state->an_enabled = 1; - state->an_complete = state->link; - - return 0; -} - -int mv88e6185_port_link_state(struct mv88e6xxx_chip *chip, int port, - struct phylink_link_state *state) -{ - if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { - u8 cmode = chip->ports[port].cmode; - - /* When a port is in "Cross-chip serdes" mode, it uses - * 1000Base-X full duplex mode, but there is no automatic - * link detection. Use the sync OK status for link (as it - * would do for 1000Base-X mode.) - */ - if (cmode == MV88E6185_PORT_STS_CMODE_SERDES) { - u16 mac; - int err; - - err = mv88e6xxx_port_read(chip, port, - MV88E6XXX_PORT_MAC_CTL, &mac); - if (err) - return err; - - state->link = !!(mac & MV88E6185_PORT_MAC_CTL_SYNC_OK); - state->an_enabled = 1; - state->an_complete = - !!(mac & MV88E6185_PORT_MAC_CTL_AN_DONE); - state->duplex = - state->link ? DUPLEX_FULL : DUPLEX_UNKNOWN; - state->speed = - state->link ? SPEED_1000 : SPEED_UNKNOWN; - - return 0; - } - } - - return mv88e6352_port_link_state(chip, port, state); } /* Offset 0x02: Jamming Control @@ -969,6 +1002,43 @@ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg); } +int mv88e6xxx_port_set_mirror(struct mv88e6xxx_chip *chip, int port, + enum mv88e6xxx_egress_direction direction, + bool mirror) +{ + bool *mirror_port; + u16 reg; + u16 bit; + int err; + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, ®); + if (err) + return err; + + switch (direction) { + case MV88E6XXX_EGRESS_DIR_INGRESS: + bit = MV88E6XXX_PORT_CTL2_INGRESS_MONITOR; + mirror_port = &chip->ports[port].mirror_ingress; + break; + case MV88E6XXX_EGRESS_DIR_EGRESS: + bit = MV88E6XXX_PORT_CTL2_EGRESS_MONITOR; + mirror_port = &chip->ports[port].mirror_egress; + break; + default: + return -EINVAL; + } + + reg &= ~bit; + if (mirror) + reg |= bit; + + err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg); + if (!err) + *mirror_port = mirror; + + return err; +} + int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port, u16 mode) { @@ -1011,6 +1081,8 @@ { u16 reg; int err; + + size += VLAN_ETH_HLEN + ETH_FCS_LEN; err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, ®); if (err) @@ -1129,3 +1201,77 @@ return 0; } + +/* Offset 0x0E: Policy Control Register */ + +int mv88e6352_port_set_policy(struct mv88e6xxx_chip *chip, int port, + enum mv88e6xxx_policy_mapping mapping, + enum mv88e6xxx_policy_action action) +{ + u16 reg, mask, val; + int shift; + int err; + + switch (mapping) { + case MV88E6XXX_POLICY_MAPPING_DA: + shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_DA_MASK); + mask = MV88E6XXX_PORT_POLICY_CTL_DA_MASK; + break; + case MV88E6XXX_POLICY_MAPPING_SA: + shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_SA_MASK); + mask = MV88E6XXX_PORT_POLICY_CTL_SA_MASK; + break; + case MV88E6XXX_POLICY_MAPPING_VTU: + shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_VTU_MASK); + mask = MV88E6XXX_PORT_POLICY_CTL_VTU_MASK; + break; + case MV88E6XXX_POLICY_MAPPING_ETYPE: + shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_ETYPE_MASK); + mask = MV88E6XXX_PORT_POLICY_CTL_ETYPE_MASK; + break; + case MV88E6XXX_POLICY_MAPPING_PPPOE: + shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_PPPOE_MASK); + mask = MV88E6XXX_PORT_POLICY_CTL_PPPOE_MASK; + break; + case MV88E6XXX_POLICY_MAPPING_VBAS: + shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_VBAS_MASK); + mask = MV88E6XXX_PORT_POLICY_CTL_VBAS_MASK; + break; + case MV88E6XXX_POLICY_MAPPING_OPT82: + shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_OPT82_MASK); + mask = MV88E6XXX_PORT_POLICY_CTL_OPT82_MASK; + break; + case MV88E6XXX_POLICY_MAPPING_UDP: + shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_UDP_MASK); + mask = MV88E6XXX_PORT_POLICY_CTL_UDP_MASK; + break; + default: + return -EOPNOTSUPP; + } + + switch (action) { + case MV88E6XXX_POLICY_ACTION_NORMAL: + val = MV88E6XXX_PORT_POLICY_CTL_NORMAL; + break; + case MV88E6XXX_POLICY_ACTION_MIRROR: + val = MV88E6XXX_PORT_POLICY_CTL_MIRROR; + break; + case MV88E6XXX_POLICY_ACTION_TRAP: + val = MV88E6XXX_PORT_POLICY_CTL_TRAP; + break; + case MV88E6XXX_POLICY_ACTION_DISCARD: + val = MV88E6XXX_PORT_POLICY_CTL_DISCARD; + break; + default: + return -EOPNOTSUPP; + } + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_POLICY_CTL, ®); + if (err) + return err; + + reg &= ~mask; + reg |= (val << shift) & mask; + + return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_POLICY_CTL, reg); +} -- Gitblit v1.6.2