1ec561276SVivien Didelot /* 2dc30c35bSAndrew Lunn * Marvell 88E6xxx Switch Global 2 Registers support (device address 3dc30c35bSAndrew Lunn * 0x1C) 4ec561276SVivien Didelot * 5ec561276SVivien Didelot * Copyright (c) 2008 Marvell Semiconductor 6ec561276SVivien Didelot * 7ec561276SVivien Didelot * Copyright (c) 2016 Vivien Didelot <vivien.didelot@savoirfairelinux.com> 8ec561276SVivien Didelot * 9ec561276SVivien Didelot * This program is free software; you can redistribute it and/or modify 10ec561276SVivien Didelot * it under the terms of the GNU General Public License as published by 11ec561276SVivien Didelot * the Free Software Foundation; either version 2 of the License, or 12ec561276SVivien Didelot * (at your option) any later version. 13ec561276SVivien Didelot */ 14ec561276SVivien Didelot 15dc30c35bSAndrew Lunn #include <linux/irqdomain.h> 16ec561276SVivien Didelot #include "mv88e6xxx.h" 17ec561276SVivien Didelot #include "global2.h" 18ec561276SVivien Didelot 199fe850fbSVivien Didelot #define ADDR_GLOBAL2 0x1c 209fe850fbSVivien Didelot 219fe850fbSVivien Didelot static int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val) 229fe850fbSVivien Didelot { 239fe850fbSVivien Didelot return mv88e6xxx_read(chip, ADDR_GLOBAL2, reg, val); 249fe850fbSVivien Didelot } 259fe850fbSVivien Didelot 269fe850fbSVivien Didelot static int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val) 279fe850fbSVivien Didelot { 289fe850fbSVivien Didelot return mv88e6xxx_write(chip, ADDR_GLOBAL2, reg, val); 299fe850fbSVivien Didelot } 309fe850fbSVivien Didelot 319fe850fbSVivien Didelot static int mv88e6xxx_g2_update(struct mv88e6xxx_chip *chip, int reg, u16 update) 329fe850fbSVivien Didelot { 339fe850fbSVivien Didelot return mv88e6xxx_update(chip, ADDR_GLOBAL2, reg, update); 349fe850fbSVivien Didelot } 359fe850fbSVivien Didelot 369fe850fbSVivien Didelot static int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask) 379fe850fbSVivien Didelot { 389fe850fbSVivien Didelot return mv88e6xxx_wait(chip, ADDR_GLOBAL2, reg, mask); 399fe850fbSVivien Didelot } 409fe850fbSVivien Didelot 416e55f698SAndrew Lunn /* Offset 0x02: Management Enable 2x */ 426e55f698SAndrew Lunn /* Offset 0x03: Management Enable 0x */ 436e55f698SAndrew Lunn 446e55f698SAndrew Lunn int mv88e6095_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip) 456e55f698SAndrew Lunn { 466e55f698SAndrew Lunn int err; 476e55f698SAndrew Lunn 486e55f698SAndrew Lunn /* Consider the frames with reserved multicast destination 496e55f698SAndrew Lunn * addresses matching 01:80:c2:00:00:2x as MGMT. 506e55f698SAndrew Lunn */ 516e55f698SAndrew Lunn if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X)) { 526e55f698SAndrew Lunn err = mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_2X, 0xffff); 536e55f698SAndrew Lunn if (err) 546e55f698SAndrew Lunn return err; 556e55f698SAndrew Lunn } 566e55f698SAndrew Lunn 576e55f698SAndrew Lunn /* Consider the frames with reserved multicast destination 586e55f698SAndrew Lunn * addresses matching 01:80:c2:00:00:0x as MGMT. 596e55f698SAndrew Lunn */ 606e55f698SAndrew Lunn if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X)) 616e55f698SAndrew Lunn return mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_0X, 0xffff); 626e55f698SAndrew Lunn 636e55f698SAndrew Lunn return 0; 646e55f698SAndrew Lunn } 656e55f698SAndrew Lunn 66ec561276SVivien Didelot /* Offset 0x06: Device Mapping Table register */ 67ec561276SVivien Didelot 68ec561276SVivien Didelot static int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip, 69ec561276SVivien Didelot int target, int port) 70ec561276SVivien Didelot { 71ec561276SVivien Didelot u16 val = (target << 8) | (port & 0xf); 72ec561276SVivien Didelot 739fe850fbSVivien Didelot return mv88e6xxx_g2_update(chip, GLOBAL2_DEVICE_MAPPING, val); 74ec561276SVivien Didelot } 75ec561276SVivien Didelot 76ec561276SVivien Didelot static int mv88e6xxx_g2_set_device_mapping(struct mv88e6xxx_chip *chip) 77ec561276SVivien Didelot { 78ec561276SVivien Didelot int target, port; 79ec561276SVivien Didelot int err; 80ec561276SVivien Didelot 81ec561276SVivien Didelot /* Initialize the routing port to the 32 possible target devices */ 82ec561276SVivien Didelot for (target = 0; target < 32; ++target) { 83ec561276SVivien Didelot port = 0xf; 84ec561276SVivien Didelot 85ec561276SVivien Didelot if (target < DSA_MAX_SWITCHES) { 86ec561276SVivien Didelot port = chip->ds->rtable[target]; 87ec561276SVivien Didelot if (port == DSA_RTABLE_NONE) 88ec561276SVivien Didelot port = 0xf; 89ec561276SVivien Didelot } 90ec561276SVivien Didelot 91ec561276SVivien Didelot err = mv88e6xxx_g2_device_mapping_write(chip, target, port); 92ec561276SVivien Didelot if (err) 93ec561276SVivien Didelot break; 94ec561276SVivien Didelot } 95ec561276SVivien Didelot 96ec561276SVivien Didelot return err; 97ec561276SVivien Didelot } 98ec561276SVivien Didelot 99ec561276SVivien Didelot /* Offset 0x07: Trunk Mask Table register */ 100ec561276SVivien Didelot 101ec561276SVivien Didelot static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num, 102ec561276SVivien Didelot bool hask, u16 mask) 103ec561276SVivien Didelot { 104370b4ffbSVivien Didelot const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1; 105ec561276SVivien Didelot u16 val = (num << 12) | (mask & port_mask); 106ec561276SVivien Didelot 107ec561276SVivien Didelot if (hask) 108ec561276SVivien Didelot val |= GLOBAL2_TRUNK_MASK_HASK; 109ec561276SVivien Didelot 1109fe850fbSVivien Didelot return mv88e6xxx_g2_update(chip, GLOBAL2_TRUNK_MASK, val); 111ec561276SVivien Didelot } 112ec561276SVivien Didelot 113ec561276SVivien Didelot /* Offset 0x08: Trunk Mapping Table register */ 114ec561276SVivien Didelot 115ec561276SVivien Didelot static int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id, 116ec561276SVivien Didelot u16 map) 117ec561276SVivien Didelot { 118370b4ffbSVivien Didelot const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1; 119ec561276SVivien Didelot u16 val = (id << 11) | (map & port_mask); 120ec561276SVivien Didelot 1219fe850fbSVivien Didelot return mv88e6xxx_g2_update(chip, GLOBAL2_TRUNK_MAPPING, val); 122ec561276SVivien Didelot } 123ec561276SVivien Didelot 124ec561276SVivien Didelot static int mv88e6xxx_g2_clear_trunk(struct mv88e6xxx_chip *chip) 125ec561276SVivien Didelot { 126370b4ffbSVivien Didelot const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1; 127ec561276SVivien Didelot int i, err; 128ec561276SVivien Didelot 129ec561276SVivien Didelot /* Clear all eight possible Trunk Mask vectors */ 130ec561276SVivien Didelot for (i = 0; i < 8; ++i) { 131ec561276SVivien Didelot err = mv88e6xxx_g2_trunk_mask_write(chip, i, false, port_mask); 132ec561276SVivien Didelot if (err) 133ec561276SVivien Didelot return err; 134ec561276SVivien Didelot } 135ec561276SVivien Didelot 136ec561276SVivien Didelot /* Clear all sixteen possible Trunk ID routing vectors */ 137ec561276SVivien Didelot for (i = 0; i < 16; ++i) { 138ec561276SVivien Didelot err = mv88e6xxx_g2_trunk_mapping_write(chip, i, 0); 139ec561276SVivien Didelot if (err) 140ec561276SVivien Didelot return err; 141ec561276SVivien Didelot } 142ec561276SVivien Didelot 143ec561276SVivien Didelot return 0; 144ec561276SVivien Didelot } 145ec561276SVivien Didelot 146ec561276SVivien Didelot /* Offset 0x09: Ingress Rate Command register 147ec561276SVivien Didelot * Offset 0x0A: Ingress Rate Data register 148ec561276SVivien Didelot */ 149ec561276SVivien Didelot 150ec561276SVivien Didelot static int mv88e6xxx_g2_clear_irl(struct mv88e6xxx_chip *chip) 151ec561276SVivien Didelot { 152ec561276SVivien Didelot int port, err; 153ec561276SVivien Didelot 154ec561276SVivien Didelot /* Init all Ingress Rate Limit resources of all ports */ 155370b4ffbSVivien Didelot for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) { 156ec561276SVivien Didelot /* XXX newer chips (like 88E6390) have different 2-bit ops */ 1579fe850fbSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_IRL_CMD, 158ec561276SVivien Didelot GLOBAL2_IRL_CMD_OP_INIT_ALL | 159ec561276SVivien Didelot (port << 8)); 160ec561276SVivien Didelot if (err) 161ec561276SVivien Didelot break; 162ec561276SVivien Didelot 163ec561276SVivien Didelot /* Wait for the operation to complete */ 1649fe850fbSVivien Didelot err = mv88e6xxx_g2_wait(chip, GLOBAL2_IRL_CMD, 165ec561276SVivien Didelot GLOBAL2_IRL_CMD_BUSY); 166ec561276SVivien Didelot if (err) 167ec561276SVivien Didelot break; 168ec561276SVivien Didelot } 169ec561276SVivien Didelot 170ec561276SVivien Didelot return err; 171ec561276SVivien Didelot } 172ec561276SVivien Didelot 173ec561276SVivien Didelot /* Offset 0x0D: Switch MAC/WoL/WoF register */ 174ec561276SVivien Didelot 175ec561276SVivien Didelot static int mv88e6xxx_g2_switch_mac_write(struct mv88e6xxx_chip *chip, 176ec561276SVivien Didelot unsigned int pointer, u8 data) 177ec561276SVivien Didelot { 178ec561276SVivien Didelot u16 val = (pointer << 8) | data; 179ec561276SVivien Didelot 1809fe850fbSVivien Didelot return mv88e6xxx_g2_update(chip, GLOBAL2_SWITCH_MAC, val); 181ec561276SVivien Didelot } 182ec561276SVivien Didelot 183ec561276SVivien Didelot int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr) 184ec561276SVivien Didelot { 185ec561276SVivien Didelot int i, err; 186ec561276SVivien Didelot 187ec561276SVivien Didelot for (i = 0; i < 6; i++) { 188ec561276SVivien Didelot err = mv88e6xxx_g2_switch_mac_write(chip, i, addr[i]); 189ec561276SVivien Didelot if (err) 190ec561276SVivien Didelot break; 191ec561276SVivien Didelot } 192ec561276SVivien Didelot 193ec561276SVivien Didelot return err; 194ec561276SVivien Didelot } 195ec561276SVivien Didelot 196ec561276SVivien Didelot /* Offset 0x0F: Priority Override Table */ 197ec561276SVivien Didelot 198ec561276SVivien Didelot static int mv88e6xxx_g2_pot_write(struct mv88e6xxx_chip *chip, int pointer, 199ec561276SVivien Didelot u8 data) 200ec561276SVivien Didelot { 201ec561276SVivien Didelot u16 val = (pointer << 8) | (data & 0x7); 202ec561276SVivien Didelot 2039fe850fbSVivien Didelot return mv88e6xxx_g2_update(chip, GLOBAL2_PRIO_OVERRIDE, val); 204ec561276SVivien Didelot } 205ec561276SVivien Didelot 206ec561276SVivien Didelot static int mv88e6xxx_g2_clear_pot(struct mv88e6xxx_chip *chip) 207ec561276SVivien Didelot { 208ec561276SVivien Didelot int i, err; 209ec561276SVivien Didelot 210ec561276SVivien Didelot /* Clear all sixteen possible Priority Override entries */ 211ec561276SVivien Didelot for (i = 0; i < 16; i++) { 212ec561276SVivien Didelot err = mv88e6xxx_g2_pot_write(chip, i, 0); 213ec561276SVivien Didelot if (err) 214ec561276SVivien Didelot break; 215ec561276SVivien Didelot } 216ec561276SVivien Didelot 217ec561276SVivien Didelot return err; 218ec561276SVivien Didelot } 219ec561276SVivien Didelot 220ec561276SVivien Didelot /* Offset 0x14: EEPROM Command 22198fc3c6fSVivien Didelot * Offset 0x15: EEPROM Data (for 16-bit data access) 22298fc3c6fSVivien Didelot * Offset 0x15: EEPROM Addr (for 8-bit data access) 223ec561276SVivien Didelot */ 224ec561276SVivien Didelot 225ec561276SVivien Didelot static int mv88e6xxx_g2_eeprom_wait(struct mv88e6xxx_chip *chip) 226ec561276SVivien Didelot { 2279fe850fbSVivien Didelot return mv88e6xxx_g2_wait(chip, GLOBAL2_EEPROM_CMD, 228ec561276SVivien Didelot GLOBAL2_EEPROM_CMD_BUSY | 229ec561276SVivien Didelot GLOBAL2_EEPROM_CMD_RUNNING); 230ec561276SVivien Didelot } 231ec561276SVivien Didelot 232ec561276SVivien Didelot static int mv88e6xxx_g2_eeprom_cmd(struct mv88e6xxx_chip *chip, u16 cmd) 233ec561276SVivien Didelot { 234ec561276SVivien Didelot int err; 235ec561276SVivien Didelot 2369fe850fbSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_CMD, cmd); 237ec561276SVivien Didelot if (err) 238ec561276SVivien Didelot return err; 239ec561276SVivien Didelot 240ec561276SVivien Didelot return mv88e6xxx_g2_eeprom_wait(chip); 241ec561276SVivien Didelot } 242ec561276SVivien Didelot 24398fc3c6fSVivien Didelot static int mv88e6xxx_g2_eeprom_read8(struct mv88e6xxx_chip *chip, 24498fc3c6fSVivien Didelot u16 addr, u8 *data) 24598fc3c6fSVivien Didelot { 24698fc3c6fSVivien Didelot u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ; 24798fc3c6fSVivien Didelot int err; 24898fc3c6fSVivien Didelot 24998fc3c6fSVivien Didelot err = mv88e6xxx_g2_eeprom_wait(chip); 25098fc3c6fSVivien Didelot if (err) 25198fc3c6fSVivien Didelot return err; 25298fc3c6fSVivien Didelot 25398fc3c6fSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_ADDR, addr); 25498fc3c6fSVivien Didelot if (err) 25598fc3c6fSVivien Didelot return err; 25698fc3c6fSVivien Didelot 25798fc3c6fSVivien Didelot err = mv88e6xxx_g2_eeprom_cmd(chip, cmd); 25898fc3c6fSVivien Didelot if (err) 25998fc3c6fSVivien Didelot return err; 26098fc3c6fSVivien Didelot 26198fc3c6fSVivien Didelot err = mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_CMD, &cmd); 26298fc3c6fSVivien Didelot if (err) 26398fc3c6fSVivien Didelot return err; 26498fc3c6fSVivien Didelot 26598fc3c6fSVivien Didelot *data = cmd & 0xff; 26698fc3c6fSVivien Didelot 26798fc3c6fSVivien Didelot return 0; 26898fc3c6fSVivien Didelot } 26998fc3c6fSVivien Didelot 27098fc3c6fSVivien Didelot static int mv88e6xxx_g2_eeprom_write8(struct mv88e6xxx_chip *chip, 27198fc3c6fSVivien Didelot u16 addr, u8 data) 27298fc3c6fSVivien Didelot { 27398fc3c6fSVivien Didelot u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | GLOBAL2_EEPROM_CMD_WRITE_EN; 27498fc3c6fSVivien Didelot int err; 27598fc3c6fSVivien Didelot 27698fc3c6fSVivien Didelot err = mv88e6xxx_g2_eeprom_wait(chip); 27798fc3c6fSVivien Didelot if (err) 27898fc3c6fSVivien Didelot return err; 27998fc3c6fSVivien Didelot 28098fc3c6fSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_ADDR, addr); 28198fc3c6fSVivien Didelot if (err) 28298fc3c6fSVivien Didelot return err; 28398fc3c6fSVivien Didelot 28498fc3c6fSVivien Didelot return mv88e6xxx_g2_eeprom_cmd(chip, cmd | data); 28598fc3c6fSVivien Didelot } 28698fc3c6fSVivien Didelot 287ec561276SVivien Didelot static int mv88e6xxx_g2_eeprom_read16(struct mv88e6xxx_chip *chip, 288ec561276SVivien Didelot u8 addr, u16 *data) 289ec561276SVivien Didelot { 290ec561276SVivien Didelot u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ | addr; 291ec561276SVivien Didelot int err; 292ec561276SVivien Didelot 293ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_wait(chip); 294ec561276SVivien Didelot if (err) 295ec561276SVivien Didelot return err; 296ec561276SVivien Didelot 297ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_cmd(chip, cmd); 298ec561276SVivien Didelot if (err) 299ec561276SVivien Didelot return err; 300ec561276SVivien Didelot 3019fe850fbSVivien Didelot return mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_DATA, data); 302ec561276SVivien Didelot } 303ec561276SVivien Didelot 304ec561276SVivien Didelot static int mv88e6xxx_g2_eeprom_write16(struct mv88e6xxx_chip *chip, 305ec561276SVivien Didelot u8 addr, u16 data) 306ec561276SVivien Didelot { 307ec561276SVivien Didelot u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | addr; 308ec561276SVivien Didelot int err; 309ec561276SVivien Didelot 310ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_wait(chip); 311ec561276SVivien Didelot if (err) 312ec561276SVivien Didelot return err; 313ec561276SVivien Didelot 3149fe850fbSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_DATA, data); 315ec561276SVivien Didelot if (err) 316ec561276SVivien Didelot return err; 317ec561276SVivien Didelot 318ec561276SVivien Didelot return mv88e6xxx_g2_eeprom_cmd(chip, cmd); 319ec561276SVivien Didelot } 320ec561276SVivien Didelot 32198fc3c6fSVivien Didelot int mv88e6xxx_g2_get_eeprom8(struct mv88e6xxx_chip *chip, 32298fc3c6fSVivien Didelot struct ethtool_eeprom *eeprom, u8 *data) 32398fc3c6fSVivien Didelot { 32498fc3c6fSVivien Didelot unsigned int offset = eeprom->offset; 32598fc3c6fSVivien Didelot unsigned int len = eeprom->len; 32698fc3c6fSVivien Didelot int err; 32798fc3c6fSVivien Didelot 32898fc3c6fSVivien Didelot eeprom->len = 0; 32998fc3c6fSVivien Didelot 33098fc3c6fSVivien Didelot while (len) { 33198fc3c6fSVivien Didelot err = mv88e6xxx_g2_eeprom_read8(chip, offset, data); 33298fc3c6fSVivien Didelot if (err) 33398fc3c6fSVivien Didelot return err; 33498fc3c6fSVivien Didelot 33598fc3c6fSVivien Didelot eeprom->len++; 33698fc3c6fSVivien Didelot offset++; 33798fc3c6fSVivien Didelot data++; 33898fc3c6fSVivien Didelot len--; 33998fc3c6fSVivien Didelot } 34098fc3c6fSVivien Didelot 34198fc3c6fSVivien Didelot return 0; 34298fc3c6fSVivien Didelot } 34398fc3c6fSVivien Didelot 34498fc3c6fSVivien Didelot int mv88e6xxx_g2_set_eeprom8(struct mv88e6xxx_chip *chip, 34598fc3c6fSVivien Didelot struct ethtool_eeprom *eeprom, u8 *data) 34698fc3c6fSVivien Didelot { 34798fc3c6fSVivien Didelot unsigned int offset = eeprom->offset; 34898fc3c6fSVivien Didelot unsigned int len = eeprom->len; 34998fc3c6fSVivien Didelot int err; 35098fc3c6fSVivien Didelot 35198fc3c6fSVivien Didelot eeprom->len = 0; 35298fc3c6fSVivien Didelot 35398fc3c6fSVivien Didelot while (len) { 35498fc3c6fSVivien Didelot err = mv88e6xxx_g2_eeprom_write8(chip, offset, *data); 35598fc3c6fSVivien Didelot if (err) 35698fc3c6fSVivien Didelot return err; 35798fc3c6fSVivien Didelot 35898fc3c6fSVivien Didelot eeprom->len++; 35998fc3c6fSVivien Didelot offset++; 36098fc3c6fSVivien Didelot data++; 36198fc3c6fSVivien Didelot len--; 36298fc3c6fSVivien Didelot } 36398fc3c6fSVivien Didelot 36498fc3c6fSVivien Didelot return 0; 36598fc3c6fSVivien Didelot } 36698fc3c6fSVivien Didelot 367ec561276SVivien Didelot int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip, 368ec561276SVivien Didelot struct ethtool_eeprom *eeprom, u8 *data) 369ec561276SVivien Didelot { 370ec561276SVivien Didelot unsigned int offset = eeprom->offset; 371ec561276SVivien Didelot unsigned int len = eeprom->len; 372ec561276SVivien Didelot u16 val; 373ec561276SVivien Didelot int err; 374ec561276SVivien Didelot 375ec561276SVivien Didelot eeprom->len = 0; 376ec561276SVivien Didelot 377ec561276SVivien Didelot if (offset & 1) { 378ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); 379ec561276SVivien Didelot if (err) 380ec561276SVivien Didelot return err; 381ec561276SVivien Didelot 382ec561276SVivien Didelot *data++ = (val >> 8) & 0xff; 383ec561276SVivien Didelot 384ec561276SVivien Didelot offset++; 385ec561276SVivien Didelot len--; 386ec561276SVivien Didelot eeprom->len++; 387ec561276SVivien Didelot } 388ec561276SVivien Didelot 389ec561276SVivien Didelot while (len >= 2) { 390ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); 391ec561276SVivien Didelot if (err) 392ec561276SVivien Didelot return err; 393ec561276SVivien Didelot 394ec561276SVivien Didelot *data++ = val & 0xff; 395ec561276SVivien Didelot *data++ = (val >> 8) & 0xff; 396ec561276SVivien Didelot 397ec561276SVivien Didelot offset += 2; 398ec561276SVivien Didelot len -= 2; 399ec561276SVivien Didelot eeprom->len += 2; 400ec561276SVivien Didelot } 401ec561276SVivien Didelot 402ec561276SVivien Didelot if (len) { 403ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); 404ec561276SVivien Didelot if (err) 405ec561276SVivien Didelot return err; 406ec561276SVivien Didelot 407ec561276SVivien Didelot *data++ = val & 0xff; 408ec561276SVivien Didelot 409ec561276SVivien Didelot offset++; 410ec561276SVivien Didelot len--; 411ec561276SVivien Didelot eeprom->len++; 412ec561276SVivien Didelot } 413ec561276SVivien Didelot 414ec561276SVivien Didelot return 0; 415ec561276SVivien Didelot } 416ec561276SVivien Didelot 417ec561276SVivien Didelot int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip, 418ec561276SVivien Didelot struct ethtool_eeprom *eeprom, u8 *data) 419ec561276SVivien Didelot { 420ec561276SVivien Didelot unsigned int offset = eeprom->offset; 421ec561276SVivien Didelot unsigned int len = eeprom->len; 422ec561276SVivien Didelot u16 val; 423ec561276SVivien Didelot int err; 424ec561276SVivien Didelot 425ec561276SVivien Didelot /* Ensure the RO WriteEn bit is set */ 4269fe850fbSVivien Didelot err = mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_CMD, &val); 427ec561276SVivien Didelot if (err) 428ec561276SVivien Didelot return err; 429ec561276SVivien Didelot 430ec561276SVivien Didelot if (!(val & GLOBAL2_EEPROM_CMD_WRITE_EN)) 431ec561276SVivien Didelot return -EROFS; 432ec561276SVivien Didelot 433ec561276SVivien Didelot eeprom->len = 0; 434ec561276SVivien Didelot 435ec561276SVivien Didelot if (offset & 1) { 436ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); 437ec561276SVivien Didelot if (err) 438ec561276SVivien Didelot return err; 439ec561276SVivien Didelot 440ec561276SVivien Didelot val = (*data++ << 8) | (val & 0xff); 441ec561276SVivien Didelot 442ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val); 443ec561276SVivien Didelot if (err) 444ec561276SVivien Didelot return err; 445ec561276SVivien Didelot 446ec561276SVivien Didelot offset++; 447ec561276SVivien Didelot len--; 448ec561276SVivien Didelot eeprom->len++; 449ec561276SVivien Didelot } 450ec561276SVivien Didelot 451ec561276SVivien Didelot while (len >= 2) { 452ec561276SVivien Didelot val = *data++; 453ec561276SVivien Didelot val |= *data++ << 8; 454ec561276SVivien Didelot 455ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val); 456ec561276SVivien Didelot if (err) 457ec561276SVivien Didelot return err; 458ec561276SVivien Didelot 459ec561276SVivien Didelot offset += 2; 460ec561276SVivien Didelot len -= 2; 461ec561276SVivien Didelot eeprom->len += 2; 462ec561276SVivien Didelot } 463ec561276SVivien Didelot 464ec561276SVivien Didelot if (len) { 465ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); 466ec561276SVivien Didelot if (err) 467ec561276SVivien Didelot return err; 468ec561276SVivien Didelot 469ec561276SVivien Didelot val = (val & 0xff00) | *data++; 470ec561276SVivien Didelot 471ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val); 472ec561276SVivien Didelot if (err) 473ec561276SVivien Didelot return err; 474ec561276SVivien Didelot 475ec561276SVivien Didelot offset++; 476ec561276SVivien Didelot len--; 477ec561276SVivien Didelot eeprom->len++; 478ec561276SVivien Didelot } 479ec561276SVivien Didelot 480ec561276SVivien Didelot return 0; 481ec561276SVivien Didelot } 482ec561276SVivien Didelot 483ec561276SVivien Didelot /* Offset 0x18: SMI PHY Command Register 484ec561276SVivien Didelot * Offset 0x19: SMI PHY Data Register 485ec561276SVivien Didelot */ 486ec561276SVivien Didelot 487ec561276SVivien Didelot static int mv88e6xxx_g2_smi_phy_wait(struct mv88e6xxx_chip *chip) 488ec561276SVivien Didelot { 4899fe850fbSVivien Didelot return mv88e6xxx_g2_wait(chip, GLOBAL2_SMI_PHY_CMD, 490ec561276SVivien Didelot GLOBAL2_SMI_PHY_CMD_BUSY); 491ec561276SVivien Didelot } 492ec561276SVivien Didelot 493ec561276SVivien Didelot static int mv88e6xxx_g2_smi_phy_cmd(struct mv88e6xxx_chip *chip, u16 cmd) 494ec561276SVivien Didelot { 495ec561276SVivien Didelot int err; 496ec561276SVivien Didelot 4979fe850fbSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_SMI_PHY_CMD, cmd); 498ec561276SVivien Didelot if (err) 499ec561276SVivien Didelot return err; 500ec561276SVivien Didelot 501ec561276SVivien Didelot return mv88e6xxx_g2_smi_phy_wait(chip); 502ec561276SVivien Didelot } 503ec561276SVivien Didelot 504ee26a228SAndrew Lunn int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip, 505ee26a228SAndrew Lunn struct mii_bus *bus, 506ee26a228SAndrew Lunn int addr, int reg, u16 *val) 507ec561276SVivien Didelot { 508ec561276SVivien Didelot u16 cmd = GLOBAL2_SMI_PHY_CMD_OP_22_READ_DATA | (addr << 5) | reg; 509ec561276SVivien Didelot int err; 510ec561276SVivien Didelot 511ec561276SVivien Didelot err = mv88e6xxx_g2_smi_phy_wait(chip); 512ec561276SVivien Didelot if (err) 513ec561276SVivien Didelot return err; 514ec561276SVivien Didelot 515ec561276SVivien Didelot err = mv88e6xxx_g2_smi_phy_cmd(chip, cmd); 516ec561276SVivien Didelot if (err) 517ec561276SVivien Didelot return err; 518ec561276SVivien Didelot 5199fe850fbSVivien Didelot return mv88e6xxx_g2_read(chip, GLOBAL2_SMI_PHY_DATA, val); 520ec561276SVivien Didelot } 521ec561276SVivien Didelot 522ee26a228SAndrew Lunn int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, 523ee26a228SAndrew Lunn struct mii_bus *bus, 524ee26a228SAndrew Lunn int addr, int reg, u16 val) 525ec561276SVivien Didelot { 526ec561276SVivien Didelot u16 cmd = GLOBAL2_SMI_PHY_CMD_OP_22_WRITE_DATA | (addr << 5) | reg; 527ec561276SVivien Didelot int err; 528ec561276SVivien Didelot 529ec561276SVivien Didelot err = mv88e6xxx_g2_smi_phy_wait(chip); 530ec561276SVivien Didelot if (err) 531ec561276SVivien Didelot return err; 532ec561276SVivien Didelot 5339fe850fbSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_SMI_PHY_DATA, val); 534ec561276SVivien Didelot if (err) 535ec561276SVivien Didelot return err; 536ec561276SVivien Didelot 537ec561276SVivien Didelot return mv88e6xxx_g2_smi_phy_cmd(chip, cmd); 538ec561276SVivien Didelot } 539ec561276SVivien Didelot 540dc30c35bSAndrew Lunn static void mv88e6xxx_g2_irq_mask(struct irq_data *d) 541dc30c35bSAndrew Lunn { 542dc30c35bSAndrew Lunn struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); 543dc30c35bSAndrew Lunn unsigned int n = d->hwirq; 544dc30c35bSAndrew Lunn 545dc30c35bSAndrew Lunn chip->g2_irq.masked |= (1 << n); 546dc30c35bSAndrew Lunn } 547dc30c35bSAndrew Lunn 548dc30c35bSAndrew Lunn static void mv88e6xxx_g2_irq_unmask(struct irq_data *d) 549dc30c35bSAndrew Lunn { 550dc30c35bSAndrew Lunn struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); 551dc30c35bSAndrew Lunn unsigned int n = d->hwirq; 552dc30c35bSAndrew Lunn 553dc30c35bSAndrew Lunn chip->g2_irq.masked &= ~(1 << n); 554dc30c35bSAndrew Lunn } 555dc30c35bSAndrew Lunn 556dc30c35bSAndrew Lunn static irqreturn_t mv88e6xxx_g2_irq_thread_fn(int irq, void *dev_id) 557dc30c35bSAndrew Lunn { 558dc30c35bSAndrew Lunn struct mv88e6xxx_chip *chip = dev_id; 559dc30c35bSAndrew Lunn unsigned int nhandled = 0; 560dc30c35bSAndrew Lunn unsigned int sub_irq; 561dc30c35bSAndrew Lunn unsigned int n; 562dc30c35bSAndrew Lunn int err; 563dc30c35bSAndrew Lunn u16 reg; 564dc30c35bSAndrew Lunn 565dc30c35bSAndrew Lunn mutex_lock(&chip->reg_lock); 566dc30c35bSAndrew Lunn err = mv88e6xxx_g2_read(chip, GLOBAL2_INT_SOURCE, ®); 567dc30c35bSAndrew Lunn mutex_unlock(&chip->reg_lock); 568dc30c35bSAndrew Lunn if (err) 569dc30c35bSAndrew Lunn goto out; 570dc30c35bSAndrew Lunn 571dc30c35bSAndrew Lunn for (n = 0; n < 16; ++n) { 572dc30c35bSAndrew Lunn if (reg & (1 << n)) { 573dc30c35bSAndrew Lunn sub_irq = irq_find_mapping(chip->g2_irq.domain, n); 574dc30c35bSAndrew Lunn handle_nested_irq(sub_irq); 575dc30c35bSAndrew Lunn ++nhandled; 576dc30c35bSAndrew Lunn } 577dc30c35bSAndrew Lunn } 578dc30c35bSAndrew Lunn out: 579dc30c35bSAndrew Lunn return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE); 580dc30c35bSAndrew Lunn } 581dc30c35bSAndrew Lunn 582dc30c35bSAndrew Lunn static void mv88e6xxx_g2_irq_bus_lock(struct irq_data *d) 583dc30c35bSAndrew Lunn { 584dc30c35bSAndrew Lunn struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); 585dc30c35bSAndrew Lunn 586dc30c35bSAndrew Lunn mutex_lock(&chip->reg_lock); 587dc30c35bSAndrew Lunn } 588dc30c35bSAndrew Lunn 589dc30c35bSAndrew Lunn static void mv88e6xxx_g2_irq_bus_sync_unlock(struct irq_data *d) 590dc30c35bSAndrew Lunn { 591dc30c35bSAndrew Lunn struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); 592dc30c35bSAndrew Lunn 593dc30c35bSAndrew Lunn mv88e6xxx_g2_write(chip, GLOBAL2_INT_MASK, ~chip->g2_irq.masked); 594dc30c35bSAndrew Lunn 595dc30c35bSAndrew Lunn mutex_unlock(&chip->reg_lock); 596dc30c35bSAndrew Lunn } 597dc30c35bSAndrew Lunn 598dc30c35bSAndrew Lunn static struct irq_chip mv88e6xxx_g2_irq_chip = { 599dc30c35bSAndrew Lunn .name = "mv88e6xxx-g2", 600dc30c35bSAndrew Lunn .irq_mask = mv88e6xxx_g2_irq_mask, 601dc30c35bSAndrew Lunn .irq_unmask = mv88e6xxx_g2_irq_unmask, 602dc30c35bSAndrew Lunn .irq_bus_lock = mv88e6xxx_g2_irq_bus_lock, 603dc30c35bSAndrew Lunn .irq_bus_sync_unlock = mv88e6xxx_g2_irq_bus_sync_unlock, 604dc30c35bSAndrew Lunn }; 605dc30c35bSAndrew Lunn 606dc30c35bSAndrew Lunn static int mv88e6xxx_g2_irq_domain_map(struct irq_domain *d, 607dc30c35bSAndrew Lunn unsigned int irq, 608dc30c35bSAndrew Lunn irq_hw_number_t hwirq) 609dc30c35bSAndrew Lunn { 610dc30c35bSAndrew Lunn struct mv88e6xxx_chip *chip = d->host_data; 611dc30c35bSAndrew Lunn 612dc30c35bSAndrew Lunn irq_set_chip_data(irq, d->host_data); 613dc30c35bSAndrew Lunn irq_set_chip_and_handler(irq, &chip->g2_irq.chip, handle_level_irq); 614dc30c35bSAndrew Lunn irq_set_noprobe(irq); 615dc30c35bSAndrew Lunn 616dc30c35bSAndrew Lunn return 0; 617dc30c35bSAndrew Lunn } 618dc30c35bSAndrew Lunn 619dc30c35bSAndrew Lunn static const struct irq_domain_ops mv88e6xxx_g2_irq_domain_ops = { 620dc30c35bSAndrew Lunn .map = mv88e6xxx_g2_irq_domain_map, 621dc30c35bSAndrew Lunn .xlate = irq_domain_xlate_twocell, 622dc30c35bSAndrew Lunn }; 623dc30c35bSAndrew Lunn 624dc30c35bSAndrew Lunn void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip) 625dc30c35bSAndrew Lunn { 626dc30c35bSAndrew Lunn int irq, virq; 627dc30c35bSAndrew Lunn 6288e757ebaSAndrew Lunn free_irq(chip->device_irq, chip); 6298e757ebaSAndrew Lunn irq_dispose_mapping(chip->device_irq); 6308e757ebaSAndrew Lunn 631dc30c35bSAndrew Lunn for (irq = 0; irq < 16; irq++) { 632dc30c35bSAndrew Lunn virq = irq_find_mapping(chip->g2_irq.domain, irq); 633dc30c35bSAndrew Lunn irq_dispose_mapping(virq); 634dc30c35bSAndrew Lunn } 635dc30c35bSAndrew Lunn 636dc30c35bSAndrew Lunn irq_domain_remove(chip->g2_irq.domain); 637dc30c35bSAndrew Lunn } 638dc30c35bSAndrew Lunn 639dc30c35bSAndrew Lunn int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip) 640dc30c35bSAndrew Lunn { 6418e757ebaSAndrew Lunn int err, irq, virq; 642dc30c35bSAndrew Lunn 643dc30c35bSAndrew Lunn if (!chip->dev->of_node) 644dc30c35bSAndrew Lunn return -EINVAL; 645dc30c35bSAndrew Lunn 646dc30c35bSAndrew Lunn chip->g2_irq.domain = irq_domain_add_simple( 647dc30c35bSAndrew Lunn chip->dev->of_node, 16, 0, &mv88e6xxx_g2_irq_domain_ops, chip); 648dc30c35bSAndrew Lunn if (!chip->g2_irq.domain) 649dc30c35bSAndrew Lunn return -ENOMEM; 650dc30c35bSAndrew Lunn 651dc30c35bSAndrew Lunn for (irq = 0; irq < 16; irq++) 652dc30c35bSAndrew Lunn irq_create_mapping(chip->g2_irq.domain, irq); 653dc30c35bSAndrew Lunn 654dc30c35bSAndrew Lunn chip->g2_irq.chip = mv88e6xxx_g2_irq_chip; 655dc30c35bSAndrew Lunn chip->g2_irq.masked = ~0; 656dc30c35bSAndrew Lunn 6578e757ebaSAndrew Lunn chip->device_irq = irq_find_mapping(chip->g1_irq.domain, 658dc30c35bSAndrew Lunn GLOBAL_STATUS_IRQ_DEVICE); 6598e757ebaSAndrew Lunn if (chip->device_irq < 0) { 6608e757ebaSAndrew Lunn err = chip->device_irq; 661dc30c35bSAndrew Lunn goto out; 662dc30c35bSAndrew Lunn } 663dc30c35bSAndrew Lunn 6648e757ebaSAndrew Lunn err = request_threaded_irq(chip->device_irq, NULL, 665dc30c35bSAndrew Lunn mv88e6xxx_g2_irq_thread_fn, 666dc30c35bSAndrew Lunn IRQF_ONESHOT, "mv88e6xxx-g1", chip); 667dc30c35bSAndrew Lunn if (err) 668dc30c35bSAndrew Lunn goto out; 669dc30c35bSAndrew Lunn 670dc30c35bSAndrew Lunn return 0; 6718e757ebaSAndrew Lunn 672dc30c35bSAndrew Lunn out: 6738e757ebaSAndrew Lunn for (irq = 0; irq < 16; irq++) { 6748e757ebaSAndrew Lunn virq = irq_find_mapping(chip->g2_irq.domain, irq); 6758e757ebaSAndrew Lunn irq_dispose_mapping(virq); 6768e757ebaSAndrew Lunn } 6778e757ebaSAndrew Lunn 6788e757ebaSAndrew Lunn irq_domain_remove(chip->g2_irq.domain); 679dc30c35bSAndrew Lunn 680dc30c35bSAndrew Lunn return err; 681dc30c35bSAndrew Lunn } 682dc30c35bSAndrew Lunn 683ec561276SVivien Didelot int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip) 684ec561276SVivien Didelot { 685ec561276SVivien Didelot u16 reg; 686ec561276SVivien Didelot int err; 687ec561276SVivien Didelot 688ec561276SVivien Didelot /* Ignore removed tag data on doubly tagged packets, disable 689ec561276SVivien Didelot * flow control messages, force flow control priority to the 690ec561276SVivien Didelot * highest, and send all special multicast frames to the CPU 691ec561276SVivien Didelot * port at the highest priority. 692ec561276SVivien Didelot */ 693ec561276SVivien Didelot reg = GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI | (0x7 << 4); 694ec561276SVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X) || 695ec561276SVivien Didelot mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X)) 696ec561276SVivien Didelot reg |= GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x7; 6979fe850fbSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_SWITCH_MGMT, reg); 698ec561276SVivien Didelot if (err) 699ec561276SVivien Didelot return err; 700ec561276SVivien Didelot 701ec561276SVivien Didelot /* Program the DSA routing table. */ 702ec561276SVivien Didelot err = mv88e6xxx_g2_set_device_mapping(chip); 703ec561276SVivien Didelot if (err) 704ec561276SVivien Didelot return err; 705ec561276SVivien Didelot 706ec561276SVivien Didelot /* Clear all trunk masks and mapping. */ 707ec561276SVivien Didelot err = mv88e6xxx_g2_clear_trunk(chip); 708ec561276SVivien Didelot if (err) 709ec561276SVivien Didelot return err; 710ec561276SVivien Didelot 711ec561276SVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_IRL)) { 712ec561276SVivien Didelot /* Disable ingress rate limiting by resetting all per port 713ec561276SVivien Didelot * ingress rate limit resources to their initial state. 714ec561276SVivien Didelot */ 715ec561276SVivien Didelot err = mv88e6xxx_g2_clear_irl(chip); 716ec561276SVivien Didelot if (err) 717ec561276SVivien Didelot return err; 718ec561276SVivien Didelot } 719ec561276SVivien Didelot 720ec561276SVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_PVT)) { 721ec561276SVivien Didelot /* Initialize Cross-chip Port VLAN Table to reset defaults */ 7229fe850fbSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_ADDR, 723ec561276SVivien Didelot GLOBAL2_PVT_ADDR_OP_INIT_ONES); 724ec561276SVivien Didelot if (err) 725ec561276SVivien Didelot return err; 726ec561276SVivien Didelot } 727ec561276SVivien Didelot 728ec561276SVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_POT)) { 729ec561276SVivien Didelot /* Clear the priority override table. */ 730ec561276SVivien Didelot err = mv88e6xxx_g2_clear_pot(chip); 731ec561276SVivien Didelot if (err) 732ec561276SVivien Didelot return err; 733ec561276SVivien Didelot } 734ec561276SVivien Didelot 735ec561276SVivien Didelot return 0; 736ec561276SVivien Didelot } 737