diff options
Diffstat (limited to 'drivers/net/dsa/mv88e6xxx')
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/chip.c | 817 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/chip.h | 76 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global1.c | 95 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global1.h | 48 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global1_atu.c | 13 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global1_vtu.c | 6 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global2.c | 72 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global2.h | 12 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global2_avb.c | 29 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global2_scratch.c | 3 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/port.c | 209 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/port.h | 49 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/port_hidden.c | 70 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/ptp.c | 95 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/ptp.h | 6 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/serdes.c | 473 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/serdes.h | 100 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/smi.c | 4 |
19 files changed, 1396 insertions, 782 deletions
diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile index e85755dde90b..aa645ff86f64 100644 --- a/drivers/net/dsa/mv88e6xxx/Makefile +++ b/drivers/net/dsa/mv88e6xxx/Makefile @@ -10,6 +10,7 @@ mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2_scratch.o mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += hwtstamp.o mv88e6xxx-objs += phy.o mv88e6xxx-objs += port.o +mv88e6xxx-objs += port_hidden.o mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += ptp.o mv88e6xxx-objs += serdes.o mv88e6xxx-objs += smi.o diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index d0a97eb73a37..6787d560e9e3 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -10,6 +10,7 @@ * Vivien Didelot <vivien.didelot@savoirfairelinux.com> */ +#include <linux/bitfield.h> #include <linux/delay.h> #include <linux/etherdevice.h> #include <linux/ethtool.h> @@ -80,6 +81,36 @@ int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val) return 0; } +int mv88e6xxx_wait_mask(struct mv88e6xxx_chip *chip, int addr, int reg, + u16 mask, u16 val) +{ + u16 data; + int err; + int i; + + /* There's no bus specific operation to wait for a mask */ + for (i = 0; i < 16; i++) { + err = mv88e6xxx_read(chip, addr, reg, &data); + if (err) + return err; + + if ((data & mask) == val) + return 0; + + usleep_range(1000, 2000); + } + + dev_err(chip->dev, "Timeout while waiting for switch\n"); + return -ETIMEDOUT; +} + +int mv88e6xxx_wait_bit(struct mv88e6xxx_chip *chip, int addr, int reg, + int bit, int val) +{ + return mv88e6xxx_wait_mask(chip, addr, reg, BIT(bit), + val ? BIT(bit) : 0x0000); +} + struct mii_bus *mv88e6xxx_default_mdio_bus(struct mv88e6xxx_chip *chip) { struct mv88e6xxx_mdio_bus *mdio_bus; @@ -363,45 +394,6 @@ static void mv88e6xxx_irq_poll_free(struct mv88e6xxx_chip *chip) mv88e6xxx_reg_unlock(chip); } -int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask) -{ - int i; - - for (i = 0; i < 16; i++) { - u16 val; - int err; - - err = mv88e6xxx_read(chip, addr, reg, &val); - if (err) - return err; - - if (!(val & mask)) - return 0; - - usleep_range(1000, 2000); - } - - dev_err(chip->dev, "Timeout while waiting for switch\n"); - return -ETIMEDOUT; -} - -/* Indirect write to single pointer-data register with an Update bit */ -int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg, u16 update) -{ - u16 val; - int err; - - /* Wait until the previous operation is completed */ - err = mv88e6xxx_wait(chip, addr, reg, BIT(15)); - if (err) - return err; - - /* Set the Update bit to trigger a write operation */ - val = BIT(15) | update; - - return mv88e6xxx_write(chip, addr, reg, val); -} - int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, int link, int speed, int duplex, int pause, phy_interface_t mode) @@ -425,7 +417,9 @@ int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, int link, */ if (state.link == link && state.speed == speed && - state.duplex == duplex) + state.duplex == duplex && + (state.interface == mode || + state.interface == PHY_INTERFACE_MODE_NA)) return 0; /* Port's MAC control must not be changed unless the link is down */ @@ -1336,9 +1330,7 @@ static int mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip, static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) { DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID); - struct mv88e6xxx_vtu_entry vlan = { - .vid = chip->info->max_vid, - }; + struct mv88e6xxx_vtu_entry vlan; int i, err; bitmap_zero(fid_bitmap, MV88E6XXX_N_FID); @@ -1353,6 +1345,9 @@ static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) } /* Set every FID bit used by the VLAN entries */ + vlan.vid = chip->info->max_vid; + vlan.valid = false; + do { err = mv88e6xxx_vtu_getnext(chip, &vlan); if (err) @@ -1375,51 +1370,11 @@ static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) return mv88e6xxx_g1_atu_flush(chip, *fid, true); } -static int mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid, - struct mv88e6xxx_vtu_entry *entry, bool new) -{ - int err; - - if (!vid) - return -EOPNOTSUPP; - - entry->vid = vid - 1; - entry->valid = false; - - err = mv88e6xxx_vtu_getnext(chip, entry); - if (err) - return err; - - if (entry->vid == vid && entry->valid) - return 0; - - if (new) { - int i; - - /* Initialize a fresh VLAN entry */ - memset(entry, 0, sizeof(*entry)); - entry->valid = true; - entry->vid = vid; - - /* Exclude all ports */ - for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) - entry->member[i] = - MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER; - - return mv88e6xxx_atu_new(chip, &entry->fid); - } - - /* switchdev expects -EOPNOTSUPP to honor software VLANs */ - return -EOPNOTSUPP; -} - static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, u16 vid_begin, u16 vid_end) { struct mv88e6xxx_chip *chip = ds->priv; - struct mv88e6xxx_vtu_entry vlan = { - .vid = vid_begin - 1, - }; + struct mv88e6xxx_vtu_entry vlan; int i, err; /* DSA and CPU ports have to be members of multiple vlans */ @@ -1429,12 +1384,13 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, if (!vid_begin) return -EOPNOTSUPP; - mv88e6xxx_reg_lock(chip); + vlan.vid = vid_begin - 1; + vlan.valid = false; do { err = mv88e6xxx_vtu_getnext(chip, &vlan); if (err) - goto unlock; + return err; if (!vlan.valid) break; @@ -1463,15 +1419,11 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, dev_err(ds->dev, "p%d: hw VLAN %d already used by port %d in %s\n", port, vlan.vid, i, netdev_name(dsa_to_port(ds, i)->bridge_dev)); - err = -EOPNOTSUPP; - goto unlock; + return -EOPNOTSUPP; } } while (vlan.vid < vid_end); -unlock: - mv88e6xxx_reg_unlock(chip); - - return err; + return 0; } static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port, @@ -1505,59 +1457,281 @@ mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port, /* If the requested port doesn't belong to the same bridge as the VLAN * members, do not support it (yet) and fallback to software VLAN. */ + mv88e6xxx_reg_lock(chip); err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin, vlan->vid_end); - if (err) - return err; + mv88e6xxx_reg_unlock(chip); /* We don't need any dynamic resource from the kernel (yet), * so skip the prepare phase. */ - return 0; + return err; } static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port, const unsigned char *addr, u16 vid, u8 state) { - struct mv88e6xxx_vtu_entry vlan; struct mv88e6xxx_atu_entry entry; + struct mv88e6xxx_vtu_entry vlan; + u16 fid; int err; /* Null VLAN ID corresponds to the port private database */ - if (vid == 0) - err = mv88e6xxx_port_get_fid(chip, port, &vlan.fid); - else - err = mv88e6xxx_vtu_get(chip, vid, &vlan, false); - if (err) - return err; + if (vid == 0) { + err = mv88e6xxx_port_get_fid(chip, port, &fid); + if (err) + return err; + } else { + vlan.vid = vid - 1; + vlan.valid = false; + + err = mv88e6xxx_vtu_getnext(chip, &vlan); + if (err) + return err; + + /* switchdev expects -EOPNOTSUPP to honor software VLANs */ + if (vlan.vid != vid || !vlan.valid) + return -EOPNOTSUPP; + + fid = vlan.fid; + } - entry.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED; + entry.state = 0; ether_addr_copy(entry.mac, addr); eth_addr_dec(entry.mac); - err = mv88e6xxx_g1_atu_getnext(chip, vlan.fid, &entry); + err = mv88e6xxx_g1_atu_getnext(chip, fid, &entry); if (err) return err; /* Initialize a fresh ATU entry if it isn't found */ - if (entry.state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED || - !ether_addr_equal(entry.mac, addr)) { + if (!entry.state || !ether_addr_equal(entry.mac, addr)) { memset(&entry, 0, sizeof(entry)); ether_addr_copy(entry.mac, addr); } /* Purge the ATU entry only if no port is using it anymore */ - if (state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) { + if (!state) { entry.portvec &= ~BIT(port); if (!entry.portvec) - entry.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED; + entry.state = 0; } else { entry.portvec |= BIT(port); entry.state = state; } - return mv88e6xxx_g1_atu_loadpurge(chip, vlan.fid, &entry); + return mv88e6xxx_g1_atu_loadpurge(chip, fid, &entry); +} + +static int mv88e6xxx_policy_apply(struct mv88e6xxx_chip *chip, int port, + const struct mv88e6xxx_policy *policy) +{ + enum mv88e6xxx_policy_mapping mapping = policy->mapping; + enum mv88e6xxx_policy_action action = policy->action; + const u8 *addr = policy->addr; + u16 vid = policy->vid; + u8 state; + int err; + int id; + + if (!chip->info->ops->port_set_policy) + return -EOPNOTSUPP; + + switch (mapping) { + case MV88E6XXX_POLICY_MAPPING_DA: + case MV88E6XXX_POLICY_MAPPING_SA: + if (action == MV88E6XXX_POLICY_ACTION_NORMAL) + state = 0; /* Dissociate the port and address */ + else if (action == MV88E6XXX_POLICY_ACTION_DISCARD && + is_multicast_ether_addr(addr)) + state = MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_POLICY; + else if (action == MV88E6XXX_POLICY_ACTION_DISCARD && + is_unicast_ether_addr(addr)) + state = MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_POLICY; + else + return -EOPNOTSUPP; + + err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid, + state); + if (err) + return err; + break; + default: + return -EOPNOTSUPP; + } + + /* Skip the port's policy clearing if the mapping is still in use */ + if (action == MV88E6XXX_POLICY_ACTION_NORMAL) + idr_for_each_entry(&chip->policies, policy, id) + if (policy->port == port && + policy->mapping == mapping && + policy->action != action) + return 0; + + return chip->info->ops->port_set_policy(chip, port, mapping, action); +} + +static int mv88e6xxx_policy_insert(struct mv88e6xxx_chip *chip, int port, + struct ethtool_rx_flow_spec *fs) +{ + struct ethhdr *mac_entry = &fs->h_u.ether_spec; + struct ethhdr *mac_mask = &fs->m_u.ether_spec; + enum mv88e6xxx_policy_mapping mapping; + enum mv88e6xxx_policy_action action; + struct mv88e6xxx_policy *policy; + u16 vid = 0; + u8 *addr; + int err; + int id; + + if (fs->location != RX_CLS_LOC_ANY) + return -EINVAL; + + if (fs->ring_cookie == RX_CLS_FLOW_DISC) + action = MV88E6XXX_POLICY_ACTION_DISCARD; + else + return -EOPNOTSUPP; + + switch (fs->flow_type & ~FLOW_EXT) { + case ETHER_FLOW: + if (!is_zero_ether_addr(mac_mask->h_dest) && + is_zero_ether_addr(mac_mask->h_source)) { + mapping = MV88E6XXX_POLICY_MAPPING_DA; + addr = mac_entry->h_dest; + } else if (is_zero_ether_addr(mac_mask->h_dest) && + !is_zero_ether_addr(mac_mask->h_source)) { + mapping = MV88E6XXX_POLICY_MAPPING_SA; + addr = mac_entry->h_source; + } else { + /* Cannot support DA and SA mapping in the same rule */ + return -EOPNOTSUPP; + } + break; + default: + return -EOPNOTSUPP; + } + + if ((fs->flow_type & FLOW_EXT) && fs->m_ext.vlan_tci) { + if (fs->m_ext.vlan_tci != 0xffff) + return -EOPNOTSUPP; + vid = be16_to_cpu(fs->h_ext.vlan_tci) & VLAN_VID_MASK; + } + + idr_for_each_entry(&chip->policies, policy, id) { + if (policy->port == port && policy->mapping == mapping && + policy->action == action && policy->vid == vid && + ether_addr_equal(policy->addr, addr)) + return -EEXIST; + } + + policy = devm_kzalloc(chip->dev, sizeof(*policy), GFP_KERNEL); + if (!policy) + return -ENOMEM; + + fs->location = 0; + err = idr_alloc_u32(&chip->policies, policy, &fs->location, 0xffffffff, + GFP_KERNEL); + if (err) { + devm_kfree(chip->dev, policy); + return err; + } + + memcpy(&policy->fs, fs, sizeof(*fs)); + ether_addr_copy(policy->addr, addr); + policy->mapping = mapping; + policy->action = action; + policy->port = port; + policy->vid = vid; + + err = mv88e6xxx_policy_apply(chip, port, policy); + if (err) { + idr_remove(&chip->policies, fs->location); + devm_kfree(chip->dev, policy); + return err; + } + + return 0; +} + +static int mv88e6xxx_get_rxnfc(struct dsa_switch *ds, int port, + struct ethtool_rxnfc *rxnfc, u32 *rule_locs) +{ + struct ethtool_rx_flow_spec *fs = &rxnfc->fs; + struct mv88e6xxx_chip *chip = ds->priv; + struct mv88e6xxx_policy *policy; + int err; + int id; + + mv88e6xxx_reg_lock(chip); + + switch (rxnfc->cmd) { + case ETHTOOL_GRXCLSRLCNT: + rxnfc->data = 0; + rxnfc->data |= RX_CLS_LOC_SPECIAL; + rxnfc->rule_cnt = 0; + idr_for_each_entry(&chip->policies, policy, id) + if (policy->port == port) + rxnfc->rule_cnt++; + err = 0; + break; + case ETHTOOL_GRXCLSRULE: + err = -ENOENT; + policy = idr_find(&chip->policies, fs->location); + if (policy) { + memcpy(fs, &policy->fs, sizeof(*fs)); + err = 0; + } + break; + case ETHTOOL_GRXCLSRLALL: + rxnfc->data = 0; + rxnfc->rule_cnt = 0; + idr_for_each_entry(&chip->policies, policy, id) + if (policy->port == port) + rule_locs[rxnfc->rule_cnt++] = id; + err = 0; + break; + default: + err = -EOPNOTSUPP; + break; + } + + mv88e6xxx_reg_unlock(chip); + + return err; +} + +static int mv88e6xxx_set_rxnfc(struct dsa_switch *ds, int port, + struct ethtool_rxnfc *rxnfc) +{ + struct ethtool_rx_flow_spec *fs = &rxnfc->fs; + struct mv88e6xxx_chip *chip = ds->priv; + struct mv88e6xxx_policy *policy; + int err; + + mv88e6xxx_reg_lock(chip); + + switch (rxnfc->cmd) { + case ETHTOOL_SRXCLSRLINS: + err = mv88e6xxx_policy_insert(chip, port, fs); + break; + case ETHTOOL_SRXCLSRLDEL: + err = -ENOENT; + policy = idr_remove(&chip->policies, fs->location); + if (policy) { + policy->action = MV88E6XXX_POLICY_ACTION_NORMAL; + err = mv88e6xxx_policy_apply(chip, port, policy); + devm_kfree(chip->dev, policy); + } + break; + default: + err = -EOPNOTSUPP; + break; + } + + mv88e6xxx_reg_unlock(chip); + + return err; } static int mv88e6xxx_port_add_broadcast(struct mv88e6xxx_chip *chip, int port, @@ -1583,23 +1757,58 @@ static int mv88e6xxx_broadcast_setup(struct mv88e6xxx_chip *chip, u16 vid) return 0; } -static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port, +static int mv88e6xxx_port_vlan_join(struct mv88e6xxx_chip *chip, int port, u16 vid, u8 member) { + const u8 non_member = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER; struct mv88e6xxx_vtu_entry vlan; - int err; + int i, err; - err = mv88e6xxx_vtu_get(chip, vid, &vlan, true); - if (err) - return err; + if (!vid) + return -EOPNOTSUPP; - vlan.member[port] = member; + vlan.vid = vid - 1; + vlan.valid = false; - err = mv88e6xxx_vtu_loadpurge(chip, &vlan); + err = mv88e6xxx_vtu_getnext(chip, &vlan); if (err) return err; - return mv88e6xxx_broadcast_setup(chip, vid); + if (vlan.vid != vid || !vlan.valid) { + memset(&vlan, 0, sizeof(vlan)); + + err = mv88e6xxx_atu_new(chip, &vlan.fid); + if (err) + return err; + + for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) + if (i == port) + vlan.member[i] = member; + else + vlan.member[i] = non_member; + + vlan.vid = vid; + vlan.valid = true; + + err = mv88e6xxx_vtu_loadpurge(chip, &vlan); + if (err) + return err; + + err = mv88e6xxx_broadcast_setup(chip, vlan.vid); + if (err) + return err; + } else if (vlan.member[port] != member) { + vlan.member[port] = member; + + err = mv88e6xxx_vtu_loadpurge(chip, &vlan); + if (err) + return err; + } else { + dev_info(chip->dev, "p%d: already a member of VLAN %d\n", + port, vid); + } + + return 0; } static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, @@ -1624,7 +1833,7 @@ static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, mv88e6xxx_reg_lock(chip); for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) - if (_mv88e6xxx_port_vlan_add(chip, port, vid, member)) + if (mv88e6xxx_port_vlan_join(chip, port, vid, member)) dev_err(ds->dev, "p%d: failed to add VLAN %d%c\n", port, vid, untagged ? 'u' : 't'); @@ -1635,18 +1844,27 @@ static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, mv88e6xxx_reg_unlock(chip); } -static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_chip *chip, - int port, u16 vid) +static int mv88e6xxx_port_vlan_leave(struct mv88e6xxx_chip *chip, + int port, u16 vid) { struct mv88e6xxx_vtu_entry vlan; int i, err; - err = mv88e6xxx_vtu_get(chip, vid, &vlan, false); + if (!vid) + return -EOPNOTSUPP; + + vlan.vid = vid - 1; + vlan.valid = false; + + err = mv88e6xxx_vtu_getnext(chip, &vlan); if (err) return err; - /* Tell switchdev if this VLAN is handled in software */ - if (vlan.member[port] == MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER) + /* If the VLAN doesn't exist in hardware or the port isn't a member, + * tell switchdev that this VLAN is likely handled in software. + */ + if (vlan.vid != vid || !vlan.valid || + vlan.member[port] == MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER) return -EOPNOTSUPP; vlan.member[port] = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER; @@ -1685,7 +1903,7 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, goto unlock; for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { - err = _mv88e6xxx_port_vlan_del(chip, port, vid); + err = mv88e6xxx_port_vlan_leave(chip, port, vid); if (err) goto unlock; @@ -1723,8 +1941,7 @@ static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, int err; mv88e6xxx_reg_lock(chip); - err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid, - MV88E6XXX_G1_ATU_DATA_STATE_UNUSED); + err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid, 0); mv88e6xxx_reg_unlock(chip); return err; @@ -1738,7 +1955,7 @@ static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip, bool is_static; int err; - addr.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED; + addr.state = 0; eth_broadcast_addr(addr.mac); do { @@ -1746,7 +1963,7 @@ static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip, if (err) return err; - if (addr.state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) + if (!addr.state) break; if (addr.trunk || (addr.portvec & BIT(port)) == 0) @@ -1768,9 +1985,7 @@ static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip, static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port, dsa_fdb_dump_cb_t *cb, void *data) { - struct mv88e6xxx_vtu_entry vlan = { - .vid = chip->info->max_vid, - }; + struct mv88e6xxx_vtu_entry vlan; u16 fid; int err; @@ -1784,6 +1999,9 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port, return err; /* Dump VLANs' Filtering Information Databases */ + vlan.vid = chip->info->max_vid; + vlan.valid = false; + do { err = mv88e6xxx_vtu_getnext(chip, &vlan); if (err) @@ -2044,13 +2262,96 @@ static int mv88e6xxx_setup_egress_floods(struct mv88e6xxx_chip *chip, int port) return 0; } +static irqreturn_t mv88e6xxx_serdes_irq_thread_fn(int irq, void *dev_id) +{ + struct mv88e6xxx_port *mvp = dev_id; + struct mv88e6xxx_chip *chip = mvp->chip; + irqreturn_t ret = IRQ_NONE; + int port = mvp->port; + u8 lane; + + mv88e6xxx_reg_lock(chip); + lane = mv88e6xxx_serdes_get_lane(chip, port); + if (lane) + ret = mv88e6xxx_serdes_irq_status(chip, port, lane); + mv88e6xxx_reg_unlock(chip); + + return ret; +} + +static int mv88e6xxx_serdes_irq_request(struct mv88e6xxx_chip *chip, int port, + u8 lane) +{ + struct mv88e6xxx_port *dev_id = &chip->ports[port]; + unsigned int irq; + int err; + + /* Nothing to request if this SERDES port has no IRQ */ + irq = mv88e6xxx_serdes_irq_mapping(chip, port); + if (!irq) + return 0; + + /* Requesting the IRQ will trigger IRQ callbacks, so release the lock */ + mv88e6xxx_reg_unlock(chip); + err = request_threaded_irq(irq, NULL, mv88e6xxx_serdes_irq_thread_fn, + IRQF_ONESHOT, "mv88e6xxx-serdes", dev_id); + mv88e6xxx_reg_lock(chip); + if (err) + return err; + + dev_id->serdes_irq = irq; + + return mv88e6xxx_serdes_irq_enable(chip, port, lane); +} + +static int mv88e6xxx_serdes_irq_free(struct mv88e6xxx_chip *chip, int port, + u8 lane) +{ + struct mv88e6xxx_port *dev_id = &chip->ports[port]; + unsigned int irq = dev_id->serdes_irq; + int err; + + /* Nothing to free if no IRQ has been requested */ + if (!irq) + return 0; + + err = mv88e6xxx_serdes_irq_disable(chip, port, lane); + + /* Freeing the IRQ will trigger IRQ callbacks, so release the lock */ + mv88e6xxx_reg_unlock(chip); + free_irq(irq, dev_id); + mv88e6xxx_reg_lock(chip); + + dev_id->serdes_irq = 0; + + return err; +} + static int mv88e6xxx_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) { - if (chip->info->ops->serdes_power) - return chip->info->ops->serdes_power(chip, port, on); + u8 lane; + int err; - return 0; + lane = mv88e6xxx_serdes_get_lane(chip, port); + if (!lane) + return 0; + + if (on) { + err = mv88e6xxx_serdes_power_up(chip, port, lane); + if (err) + return err; + + err = mv88e6xxx_serdes_irq_request(chip, port, lane); + } else { + err = mv88e6xxx_serdes_irq_free(chip, port, lane); + if (err) + return err; + + err = mv88e6xxx_serdes_power_down(chip, port, lane); + } + + return err; } static int mv88e6xxx_setup_upstream_port(struct mv88e6xxx_chip *chip, int port) @@ -2141,16 +2442,6 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) if (err) return err; - /* Enable the SERDES interface for DSA and CPU ports. Normal - * ports SERDES are enabled when the port is enabled, thus - * saving a bit of power. - */ - if ((dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))) { - err = mv88e6xxx_serdes_power(chip, port, true); - 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 @@ -2227,9 +2518,11 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) return err; } - err = mv88e6xxx_setup_message_port(chip, port); - if (err) - return err; + if (chip->info->ops->port_setup_message_port) { + err = chip->info->ops->port_setup_message_port(chip, port); + if (err) + return err; + } /* Port based VLAN map: give each port the same default address * database, and allow bidirectional communication between the @@ -2256,12 +2549,7 @@ static int mv88e6xxx_port_enable(struct dsa_switch *ds, int port, int err; mv88e6xxx_reg_lock(chip); - err = mv88e6xxx_serdes_power(chip, port, true); - - if (!err && chip->info->ops->serdes_irq_setup) - err = chip->info->ops->serdes_irq_setup(chip, port); - mv88e6xxx_reg_unlock(chip); return err; @@ -2272,16 +2560,8 @@ static void mv88e6xxx_port_disable(struct dsa_switch *ds, int port) struct mv88e6xxx_chip *chip = ds->priv; mv88e6xxx_reg_lock(chip); - - if (mv88e6xxx_port_set_state(chip, port, BR_STATE_DISABLED)) - dev_err(chip->dev, "failed to disable port\n"); - - if (chip->info->ops->serdes_irq_free) - chip->info->ops->serdes_irq_free(chip, port); - if (mv88e6xxx_serdes_power(chip, port, false)) dev_err(chip->dev, "failed to power off SERDES\n"); - mv88e6xxx_reg_unlock(chip); } @@ -2312,58 +2592,6 @@ static int mv88e6xxx_stats_setup(struct mv88e6xxx_chip *chip) return mv88e6xxx_g1_stats_clear(chip); } -/* The mv88e6390 has some hidden registers used for debug and - * development. The errata also makes use of them. - */ -static int mv88e6390_hidden_write(struct mv88e6xxx_chip *chip, int port, - int reg, u16 val) -{ - u16 ctrl; - int err; - - err = mv88e6xxx_port_write(chip, PORT_RESERVED_1A_DATA_PORT, - PORT_RESERVED_1A, val); - if (err) - return err; - - ctrl = PORT_RESERVED_1A_BUSY | PORT_RESERVED_1A_WRITE | - PORT_RESERVED_1A_BLOCK | port << PORT_RESERVED_1A_PORT_SHIFT | - reg; - - return mv88e6xxx_port_write(chip, PORT_RESERVED_1A_CTRL_PORT, - PORT_RESERVED_1A, ctrl); -} - -static int mv88e6390_hidden_wait(struct mv88e6xxx_chip *chip) -{ - return mv88e6xxx_wait(chip, PORT_RESERVED_1A_CTRL_PORT, - PORT_RESERVED_1A, PORT_RESERVED_1A_BUSY); -} - - -static int mv88e6390_hidden_read(struct mv88e6xxx_chip *chip, int port, - int reg, u16 *val) -{ - u16 ctrl; - int err; - - ctrl = PORT_RESERVED_1A_BUSY | PORT_RESERVED_1A_READ | - PORT_RESERVED_1A_BLOCK | port << PORT_RESERVED_1A_PORT_SHIFT | - reg; - - err = mv88e6xxx_port_write(chip, PORT_RESERVED_1A_CTRL_PORT, - PORT_RESERVED_1A, ctrl); - if (err) - return err; - - err = mv88e6390_hidden_wait(chip); - if (err) - return err; - - return mv88e6xxx_port_read(chip, PORT_RESERVED_1A_DATA_PORT, - PORT_RESERVED_1A, val); -} - /* Check if the errata has already been applied. */ static bool mv88e6390_setup_errata_applied(struct mv88e6xxx_chip *chip) { @@ -2372,7 +2600,7 @@ static bool mv88e6390_setup_errata_applied(struct mv88e6xxx_chip *chip) u16 val; for (port = 0; port < mv88e6xxx_num_ports(chip); port++) { - err = mv88e6390_hidden_read(chip, port, 0, &val); + err = mv88e6xxx_port_hidden_read(chip, 0xf, port, 0, &val); if (err) { dev_err(chip->dev, "Error reading hidden register: %d\n", err); @@ -2405,7 +2633,7 @@ static int mv88e6390_setup_errata(struct mv88e6xxx_chip *chip) } for (port = 0; port < mv88e6xxx_num_ports(chip); port++) { - err = mv88e6390_hidden_write(chip, port, 0, 0x01c0); + err = mv88e6xxx_port_hidden_write(chip, 0xf, port, 0, 0x01c0); if (err) return err; } @@ -2444,17 +2672,14 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) /* Setup Switch Port Registers */ for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { - if (dsa_is_unused_port(ds, i)) { - err = mv88e6xxx_port_set_state(chip, i, - BR_STATE_DISABLED); - if (err) - goto unlock; - - err = mv88e6xxx_serdes_power(chip, i, false); - if (err) - goto unlock; - + if (dsa_is_unused_port(ds, i)) continue; + + /* Prevent the use of an invalid port. */ + if (mv88e6xxx_is_invalid_port(chip, i)) { + dev_err(chip->dev, "port %d is invalid\n", i); + err = -EINVAL; + goto unlock; } err = mv88e6xxx_setup_port(chip, i); @@ -2773,6 +2998,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6185_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2807,6 +3033,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .port_set_upstream_port = mv88e6095_port_set_upstream_port, .port_link_state = mv88e6185_port_link_state, .port_get_cmode = mv88e6185_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2843,6 +3070,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6185_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2877,6 +3105,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6185_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2914,6 +3143,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .port_set_pause = mv88e6185_port_set_pause, .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6185_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2958,6 +3188,8 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_set_cmode = mv88e6341_port_set_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -2971,7 +3203,11 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, - .serdes_power = mv88e6341_serdes_power, + .serdes_power = mv88e6390_serdes_power, + .serdes_get_lane = mv88e6341_serdes_get_lane, + .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, + .serdes_irq_enable = mv88e6390_serdes_irq_enable, + .serdes_irq_status = mv88e6390_serdes_irq_status, .gpio_ops = &mv88e6352_gpio_ops, .phylink_validate = mv88e6341_phylink_validate, }; @@ -2998,6 +3234,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6185_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3031,6 +3268,7 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6185_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3072,6 +3310,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3103,6 +3342,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6352_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, @@ -3113,6 +3353,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3127,6 +3368,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .rmu_disable = mv88e6352_g1_rmu_disable, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .serdes_get_lane = mv88e6352_serdes_get_lane, .serdes_power = mv88e6352_serdes_power, .gpio_ops = &mv88e6352_gpio_ops, .phylink_validate = mv88e6352_phylink_validate, @@ -3155,6 +3397,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3186,6 +3429,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6352_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, @@ -3196,6 +3440,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3210,9 +3455,11 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .rmu_disable = mv88e6352_g1_rmu_disable, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .serdes_get_lane = mv88e6352_serdes_get_lane, .serdes_power = mv88e6352_serdes_power, - .serdes_irq_setup = mv88e6352_serdes_irq_setup, - .serdes_irq_free = mv88e6352_serdes_irq_free, + .serdes_irq_mapping = mv88e6352_serdes_irq_mapping, + .serdes_irq_enable = mv88e6352_serdes_irq_enable, + .serdes_irq_status = mv88e6352_serdes_irq_status, .gpio_ops = &mv88e6352_gpio_ops, .phylink_validate = mv88e6352_phylink_validate, }; @@ -3234,6 +3481,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .port_set_pause = mv88e6185_port_set_pause, .port_link_state = mv88e6185_port_link_state, .port_get_cmode = mv88e6185_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3267,6 +3515,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .port_set_speed = mv88e6390_port_set_speed, .port_max_speed_mode = mv88e6390_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, @@ -3276,6 +3525,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, .port_set_cmode = mv88e6390_port_set_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3291,8 +3541,10 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, - .serdes_irq_setup = mv88e6390_serdes_irq_setup, - .serdes_irq_free = mv88e6390_serdes_irq_free, + .serdes_get_lane = mv88e6390_serdes_get_lane, + .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, + .serdes_irq_enable = mv88e6390_serdes_irq_enable, + .serdes_irq_status = mv88e6390_serdes_irq_status, .gpio_ops = &mv88e6352_gpio_ops, .phylink_validate = mv88e6390_phylink_validate, }; @@ -3312,6 +3564,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .port_set_speed = mv88e6390x_port_set_speed, .port_max_speed_mode = mv88e6390x_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, @@ -3321,6 +3574,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, .port_set_cmode = mv88e6390x_port_set_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3335,9 +3589,11 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .rmu_disable = mv88e6390_g1_rmu_disable, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, - .serdes_power = mv88e6390x_serdes_power, - .serdes_irq_setup = mv88e6390x_serdes_irq_setup, - .serdes_irq_free = mv88e6390x_serdes_irq_free, + .serdes_power = mv88e6390_serdes_power, + .serdes_get_lane = mv88e6390x_serdes_get_lane, + .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, + .serdes_irq_enable = mv88e6390_serdes_irq_enable, + .serdes_irq_status = mv88e6390_serdes_irq_status, .gpio_ops = &mv88e6352_gpio_ops, .phylink_validate = mv88e6390x_phylink_validate, }; @@ -3366,6 +3622,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, .port_set_cmode = mv88e6390_port_set_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3381,8 +3638,10 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, - .serdes_irq_setup = mv88e6390_serdes_irq_setup, - .serdes_irq_free = mv88e6390_serdes_irq_free, + .serdes_get_lane = mv88e6390_serdes_get_lane, + .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, + .serdes_irq_enable = mv88e6390_serdes_irq_enable, + .serdes_irq_status = mv88e6390_serdes_irq_status, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, .phylink_validate = mv88e6390_phylink_validate, @@ -3403,6 +3662,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6352_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, @@ -3413,6 +3673,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3427,9 +3688,11 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .rmu_disable = mv88e6352_g1_rmu_disable, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .serdes_get_lane = mv88e6352_serdes_get_lane, .serdes_power = mv88e6352_serdes_power, - .serdes_irq_setup = mv88e6352_serdes_irq_setup, - .serdes_irq_free = mv88e6352_serdes_irq_free, + .serdes_irq_mapping = mv88e6352_serdes_irq_mapping, + .serdes_irq_enable = mv88e6352_serdes_irq_enable, + .serdes_irq_status = mv88e6352_serdes_irq_status, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, @@ -3471,6 +3734,8 @@ static const struct mv88e6xxx_ops mv88e6250_ops = { .reset = mv88e6250_g1_reset, .vtu_getnext = mv88e6250_g1_vtu_getnext, .vtu_loadpurge = mv88e6250_g1_vtu_loadpurge, + .avb_ops = &mv88e6352_avb_ops, + .ptp_ops = &mv88e6250_ptp_ops, .phylink_validate = mv88e6065_phylink_validate, }; @@ -3489,6 +3754,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .port_set_speed = mv88e6390_port_set_speed, .port_max_speed_mode = mv88e6390_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, @@ -3498,6 +3764,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, .port_set_cmode = mv88e6390_port_set_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3513,8 +3780,10 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, - .serdes_irq_setup = mv88e6390_serdes_irq_setup, - .serdes_irq_free = mv88e6390_serdes_irq_free, + .serdes_get_lane = mv88e6390_serdes_get_lane, + .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, + .serdes_irq_enable = mv88e6390_serdes_irq_enable, + .serdes_irq_status = mv88e6390_serdes_irq_status, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, @@ -3545,6 +3814,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3588,6 +3858,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3631,6 +3902,8 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_set_cmode = mv88e6341_port_set_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3644,7 +3917,11 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, - .serdes_power = mv88e6341_serdes_power, + .serdes_power = mv88e6390_serdes_power, + .serdes_get_lane = mv88e6341_serdes_get_lane, + .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, + .serdes_irq_enable = mv88e6390_serdes_irq_enable, + .serdes_irq_status = mv88e6390_serdes_irq_status, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, @@ -3674,6 +3951,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3713,6 +3991,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3746,6 +4025,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6352_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, @@ -3756,6 +4036,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3770,9 +4051,11 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .rmu_disable = mv88e6352_g1_rmu_disable, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .serdes_get_lane = mv88e6352_serdes_get_lane, .serdes_power = mv88e6352_serdes_power, - .serdes_irq_setup = mv88e6352_serdes_irq_setup, - .serdes_irq_free = mv88e6352_serdes_irq_free, + .serdes_irq_mapping = mv88e6352_serdes_irq_mapping, + .serdes_irq_enable = mv88e6352_serdes_irq_enable, + .serdes_irq_status = mv88e6352_serdes_irq_status, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, @@ -3797,6 +4080,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .port_set_speed = mv88e6390_port_set_speed, .port_max_speed_mode = mv88e6390_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, @@ -3808,6 +4092,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, .port_set_cmode = mv88e6390_port_set_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3823,8 +4108,10 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, - .serdes_irq_setup = mv88e6390_serdes_irq_setup, - .serdes_irq_free = mv88e6390_serdes_irq_free, + .serdes_get_lane = mv88e6390_serdes_get_lane, + .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, + .serdes_irq_enable = mv88e6390_serdes_irq_enable, + .serdes_irq_status = mv88e6390_serdes_irq_status, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, @@ -3846,6 +4133,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .port_set_speed = mv88e6390x_port_set_speed, .port_max_speed_mode = mv88e6390x_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, @@ -3857,6 +4145,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, .port_set_cmode = mv88e6390x_port_set_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3871,9 +4160,11 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .rmu_disable = mv88e6390_g1_rmu_disable, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, - .serdes_power = mv88e6390x_serdes_power, - .serdes_irq_setup = mv88e6390x_serdes_irq_setup, - .serdes_irq_free = mv88e6390x_serdes_irq_free, + .serdes_power = mv88e6390_serdes_power, + .serdes_get_lane = mv88e6390x_serdes_get_lane, + .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, + .serdes_irq_enable = mv88e6390_serdes_irq_enable, + .serdes_irq_status = mv88e6390_serdes_irq_status, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, @@ -4235,6 +4526,33 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .ops = &mv88e6191_ops, }, + [MV88E6220] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6220, + .family = MV88E6XXX_FAMILY_6250, + .name = "Marvell 88E6220", + .num_databases = 64, + + /* Ports 2-4 are not routed to pins + * => usable ports 0, 1, 5, 6 + */ + .num_ports = 7, + .num_internal_phys = 2, + .invalid_port_mask = BIT(2) | BIT(3) | BIT(4), + .max_vid = 4095, + .port_base_addr = 0x08, + .phy_base_addr = 0x00, + .global1_addr = 0x0f, + .global2_addr = 0x07, + .age_time_coeff = 15000, + .g1_irqs = 9, + .g2_irqs = 10, + .atu_move_port_mask = 0xf, + .dual_chip = true, + .tag_protocol = DSA_TAG_PROTO_DSA, + .ptp_support = true, + .ops = &mv88e6250_ops, + }, + [MV88E6240] = { .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6240, .family = MV88E6XXX_FAMILY_6352, @@ -4277,6 +4595,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .atu_move_port_mask = 0xf, .dual_chip = true, .tag_protocol = DSA_TAG_PROTO_DSA, + .ptp_support = true, .ops = &mv88e6250_ops, }, @@ -4546,6 +4865,7 @@ static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev) mutex_init(&chip->reg_lock); INIT_LIST_HEAD(&chip->mdios); + idr_init(&chip->policies); return chip; } @@ -4588,8 +4908,7 @@ static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port, int err; mv88e6xxx_reg_lock(chip); - err = mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid, - MV88E6XXX_G1_ATU_DATA_STATE_UNUSED); + err = mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid, 0); mv88e6xxx_reg_unlock(chip); return err; @@ -4631,6 +4950,8 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .set_eeprom = mv88e6xxx_set_eeprom, .get_regs_len = mv88e6xxx_get_regs_len, .get_regs = mv88e6xxx_get_regs, + .get_rxnfc = mv88e6xxx_get_rxnfc, + .set_rxnfc = mv88e6xxx_set_rxnfc, .set_ageing_time = mv88e6xxx_set_ageing_time, .port_bridge_join = mv88e6xxx_port_bridge_join, .port_bridge_leave = mv88e6xxx_port_bridge_leave, diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 4646e46d47f2..e9b1a1ac9a8e 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -8,6 +8,7 @@ #ifndef _MV88E6XXX_CHIP_H #define _MV88E6XXX_CHIP_H +#include <linux/idr.h> #include <linux/if_vlan.h> #include <linux/irq.h> #include <linux/gpio/consumer.h> @@ -57,6 +58,7 @@ enum mv88e6xxx_model { MV88E6190, MV88E6190X, MV88E6191, + MV88E6220, MV88E6240, MV88E6250, MV88E6290, @@ -77,7 +79,7 @@ enum mv88e6xxx_family { MV88E6XXX_FAMILY_6097, /* 6046 6085 6096 6097 */ MV88E6XXX_FAMILY_6165, /* 6123 6161 6165 */ MV88E6XXX_FAMILY_6185, /* 6108 6121 6122 6131 6152 6155 6182 6185 */ - MV88E6XXX_FAMILY_6250, /* 6250 */ + MV88E6XXX_FAMILY_6250, /* 6220 6250 */ MV88E6XXX_FAMILY_6320, /* 6320 6321 */ MV88E6XXX_FAMILY_6341, /* 6141 6341 */ MV88E6XXX_FAMILY_6351, /* 6171 6175 6350 6351 */ @@ -105,6 +107,11 @@ struct mv88e6xxx_info { unsigned int g2_irqs; bool pvt; + /* Mark certain ports as invalid. This is required for example for the + * MV88E6220 (which is in general a MV88E6250 with 7 ports) but the + * ports 2-4 are not routet to pins. + */ + unsigned int invalid_port_mask; /* Multi-chip Addressing Mode. * Some chips respond to only 2 registers of its own SMI device address * when it is non-zero, and use indirect access to internal registers. @@ -183,6 +190,33 @@ struct mv88e6xxx_port_hwtstamp { struct hwtstamp_config tstamp_config; }; +enum mv88e6xxx_policy_mapping { + MV88E6XXX_POLICY_MAPPING_DA, + MV88E6XXX_POLICY_MAPPING_SA, + MV88E6XXX_POLICY_MAPPING_VTU, + MV88E6XXX_POLICY_MAPPING_ETYPE, + MV88E6XXX_POLICY_MAPPING_PPPOE, + MV88E6XXX_POLICY_MAPPING_VBAS, + MV88E6XXX_POLICY_MAPPING_OPT82, + MV88E6XXX_POLICY_MAPPING_UDP, +}; + +enum mv88e6xxx_policy_action { + MV88E6XXX_POLICY_ACTION_NORMAL, + MV88E6XXX_POLICY_ACTION_MIRROR, + MV88E6XXX_POLICY_ACTION_TRAP, + MV88E6XXX_POLICY_ACTION_DISCARD, +}; + +struct mv88e6xxx_policy { + enum mv88e6xxx_policy_mapping mapping; + enum mv88e6xxx_policy_action action; + struct ethtool_rx_flow_spec fs; + u8 addr[ETH_ALEN]; + int port; + u16 vid; +}; + struct mv88e6xxx_port { struct mv88e6xxx_chip *chip; int port; @@ -193,7 +227,7 @@ struct mv88e6xxx_port { u64 vtu_member_violation; u64 vtu_miss_violation; u8 cmode; - int serdes_irq; + unsigned int serdes_irq; }; struct mv88e6xxx_chip { @@ -241,6 +275,9 @@ struct mv88e6xxx_chip { /* List of mdio busses */ struct list_head mdios; + /* Policy Control List IDs and rules */ + struct idr policies; + /* There can be two interrupt controllers, which are chained * off a GPIO as interrupt source */ @@ -375,6 +412,10 @@ struct mv88e6xxx_ops { int (*port_tag_remap)(struct mv88e6xxx_chip *chip, int port); + int (*port_set_policy)(struct mv88e6xxx_chip *chip, int port, + enum mv88e6xxx_policy_mapping mapping, + enum mv88e6xxx_policy_action action); + int (*port_set_frame_mode)(struct mv88e6xxx_chip *chip, int port, enum mv88e6xxx_frame_mode mode); int (*port_set_egress_floods)(struct mv88e6xxx_chip *chip, int port, @@ -389,6 +430,7 @@ struct mv88e6xxx_ops { u8 out); int (*port_disable_learn_limit)(struct mv88e6xxx_chip *chip, int port); int (*port_disable_pri_override)(struct mv88e6xxx_chip *chip, int port); + int (*port_setup_message_port)(struct mv88e6xxx_chip *chip, int port); /* CMODE control what PHY mode the MAC will use, eg. SGMII, RGMII, etc. * Some chips allow this to be configured on specific ports. @@ -434,11 +476,19 @@ struct mv88e6xxx_ops { int (*mgmt_rsvd2cpu)(struct mv88e6xxx_chip *chip); /* Power on/off a SERDES interface */ - int (*serdes_power)(struct mv88e6xxx_chip *chip, int port, bool on); + int (*serdes_power)(struct mv88e6xxx_chip *chip, int port, u8 lane, + bool up); + + /* SERDES lane mapping */ + u8 (*serdes_get_lane)(struct mv88e6xxx_chip *chip, int port); /* SERDES interrupt handling */ - int (*serdes_irq_setup)(struct mv88e6xxx_chip *chip, int port); - void (*serdes_irq_free)(struct mv88e6xxx_chip *chip, int port); + unsigned int (*serdes_irq_mapping)(struct mv88e6xxx_chip *chip, + int port); + int (*serdes_irq_enable)(struct mv88e6xxx_chip *chip, int port, u8 lane, + bool enable); + irqreturn_t (*serdes_irq_status)(struct mv88e6xxx_chip *chip, int port, + u8 lane); /* Statistics from the SERDES interface */ int (*serdes_get_sset_count)(struct mv88e6xxx_chip *chip, int port); @@ -532,6 +582,10 @@ struct mv88e6xxx_ptp_ops { int arr1_sts_reg; int dep_sts_reg; u32 rx_filters; + u32 cc_shift; + u32 cc_mult; + u32 cc_mult_num; + u32 cc_mult_dem; }; #define STATS_TYPE_PORT BIT(0) @@ -570,11 +624,17 @@ static inline unsigned int mv88e6xxx_num_gpio(struct mv88e6xxx_chip *chip) return chip->info->num_gpio; } +static inline bool mv88e6xxx_is_invalid_port(struct mv88e6xxx_chip *chip, int port) +{ + return (chip->info->invalid_port_mask & BIT(port)) != 0; +} + int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val); int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val); -int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg, - u16 update); -int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask); +int mv88e6xxx_wait_mask(struct mv88e6xxx_chip *chip, int addr, int reg, + u16 mask, u16 val); +int mv88e6xxx_wait_bit(struct mv88e6xxx_chip *chip, int addr, int reg, + int bit, int val); int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, int link, int speed, int duplex, int pause, phy_interface_t mode); diff --git a/drivers/net/dsa/mv88e6xxx/global1.c b/drivers/net/dsa/mv88e6xxx/global1.c index 1323ef30a5e9..25ec4c0ac589 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.c +++ b/drivers/net/dsa/mv88e6xxx/global1.c @@ -27,100 +27,52 @@ int mv88e6xxx_g1_write(struct mv88e6xxx_chip *chip, int reg, u16 val) return mv88e6xxx_write(chip, addr, reg, val); } -int mv88e6xxx_g1_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask) +int mv88e6xxx_g1_wait_bit(struct mv88e6xxx_chip *chip, int reg, int + bit, int val) { - return mv88e6xxx_wait(chip, chip->info->global1_addr, reg, mask); + return mv88e6xxx_wait_bit(chip, chip->info->global1_addr, reg, + bit, val); +} + +int mv88e6xxx_g1_wait_mask(struct mv88e6xxx_chip *chip, int reg, + u16 mask, u16 val) +{ + return mv88e6xxx_wait_mask(chip, chip->info->global1_addr, reg, + mask, val); } /* Offset 0x00: Switch Global Status Register */ static int mv88e6185_g1_wait_ppu_disabled(struct mv88e6xxx_chip *chip) { - u16 state; - int i, err; - - for (i = 0; i < 16; i++) { - err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_STS, &state); - if (err) - return err; - - /* Check the value of the PPUState bits 15:14 */ - state &= MV88E6185_G1_STS_PPU_STATE_MASK; - if (state != MV88E6185_G1_STS_PPU_STATE_POLLING) - return 0; - - usleep_range(1000, 2000); - } - - return -ETIMEDOUT; + return mv88e6xxx_g1_wait_mask(chip, MV88E6XXX_G1_STS, + MV88E6185_G1_STS_PPU_STATE_MASK, + MV88E6185_G1_STS_PPU_STATE_DISABLED); } static int mv88e6185_g1_wait_ppu_polling(struct mv88e6xxx_chip *chip) { - u16 state; - int i, err; - - for (i = 0; i < 16; ++i) { - err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_STS, &state); - if (err) - return err; - - /* Check the value of the PPUState bits 15:14 */ - state &= MV88E6185_G1_STS_PPU_STATE_MASK; - if (state == MV88E6185_G1_STS_PPU_STATE_POLLING) - return 0; - - usleep_range(1000, 2000); - } - - return -ETIMEDOUT; + return mv88e6xxx_g1_wait_mask(chip, MV88E6XXX_G1_STS, + MV88E6185_G1_STS_PPU_STATE_MASK, + MV88E6185_G1_STS_PPU_STATE_POLLING); } static int mv88e6352_g1_wait_ppu_polling(struct mv88e6xxx_chip *chip) { - u16 state; - int i, err; + int bit = __bf_shf(MV88E6352_G1_STS_PPU_STATE); - for (i = 0; i < 16; ++i) { - err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_STS, &state); - if (err) - return err; - - /* Check the value of the PPUState (or InitState) bit 15 */ - if (state & MV88E6352_G1_STS_PPU_STATE) - return 0; - - usleep_range(1000, 2000); - } - - return -ETIMEDOUT; + return mv88e6xxx_g1_wait_bit(chip, MV88E6XXX_G1_STS, bit, 1); } static int mv88e6xxx_g1_wait_init_ready(struct mv88e6xxx_chip *chip) { - const unsigned long timeout = jiffies + 1 * HZ; - u16 val; - int err; + int bit = __bf_shf(MV88E6XXX_G1_STS_INIT_READY); /* Wait up to 1 second for the switch to be ready. The InitReady bit 11 * is set to a one when all units inside the device (ATU, VTU, etc.) * have finished their initialization and are ready to accept frames. */ - while (time_before(jiffies, timeout)) { - err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_STS, &val); - if (err) - return err; - - if (val & MV88E6XXX_G1_STS_INIT_READY) - break; - - usleep_range(1000, 2000); - } - - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - - return 0; + return mv88e6xxx_g1_wait_bit(chip, MV88E6XXX_G1_STS, bit, 1); } /* Offset 0x01: Switch MAC Address Register Bytes 0 & 1 @@ -476,8 +428,9 @@ int mv88e6xxx_g1_set_device_number(struct mv88e6xxx_chip *chip, int index) static int mv88e6xxx_g1_stats_wait(struct mv88e6xxx_chip *chip) { - return mv88e6xxx_g1_wait(chip, MV88E6XXX_G1_STATS_OP, - MV88E6XXX_G1_STATS_OP_BUSY); + int bit = __bf_shf(MV88E6XXX_G1_STATS_OP_BUSY); + + return mv88e6xxx_g1_wait_bit(chip, MV88E6XXX_G1_STATS_OP, bit, 0); } int mv88e6095_g1_stats_set_histogram(struct mv88e6xxx_chip *chip) diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index d444266f7d78..0870fcc8bfc8 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -128,19 +128,36 @@ #define MV88E6XXX_G1_ATU_OP_FULL_VIOLATION BIT(4) /* Offset 0x0C: ATU Data Register */ -#define MV88E6XXX_G1_ATU_DATA 0x0c -#define MV88E6XXX_G1_ATU_DATA_TRUNK 0x8000 -#define MV88E6XXX_G1_ATU_DATA_TRUNK_ID_MASK 0x00f0 -#define MV88E6XXX_G1_ATU_DATA_PORT_VECTOR_MASK 0x3ff0 -#define MV88E6XXX_G1_ATU_DATA_STATE_MASK 0x000f -#define MV88E6XXX_G1_ATU_DATA_STATE_UNUSED 0x0000 -#define MV88E6XXX_G1_ATU_DATA_STATE_UC_MGMT 0x000d -#define MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC 0x000e -#define MV88E6XXX_G1_ATU_DATA_STATE_UC_PRIO_OVER 0x000f -#define MV88E6XXX_G1_ATU_DATA_STATE_MC_NONE_RATE 0x0005 -#define MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC 0x0007 -#define MV88E6XXX_G1_ATU_DATA_STATE_MC_MGMT 0x000e -#define MV88E6XXX_G1_ATU_DATA_STATE_MC_PRIO_OVER 0x000f +#define MV88E6XXX_G1_ATU_DATA 0x0c +#define MV88E6XXX_G1_ATU_DATA_TRUNK 0x8000 +#define MV88E6XXX_G1_ATU_DATA_TRUNK_ID_MASK 0x00f0 +#define MV88E6XXX_G1_ATU_DATA_PORT_VECTOR_MASK 0x3ff0 +#define MV88E6XXX_G1_ATU_DATA_STATE_MASK 0x000f +#define MV88E6XXX_G1_ATU_DATA_STATE_UC_UNUSED 0x0000 +#define MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_1_OLDEST 0x0001 +#define MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_2 0x0002 +#define MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_3 0x0003 +#define MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_4 0x0004 +#define MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_5 0x0005 +#define MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_6 0x0006 +#define MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_7_NEWEST 0x0007 +#define MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_POLICY 0x0008 +#define MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_POLICY_PO 0x0009 +#define MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_AVB_NRL 0x000a +#define MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_AVB_NRL_PO 0x000b +#define MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_DA_MGMT 0x000c +#define MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_DA_MGMT_PO 0x000d +#define MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC 0x000e +#define MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_PO 0x000f +#define MV88E6XXX_G1_ATU_DATA_STATE_MC_UNUSED 0x0000 +#define MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_POLICY 0x0004 +#define MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_AVB_NRL 0x0005 +#define MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_DA_MGMT 0x0006 +#define MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC 0x0007 +#define MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_POLICY_PO 0x000c +#define MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_AVB_NRL_PO 0x000d +#define MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_DA_MGMT_PO 0x000e +#define MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_PO 0x000f /* Offset 0x0D: ATU MAC Address Register Bytes 0 & 1 * Offset 0x0E: ATU MAC Address Register Bytes 2 & 3 @@ -249,7 +266,10 @@ int mv88e6xxx_g1_read(struct mv88e6xxx_chip *chip, int reg, u16 *val); int mv88e6xxx_g1_write(struct mv88e6xxx_chip *chip, int reg, u16 val); -int mv88e6xxx_g1_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask); +int mv88e6xxx_g1_wait_bit(struct mv88e6xxx_chip *chip, int reg, int + bit, int val); +int mv88e6xxx_g1_wait_mask(struct mv88e6xxx_chip *chip, int reg, + u16 mask, u16 val); int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr); diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c index 1cf388e9bd94..792a96ef418f 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_atu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c @@ -5,6 +5,8 @@ * Copyright (c) 2008 Marvell Semiconductor * Copyright (c) 2017 Savoir-faire Linux, Inc. */ + +#include <linux/bitfield.h> #include <linux/interrupt.h> #include <linux/irqdomain.h> @@ -75,8 +77,9 @@ int mv88e6xxx_g1_atu_set_age_time(struct mv88e6xxx_chip *chip, static int mv88e6xxx_g1_atu_op_wait(struct mv88e6xxx_chip *chip) { - return mv88e6xxx_g1_wait(chip, MV88E6XXX_G1_ATU_OP, - MV88E6XXX_G1_ATU_OP_BUSY); + int bit = __bf_shf(MV88E6XXX_G1_ATU_OP_BUSY); + + return mv88e6xxx_g1_wait_bit(chip, MV88E6XXX_G1_ATU_OP, bit, 0); } static int mv88e6xxx_g1_atu_op(struct mv88e6xxx_chip *chip, u16 fid, u16 op) @@ -132,7 +135,7 @@ static int mv88e6xxx_g1_atu_data_read(struct mv88e6xxx_chip *chip, return err; entry->state = val & 0xf; - if (entry->state != MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) { + if (entry->state) { entry->trunk = !!(val & MV88E6XXX_G1_ATU_DATA_TRUNK); entry->portvec = (val >> 4) & mv88e6xxx_port_mask(chip); } @@ -145,7 +148,7 @@ static int mv88e6xxx_g1_atu_data_write(struct mv88e6xxx_chip *chip, { u16 data = entry->state & 0xf; - if (entry->state != MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) { + if (entry->state) { if (entry->trunk) data |= MV88E6XXX_G1_ATU_DATA_TRUNK; @@ -206,7 +209,7 @@ int mv88e6xxx_g1_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid, return err; /* Write the MAC address to iterate from only once */ - if (entry->state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) { + if (!entry->state) { err = mv88e6xxx_g1_atu_mac_write(chip, entry); if (err) return err; diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c index 6cac997360e8..33056a609e96 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c @@ -7,6 +7,7 @@ * Copyright (c) 2017 Savoir-faire Linux, Inc. */ +#include <linux/bitfield.h> #include <linux/interrupt.h> #include <linux/irqdomain.h> @@ -67,8 +68,9 @@ static int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip, static int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip) { - return mv88e6xxx_g1_wait(chip, MV88E6XXX_G1_VTU_OP, - MV88E6XXX_G1_VTU_OP_BUSY); + int bit = __bf_shf(MV88E6XXX_G1_VTU_OP_BUSY); + + return mv88e6xxx_g1_wait_bit(chip, MV88E6XXX_G1_VTU_OP, bit, 0); } static int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op) diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index 2305b94b3051..bdbb72fc20ed 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -26,14 +26,11 @@ int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val) return mv88e6xxx_write(chip, chip->info->global2_addr, reg, val); } -int mv88e6xxx_g2_update(struct mv88e6xxx_chip *chip, int reg, u16 update) +int mv88e6xxx_g2_wait_bit(struct mv88e6xxx_chip *chip, int reg, int + bit, int val) { - return mv88e6xxx_update(chip, chip->info->global2_addr, reg, update); -} - -int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask) -{ - return mv88e6xxx_wait(chip, chip->info->global2_addr, reg, mask); + return mv88e6xxx_wait_bit(chip, chip->info->global2_addr, reg, + bit, val); } /* Offset 0x00: Interrupt Source Register */ @@ -123,7 +120,8 @@ int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip, int target, * but bit 4 is reserved on older chips, so it is safe to use. */ - return mv88e6xxx_g2_update(chip, MV88E6XXX_G2_DEVICE_MAPPING, val); + return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_DEVICE_MAPPING, + MV88E6XXX_G2_DEVICE_MAPPING_UPDATE | val); } /* Offset 0x07: Trunk Mask Table register */ @@ -136,7 +134,8 @@ static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num, if (hash) val |= MV88E6XXX_G2_TRUNK_MASK_HASH; - return mv88e6xxx_g2_update(chip, MV88E6XXX_G2_TRUNK_MASK, val); + return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_TRUNK_MASK, + MV88E6XXX_G2_TRUNK_MASK_UPDATE | val); } /* Offset 0x08: Trunk Mapping Table register */ @@ -147,7 +146,8 @@ static int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id, const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1; u16 val = (id << 11) | (map & port_mask); - return mv88e6xxx_g2_update(chip, MV88E6XXX_G2_TRUNK_MAPPING, val); + return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_TRUNK_MAPPING, + MV88E6XXX_G2_TRUNK_MAPPING_UPDATE | val); } int mv88e6xxx_g2_trunk_clear(struct mv88e6xxx_chip *chip) @@ -178,8 +178,9 @@ int mv88e6xxx_g2_trunk_clear(struct mv88e6xxx_chip *chip) static int mv88e6xxx_g2_irl_wait(struct mv88e6xxx_chip *chip) { - return mv88e6xxx_g2_wait(chip, MV88E6XXX_G2_IRL_CMD, - MV88E6XXX_G2_IRL_CMD_BUSY); + int bit = __bf_shf(MV88E6XXX_G2_IRL_CMD_BUSY); + + return mv88e6xxx_g2_wait_bit(chip, MV88E6XXX_G2_IRL_CMD, bit, 0); } static int mv88e6xxx_g2_irl_op(struct mv88e6xxx_chip *chip, u16 op, int port, @@ -214,8 +215,9 @@ int mv88e6390_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port) static int mv88e6xxx_g2_pvt_op_wait(struct mv88e6xxx_chip *chip) { - return mv88e6xxx_g2_wait(chip, MV88E6XXX_G2_PVT_ADDR, - MV88E6XXX_G2_PVT_ADDR_BUSY); + int bit = __bf_shf(MV88E6XXX_G2_PVT_ADDR_BUSY); + + return mv88e6xxx_g2_wait_bit(chip, MV88E6XXX_G2_PVT_ADDR, bit, 0); } static int mv88e6xxx_g2_pvt_op(struct mv88e6xxx_chip *chip, int src_dev, @@ -261,7 +263,8 @@ static int mv88e6xxx_g2_switch_mac_write(struct mv88e6xxx_chip *chip, { u16 val = (pointer << 8) | data; - return mv88e6xxx_g2_update(chip, MV88E6XXX_G2_SWITCH_MAC, val); + return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SWITCH_MAC, + MV88E6XXX_G2_SWITCH_MAC_UPDATE | val); } int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr) @@ -284,7 +287,8 @@ static int mv88e6xxx_g2_pot_write(struct mv88e6xxx_chip *chip, int pointer, { u16 val = (pointer << 8) | (data & 0x7); - return mv88e6xxx_g2_update(chip, MV88E6XXX_G2_PRIO_OVERRIDE, val); + return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_PRIO_OVERRIDE, + MV88E6XXX_G2_PRIO_OVERRIDE_UPDATE | val); } int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip) @@ -308,9 +312,16 @@ int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip) static int mv88e6xxx_g2_eeprom_wait(struct mv88e6xxx_chip *chip) { - return mv88e6xxx_g2_wait(chip, MV88E6XXX_G2_EEPROM_CMD, - MV88E6XXX_G2_EEPROM_CMD_BUSY | - MV88E6XXX_G2_EEPROM_CMD_RUNNING); + int bit = __bf_shf(MV88E6XXX_G2_EEPROM_CMD_BUSY); + int err; + + err = mv88e6xxx_g2_wait_bit(chip, MV88E6XXX_G2_EEPROM_CMD, bit, 0); + if (err) + return err; + + bit = __bf_shf(MV88E6XXX_G2_EEPROM_CMD_RUNNING); + + return mv88e6xxx_g2_wait_bit(chip, MV88E6XXX_G2_EEPROM_CMD, bit, 0); } static int mv88e6xxx_g2_eeprom_cmd(struct mv88e6xxx_chip *chip, u16 cmd) @@ -572,8 +583,9 @@ int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip, static int mv88e6xxx_g2_smi_phy_wait(struct mv88e6xxx_chip *chip) { - return mv88e6xxx_g2_wait(chip, MV88E6XXX_G2_SMI_PHY_CMD, - MV88E6XXX_G2_SMI_PHY_CMD_BUSY); + int bit = __bf_shf(MV88E6XXX_G2_SMI_PHY_CMD_BUSY); + + return mv88e6xxx_g2_wait_bit(chip, MV88E6XXX_G2_SMI_PHY_CMD, bit, 0); } static int mv88e6xxx_g2_smi_phy_cmd(struct mv88e6xxx_chip *chip, u16 cmd) @@ -840,12 +852,13 @@ const struct mv88e6xxx_irq_ops mv88e6250_watchdog_ops = { static int mv88e6390_watchdog_setup(struct mv88e6xxx_chip *chip) { - return mv88e6xxx_g2_update(chip, MV88E6390_G2_WDOG_CTL, - MV88E6390_G2_WDOG_CTL_PTR_INT_ENABLE | - MV88E6390_G2_WDOG_CTL_CUT_THROUGH | - MV88E6390_G2_WDOG_CTL_QUEUE_CONTROLLER | - MV88E6390_G2_WDOG_CTL_EGRESS | - MV88E6390_G2_WDOG_CTL_FORCE_IRQ); + return mv88e6xxx_g2_write(chip, MV88E6390_G2_WDOG_CTL, + MV88E6390_G2_WDOG_CTL_UPDATE | + MV88E6390_G2_WDOG_CTL_PTR_INT_ENABLE | + MV88E6390_G2_WDOG_CTL_CUT_THROUGH | + MV88E6390_G2_WDOG_CTL_QUEUE_CONTROLLER | + MV88E6390_G2_WDOG_CTL_EGRESS | + MV88E6390_G2_WDOG_CTL_FORCE_IRQ); } static int mv88e6390_watchdog_action(struct mv88e6xxx_chip *chip, int irq) @@ -878,8 +891,9 @@ static int mv88e6390_watchdog_action(struct mv88e6xxx_chip *chip, int irq) static void mv88e6390_watchdog_free(struct mv88e6xxx_chip *chip) { - mv88e6xxx_g2_update(chip, MV88E6390_G2_WDOG_CTL, - MV88E6390_G2_WDOG_CTL_PTR_INT_ENABLE); + mv88e6xxx_g2_write(chip, MV88E6390_G2_WDOG_CTL, + MV88E6390_G2_WDOG_CTL_UPDATE | + MV88E6390_G2_WDOG_CTL_PTR_INT_ENABLE); } const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = { diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h index a664fc25f132..42da4bca73e8 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.h +++ b/drivers/net/dsa/mv88e6xxx/global2.h @@ -295,8 +295,8 @@ static inline int mv88e6xxx_g2_require(struct mv88e6xxx_chip *chip) int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val); int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val); -int mv88e6xxx_g2_update(struct mv88e6xxx_chip *chip, int reg, u16 update); -int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask); +int mv88e6xxx_g2_wait_bit(struct mv88e6xxx_chip *chip, int reg, + int bit, int val); int mv88e6352_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port); int mv88e6390_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port); @@ -376,12 +376,8 @@ static inline int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 v return -EOPNOTSUPP; } -static inline int mv88e6xxx_g2_update(struct mv88e6xxx_chip *chip, int reg, u16 update) -{ - return -EOPNOTSUPP; -} - -static inline int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask) +static inline int mv88e6xxx_g2_wait_bit(struct mv88e6xxx_chip *chip, + int reg, int bit, int val) { return -EOPNOTSUPP; } diff --git a/drivers/net/dsa/mv88e6xxx/global2_avb.c b/drivers/net/dsa/mv88e6xxx/global2_avb.c index 116b8cf5a6e3..657783e043ff 100644 --- a/drivers/net/dsa/mv88e6xxx/global2_avb.c +++ b/drivers/net/dsa/mv88e6xxx/global2_avb.c @@ -11,6 +11,8 @@ * Brandon Streiff <brandon.streiff@ni.com> */ +#include <linux/bitfield.h> + #include "global2.h" /* Offset 0x16: AVB Command Register @@ -27,17 +29,33 @@ /* mv88e6xxx_g2_avb_read -- Read one or multiple 16-bit words. * The hardware supports snapshotting up to four contiguous registers. */ +static int mv88e6xxx_g2_avb_wait(struct mv88e6xxx_chip *chip) +{ + int bit = __bf_shf(MV88E6352_G2_AVB_CMD_BUSY); + + return mv88e6xxx_g2_wait_bit(chip, MV88E6352_G2_AVB_CMD, bit, 0); +} + static int mv88e6xxx_g2_avb_read(struct mv88e6xxx_chip *chip, u16 readop, u16 *data, int len) { int err; int i; + err = mv88e6xxx_g2_avb_wait(chip); + if (err) + return err; + /* Hardware can only snapshot four words. */ if (len > 4) return -E2BIG; - err = mv88e6xxx_g2_update(chip, MV88E6352_G2_AVB_CMD, readop); + err = mv88e6xxx_g2_write(chip, MV88E6352_G2_AVB_CMD, + MV88E6352_G2_AVB_CMD_BUSY | readop); + if (err) + return err; + + err = mv88e6xxx_g2_avb_wait(chip); if (err) return err; @@ -57,11 +75,18 @@ static int mv88e6xxx_g2_avb_write(struct mv88e6xxx_chip *chip, u16 writeop, { int err; + err = mv88e6xxx_g2_avb_wait(chip); + if (err) + return err; + err = mv88e6xxx_g2_write(chip, MV88E6352_G2_AVB_DATA, data); if (err) return err; - return mv88e6xxx_g2_update(chip, MV88E6352_G2_AVB_CMD, writeop); + err = mv88e6xxx_g2_write(chip, MV88E6352_G2_AVB_CMD, + MV88E6352_G2_AVB_CMD_BUSY | writeop); + + return mv88e6xxx_g2_avb_wait(chip); } static int mv88e6352_g2_avb_port_ptp_read(struct mv88e6xxx_chip *chip, diff --git a/drivers/net/dsa/mv88e6xxx/global2_scratch.c b/drivers/net/dsa/mv88e6xxx/global2_scratch.c index baddecadd8be..33b7b9570d29 100644 --- a/drivers/net/dsa/mv88e6xxx/global2_scratch.c +++ b/drivers/net/dsa/mv88e6xxx/global2_scratch.c @@ -37,7 +37,8 @@ static int mv88e6xxx_g2_scratch_write(struct mv88e6xxx_chip *chip, int reg, { u16 value = (reg << 8) | data; - return mv88e6xxx_g2_update(chip, MV88E6XXX_G2_SCRATCH_MISC_MISC, value); + return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SCRATCH_MISC_MISC, + MV88E6XXX_G2_SCRATCH_MISC_UPDATE | value); } /** diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 04309ef0a1cc..15ef81654b67 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -392,17 +392,14 @@ phy_interface_t mv88e6390x_port_max_speed_mode(int port) return PHY_INTERFACE_MODE_NA; } -int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, - phy_interface_t mode) +static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode) { - int lane; + u8 lane; u16 cmode; u16 reg; int err; - if (port != 9 && port != 10) - return -EOPNOTSUPP; - /* Default to a slow mode, so freeing up SERDES interfaces for * other ports which might use them for SFPs. */ @@ -411,7 +408,7 @@ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, 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; @@ -434,18 +431,15 @@ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, if (cmode == chip->ports[port].cmode) 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; } @@ -466,16 +460,16 @@ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, 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; } @@ -484,9 +478,21 @@ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, 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); +} + 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; @@ -498,7 +504,58 @@ int mv88e6390_port_set_cmode(struct mv88e6xxx_chip *chip, int port, break; } - return mv88e6390x_port_set_cmode(chip, port, mode); + return mv88e6xxx_port_set_cmode(chip, port, mode); +} + +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); } int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode) @@ -590,6 +647,7 @@ int mv88e6250_port_link_state(struct mv88e6xxx_chip *chip, int port, state->link = !!(reg & MV88E6250_PORT_STS_LINK); state->an_enabled = 1; state->an_complete = state->link; + state->interface = PHY_INTERFACE_MODE_NA; return 0; } @@ -600,6 +658,43 @@ int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port, int err; u16 reg; + switch (chip->ports[port].cmode) { + case MV88E6XXX_PORT_STS_CMODE_RGMII: + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, + ®); + if (err) + return err; + + if ((reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK) && + (reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK)) + state->interface = PHY_INTERFACE_MODE_RGMII_ID; + else if (reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK) + state->interface = PHY_INTERFACE_MODE_RGMII_RXID; + else if (reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK) + state->interface = PHY_INTERFACE_MODE_RGMII_TXID; + else + state->interface = PHY_INTERFACE_MODE_RGMII; + break; + case MV88E6XXX_PORT_STS_CMODE_1000BASEX: + state->interface = PHY_INTERFACE_MODE_1000BASEX; + break; + case MV88E6XXX_PORT_STS_CMODE_SGMII: + state->interface = PHY_INTERFACE_MODE_SGMII; + break; + case MV88E6XXX_PORT_STS_CMODE_2500BASEX: + state->interface = PHY_INTERFACE_MODE_2500BASEX; + break; + case MV88E6XXX_PORT_STS_CMODE_XAUI: + state->interface = PHY_INTERFACE_MODE_XAUI; + break; + case MV88E6XXX_PORT_STS_CMODE_RXAUI: + state->interface = PHY_INTERFACE_MODE_RXAUI; + break; + default: + /* we do not support other cmode values here */ + state->interface = PHY_INTERFACE_MODE_NA; + } + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); if (err) return err; @@ -1246,3 +1341,77 @@ int mv88e6390_port_tag_remap(struct mv88e6xxx_chip *chip, int port) 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); +} diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 8d5a6cd6fb19..03a480cd71b9 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -42,8 +42,9 @@ #define MV88E6XXX_PORT_STS_TX_PAUSED 0x0020 #define MV88E6XXX_PORT_STS_FLOW_CTL 0x0010 #define MV88E6XXX_PORT_STS_CMODE_MASK 0x000f -#define MV88E6XXX_PORT_STS_CMODE_100BASE_X 0x0008 -#define MV88E6XXX_PORT_STS_CMODE_1000BASE_X 0x0009 +#define MV88E6XXX_PORT_STS_CMODE_RGMII 0x0007 +#define MV88E6XXX_PORT_STS_CMODE_100BASEX 0x0008 +#define MV88E6XXX_PORT_STS_CMODE_1000BASEX 0x0009 #define MV88E6XXX_PORT_STS_CMODE_SGMII 0x000a #define MV88E6XXX_PORT_STS_CMODE_2500BASEX 0x000b #define MV88E6XXX_PORT_STS_CMODE_XAUI 0x000c @@ -117,6 +118,7 @@ #define MV88E6XXX_PORT_SWITCH_ID_PROD_6190 0x1900 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6191 0x1910 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6185 0x1a70 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6220 0x2200 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6240 0x2400 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6250 0x2500 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6290 0x2900 @@ -220,7 +222,19 @@ #define MV88E6XXX_PORT_PRI_OVERRIDE 0x0d /* Offset 0x0E: Policy Control Register */ -#define MV88E6XXX_PORT_POLICY_CTL 0x0e +#define MV88E6XXX_PORT_POLICY_CTL 0x0e +#define MV88E6XXX_PORT_POLICY_CTL_DA_MASK 0xc000 +#define MV88E6XXX_PORT_POLICY_CTL_SA_MASK 0x3000 +#define MV88E6XXX_PORT_POLICY_CTL_VTU_MASK 0x0c00 +#define MV88E6XXX_PORT_POLICY_CTL_ETYPE_MASK 0x0300 +#define MV88E6XXX_PORT_POLICY_CTL_PPPOE_MASK 0x00c0 +#define MV88E6XXX_PORT_POLICY_CTL_VBAS_MASK 0x0030 +#define MV88E6XXX_PORT_POLICY_CTL_OPT82_MASK 0x000c +#define MV88E6XXX_PORT_POLICY_CTL_UDP_MASK 0x0003 +#define MV88E6XXX_PORT_POLICY_CTL_NORMAL 0x0000 +#define MV88E6XXX_PORT_POLICY_CTL_MIRROR 0x0001 +#define MV88E6XXX_PORT_POLICY_CTL_TRAP 0x0002 +#define MV88E6XXX_PORT_POLICY_CTL_DISCARD 0x0003 /* Offset 0x0F: Port Special Ether Type */ #define MV88E6XXX_PORT_ETH_TYPE 0x0f @@ -259,14 +273,16 @@ #define MV88E6095_PORT_IEEE_PRIO_REMAP_4567 0x19 /* Offset 0x1a: Magic undocumented errata register */ -#define PORT_RESERVED_1A 0x1a -#define PORT_RESERVED_1A_BUSY BIT(15) -#define PORT_RESERVED_1A_WRITE BIT(14) -#define PORT_RESERVED_1A_READ 0 -#define PORT_RESERVED_1A_PORT_SHIFT 5 -#define PORT_RESERVED_1A_BLOCK (0xf << 10) -#define PORT_RESERVED_1A_CTRL_PORT 4 -#define PORT_RESERVED_1A_DATA_PORT 5 +#define MV88E6XXX_PORT_RESERVED_1A 0x1a +#define MV88E6XXX_PORT_RESERVED_1A_BUSY 0x8000 +#define MV88E6XXX_PORT_RESERVED_1A_WRITE 0x4000 +#define MV88E6XXX_PORT_RESERVED_1A_READ 0x0000 +#define MV88E6XXX_PORT_RESERVED_1A_PORT_SHIFT 5 +#define MV88E6XXX_PORT_RESERVED_1A_BLOCK_SHIFT 10 +#define MV88E6XXX_PORT_RESERVED_1A_CTRL_PORT 0x04 +#define MV88E6XXX_PORT_RESERVED_1A_DATA_PORT 0x05 +#define MV88E6341_PORT_RESERVED_1A_FORCE_CMODE 0x8000 +#define MV88E6341_PORT_RESERVED_1A_SGMII_AN 0x2000 int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, u16 *val); @@ -320,6 +336,9 @@ int mv88e6185_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port, bool unicast, bool multicast); int mv88e6352_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port, bool unicast, bool multicast); +int mv88e6352_port_set_policy(struct mv88e6xxx_chip *chip, int port, + enum mv88e6xxx_policy_mapping mapping, + enum mv88e6xxx_policy_action action); int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port, u16 etype); int mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port, @@ -332,6 +351,8 @@ int mv88e6097_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in, u8 out); int mv88e6390_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in, u8 out); +int mv88e6341_port_set_cmode(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode); int mv88e6390_port_set_cmode(struct mv88e6xxx_chip *chip, int port, phy_interface_t mode); int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, @@ -351,4 +372,10 @@ int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port, int mv88e6xxx_port_disable_learn_limit(struct mv88e6xxx_chip *chip, int port); int mv88e6xxx_port_disable_pri_override(struct mv88e6xxx_chip *chip, int port); +int mv88e6xxx_port_hidden_write(struct mv88e6xxx_chip *chip, int block, + int port, int reg, u16 val); +int mv88e6xxx_port_hidden_wait(struct mv88e6xxx_chip *chip); +int mv88e6xxx_port_hidden_read(struct mv88e6xxx_chip *chip, int block, int port, + int reg, u16 *val); + #endif /* _MV88E6XXX_PORT_H */ diff --git a/drivers/net/dsa/mv88e6xxx/port_hidden.c b/drivers/net/dsa/mv88e6xxx/port_hidden.c new file mode 100644 index 000000000000..b49d05f0e117 --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/port_hidden.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Marvell 88E6xxx Switch Hidden Registers support + * + * Copyright (c) 2008 Marvell Semiconductor + * + * Copyright (c) 2019 Andrew Lunn <andrew@lunn.ch> + */ + +#include <linux/bitfield.h> + +#include "chip.h" +#include "port.h" + +/* The mv88e6390 and mv88e6341 have some hidden registers used for debug and + * development. The errata also makes use of them. + */ +int mv88e6xxx_port_hidden_write(struct mv88e6xxx_chip *chip, int block, + int port, int reg, u16 val) +{ + u16 ctrl; + int err; + + err = mv88e6xxx_port_write(chip, MV88E6XXX_PORT_RESERVED_1A_DATA_PORT, + MV88E6XXX_PORT_RESERVED_1A, val); + if (err) + return err; + + ctrl = MV88E6XXX_PORT_RESERVED_1A_BUSY | + MV88E6XXX_PORT_RESERVED_1A_WRITE | + block << MV88E6XXX_PORT_RESERVED_1A_BLOCK_SHIFT | + port << MV88E6XXX_PORT_RESERVED_1A_PORT_SHIFT | + reg; + + return mv88e6xxx_port_write(chip, MV88E6XXX_PORT_RESERVED_1A_CTRL_PORT, + MV88E6XXX_PORT_RESERVED_1A, ctrl); +} + +int mv88e6xxx_port_hidden_wait(struct mv88e6xxx_chip *chip) +{ + int bit = __bf_shf(MV88E6XXX_PORT_RESERVED_1A_BUSY); + + return mv88e6xxx_wait_bit(chip, MV88E6XXX_PORT_RESERVED_1A_CTRL_PORT, + MV88E6XXX_PORT_RESERVED_1A, bit, 0); +} + +int mv88e6xxx_port_hidden_read(struct mv88e6xxx_chip *chip, int block, int port, + int reg, u16 *val) +{ + u16 ctrl; + int err; + + ctrl = MV88E6XXX_PORT_RESERVED_1A_BUSY | + MV88E6XXX_PORT_RESERVED_1A_READ | + block << MV88E6XXX_PORT_RESERVED_1A_BLOCK_SHIFT | + port << MV88E6XXX_PORT_RESERVED_1A_PORT_SHIFT | + reg; + + err = mv88e6xxx_port_write(chip, MV88E6XXX_PORT_RESERVED_1A_CTRL_PORT, + MV88E6XXX_PORT_RESERVED_1A, ctrl); + if (err) + return err; + + err = mv88e6xxx_port_hidden_wait(chip); + if (err) + return err; + + return mv88e6xxx_port_read(chip, MV88E6XXX_PORT_RESERVED_1A_DATA_PORT, + MV88E6XXX_PORT_RESERVED_1A, val); +} diff --git a/drivers/net/dsa/mv88e6xxx/ptp.c b/drivers/net/dsa/mv88e6xxx/ptp.c index 768d256f7c9f..073cbd0bb91b 100644 --- a/drivers/net/dsa/mv88e6xxx/ptp.c +++ b/drivers/net/dsa/mv88e6xxx/ptp.c @@ -15,11 +15,31 @@ #include "hwtstamp.h" #include "ptp.h" -/* Raw timestamps are in units of 8-ns clock periods. */ -#define CC_SHIFT 28 -#define CC_MULT (8 << CC_SHIFT) -#define CC_MULT_NUM (1 << 9) -#define CC_MULT_DEM 15625ULL +#define MV88E6XXX_MAX_ADJ_PPB 1000000 + +/* Family MV88E6250: + * Raw timestamps are in units of 10-ns clock periods. + * + * clkadj = scaled_ppm * 10*2^28 / (10^6 * 2^16) + * simplifies to + * clkadj = scaled_ppm * 2^7 / 5^5 + */ +#define MV88E6250_CC_SHIFT 28 +#define MV88E6250_CC_MULT (10 << MV88E6250_CC_SHIFT) +#define MV88E6250_CC_MULT_NUM (1 << 7) +#define MV88E6250_CC_MULT_DEM 3125ULL + +/* Other families: + * Raw timestamps are in units of 8-ns clock periods. + * + * clkadj = scaled_ppm * 8*2^28 / (10^6 * 2^16) + * simplifies to + * clkadj = scaled_ppm * 2^9 / 5^6 + */ +#define MV88E6XXX_CC_SHIFT 28 +#define MV88E6XXX_CC_MULT (8 << MV88E6XXX_CC_SHIFT) +#define MV88E6XXX_CC_MULT_NUM (1 << 9) +#define MV88E6XXX_CC_MULT_DEM 15625ULL #define TAI_EVENT_WORK_INTERVAL msecs_to_jiffies(100) @@ -179,6 +199,7 @@ out: static int mv88e6xxx_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) { struct mv88e6xxx_chip *chip = ptp_to_chip(ptp); + const struct mv88e6xxx_ptp_ops *ptp_ops = chip->info->ops->ptp_ops; int neg_adj = 0; u32 diff, mult; u64 adj; @@ -187,10 +208,11 @@ static int mv88e6xxx_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) neg_adj = 1; scaled_ppm = -scaled_ppm; } - mult = CC_MULT; - adj = CC_MULT_NUM; + + mult = ptp_ops->cc_mult; + adj = ptp_ops->cc_mult_num; adj *= scaled_ppm; - diff = div_u64(adj, CC_MULT_DEM); + diff = div_u64(adj, ptp_ops->cc_mult_dem); mv88e6xxx_reg_lock(chip); @@ -310,7 +332,27 @@ static int mv88e6352_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin, return 0; } -const struct mv88e6xxx_ptp_ops mv88e6352_ptp_ops = { +const struct mv88e6xxx_ptp_ops mv88e6165_ptp_ops = { + .clock_read = mv88e6165_ptp_clock_read, + .global_enable = mv88e6165_global_enable, + .global_disable = mv88e6165_global_disable, + .arr0_sts_reg = MV88E6165_PORT_PTP_ARR0_STS, + .arr1_sts_reg = MV88E6165_PORT_PTP_ARR1_STS, + .dep_sts_reg = MV88E6165_PORT_PTP_DEP_STS, + .rx_filters = (1 << HWTSTAMP_FILTER_NONE) | + (1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT) | + (1 << HWTSTAMP_FILTER_PTP_V2_L2_SYNC) | + (1 << HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ) | + (1 << HWTSTAMP_FILTER_PTP_V2_EVENT) | + (1 << HWTSTAMP_FILTER_PTP_V2_SYNC) | + (1 << HWTSTAMP_FILTER_PTP_V2_DELAY_REQ), + .cc_shift = MV88E6XXX_CC_SHIFT, + .cc_mult = MV88E6XXX_CC_MULT, + .cc_mult_num = MV88E6XXX_CC_MULT_NUM, + .cc_mult_dem = MV88E6XXX_CC_MULT_DEM, +}; + +const struct mv88e6xxx_ptp_ops mv88e6250_ptp_ops = { .clock_read = mv88e6352_ptp_clock_read, .ptp_enable = mv88e6352_ptp_enable, .ptp_verify = mv88e6352_ptp_verify, @@ -331,22 +373,37 @@ const struct mv88e6xxx_ptp_ops mv88e6352_ptp_ops = { (1 << HWTSTAMP_FILTER_PTP_V2_EVENT) | (1 << HWTSTAMP_FILTER_PTP_V2_SYNC) | (1 << HWTSTAMP_FILTER_PTP_V2_DELAY_REQ), + .cc_shift = MV88E6250_CC_SHIFT, + .cc_mult = MV88E6250_CC_MULT, + .cc_mult_num = MV88E6250_CC_MULT_NUM, + .cc_mult_dem = MV88E6250_CC_MULT_DEM, }; -const struct mv88e6xxx_ptp_ops mv88e6165_ptp_ops = { - .clock_read = mv88e6165_ptp_clock_read, - .global_enable = mv88e6165_global_enable, - .global_disable = mv88e6165_global_disable, - .arr0_sts_reg = MV88E6165_PORT_PTP_ARR0_STS, - .arr1_sts_reg = MV88E6165_PORT_PTP_ARR1_STS, - .dep_sts_reg = MV88E6165_PORT_PTP_DEP_STS, +const struct mv88e6xxx_ptp_ops mv88e6352_ptp_ops = { + .clock_read = mv88e6352_ptp_clock_read, + .ptp_enable = mv88e6352_ptp_enable, + .ptp_verify = mv88e6352_ptp_verify, + .event_work = mv88e6352_tai_event_work, + .port_enable = mv88e6352_hwtstamp_port_enable, + .port_disable = mv88e6352_hwtstamp_port_disable, + .n_ext_ts = 1, + .arr0_sts_reg = MV88E6XXX_PORT_PTP_ARR0_STS, + .arr1_sts_reg = MV88E6XXX_PORT_PTP_ARR1_STS, + .dep_sts_reg = MV88E6XXX_PORT_PTP_DEP_STS, .rx_filters = (1 << HWTSTAMP_FILTER_NONE) | + (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT) | + (1 << HWTSTAMP_FILTER_PTP_V2_L4_SYNC) | + (1 << HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) | (1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT) | (1 << HWTSTAMP_FILTER_PTP_V2_L2_SYNC) | (1 << HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ) | (1 << HWTSTAMP_FILTER_PTP_V2_EVENT) | (1 << HWTSTAMP_FILTER_PTP_V2_SYNC) | (1 << HWTSTAMP_FILTER_PTP_V2_DELAY_REQ), + .cc_shift = MV88E6XXX_CC_SHIFT, + .cc_mult = MV88E6XXX_CC_MULT, + .cc_mult_num = MV88E6XXX_CC_MULT_NUM, + .cc_mult_dem = MV88E6XXX_CC_MULT_DEM, }; static u64 mv88e6xxx_ptp_clock_read(const struct cyclecounter *cc) @@ -384,8 +441,8 @@ int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip) memset(&chip->tstamp_cc, 0, sizeof(chip->tstamp_cc)); chip->tstamp_cc.read = mv88e6xxx_ptp_clock_read; chip->tstamp_cc.mask = CYCLECOUNTER_MASK(32); - chip->tstamp_cc.mult = CC_MULT; - chip->tstamp_cc.shift = CC_SHIFT; + chip->tstamp_cc.mult = ptp_ops->cc_mult; + chip->tstamp_cc.shift = ptp_ops->cc_shift; timecounter_init(&chip->tstamp_tc, &chip->tstamp_cc, ktime_to_ns(ktime_get_real())); @@ -397,7 +454,6 @@ int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip) chip->ptp_clock_info.owner = THIS_MODULE; snprintf(chip->ptp_clock_info.name, sizeof(chip->ptp_clock_info.name), "%s", dev_name(chip->dev)); - chip->ptp_clock_info.max_adj = 1000000; chip->ptp_clock_info.n_ext_ts = ptp_ops->n_ext_ts; chip->ptp_clock_info.n_per_out = 0; @@ -413,6 +469,7 @@ int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip) } chip->ptp_clock_info.pin_config = chip->pin_config; + chip->ptp_clock_info.max_adj = MV88E6XXX_MAX_ADJ_PPB; chip->ptp_clock_info.adjfine = mv88e6xxx_ptp_adjfine; chip->ptp_clock_info.adjtime = mv88e6xxx_ptp_adjtime; chip->ptp_clock_info.gettime64 = mv88e6xxx_ptp_gettime; diff --git a/drivers/net/dsa/mv88e6xxx/ptp.h b/drivers/net/dsa/mv88e6xxx/ptp.h index 0a1f8de8f062..269d5d16a466 100644 --- a/drivers/net/dsa/mv88e6xxx/ptp.h +++ b/drivers/net/dsa/mv88e6xxx/ptp.h @@ -148,8 +148,9 @@ void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip); #define ptp_to_chip(ptp) container_of(ptp, struct mv88e6xxx_chip, \ ptp_clock_info) -extern const struct mv88e6xxx_ptp_ops mv88e6352_ptp_ops; extern const struct mv88e6xxx_ptp_ops mv88e6165_ptp_ops; +extern const struct mv88e6xxx_ptp_ops mv88e6250_ptp_ops; +extern const struct mv88e6xxx_ptp_ops mv88e6352_ptp_ops; #else /* !CONFIG_NET_DSA_MV88E6XXX_PTP */ @@ -167,8 +168,9 @@ static inline void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip) { } -static const struct mv88e6xxx_ptp_ops mv88e6352_ptp_ops = {}; static const struct mv88e6xxx_ptp_ops mv88e6165_ptp_ops = {}; +static const struct mv88e6xxx_ptp_ops mv88e6250_ptp_ops = {}; +static const struct mv88e6xxx_ptp_ops mv88e6352_ptp_ops = {}; #endif /* CONFIG_NET_DSA_MV88E6XXX_PTP */ diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index 20c526c2a9ee..902feb398746 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.c +++ b/drivers/net/dsa/mv88e6xxx/serdes.c @@ -49,7 +49,8 @@ static int mv88e6390_serdes_write(struct mv88e6xxx_chip *chip, return mv88e6xxx_phy_write(chip, lane, reg_c45, val); } -static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on) +int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, + bool up) { u16 val, new_val; int err; @@ -58,7 +59,7 @@ static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on) if (err) return err; - if (on) + if (up) new_val = val & ~BMCR_PDOWN; else new_val = val | BMCR_PDOWN; @@ -69,29 +70,25 @@ static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on) return err; } -static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port) +u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) { u8 cmode = chip->ports[port].cmode; + u8 lane = 0; - if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASE_X) || - (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) || + if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASEX) || + (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX) || (cmode == MV88E6XXX_PORT_STS_CMODE_SGMII)) - return true; + lane = 0xff; /* Unused */ - return false; + return lane; } -int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) +static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port) { - int err; - - if (mv88e6352_port_has_serdes(chip, port)) { - err = mv88e6352_serdes_power_set(chip, on); - if (err < 0) - return err; - } + if (mv88e6xxx_serdes_get_lane(chip, port)) + return true; - return 0; + return false; } struct mv88e6352_serdes_hw_stat { @@ -186,214 +183,178 @@ static void mv88e6352_serdes_irq_link(struct mv88e6xxx_chip *chip, int port) struct dsa_switch *ds = chip->ds; u16 status; bool up; + int err; - mv88e6352_serdes_read(chip, MII_BMSR, &status); + err = mv88e6352_serdes_read(chip, MII_BMSR, &status); + if (err) + return; /* Status must be read twice in order to give the current link * status. Otherwise the change in link status since the last * read of the register is returned. */ - mv88e6352_serdes_read(chip, MII_BMSR, &status); + err = mv88e6352_serdes_read(chip, MII_BMSR, &status); + if (err) + return; up = status & BMSR_LSTATUS; dsa_port_phylink_mac_change(ds, port, up); } -static irqreturn_t mv88e6352_serdes_thread_fn(int irq, void *dev_id) +irqreturn_t mv88e6352_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, + u8 lane) { - struct mv88e6xxx_port *port = dev_id; - struct mv88e6xxx_chip *chip = port->chip; irqreturn_t ret = IRQ_NONE; u16 status; int err; - mv88e6xxx_reg_lock(chip); - err = mv88e6352_serdes_read(chip, MV88E6352_SERDES_INT_STATUS, &status); if (err) - goto out; + return ret; if (status & MV88E6352_SERDES_INT_LINK_CHANGE) { ret = IRQ_HANDLED; - mv88e6352_serdes_irq_link(chip, port->port); + mv88e6352_serdes_irq_link(chip, port); } -out: - mv88e6xxx_reg_unlock(chip); return ret; } -static int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip) +int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 lane, + bool enable) { - return mv88e6352_serdes_write(chip, MV88E6352_SERDES_INT_ENABLE, - MV88E6352_SERDES_INT_LINK_CHANGE); -} + u16 val = 0; -static int mv88e6352_serdes_irq_disable(struct mv88e6xxx_chip *chip) -{ - return mv88e6352_serdes_write(chip, MV88E6352_SERDES_INT_ENABLE, 0); + if (enable) + val |= MV88E6352_SERDES_INT_LINK_CHANGE; + + return mv88e6352_serdes_write(chip, MV88E6352_SERDES_INT_ENABLE, val); } -int mv88e6352_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port) +unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port) { - int err; - - if (!mv88e6352_port_has_serdes(chip, port)) - return 0; - - chip->ports[port].serdes_irq = irq_find_mapping(chip->g2_irq.domain, - MV88E6352_SERDES_IRQ); - if (chip->ports[port].serdes_irq < 0) { - dev_err(chip->dev, "Unable to map SERDES irq: %d\n", - chip->ports[port].serdes_irq); - return chip->ports[port].serdes_irq; - } - - /* Requesting the IRQ will trigger irq callbacks. So we cannot - * hold the reg_lock. - */ - mv88e6xxx_reg_unlock(chip); - err = request_threaded_irq(chip->ports[port].serdes_irq, NULL, - mv88e6352_serdes_thread_fn, - IRQF_ONESHOT, "mv88e6xxx-serdes", - &chip->ports[port]); - mv88e6xxx_reg_lock(chip); - - if (err) { - dev_err(chip->dev, "Unable to request SERDES interrupt: %d\n", - err); - return err; - } - - return mv88e6352_serdes_irq_enable(chip); + return irq_find_mapping(chip->g2_irq.domain, MV88E6352_SERDES_IRQ); } -void mv88e6352_serdes_irq_free(struct mv88e6xxx_chip *chip, int port) +u8 mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) { - if (!mv88e6352_port_has_serdes(chip, port)) - return; - - mv88e6352_serdes_irq_disable(chip); + u8 cmode = chip->ports[port].cmode; + u8 lane = 0; - /* Freeing the IRQ will trigger irq callbacks. So we cannot - * hold the reg_lock. - */ - mv88e6xxx_reg_unlock(chip); - free_irq(chip->ports[port].serdes_irq, &chip->ports[port]); - mv88e6xxx_reg_lock(chip); + switch (port) { + case 5: + if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX || + cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || + cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) + lane = MV88E6341_PORT5_LANE; + break; + } - chip->ports[port].serdes_irq = 0; + return lane; } -/* Return the SERDES lane address a port is using. Only Ports 9 and 10 - * have SERDES lanes. Returns -ENODEV if a port does not have a lane. - */ -static int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) +u8 mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) { u8 cmode = chip->ports[port].cmode; + u8 lane = 0; switch (port) { case 9: - if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX || cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) - return MV88E6390_PORT9_LANE0; - return -ENODEV; + lane = MV88E6390_PORT9_LANE0; + break; case 10: - if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX || cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) - return MV88E6390_PORT10_LANE0; - return -ENODEV; - default: - return -ENODEV; + lane = MV88E6390_PORT10_LANE0; + break; } + + return lane; } -/* Return the SERDES lane address a port is using. Ports 9 and 10 can - * use multiple lanes. If so, return the first lane the port uses. - * Returns -ENODEV if a port does not have a lane. - */ -int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) +u8 mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) { - u8 cmode_port9, cmode_port10, cmode_port; - - cmode_port9 = chip->ports[9].cmode; - cmode_port10 = chip->ports[10].cmode; - cmode_port = chip->ports[port].cmode; + u8 cmode_port = chip->ports[port].cmode; + u8 cmode_port10 = chip->ports[10].cmode; + u8 cmode_port9 = chip->ports[9].cmode; + u8 lane = 0; switch (port) { case 2: - if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX || cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX) - if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) - return MV88E6390_PORT9_LANE1; - return -ENODEV; + if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX) + lane = MV88E6390_PORT9_LANE1; + break; case 3: - if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX || cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) - if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) - return MV88E6390_PORT9_LANE2; - return -ENODEV; + if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX) + lane = MV88E6390_PORT9_LANE2; + break; case 4: - if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX || cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) - if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) - return MV88E6390_PORT9_LANE3; - return -ENODEV; + if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX) + lane = MV88E6390_PORT9_LANE3; + break; case 5: - if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX || cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX) - if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) - return MV88E6390_PORT10_LANE1; - return -ENODEV; + if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX) + lane = MV88E6390_PORT10_LANE1; + break; case 6: - if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX || cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) - if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) - return MV88E6390_PORT10_LANE2; - return -ENODEV; + if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX) + lane = MV88E6390_PORT10_LANE2; + break; case 7: - if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX || cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) - if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) - return MV88E6390_PORT10_LANE3; - return -ENODEV; + if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX) + lane = MV88E6390_PORT10_LANE3; + break; case 9: - if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX || cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || cmode_port9 == MV88E6XXX_PORT_STS_CMODE_XAUI || cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) - return MV88E6390_PORT9_LANE0; - return -ENODEV; + lane = MV88E6390_PORT9_LANE0; + break; case 10: - if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX || cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || cmode_port10 == MV88E6XXX_PORT_STS_CMODE_XAUI || cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) - return MV88E6390_PORT10_LANE0; - return -ENODEV; - default: - return -ENODEV; + lane = MV88E6390_PORT10_LANE0; + break; } + + return lane; } -/* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */ -static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, int lane, - bool on) +/* Set power up/down for 10GBASE-R and 10GBASE-X4/X2 */ +static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, u8 lane, + bool up) { u16 val, new_val; int err; @@ -404,7 +365,7 @@ static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, int lane, if (err) return err; - if (on) + if (up) new_val = val & ~(MV88E6390_PCS_CONTROL_1_RESET | MV88E6390_PCS_CONTROL_1_LOOPBACK | MV88E6390_PCS_CONTROL_1_PDOWN); @@ -418,9 +379,9 @@ static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, int lane, return err; } -/* Set the power on/off for SGMII and 1000Base-X */ -static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, int lane, - bool on) +/* Set power up/down for SGMII and 1000Base-X */ +static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, u8 lane, + bool up) { u16 val, new_val; int err; @@ -430,7 +391,7 @@ static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, int lane, if (err) return err; - if (on) + if (up) new_val = val & ~(MV88E6390_SGMII_CONTROL_RESET | MV88E6390_SGMII_CONTROL_LOOPBACK | MV88E6390_SGMII_CONTROL_PDOWN); @@ -444,70 +405,32 @@ static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, int lane, return err; } -static int mv88e6390_serdes_power_lane(struct mv88e6xxx_chip *chip, int port, - int lane, bool on) +int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, + bool up) { u8 cmode = chip->ports[port].cmode; switch (cmode) { case MV88E6XXX_PORT_STS_CMODE_SGMII: - case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: + case MV88E6XXX_PORT_STS_CMODE_1000BASEX: case MV88E6XXX_PORT_STS_CMODE_2500BASEX: - return mv88e6390_serdes_power_sgmii(chip, lane, on); + return mv88e6390_serdes_power_sgmii(chip, lane, up); case MV88E6XXX_PORT_STS_CMODE_XAUI: case MV88E6XXX_PORT_STS_CMODE_RXAUI: - return mv88e6390_serdes_power_10g(chip, lane, on); - } - - return 0; -} - -int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) -{ - int lane; - - lane = mv88e6390_serdes_get_lane(chip, port); - if (lane == -ENODEV) - return 0; - - if (lane < 0) - return lane; - - switch (port) { - case 9 ... 10: - return mv88e6390_serdes_power_lane(chip, port, lane, on); - } - - return 0; -} - -int mv88e6390x_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) -{ - int lane; - - lane = mv88e6390x_serdes_get_lane(chip, port); - if (lane == -ENODEV) - return 0; - - if (lane < 0) - return lane; - - switch (port) { - case 2 ... 4: - case 5 ... 7: - case 9 ... 10: - return mv88e6390_serdes_power_lane(chip, port, lane, on); + return mv88e6390_serdes_power_10g(chip, lane, up); } return 0; } static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip, - int port, int lane) + int port, u8 lane) { + u8 cmode = chip->ports[port].cmode; struct dsa_switch *ds = chip->ds; int duplex = DUPLEX_UNKNOWN; int speed = SPEED_UNKNOWN; + phy_interface_t mode; int link, err; u16 status; @@ -527,7 +450,10 @@ static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip, switch (status & MV88E6390_SGMII_PHY_STATUS_SPEED_MASK) { case MV88E6390_SGMII_PHY_STATUS_SPEED_1000: - speed = SPEED_1000; + if (cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) + speed = SPEED_2500; + else + speed = SPEED_1000; break; case MV88E6390_SGMII_PHY_STATUS_SPEED_100: speed = SPEED_100; @@ -541,8 +467,22 @@ static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip, } } + switch (cmode) { + case MV88E6XXX_PORT_STS_CMODE_SGMII: + mode = PHY_INTERFACE_MODE_SGMII; + break; + case MV88E6XXX_PORT_STS_CMODE_1000BASEX: + mode = PHY_INTERFACE_MODE_1000BASEX; + break; + case MV88E6XXX_PORT_STS_CMODE_2500BASEX: + mode = PHY_INTERFACE_MODE_2500BASEX; + break; + default: + mode = PHY_INTERFACE_MODE_NA; + } + err = mv88e6xxx_port_setup_mac(chip, port, link, speed, duplex, - PAUSE_OFF, PHY_INTERFACE_MODE_NA); + PAUSE_OFF, mode); if (err) dev_err(chip->dev, "can't propagate PHY settings to MAC: %d\n", err); @@ -551,55 +491,35 @@ static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip, } static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip, - int lane) -{ - return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_SGMII_INT_ENABLE, - MV88E6390_SGMII_INT_LINK_DOWN | - MV88E6390_SGMII_INT_LINK_UP); -} - -static int mv88e6390_serdes_irq_disable_sgmii(struct mv88e6xxx_chip *chip, - int lane) + u8 lane, bool enable) { - return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_SGMII_INT_ENABLE, 0); -} + u16 val = 0; -int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, - int lane) -{ - u8 cmode = chip->ports[port].cmode; - int err = 0; - - switch (cmode) { - case MV88E6XXX_PORT_STS_CMODE_SGMII: - case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: - case MV88E6XXX_PORT_STS_CMODE_2500BASEX: - err = mv88e6390_serdes_irq_enable_sgmii(chip, lane); - } + if (enable) + val |= MV88E6390_SGMII_INT_LINK_DOWN | + MV88E6390_SGMII_INT_LINK_UP; - return err; + return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_INT_ENABLE, val); } -int mv88e6390_serdes_irq_disable(struct mv88e6xxx_chip *chip, int port, - int lane) +int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 lane, + bool enable) { u8 cmode = chip->ports[port].cmode; - int err = 0; switch (cmode) { case MV88E6XXX_PORT_STS_CMODE_SGMII: - case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: + case MV88E6XXX_PORT_STS_CMODE_1000BASEX: case MV88E6XXX_PORT_STS_CMODE_2500BASEX: - err = mv88e6390_serdes_irq_disable_sgmii(chip, lane); + return mv88e6390_serdes_irq_enable_sgmii(chip, lane, enable); } - return err; + return 0; } static int mv88e6390_serdes_irq_status_sgmii(struct mv88e6xxx_chip *chip, - int lane, u16 *status) + u8 lane, u16 *status) { int err; @@ -609,129 +529,32 @@ static int mv88e6390_serdes_irq_status_sgmii(struct mv88e6xxx_chip *chip, return err; } -static irqreturn_t mv88e6390_serdes_thread_fn(int irq, void *dev_id) +irqreturn_t mv88e6390_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, + u8 lane) { - struct mv88e6xxx_port *port = dev_id; - struct mv88e6xxx_chip *chip = port->chip; + u8 cmode = chip->ports[port].cmode; irqreturn_t ret = IRQ_NONE; - u8 cmode = port->cmode; u16 status; - int lane; int err; - lane = mv88e6390x_serdes_get_lane(chip, port->port); - - mv88e6xxx_reg_lock(chip); - switch (cmode) { case MV88E6XXX_PORT_STS_CMODE_SGMII: - case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: + case MV88E6XXX_PORT_STS_CMODE_1000BASEX: case MV88E6XXX_PORT_STS_CMODE_2500BASEX: err = mv88e6390_serdes_irq_status_sgmii(chip, lane, &status); if (err) - goto out; + return ret; if (status & (MV88E6390_SGMII_INT_LINK_DOWN | MV88E6390_SGMII_INT_LINK_UP)) { ret = IRQ_HANDLED; - mv88e6390_serdes_irq_link_sgmii(chip, port->port, lane); + mv88e6390_serdes_irq_link_sgmii(chip, port, lane); } } -out: - mv88e6xxx_reg_unlock(chip); return ret; } -int mv88e6390x_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port) -{ - int lane; - int err; - - lane = mv88e6390x_serdes_get_lane(chip, port); - - if (lane == -ENODEV) - return 0; - - if (lane < 0) - return lane; - - chip->ports[port].serdes_irq = irq_find_mapping(chip->g2_irq.domain, - port); - if (chip->ports[port].serdes_irq < 0) { - dev_err(chip->dev, "Unable to map SERDES irq: %d\n", - chip->ports[port].serdes_irq); - return chip->ports[port].serdes_irq; - } - - /* Requesting the IRQ will trigger irq callbacks. So we cannot - * hold the reg_lock. - */ - mv88e6xxx_reg_unlock(chip); - err = request_threaded_irq(chip->ports[port].serdes_irq, NULL, - mv88e6390_serdes_thread_fn, - IRQF_ONESHOT, "mv88e6xxx-serdes", - &chip->ports[port]); - mv88e6xxx_reg_lock(chip); - - if (err) { - dev_err(chip->dev, "Unable to request SERDES interrupt: %d\n", - err); - return err; - } - - return mv88e6390_serdes_irq_enable(chip, port, lane); -} - -int mv88e6390_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port) -{ - if (port < 9) - return 0; - - return mv88e6390x_serdes_irq_setup(chip, port); -} - -void mv88e6390x_serdes_irq_free(struct mv88e6xxx_chip *chip, int port) -{ - int lane = mv88e6390x_serdes_get_lane(chip, port); - - if (lane == -ENODEV) - return; - - if (lane < 0) - return; - - mv88e6390_serdes_irq_disable(chip, port, lane); - - /* Freeing the IRQ will trigger irq callbacks. So we cannot - * hold the reg_lock. - */ - mv88e6xxx_reg_unlock(chip); - free_irq(chip->ports[port].serdes_irq, &chip->ports[port]); - mv88e6xxx_reg_lock(chip); - - chip->ports[port].serdes_irq = 0; -} - -void mv88e6390_serdes_irq_free(struct mv88e6xxx_chip *chip, int port) -{ - if (port < 9) - return; - - mv88e6390x_serdes_irq_free(chip, port); -} - -int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) +unsigned int mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port) { - u8 cmode = chip->ports[port].cmode; - - if (port != 5) - return 0; - - if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || - cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || - cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) - return mv88e6390_serdes_power_sgmii(chip, MV88E6341_ADDR_SERDES, - on); - - return 0; + return irq_find_mapping(chip->g2_irq.domain, port); } diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h index ff5b94439335..bd8df36ab537 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.h +++ b/drivers/net/dsa/mv88e6xxx/serdes.h @@ -28,7 +28,7 @@ #define MV88E6352_SERDES_INT_STATUS 0x13 -#define MV88E6341_ADDR_SERDES 0x15 +#define MV88E6341_PORT5_LANE 0x15 #define MV88E6390_PORT9_LANE0 0x09 #define MV88E6390_PORT9_LANE1 0x12 @@ -74,26 +74,94 @@ #define MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID BIT(11) #define MV88E6390_SGMII_PHY_STATUS_LINK BIT(10) -int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); -int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on); -int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on); -int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on); -int mv88e6390x_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on); -int mv88e6390_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port); -void mv88e6390_serdes_irq_free(struct mv88e6xxx_chip *chip, int port); -int mv88e6390x_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port); -void mv88e6390x_serdes_irq_free(struct mv88e6xxx_chip *chip, int port); +u8 mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); +u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); +u8 mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); +u8 mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); +unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip, + int port); +unsigned int mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip *chip, + int port); +int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, + bool on); +int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, + bool on); +int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 lane, + bool enable); +int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 lane, + bool enable); +irqreturn_t mv88e6352_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, + u8 lane); +irqreturn_t mv88e6390_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, + u8 lane); int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port); int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip, int port, uint8_t *data); int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, uint64_t *data); -int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, - int lane); -int mv88e6390_serdes_irq_disable(struct mv88e6xxx_chip *chip, int port, - int lane); -int mv88e6352_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port); -void mv88e6352_serdes_irq_free(struct mv88e6xxx_chip *chip, int port); +/* Return the (first) SERDES lane address a port is using, 0 otherwise. */ +static inline u8 mv88e6xxx_serdes_get_lane(struct mv88e6xxx_chip *chip, + int port) +{ + if (!chip->info->ops->serdes_get_lane) + return 0; + + return chip->info->ops->serdes_get_lane(chip, port); +} + +static inline int mv88e6xxx_serdes_power_up(struct mv88e6xxx_chip *chip, + int port, u8 lane) +{ + if (!chip->info->ops->serdes_power) + return -EOPNOTSUPP; + + return chip->info->ops->serdes_power(chip, port, lane, true); +} + +static inline int mv88e6xxx_serdes_power_down(struct mv88e6xxx_chip *chip, + int port, u8 lane) +{ + if (!chip->info->ops->serdes_power) + return -EOPNOTSUPP; + + return chip->info->ops->serdes_power(chip, port, lane, false); +} + +static inline unsigned int +mv88e6xxx_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port) +{ + if (!chip->info->ops->serdes_irq_mapping) + return 0; + + return chip->info->ops->serdes_irq_mapping(chip, port); +} + +static inline int mv88e6xxx_serdes_irq_enable(struct mv88e6xxx_chip *chip, + int port, u8 lane) +{ + if (!chip->info->ops->serdes_irq_enable) + return -EOPNOTSUPP; + + return chip->info->ops->serdes_irq_enable(chip, port, lane, true); +} + +static inline int mv88e6xxx_serdes_irq_disable(struct mv88e6xxx_chip *chip, + int port, u8 lane) +{ + if (!chip->info->ops->serdes_irq_enable) + return -EOPNOTSUPP; + + return chip->info->ops->serdes_irq_enable(chip, port, lane, false); +} + +static inline irqreturn_t +mv88e6xxx_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, u8 lane) +{ + if (!chip->info->ops->serdes_irq_status) + return IRQ_NONE; + + return chip->info->ops->serdes_irq_status(chip, port, lane); +} #endif diff --git a/drivers/net/dsa/mv88e6xxx/smi.c b/drivers/net/dsa/mv88e6xxx/smi.c index 5fc78a063843..282fe08db050 100644 --- a/drivers/net/dsa/mv88e6xxx/smi.c +++ b/drivers/net/dsa/mv88e6xxx/smi.c @@ -64,8 +64,10 @@ static int mv88e6xxx_smi_direct_wait(struct mv88e6xxx_chip *chip, if (err) return err; - if (!!(data >> bit) == !!val) + if (!!(data & BIT(bit)) == !!val) return 0; + + usleep_range(1000, 2000); } return -ETIMEDOUT; |