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 * 74333d619SVivien Didelot * Copyright (c) 2016-2017 Savoir-faire Linux Inc. 84333d619SVivien Didelot * Vivien Didelot <vivien.didelot@savoirfairelinux.com> 9ec561276SVivien Didelot * 10ec561276SVivien Didelot * This program is free software; you can redistribute it and/or modify 11ec561276SVivien Didelot * it under the terms of the GNU General Public License as published by 12ec561276SVivien Didelot * the Free Software Foundation; either version 2 of the License, or 13ec561276SVivien Didelot * (at your option) any later version. 14ec561276SVivien Didelot */ 15ec561276SVivien Didelot 16282ccf6eSFlorian Westphal #include <linux/interrupt.h> 17dc30c35bSAndrew Lunn #include <linux/irqdomain.h> 184d5f2ba7SVivien Didelot 194d5f2ba7SVivien Didelot #include "chip.h" 2082466921SVivien Didelot #include "global1.h" /* for MV88E6XXX_G1_STS_IRQ_DEVICE */ 21ec561276SVivien Didelot #include "global2.h" 22ec561276SVivien Didelot 239fe850fbSVivien Didelot static int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val) 249fe850fbSVivien Didelot { 259fe850fbSVivien Didelot return mv88e6xxx_read(chip, ADDR_GLOBAL2, reg, val); 269fe850fbSVivien Didelot } 279fe850fbSVivien Didelot 289fe850fbSVivien Didelot static int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val) 299fe850fbSVivien Didelot { 309fe850fbSVivien Didelot return mv88e6xxx_write(chip, ADDR_GLOBAL2, reg, val); 319fe850fbSVivien Didelot } 329fe850fbSVivien Didelot 339fe850fbSVivien Didelot static int mv88e6xxx_g2_update(struct mv88e6xxx_chip *chip, int reg, u16 update) 349fe850fbSVivien Didelot { 359fe850fbSVivien Didelot return mv88e6xxx_update(chip, ADDR_GLOBAL2, reg, update); 369fe850fbSVivien Didelot } 379fe850fbSVivien Didelot 389fe850fbSVivien Didelot static int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask) 399fe850fbSVivien Didelot { 409fe850fbSVivien Didelot return mv88e6xxx_wait(chip, ADDR_GLOBAL2, reg, mask); 419fe850fbSVivien Didelot } 429fe850fbSVivien Didelot 436e55f698SAndrew Lunn /* Offset 0x02: Management Enable 2x */ 446e55f698SAndrew Lunn /* Offset 0x03: Management Enable 0x */ 456e55f698SAndrew Lunn 466e55f698SAndrew Lunn int mv88e6095_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip) 476e55f698SAndrew Lunn { 486e55f698SAndrew Lunn int err; 496e55f698SAndrew Lunn 506e55f698SAndrew Lunn /* Consider the frames with reserved multicast destination 516e55f698SAndrew Lunn * addresses matching 01:80:c2:00:00:2x as MGMT. 526e55f698SAndrew Lunn */ 536e55f698SAndrew Lunn if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X)) { 546e55f698SAndrew Lunn err = mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_2X, 0xffff); 556e55f698SAndrew Lunn if (err) 566e55f698SAndrew Lunn return err; 576e55f698SAndrew Lunn } 586e55f698SAndrew Lunn 596e55f698SAndrew Lunn /* Consider the frames with reserved multicast destination 606e55f698SAndrew Lunn * addresses matching 01:80:c2:00:00:0x as MGMT. 616e55f698SAndrew Lunn */ 626e55f698SAndrew Lunn if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X)) 636e55f698SAndrew Lunn return mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_0X, 0xffff); 646e55f698SAndrew Lunn 656e55f698SAndrew Lunn return 0; 666e55f698SAndrew Lunn } 676e55f698SAndrew Lunn 68ec561276SVivien Didelot /* Offset 0x06: Device Mapping Table register */ 69ec561276SVivien Didelot 70ec561276SVivien Didelot static int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip, 71ec561276SVivien Didelot int target, int port) 72ec561276SVivien Didelot { 73ec561276SVivien Didelot u16 val = (target << 8) | (port & 0xf); 74ec561276SVivien Didelot 759fe850fbSVivien Didelot return mv88e6xxx_g2_update(chip, GLOBAL2_DEVICE_MAPPING, val); 76ec561276SVivien Didelot } 77ec561276SVivien Didelot 78ec561276SVivien Didelot static int mv88e6xxx_g2_set_device_mapping(struct mv88e6xxx_chip *chip) 79ec561276SVivien Didelot { 80ec561276SVivien Didelot int target, port; 81ec561276SVivien Didelot int err; 82ec561276SVivien Didelot 83ec561276SVivien Didelot /* Initialize the routing port to the 32 possible target devices */ 84ec561276SVivien Didelot for (target = 0; target < 32; ++target) { 85ec561276SVivien Didelot port = 0xf; 86ec561276SVivien Didelot 87ec561276SVivien Didelot if (target < DSA_MAX_SWITCHES) { 88ec561276SVivien Didelot port = chip->ds->rtable[target]; 89ec561276SVivien Didelot if (port == DSA_RTABLE_NONE) 90ec561276SVivien Didelot port = 0xf; 91ec561276SVivien Didelot } 92ec561276SVivien Didelot 93ec561276SVivien Didelot err = mv88e6xxx_g2_device_mapping_write(chip, target, port); 94ec561276SVivien Didelot if (err) 95ec561276SVivien Didelot break; 96ec561276SVivien Didelot } 97ec561276SVivien Didelot 98ec561276SVivien Didelot return err; 99ec561276SVivien Didelot } 100ec561276SVivien Didelot 101ec561276SVivien Didelot /* Offset 0x07: Trunk Mask Table register */ 102ec561276SVivien Didelot 103ec561276SVivien Didelot static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num, 104ec561276SVivien Didelot bool hask, u16 mask) 105ec561276SVivien Didelot { 106370b4ffbSVivien Didelot const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1; 107ec561276SVivien Didelot u16 val = (num << 12) | (mask & port_mask); 108ec561276SVivien Didelot 109ec561276SVivien Didelot if (hask) 110ec561276SVivien Didelot val |= GLOBAL2_TRUNK_MASK_HASK; 111ec561276SVivien Didelot 1129fe850fbSVivien Didelot return mv88e6xxx_g2_update(chip, GLOBAL2_TRUNK_MASK, val); 113ec561276SVivien Didelot } 114ec561276SVivien Didelot 115ec561276SVivien Didelot /* Offset 0x08: Trunk Mapping Table register */ 116ec561276SVivien Didelot 117ec561276SVivien Didelot static int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id, 118ec561276SVivien Didelot u16 map) 119ec561276SVivien Didelot { 120370b4ffbSVivien Didelot const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1; 121ec561276SVivien Didelot u16 val = (id << 11) | (map & port_mask); 122ec561276SVivien Didelot 1239fe850fbSVivien Didelot return mv88e6xxx_g2_update(chip, GLOBAL2_TRUNK_MAPPING, val); 124ec561276SVivien Didelot } 125ec561276SVivien Didelot 126ec561276SVivien Didelot static int mv88e6xxx_g2_clear_trunk(struct mv88e6xxx_chip *chip) 127ec561276SVivien Didelot { 128370b4ffbSVivien Didelot const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1; 129ec561276SVivien Didelot int i, err; 130ec561276SVivien Didelot 131ec561276SVivien Didelot /* Clear all eight possible Trunk Mask vectors */ 132ec561276SVivien Didelot for (i = 0; i < 8; ++i) { 133ec561276SVivien Didelot err = mv88e6xxx_g2_trunk_mask_write(chip, i, false, port_mask); 134ec561276SVivien Didelot if (err) 135ec561276SVivien Didelot return err; 136ec561276SVivien Didelot } 137ec561276SVivien Didelot 138ec561276SVivien Didelot /* Clear all sixteen possible Trunk ID routing vectors */ 139ec561276SVivien Didelot for (i = 0; i < 16; ++i) { 140ec561276SVivien Didelot err = mv88e6xxx_g2_trunk_mapping_write(chip, i, 0); 141ec561276SVivien Didelot if (err) 142ec561276SVivien Didelot return err; 143ec561276SVivien Didelot } 144ec561276SVivien Didelot 145ec561276SVivien Didelot return 0; 146ec561276SVivien Didelot } 147ec561276SVivien Didelot 148ec561276SVivien Didelot /* Offset 0x09: Ingress Rate Command register 149ec561276SVivien Didelot * Offset 0x0A: Ingress Rate Data register 150ec561276SVivien Didelot */ 151ec561276SVivien Didelot 152ec561276SVivien Didelot static int mv88e6xxx_g2_clear_irl(struct mv88e6xxx_chip *chip) 153ec561276SVivien Didelot { 154ec561276SVivien Didelot int port, err; 155ec561276SVivien Didelot 156ec561276SVivien Didelot /* Init all Ingress Rate Limit resources of all ports */ 157370b4ffbSVivien Didelot for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) { 158ec561276SVivien Didelot /* XXX newer chips (like 88E6390) have different 2-bit ops */ 1599fe850fbSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_IRL_CMD, 160ec561276SVivien Didelot GLOBAL2_IRL_CMD_OP_INIT_ALL | 161ec561276SVivien Didelot (port << 8)); 162ec561276SVivien Didelot if (err) 163ec561276SVivien Didelot break; 164ec561276SVivien Didelot 165ec561276SVivien Didelot /* Wait for the operation to complete */ 1669fe850fbSVivien Didelot err = mv88e6xxx_g2_wait(chip, GLOBAL2_IRL_CMD, 167ec561276SVivien Didelot GLOBAL2_IRL_CMD_BUSY); 168ec561276SVivien Didelot if (err) 169ec561276SVivien Didelot break; 170ec561276SVivien Didelot } 171ec561276SVivien Didelot 172ec561276SVivien Didelot return err; 173ec561276SVivien Didelot } 174ec561276SVivien Didelot 17517a1594eSVivien Didelot /* Offset 0x0B: Cross-chip Port VLAN (Addr) Register 17617a1594eSVivien Didelot * Offset 0x0C: Cross-chip Port VLAN Data Register 17717a1594eSVivien Didelot */ 17817a1594eSVivien Didelot 17917a1594eSVivien Didelot static int mv88e6xxx_g2_pvt_op_wait(struct mv88e6xxx_chip *chip) 18017a1594eSVivien Didelot { 18117a1594eSVivien Didelot return mv88e6xxx_g2_wait(chip, GLOBAL2_PVT_ADDR, GLOBAL2_PVT_ADDR_BUSY); 18217a1594eSVivien Didelot } 18317a1594eSVivien Didelot 18417a1594eSVivien Didelot static int mv88e6xxx_g2_pvt_op(struct mv88e6xxx_chip *chip, int src_dev, 18517a1594eSVivien Didelot int src_port, u16 op) 18617a1594eSVivien Didelot { 18717a1594eSVivien Didelot int err; 18817a1594eSVivien Didelot 18917a1594eSVivien Didelot /* 9-bit Cross-chip PVT pointer: with GLOBAL2_MISC_5_BIT_PORT cleared, 19017a1594eSVivien Didelot * source device is 5-bit, source port is 4-bit. 19117a1594eSVivien Didelot */ 19217a1594eSVivien Didelot op |= (src_dev & 0x1f) << 4; 19317a1594eSVivien Didelot op |= (src_port & 0xf); 19417a1594eSVivien Didelot 19517a1594eSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_ADDR, op); 19617a1594eSVivien Didelot if (err) 19717a1594eSVivien Didelot return err; 19817a1594eSVivien Didelot 19917a1594eSVivien Didelot return mv88e6xxx_g2_pvt_op_wait(chip); 20017a1594eSVivien Didelot } 20117a1594eSVivien Didelot 20217a1594eSVivien Didelot int mv88e6xxx_g2_pvt_write(struct mv88e6xxx_chip *chip, int src_dev, 20317a1594eSVivien Didelot int src_port, u16 data) 20417a1594eSVivien Didelot { 20517a1594eSVivien Didelot int err; 20617a1594eSVivien Didelot 20717a1594eSVivien Didelot err = mv88e6xxx_g2_pvt_op_wait(chip); 20817a1594eSVivien Didelot if (err) 20917a1594eSVivien Didelot return err; 21017a1594eSVivien Didelot 21117a1594eSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_DATA, data); 21217a1594eSVivien Didelot if (err) 21317a1594eSVivien Didelot return err; 21417a1594eSVivien Didelot 21517a1594eSVivien Didelot return mv88e6xxx_g2_pvt_op(chip, src_dev, src_port, 21617a1594eSVivien Didelot GLOBAL2_PVT_ADDR_OP_WRITE_PVLAN); 21717a1594eSVivien Didelot } 21817a1594eSVivien Didelot 219ec561276SVivien Didelot /* Offset 0x0D: Switch MAC/WoL/WoF register */ 220ec561276SVivien Didelot 221ec561276SVivien Didelot static int mv88e6xxx_g2_switch_mac_write(struct mv88e6xxx_chip *chip, 222ec561276SVivien Didelot unsigned int pointer, u8 data) 223ec561276SVivien Didelot { 224ec561276SVivien Didelot u16 val = (pointer << 8) | data; 225ec561276SVivien Didelot 2269fe850fbSVivien Didelot return mv88e6xxx_g2_update(chip, GLOBAL2_SWITCH_MAC, val); 227ec561276SVivien Didelot } 228ec561276SVivien Didelot 229ec561276SVivien Didelot int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr) 230ec561276SVivien Didelot { 231ec561276SVivien Didelot int i, err; 232ec561276SVivien Didelot 233ec561276SVivien Didelot for (i = 0; i < 6; i++) { 234ec561276SVivien Didelot err = mv88e6xxx_g2_switch_mac_write(chip, i, addr[i]); 235ec561276SVivien Didelot if (err) 236ec561276SVivien Didelot break; 237ec561276SVivien Didelot } 238ec561276SVivien Didelot 239ec561276SVivien Didelot return err; 240ec561276SVivien Didelot } 241ec561276SVivien Didelot 242ec561276SVivien Didelot /* Offset 0x0F: Priority Override Table */ 243ec561276SVivien Didelot 244ec561276SVivien Didelot static int mv88e6xxx_g2_pot_write(struct mv88e6xxx_chip *chip, int pointer, 245ec561276SVivien Didelot u8 data) 246ec561276SVivien Didelot { 247ec561276SVivien Didelot u16 val = (pointer << 8) | (data & 0x7); 248ec561276SVivien Didelot 2499fe850fbSVivien Didelot return mv88e6xxx_g2_update(chip, GLOBAL2_PRIO_OVERRIDE, val); 250ec561276SVivien Didelot } 251ec561276SVivien Didelot 252ec561276SVivien Didelot static int mv88e6xxx_g2_clear_pot(struct mv88e6xxx_chip *chip) 253ec561276SVivien Didelot { 254ec561276SVivien Didelot int i, err; 255ec561276SVivien Didelot 256ec561276SVivien Didelot /* Clear all sixteen possible Priority Override entries */ 257ec561276SVivien Didelot for (i = 0; i < 16; i++) { 258ec561276SVivien Didelot err = mv88e6xxx_g2_pot_write(chip, i, 0); 259ec561276SVivien Didelot if (err) 260ec561276SVivien Didelot break; 261ec561276SVivien Didelot } 262ec561276SVivien Didelot 263ec561276SVivien Didelot return err; 264ec561276SVivien Didelot } 265ec561276SVivien Didelot 266ec561276SVivien Didelot /* Offset 0x14: EEPROM Command 26798fc3c6fSVivien Didelot * Offset 0x15: EEPROM Data (for 16-bit data access) 26898fc3c6fSVivien Didelot * Offset 0x15: EEPROM Addr (for 8-bit data access) 269ec561276SVivien Didelot */ 270ec561276SVivien Didelot 271ec561276SVivien Didelot static int mv88e6xxx_g2_eeprom_wait(struct mv88e6xxx_chip *chip) 272ec561276SVivien Didelot { 2739fe850fbSVivien Didelot return mv88e6xxx_g2_wait(chip, GLOBAL2_EEPROM_CMD, 274ec561276SVivien Didelot GLOBAL2_EEPROM_CMD_BUSY | 275ec561276SVivien Didelot GLOBAL2_EEPROM_CMD_RUNNING); 276ec561276SVivien Didelot } 277ec561276SVivien Didelot 278ec561276SVivien Didelot static int mv88e6xxx_g2_eeprom_cmd(struct mv88e6xxx_chip *chip, u16 cmd) 279ec561276SVivien Didelot { 280ec561276SVivien Didelot int err; 281ec561276SVivien Didelot 2829fe850fbSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_CMD, cmd); 283ec561276SVivien Didelot if (err) 284ec561276SVivien Didelot return err; 285ec561276SVivien Didelot 286ec561276SVivien Didelot return mv88e6xxx_g2_eeprom_wait(chip); 287ec561276SVivien Didelot } 288ec561276SVivien Didelot 28998fc3c6fSVivien Didelot static int mv88e6xxx_g2_eeprom_read8(struct mv88e6xxx_chip *chip, 29098fc3c6fSVivien Didelot u16 addr, u8 *data) 29198fc3c6fSVivien Didelot { 29298fc3c6fSVivien Didelot u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ; 29398fc3c6fSVivien Didelot int err; 29498fc3c6fSVivien Didelot 29598fc3c6fSVivien Didelot err = mv88e6xxx_g2_eeprom_wait(chip); 29698fc3c6fSVivien Didelot if (err) 29798fc3c6fSVivien Didelot return err; 29898fc3c6fSVivien Didelot 29998fc3c6fSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_ADDR, addr); 30098fc3c6fSVivien Didelot if (err) 30198fc3c6fSVivien Didelot return err; 30298fc3c6fSVivien Didelot 30398fc3c6fSVivien Didelot err = mv88e6xxx_g2_eeprom_cmd(chip, cmd); 30498fc3c6fSVivien Didelot if (err) 30598fc3c6fSVivien Didelot return err; 30698fc3c6fSVivien Didelot 30798fc3c6fSVivien Didelot err = mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_CMD, &cmd); 30898fc3c6fSVivien Didelot if (err) 30998fc3c6fSVivien Didelot return err; 31098fc3c6fSVivien Didelot 31198fc3c6fSVivien Didelot *data = cmd & 0xff; 31298fc3c6fSVivien Didelot 31398fc3c6fSVivien Didelot return 0; 31498fc3c6fSVivien Didelot } 31598fc3c6fSVivien Didelot 31698fc3c6fSVivien Didelot static int mv88e6xxx_g2_eeprom_write8(struct mv88e6xxx_chip *chip, 31798fc3c6fSVivien Didelot u16 addr, u8 data) 31898fc3c6fSVivien Didelot { 31998fc3c6fSVivien Didelot u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | GLOBAL2_EEPROM_CMD_WRITE_EN; 32098fc3c6fSVivien Didelot int err; 32198fc3c6fSVivien Didelot 32298fc3c6fSVivien Didelot err = mv88e6xxx_g2_eeprom_wait(chip); 32398fc3c6fSVivien Didelot if (err) 32498fc3c6fSVivien Didelot return err; 32598fc3c6fSVivien Didelot 32698fc3c6fSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_ADDR, addr); 32798fc3c6fSVivien Didelot if (err) 32898fc3c6fSVivien Didelot return err; 32998fc3c6fSVivien Didelot 33098fc3c6fSVivien Didelot return mv88e6xxx_g2_eeprom_cmd(chip, cmd | data); 33198fc3c6fSVivien Didelot } 33298fc3c6fSVivien Didelot 333ec561276SVivien Didelot static int mv88e6xxx_g2_eeprom_read16(struct mv88e6xxx_chip *chip, 334ec561276SVivien Didelot u8 addr, u16 *data) 335ec561276SVivien Didelot { 336ec561276SVivien Didelot u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ | addr; 337ec561276SVivien Didelot int err; 338ec561276SVivien Didelot 339ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_wait(chip); 340ec561276SVivien Didelot if (err) 341ec561276SVivien Didelot return err; 342ec561276SVivien Didelot 343ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_cmd(chip, cmd); 344ec561276SVivien Didelot if (err) 345ec561276SVivien Didelot return err; 346ec561276SVivien Didelot 3479fe850fbSVivien Didelot return mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_DATA, data); 348ec561276SVivien Didelot } 349ec561276SVivien Didelot 350ec561276SVivien Didelot static int mv88e6xxx_g2_eeprom_write16(struct mv88e6xxx_chip *chip, 351ec561276SVivien Didelot u8 addr, u16 data) 352ec561276SVivien Didelot { 353ec561276SVivien Didelot u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | addr; 354ec561276SVivien Didelot int err; 355ec561276SVivien Didelot 356ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_wait(chip); 357ec561276SVivien Didelot if (err) 358ec561276SVivien Didelot return err; 359ec561276SVivien Didelot 3609fe850fbSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_DATA, data); 361ec561276SVivien Didelot if (err) 362ec561276SVivien Didelot return err; 363ec561276SVivien Didelot 364ec561276SVivien Didelot return mv88e6xxx_g2_eeprom_cmd(chip, cmd); 365ec561276SVivien Didelot } 366ec561276SVivien Didelot 36798fc3c6fSVivien Didelot int mv88e6xxx_g2_get_eeprom8(struct mv88e6xxx_chip *chip, 36898fc3c6fSVivien Didelot struct ethtool_eeprom *eeprom, u8 *data) 36998fc3c6fSVivien Didelot { 37098fc3c6fSVivien Didelot unsigned int offset = eeprom->offset; 37198fc3c6fSVivien Didelot unsigned int len = eeprom->len; 37298fc3c6fSVivien Didelot int err; 37398fc3c6fSVivien Didelot 37498fc3c6fSVivien Didelot eeprom->len = 0; 37598fc3c6fSVivien Didelot 37698fc3c6fSVivien Didelot while (len) { 37798fc3c6fSVivien Didelot err = mv88e6xxx_g2_eeprom_read8(chip, offset, data); 37898fc3c6fSVivien Didelot if (err) 37998fc3c6fSVivien Didelot return err; 38098fc3c6fSVivien Didelot 38198fc3c6fSVivien Didelot eeprom->len++; 38298fc3c6fSVivien Didelot offset++; 38398fc3c6fSVivien Didelot data++; 38498fc3c6fSVivien Didelot len--; 38598fc3c6fSVivien Didelot } 38698fc3c6fSVivien Didelot 38798fc3c6fSVivien Didelot return 0; 38898fc3c6fSVivien Didelot } 38998fc3c6fSVivien Didelot 39098fc3c6fSVivien Didelot int mv88e6xxx_g2_set_eeprom8(struct mv88e6xxx_chip *chip, 39198fc3c6fSVivien Didelot struct ethtool_eeprom *eeprom, u8 *data) 39298fc3c6fSVivien Didelot { 39398fc3c6fSVivien Didelot unsigned int offset = eeprom->offset; 39498fc3c6fSVivien Didelot unsigned int len = eeprom->len; 39598fc3c6fSVivien Didelot int err; 39698fc3c6fSVivien Didelot 39798fc3c6fSVivien Didelot eeprom->len = 0; 39898fc3c6fSVivien Didelot 39998fc3c6fSVivien Didelot while (len) { 40098fc3c6fSVivien Didelot err = mv88e6xxx_g2_eeprom_write8(chip, offset, *data); 40198fc3c6fSVivien Didelot if (err) 40298fc3c6fSVivien Didelot return err; 40398fc3c6fSVivien Didelot 40498fc3c6fSVivien Didelot eeprom->len++; 40598fc3c6fSVivien Didelot offset++; 40698fc3c6fSVivien Didelot data++; 40798fc3c6fSVivien Didelot len--; 40898fc3c6fSVivien Didelot } 40998fc3c6fSVivien Didelot 41098fc3c6fSVivien Didelot return 0; 41198fc3c6fSVivien Didelot } 41298fc3c6fSVivien Didelot 413ec561276SVivien Didelot int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip, 414ec561276SVivien Didelot struct ethtool_eeprom *eeprom, u8 *data) 415ec561276SVivien Didelot { 416ec561276SVivien Didelot unsigned int offset = eeprom->offset; 417ec561276SVivien Didelot unsigned int len = eeprom->len; 418ec561276SVivien Didelot u16 val; 419ec561276SVivien Didelot int err; 420ec561276SVivien Didelot 421ec561276SVivien Didelot eeprom->len = 0; 422ec561276SVivien Didelot 423ec561276SVivien Didelot if (offset & 1) { 424ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); 425ec561276SVivien Didelot if (err) 426ec561276SVivien Didelot return err; 427ec561276SVivien Didelot 428ec561276SVivien Didelot *data++ = (val >> 8) & 0xff; 429ec561276SVivien Didelot 430ec561276SVivien Didelot offset++; 431ec561276SVivien Didelot len--; 432ec561276SVivien Didelot eeprom->len++; 433ec561276SVivien Didelot } 434ec561276SVivien Didelot 435ec561276SVivien Didelot while (len >= 2) { 436ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); 437ec561276SVivien Didelot if (err) 438ec561276SVivien Didelot return err; 439ec561276SVivien Didelot 440ec561276SVivien Didelot *data++ = val & 0xff; 441ec561276SVivien Didelot *data++ = (val >> 8) & 0xff; 442ec561276SVivien Didelot 443ec561276SVivien Didelot offset += 2; 444ec561276SVivien Didelot len -= 2; 445ec561276SVivien Didelot eeprom->len += 2; 446ec561276SVivien Didelot } 447ec561276SVivien Didelot 448ec561276SVivien Didelot if (len) { 449ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); 450ec561276SVivien Didelot if (err) 451ec561276SVivien Didelot return err; 452ec561276SVivien Didelot 453ec561276SVivien Didelot *data++ = val & 0xff; 454ec561276SVivien Didelot 455ec561276SVivien Didelot offset++; 456ec561276SVivien Didelot len--; 457ec561276SVivien Didelot eeprom->len++; 458ec561276SVivien Didelot } 459ec561276SVivien Didelot 460ec561276SVivien Didelot return 0; 461ec561276SVivien Didelot } 462ec561276SVivien Didelot 463ec561276SVivien Didelot int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip, 464ec561276SVivien Didelot struct ethtool_eeprom *eeprom, u8 *data) 465ec561276SVivien Didelot { 466ec561276SVivien Didelot unsigned int offset = eeprom->offset; 467ec561276SVivien Didelot unsigned int len = eeprom->len; 468ec561276SVivien Didelot u16 val; 469ec561276SVivien Didelot int err; 470ec561276SVivien Didelot 471ec561276SVivien Didelot /* Ensure the RO WriteEn bit is set */ 4729fe850fbSVivien Didelot err = mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_CMD, &val); 473ec561276SVivien Didelot if (err) 474ec561276SVivien Didelot return err; 475ec561276SVivien Didelot 476ec561276SVivien Didelot if (!(val & GLOBAL2_EEPROM_CMD_WRITE_EN)) 477ec561276SVivien Didelot return -EROFS; 478ec561276SVivien Didelot 479ec561276SVivien Didelot eeprom->len = 0; 480ec561276SVivien Didelot 481ec561276SVivien Didelot if (offset & 1) { 482ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); 483ec561276SVivien Didelot if (err) 484ec561276SVivien Didelot return err; 485ec561276SVivien Didelot 486ec561276SVivien Didelot val = (*data++ << 8) | (val & 0xff); 487ec561276SVivien Didelot 488ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val); 489ec561276SVivien Didelot if (err) 490ec561276SVivien Didelot return err; 491ec561276SVivien Didelot 492ec561276SVivien Didelot offset++; 493ec561276SVivien Didelot len--; 494ec561276SVivien Didelot eeprom->len++; 495ec561276SVivien Didelot } 496ec561276SVivien Didelot 497ec561276SVivien Didelot while (len >= 2) { 498ec561276SVivien Didelot val = *data++; 499ec561276SVivien Didelot val |= *data++ << 8; 500ec561276SVivien Didelot 501ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val); 502ec561276SVivien Didelot if (err) 503ec561276SVivien Didelot return err; 504ec561276SVivien Didelot 505ec561276SVivien Didelot offset += 2; 506ec561276SVivien Didelot len -= 2; 507ec561276SVivien Didelot eeprom->len += 2; 508ec561276SVivien Didelot } 509ec561276SVivien Didelot 510ec561276SVivien Didelot if (len) { 511ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); 512ec561276SVivien Didelot if (err) 513ec561276SVivien Didelot return err; 514ec561276SVivien Didelot 515ec561276SVivien Didelot val = (val & 0xff00) | *data++; 516ec561276SVivien Didelot 517ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val); 518ec561276SVivien Didelot if (err) 519ec561276SVivien Didelot return err; 520ec561276SVivien Didelot 521ec561276SVivien Didelot offset++; 522ec561276SVivien Didelot len--; 523ec561276SVivien Didelot eeprom->len++; 524ec561276SVivien Didelot } 525ec561276SVivien Didelot 526ec561276SVivien Didelot return 0; 527ec561276SVivien Didelot } 528ec561276SVivien Didelot 529ec561276SVivien Didelot /* Offset 0x18: SMI PHY Command Register 530ec561276SVivien Didelot * Offset 0x19: SMI PHY Data Register 531ec561276SVivien Didelot */ 532ec561276SVivien Didelot 533ec561276SVivien Didelot static int mv88e6xxx_g2_smi_phy_wait(struct mv88e6xxx_chip *chip) 534ec561276SVivien Didelot { 5359fe850fbSVivien Didelot return mv88e6xxx_g2_wait(chip, GLOBAL2_SMI_PHY_CMD, 536ec561276SVivien Didelot GLOBAL2_SMI_PHY_CMD_BUSY); 537ec561276SVivien Didelot } 538ec561276SVivien Didelot 539ec561276SVivien Didelot static int mv88e6xxx_g2_smi_phy_cmd(struct mv88e6xxx_chip *chip, u16 cmd) 540ec561276SVivien Didelot { 541ec561276SVivien Didelot int err; 542ec561276SVivien Didelot 5439fe850fbSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_SMI_PHY_CMD, cmd); 544ec561276SVivien Didelot if (err) 545ec561276SVivien Didelot return err; 546ec561276SVivien Didelot 547ec561276SVivien Didelot return mv88e6xxx_g2_smi_phy_wait(chip); 548ec561276SVivien Didelot } 549ec561276SVivien Didelot 550cf3e80dfSAndrew Lunn static int mv88e6xxx_g2_smi_phy_write_addr(struct mv88e6xxx_chip *chip, 551cf3e80dfSAndrew Lunn int addr, int device, int reg, 552cf3e80dfSAndrew Lunn bool external) 553ec561276SVivien Didelot { 554cf3e80dfSAndrew Lunn int cmd = SMI_CMD_OP_45_WRITE_ADDR | (addr << 5) | device; 555ec561276SVivien Didelot int err; 556ec561276SVivien Didelot 557cf3e80dfSAndrew Lunn if (external) 558cf3e80dfSAndrew Lunn cmd |= GLOBAL2_SMI_PHY_CMD_EXTERNAL; 559cf3e80dfSAndrew Lunn 560cf3e80dfSAndrew Lunn err = mv88e6xxx_g2_smi_phy_wait(chip); 561cf3e80dfSAndrew Lunn if (err) 562cf3e80dfSAndrew Lunn return err; 563cf3e80dfSAndrew Lunn 564cf3e80dfSAndrew Lunn err = mv88e6xxx_g2_write(chip, GLOBAL2_SMI_PHY_DATA, reg); 565cf3e80dfSAndrew Lunn if (err) 566cf3e80dfSAndrew Lunn return err; 567cf3e80dfSAndrew Lunn 568cf3e80dfSAndrew Lunn return mv88e6xxx_g2_smi_phy_cmd(chip, cmd); 569cf3e80dfSAndrew Lunn } 570cf3e80dfSAndrew Lunn 57154a88e4cSFlorian Fainelli static int mv88e6xxx_g2_smi_phy_read_c45(struct mv88e6xxx_chip *chip, 57254a88e4cSFlorian Fainelli int addr, int reg_c45, u16 *val, 57354a88e4cSFlorian Fainelli bool external) 574cf3e80dfSAndrew Lunn { 575cf3e80dfSAndrew Lunn int device = (reg_c45 >> 16) & 0x1f; 576cf3e80dfSAndrew Lunn int reg = reg_c45 & 0xffff; 577cf3e80dfSAndrew Lunn int err; 578cf3e80dfSAndrew Lunn u16 cmd; 579cf3e80dfSAndrew Lunn 580cf3e80dfSAndrew Lunn err = mv88e6xxx_g2_smi_phy_write_addr(chip, addr, device, reg, 581cf3e80dfSAndrew Lunn external); 582cf3e80dfSAndrew Lunn if (err) 583cf3e80dfSAndrew Lunn return err; 584cf3e80dfSAndrew Lunn 585cf3e80dfSAndrew Lunn cmd = GLOBAL2_SMI_PHY_CMD_OP_45_READ_DATA | (addr << 5) | device; 586cf3e80dfSAndrew Lunn 587cf3e80dfSAndrew Lunn if (external) 588cf3e80dfSAndrew Lunn cmd |= GLOBAL2_SMI_PHY_CMD_EXTERNAL; 589cf3e80dfSAndrew Lunn 590cf3e80dfSAndrew Lunn err = mv88e6xxx_g2_smi_phy_cmd(chip, cmd); 591cf3e80dfSAndrew Lunn if (err) 592cf3e80dfSAndrew Lunn return err; 593cf3e80dfSAndrew Lunn 594cf3e80dfSAndrew Lunn err = mv88e6xxx_g2_read(chip, GLOBAL2_SMI_PHY_DATA, val); 595cf3e80dfSAndrew Lunn if (err) 596cf3e80dfSAndrew Lunn return err; 597cf3e80dfSAndrew Lunn 598cf3e80dfSAndrew Lunn err = *val; 599cf3e80dfSAndrew Lunn 600cf3e80dfSAndrew Lunn return 0; 601cf3e80dfSAndrew Lunn } 602cf3e80dfSAndrew Lunn 60354a88e4cSFlorian Fainelli static int mv88e6xxx_g2_smi_phy_read_c22(struct mv88e6xxx_chip *chip, 60454a88e4cSFlorian Fainelli int addr, int reg, u16 *val, 60554a88e4cSFlorian Fainelli bool external) 606cf3e80dfSAndrew Lunn { 607cf3e80dfSAndrew Lunn u16 cmd = GLOBAL2_SMI_PHY_CMD_OP_22_READ_DATA | (addr << 5) | reg; 608cf3e80dfSAndrew Lunn int err; 609cf3e80dfSAndrew Lunn 610cf3e80dfSAndrew Lunn if (external) 611c61a6a71SAndrew Lunn cmd |= GLOBAL2_SMI_PHY_CMD_EXTERNAL; 612c61a6a71SAndrew Lunn 613ec561276SVivien Didelot err = mv88e6xxx_g2_smi_phy_wait(chip); 614ec561276SVivien Didelot if (err) 615ec561276SVivien Didelot return err; 616ec561276SVivien Didelot 617ec561276SVivien Didelot err = mv88e6xxx_g2_smi_phy_cmd(chip, cmd); 618ec561276SVivien Didelot if (err) 619ec561276SVivien Didelot return err; 620ec561276SVivien Didelot 6219fe850fbSVivien Didelot return mv88e6xxx_g2_read(chip, GLOBAL2_SMI_PHY_DATA, val); 622ec561276SVivien Didelot } 623ec561276SVivien Didelot 624cf3e80dfSAndrew Lunn int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip, 625ee26a228SAndrew Lunn struct mii_bus *bus, 626cf3e80dfSAndrew Lunn int addr, int reg, u16 *val) 627cf3e80dfSAndrew Lunn { 628cf3e80dfSAndrew Lunn struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv; 629cf3e80dfSAndrew Lunn bool external = mdio_bus->external; 630cf3e80dfSAndrew Lunn 631cf3e80dfSAndrew Lunn if (reg & MII_ADDR_C45) 632cf3e80dfSAndrew Lunn return mv88e6xxx_g2_smi_phy_read_c45(chip, addr, reg, val, 633cf3e80dfSAndrew Lunn external); 634cf3e80dfSAndrew Lunn return mv88e6xxx_g2_smi_phy_read_c22(chip, addr, reg, val, external); 635cf3e80dfSAndrew Lunn } 636cf3e80dfSAndrew Lunn 63754a88e4cSFlorian Fainelli static int mv88e6xxx_g2_smi_phy_write_c45(struct mv88e6xxx_chip *chip, 63854a88e4cSFlorian Fainelli int addr, int reg_c45, u16 val, 63954a88e4cSFlorian Fainelli bool external) 640cf3e80dfSAndrew Lunn { 641cf3e80dfSAndrew Lunn int device = (reg_c45 >> 16) & 0x1f; 642cf3e80dfSAndrew Lunn int reg = reg_c45 & 0xffff; 643cf3e80dfSAndrew Lunn int err; 644cf3e80dfSAndrew Lunn u16 cmd; 645cf3e80dfSAndrew Lunn 646cf3e80dfSAndrew Lunn err = mv88e6xxx_g2_smi_phy_write_addr(chip, addr, device, reg, 647cf3e80dfSAndrew Lunn external); 648cf3e80dfSAndrew Lunn if (err) 649cf3e80dfSAndrew Lunn return err; 650cf3e80dfSAndrew Lunn 651cf3e80dfSAndrew Lunn cmd = GLOBAL2_SMI_PHY_CMD_OP_45_WRITE_DATA | (addr << 5) | device; 652cf3e80dfSAndrew Lunn 653cf3e80dfSAndrew Lunn if (external) 654cf3e80dfSAndrew Lunn cmd |= GLOBAL2_SMI_PHY_CMD_EXTERNAL; 655cf3e80dfSAndrew Lunn 656cf3e80dfSAndrew Lunn err = mv88e6xxx_g2_write(chip, GLOBAL2_SMI_PHY_DATA, val); 657cf3e80dfSAndrew Lunn if (err) 658cf3e80dfSAndrew Lunn return err; 659cf3e80dfSAndrew Lunn 660cf3e80dfSAndrew Lunn err = mv88e6xxx_g2_smi_phy_cmd(chip, cmd); 661cf3e80dfSAndrew Lunn if (err) 662cf3e80dfSAndrew Lunn return err; 663cf3e80dfSAndrew Lunn 664cf3e80dfSAndrew Lunn return 0; 665cf3e80dfSAndrew Lunn } 666cf3e80dfSAndrew Lunn 66754a88e4cSFlorian Fainelli static int mv88e6xxx_g2_smi_phy_write_c22(struct mv88e6xxx_chip *chip, 66854a88e4cSFlorian Fainelli int addr, int reg, u16 val, 66954a88e4cSFlorian Fainelli bool external) 670ec561276SVivien Didelot { 671ec561276SVivien Didelot u16 cmd = GLOBAL2_SMI_PHY_CMD_OP_22_WRITE_DATA | (addr << 5) | reg; 672ec561276SVivien Didelot int err; 673ec561276SVivien Didelot 674cf3e80dfSAndrew Lunn if (external) 675c61a6a71SAndrew Lunn cmd |= GLOBAL2_SMI_PHY_CMD_EXTERNAL; 676c61a6a71SAndrew Lunn 677ec561276SVivien Didelot err = mv88e6xxx_g2_smi_phy_wait(chip); 678ec561276SVivien Didelot if (err) 679ec561276SVivien Didelot return err; 680ec561276SVivien Didelot 6819fe850fbSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_SMI_PHY_DATA, val); 682ec561276SVivien Didelot if (err) 683ec561276SVivien Didelot return err; 684ec561276SVivien Didelot 685ec561276SVivien Didelot return mv88e6xxx_g2_smi_phy_cmd(chip, cmd); 686ec561276SVivien Didelot } 687ec561276SVivien Didelot 688cf3e80dfSAndrew Lunn int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, 689cf3e80dfSAndrew Lunn struct mii_bus *bus, 690cf3e80dfSAndrew Lunn int addr, int reg, u16 val) 691cf3e80dfSAndrew Lunn { 692cf3e80dfSAndrew Lunn struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv; 693cf3e80dfSAndrew Lunn bool external = mdio_bus->external; 694cf3e80dfSAndrew Lunn 695cf3e80dfSAndrew Lunn if (reg & MII_ADDR_C45) 696cf3e80dfSAndrew Lunn return mv88e6xxx_g2_smi_phy_write_c45(chip, addr, reg, val, 697cf3e80dfSAndrew Lunn external); 698cf3e80dfSAndrew Lunn 699cf3e80dfSAndrew Lunn return mv88e6xxx_g2_smi_phy_write_c22(chip, addr, reg, val, external); 700cf3e80dfSAndrew Lunn } 701cf3e80dfSAndrew Lunn 702fcd25166SAndrew Lunn static int mv88e6097_watchdog_action(struct mv88e6xxx_chip *chip, int irq) 703fcd25166SAndrew Lunn { 704fcd25166SAndrew Lunn u16 reg; 705fcd25166SAndrew Lunn 706fcd25166SAndrew Lunn mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, ®); 707fcd25166SAndrew Lunn 708fcd25166SAndrew Lunn dev_info(chip->dev, "Watchdog event: 0x%04x", reg); 709fcd25166SAndrew Lunn 710fcd25166SAndrew Lunn return IRQ_HANDLED; 711fcd25166SAndrew Lunn } 712fcd25166SAndrew Lunn 713fcd25166SAndrew Lunn static void mv88e6097_watchdog_free(struct mv88e6xxx_chip *chip) 714fcd25166SAndrew Lunn { 715fcd25166SAndrew Lunn u16 reg; 716fcd25166SAndrew Lunn 717fcd25166SAndrew Lunn mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, ®); 718fcd25166SAndrew Lunn 719fcd25166SAndrew Lunn reg &= ~(GLOBAL2_WDOG_CONTROL_EGRESS_ENABLE | 720fcd25166SAndrew Lunn GLOBAL2_WDOG_CONTROL_QC_ENABLE); 721fcd25166SAndrew Lunn 722fcd25166SAndrew Lunn mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, reg); 723fcd25166SAndrew Lunn } 724fcd25166SAndrew Lunn 725fcd25166SAndrew Lunn static int mv88e6097_watchdog_setup(struct mv88e6xxx_chip *chip) 726fcd25166SAndrew Lunn { 727fcd25166SAndrew Lunn return mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, 728fcd25166SAndrew Lunn GLOBAL2_WDOG_CONTROL_EGRESS_ENABLE | 729fcd25166SAndrew Lunn GLOBAL2_WDOG_CONTROL_QC_ENABLE | 730fcd25166SAndrew Lunn GLOBAL2_WDOG_CONTROL_SWRESET); 731fcd25166SAndrew Lunn } 732fcd25166SAndrew Lunn 733fcd25166SAndrew Lunn const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = { 734fcd25166SAndrew Lunn .irq_action = mv88e6097_watchdog_action, 735fcd25166SAndrew Lunn .irq_setup = mv88e6097_watchdog_setup, 736fcd25166SAndrew Lunn .irq_free = mv88e6097_watchdog_free, 737fcd25166SAndrew Lunn }; 738fcd25166SAndrew Lunn 73961303736SAndrew Lunn static int mv88e6390_watchdog_setup(struct mv88e6xxx_chip *chip) 74061303736SAndrew Lunn { 74161303736SAndrew Lunn return mv88e6xxx_g2_update(chip, GLOBAL2_WDOG_CONTROL, 74261303736SAndrew Lunn GLOBAL2_WDOG_INT_ENABLE | 74361303736SAndrew Lunn GLOBAL2_WDOG_CUT_THROUGH | 74461303736SAndrew Lunn GLOBAL2_WDOG_QUEUE_CONTROLLER | 74561303736SAndrew Lunn GLOBAL2_WDOG_EGRESS | 74661303736SAndrew Lunn GLOBAL2_WDOG_FORCE_IRQ); 74761303736SAndrew Lunn } 74861303736SAndrew Lunn 74961303736SAndrew Lunn static int mv88e6390_watchdog_action(struct mv88e6xxx_chip *chip, int irq) 75061303736SAndrew Lunn { 75161303736SAndrew Lunn int err; 75261303736SAndrew Lunn u16 reg; 75361303736SAndrew Lunn 75461303736SAndrew Lunn mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, GLOBAL2_WDOG_EVENT); 75561303736SAndrew Lunn err = mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, ®); 75661303736SAndrew Lunn 75761303736SAndrew Lunn dev_info(chip->dev, "Watchdog event: 0x%04x", 75861303736SAndrew Lunn reg & GLOBAL2_WDOG_DATA_MASK); 75961303736SAndrew Lunn 76061303736SAndrew Lunn mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, GLOBAL2_WDOG_HISTORY); 76161303736SAndrew Lunn err = mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, ®); 76261303736SAndrew Lunn 76361303736SAndrew Lunn dev_info(chip->dev, "Watchdog history: 0x%04x", 76461303736SAndrew Lunn reg & GLOBAL2_WDOG_DATA_MASK); 76561303736SAndrew Lunn 76661303736SAndrew Lunn /* Trigger a software reset to try to recover the switch */ 76761303736SAndrew Lunn if (chip->info->ops->reset) 76861303736SAndrew Lunn chip->info->ops->reset(chip); 76961303736SAndrew Lunn 77061303736SAndrew Lunn mv88e6390_watchdog_setup(chip); 77161303736SAndrew Lunn 77261303736SAndrew Lunn return IRQ_HANDLED; 77361303736SAndrew Lunn } 77461303736SAndrew Lunn 77561303736SAndrew Lunn static void mv88e6390_watchdog_free(struct mv88e6xxx_chip *chip) 77661303736SAndrew Lunn { 77761303736SAndrew Lunn mv88e6xxx_g2_update(chip, GLOBAL2_WDOG_CONTROL, 77861303736SAndrew Lunn GLOBAL2_WDOG_INT_ENABLE); 77961303736SAndrew Lunn } 78061303736SAndrew Lunn 78161303736SAndrew Lunn const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = { 78261303736SAndrew Lunn .irq_action = mv88e6390_watchdog_action, 78361303736SAndrew Lunn .irq_setup = mv88e6390_watchdog_setup, 78461303736SAndrew Lunn .irq_free = mv88e6390_watchdog_free, 78561303736SAndrew Lunn }; 78661303736SAndrew Lunn 787fcd25166SAndrew Lunn static irqreturn_t mv88e6xxx_g2_watchdog_thread_fn(int irq, void *dev_id) 788fcd25166SAndrew Lunn { 789fcd25166SAndrew Lunn struct mv88e6xxx_chip *chip = dev_id; 790fcd25166SAndrew Lunn irqreturn_t ret = IRQ_NONE; 791fcd25166SAndrew Lunn 792fcd25166SAndrew Lunn mutex_lock(&chip->reg_lock); 793fcd25166SAndrew Lunn if (chip->info->ops->watchdog_ops->irq_action) 794fcd25166SAndrew Lunn ret = chip->info->ops->watchdog_ops->irq_action(chip, irq); 795fcd25166SAndrew Lunn mutex_unlock(&chip->reg_lock); 796fcd25166SAndrew Lunn 797fcd25166SAndrew Lunn return ret; 798fcd25166SAndrew Lunn } 799fcd25166SAndrew Lunn 800fcd25166SAndrew Lunn static void mv88e6xxx_g2_watchdog_free(struct mv88e6xxx_chip *chip) 801fcd25166SAndrew Lunn { 802fcd25166SAndrew Lunn mutex_lock(&chip->reg_lock); 803fcd25166SAndrew Lunn if (chip->info->ops->watchdog_ops->irq_free) 804fcd25166SAndrew Lunn chip->info->ops->watchdog_ops->irq_free(chip); 805fcd25166SAndrew Lunn mutex_unlock(&chip->reg_lock); 806fcd25166SAndrew Lunn 807fcd25166SAndrew Lunn free_irq(chip->watchdog_irq, chip); 808fcd25166SAndrew Lunn irq_dispose_mapping(chip->watchdog_irq); 809fcd25166SAndrew Lunn } 810fcd25166SAndrew Lunn 811fcd25166SAndrew Lunn static int mv88e6xxx_g2_watchdog_setup(struct mv88e6xxx_chip *chip) 812fcd25166SAndrew Lunn { 813fcd25166SAndrew Lunn int err; 814fcd25166SAndrew Lunn 815fcd25166SAndrew Lunn chip->watchdog_irq = irq_find_mapping(chip->g2_irq.domain, 816fcd25166SAndrew Lunn GLOBAL2_INT_SOURCE_WATCHDOG); 817fcd25166SAndrew Lunn if (chip->watchdog_irq < 0) 818fcd25166SAndrew Lunn return chip->watchdog_irq; 819fcd25166SAndrew Lunn 820fcd25166SAndrew Lunn err = request_threaded_irq(chip->watchdog_irq, NULL, 821fcd25166SAndrew Lunn mv88e6xxx_g2_watchdog_thread_fn, 822fcd25166SAndrew Lunn IRQF_ONESHOT | IRQF_TRIGGER_FALLING, 823fcd25166SAndrew Lunn "mv88e6xxx-watchdog", chip); 824fcd25166SAndrew Lunn if (err) 825fcd25166SAndrew Lunn return err; 826fcd25166SAndrew Lunn 827fcd25166SAndrew Lunn mutex_lock(&chip->reg_lock); 828fcd25166SAndrew Lunn if (chip->info->ops->watchdog_ops->irq_setup) 829fcd25166SAndrew Lunn err = chip->info->ops->watchdog_ops->irq_setup(chip); 830fcd25166SAndrew Lunn mutex_unlock(&chip->reg_lock); 831fcd25166SAndrew Lunn 832fcd25166SAndrew Lunn return err; 833fcd25166SAndrew Lunn } 834fcd25166SAndrew Lunn 83581228996SVivien Didelot /* Offset 0x1D: Misc Register */ 83681228996SVivien Didelot 83781228996SVivien Didelot static int mv88e6xxx_g2_misc_5_bit_port(struct mv88e6xxx_chip *chip, 83881228996SVivien Didelot bool port_5_bit) 83981228996SVivien Didelot { 84081228996SVivien Didelot u16 val; 84181228996SVivien Didelot int err; 84281228996SVivien Didelot 84381228996SVivien Didelot err = mv88e6xxx_g2_read(chip, GLOBAL2_MISC, &val); 84481228996SVivien Didelot if (err) 84581228996SVivien Didelot return err; 84681228996SVivien Didelot 84781228996SVivien Didelot if (port_5_bit) 84881228996SVivien Didelot val |= GLOBAL2_MISC_5_BIT_PORT; 84981228996SVivien Didelot else 85081228996SVivien Didelot val &= ~GLOBAL2_MISC_5_BIT_PORT; 85181228996SVivien Didelot 85281228996SVivien Didelot return mv88e6xxx_g2_write(chip, GLOBAL2_MISC, val); 85381228996SVivien Didelot } 85481228996SVivien Didelot 85581228996SVivien Didelot int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip) 85681228996SVivien Didelot { 85781228996SVivien Didelot return mv88e6xxx_g2_misc_5_bit_port(chip, false); 85881228996SVivien Didelot } 85981228996SVivien Didelot 860dc30c35bSAndrew Lunn static void mv88e6xxx_g2_irq_mask(struct irq_data *d) 861dc30c35bSAndrew Lunn { 862dc30c35bSAndrew Lunn struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); 863dc30c35bSAndrew Lunn unsigned int n = d->hwirq; 864dc30c35bSAndrew Lunn 865dc30c35bSAndrew Lunn chip->g2_irq.masked |= (1 << n); 866dc30c35bSAndrew Lunn } 867dc30c35bSAndrew Lunn 868dc30c35bSAndrew Lunn static void mv88e6xxx_g2_irq_unmask(struct irq_data *d) 869dc30c35bSAndrew Lunn { 870dc30c35bSAndrew Lunn struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); 871dc30c35bSAndrew Lunn unsigned int n = d->hwirq; 872dc30c35bSAndrew Lunn 873dc30c35bSAndrew Lunn chip->g2_irq.masked &= ~(1 << n); 874dc30c35bSAndrew Lunn } 875dc30c35bSAndrew Lunn 876dc30c35bSAndrew Lunn static irqreturn_t mv88e6xxx_g2_irq_thread_fn(int irq, void *dev_id) 877dc30c35bSAndrew Lunn { 878dc30c35bSAndrew Lunn struct mv88e6xxx_chip *chip = dev_id; 879dc30c35bSAndrew Lunn unsigned int nhandled = 0; 880dc30c35bSAndrew Lunn unsigned int sub_irq; 881dc30c35bSAndrew Lunn unsigned int n; 882dc30c35bSAndrew Lunn int err; 883dc30c35bSAndrew Lunn u16 reg; 884dc30c35bSAndrew Lunn 885dc30c35bSAndrew Lunn mutex_lock(&chip->reg_lock); 886dc30c35bSAndrew Lunn err = mv88e6xxx_g2_read(chip, GLOBAL2_INT_SOURCE, ®); 887dc30c35bSAndrew Lunn mutex_unlock(&chip->reg_lock); 888dc30c35bSAndrew Lunn if (err) 889dc30c35bSAndrew Lunn goto out; 890dc30c35bSAndrew Lunn 891dc30c35bSAndrew Lunn for (n = 0; n < 16; ++n) { 892dc30c35bSAndrew Lunn if (reg & (1 << n)) { 893dc30c35bSAndrew Lunn sub_irq = irq_find_mapping(chip->g2_irq.domain, n); 894dc30c35bSAndrew Lunn handle_nested_irq(sub_irq); 895dc30c35bSAndrew Lunn ++nhandled; 896dc30c35bSAndrew Lunn } 897dc30c35bSAndrew Lunn } 898dc30c35bSAndrew Lunn out: 899dc30c35bSAndrew Lunn return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE); 900dc30c35bSAndrew Lunn } 901dc30c35bSAndrew Lunn 902dc30c35bSAndrew Lunn static void mv88e6xxx_g2_irq_bus_lock(struct irq_data *d) 903dc30c35bSAndrew Lunn { 904dc30c35bSAndrew Lunn struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); 905dc30c35bSAndrew Lunn 906dc30c35bSAndrew Lunn mutex_lock(&chip->reg_lock); 907dc30c35bSAndrew Lunn } 908dc30c35bSAndrew Lunn 909dc30c35bSAndrew Lunn static void mv88e6xxx_g2_irq_bus_sync_unlock(struct irq_data *d) 910dc30c35bSAndrew Lunn { 911dc30c35bSAndrew Lunn struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); 912dc30c35bSAndrew Lunn 913dc30c35bSAndrew Lunn mv88e6xxx_g2_write(chip, GLOBAL2_INT_MASK, ~chip->g2_irq.masked); 914dc30c35bSAndrew Lunn 915dc30c35bSAndrew Lunn mutex_unlock(&chip->reg_lock); 916dc30c35bSAndrew Lunn } 917dc30c35bSAndrew Lunn 918dc30c35bSAndrew Lunn static struct irq_chip mv88e6xxx_g2_irq_chip = { 919dc30c35bSAndrew Lunn .name = "mv88e6xxx-g2", 920dc30c35bSAndrew Lunn .irq_mask = mv88e6xxx_g2_irq_mask, 921dc30c35bSAndrew Lunn .irq_unmask = mv88e6xxx_g2_irq_unmask, 922dc30c35bSAndrew Lunn .irq_bus_lock = mv88e6xxx_g2_irq_bus_lock, 923dc30c35bSAndrew Lunn .irq_bus_sync_unlock = mv88e6xxx_g2_irq_bus_sync_unlock, 924dc30c35bSAndrew Lunn }; 925dc30c35bSAndrew Lunn 926dc30c35bSAndrew Lunn static int mv88e6xxx_g2_irq_domain_map(struct irq_domain *d, 927dc30c35bSAndrew Lunn unsigned int irq, 928dc30c35bSAndrew Lunn irq_hw_number_t hwirq) 929dc30c35bSAndrew Lunn { 930dc30c35bSAndrew Lunn struct mv88e6xxx_chip *chip = d->host_data; 931dc30c35bSAndrew Lunn 932dc30c35bSAndrew Lunn irq_set_chip_data(irq, d->host_data); 933dc30c35bSAndrew Lunn irq_set_chip_and_handler(irq, &chip->g2_irq.chip, handle_level_irq); 934dc30c35bSAndrew Lunn irq_set_noprobe(irq); 935dc30c35bSAndrew Lunn 936dc30c35bSAndrew Lunn return 0; 937dc30c35bSAndrew Lunn } 938dc30c35bSAndrew Lunn 939dc30c35bSAndrew Lunn static const struct irq_domain_ops mv88e6xxx_g2_irq_domain_ops = { 940dc30c35bSAndrew Lunn .map = mv88e6xxx_g2_irq_domain_map, 941dc30c35bSAndrew Lunn .xlate = irq_domain_xlate_twocell, 942dc30c35bSAndrew Lunn }; 943dc30c35bSAndrew Lunn 944dc30c35bSAndrew Lunn void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip) 945dc30c35bSAndrew Lunn { 946dc30c35bSAndrew Lunn int irq, virq; 947dc30c35bSAndrew Lunn 948fcd25166SAndrew Lunn mv88e6xxx_g2_watchdog_free(chip); 949fcd25166SAndrew Lunn 9508e757ebaSAndrew Lunn free_irq(chip->device_irq, chip); 9518e757ebaSAndrew Lunn irq_dispose_mapping(chip->device_irq); 9528e757ebaSAndrew Lunn 953dc30c35bSAndrew Lunn for (irq = 0; irq < 16; irq++) { 954dc30c35bSAndrew Lunn virq = irq_find_mapping(chip->g2_irq.domain, irq); 955dc30c35bSAndrew Lunn irq_dispose_mapping(virq); 956dc30c35bSAndrew Lunn } 957dc30c35bSAndrew Lunn 958dc30c35bSAndrew Lunn irq_domain_remove(chip->g2_irq.domain); 959dc30c35bSAndrew Lunn } 960dc30c35bSAndrew Lunn 961dc30c35bSAndrew Lunn int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip) 962dc30c35bSAndrew Lunn { 9638e757ebaSAndrew Lunn int err, irq, virq; 964dc30c35bSAndrew Lunn 965dc30c35bSAndrew Lunn if (!chip->dev->of_node) 966dc30c35bSAndrew Lunn return -EINVAL; 967dc30c35bSAndrew Lunn 968dc30c35bSAndrew Lunn chip->g2_irq.domain = irq_domain_add_simple( 969dc30c35bSAndrew Lunn chip->dev->of_node, 16, 0, &mv88e6xxx_g2_irq_domain_ops, chip); 970dc30c35bSAndrew Lunn if (!chip->g2_irq.domain) 971dc30c35bSAndrew Lunn return -ENOMEM; 972dc30c35bSAndrew Lunn 973dc30c35bSAndrew Lunn for (irq = 0; irq < 16; irq++) 974dc30c35bSAndrew Lunn irq_create_mapping(chip->g2_irq.domain, irq); 975dc30c35bSAndrew Lunn 976dc30c35bSAndrew Lunn chip->g2_irq.chip = mv88e6xxx_g2_irq_chip; 977dc30c35bSAndrew Lunn chip->g2_irq.masked = ~0; 978dc30c35bSAndrew Lunn 9798e757ebaSAndrew Lunn chip->device_irq = irq_find_mapping(chip->g1_irq.domain, 98082466921SVivien Didelot MV88E6XXX_G1_STS_IRQ_DEVICE); 9818e757ebaSAndrew Lunn if (chip->device_irq < 0) { 9828e757ebaSAndrew Lunn err = chip->device_irq; 983dc30c35bSAndrew Lunn goto out; 984dc30c35bSAndrew Lunn } 985dc30c35bSAndrew Lunn 9868e757ebaSAndrew Lunn err = request_threaded_irq(chip->device_irq, NULL, 987dc30c35bSAndrew Lunn mv88e6xxx_g2_irq_thread_fn, 988dc30c35bSAndrew Lunn IRQF_ONESHOT, "mv88e6xxx-g1", chip); 989dc30c35bSAndrew Lunn if (err) 990dc30c35bSAndrew Lunn goto out; 991dc30c35bSAndrew Lunn 992fcd25166SAndrew Lunn return mv88e6xxx_g2_watchdog_setup(chip); 9938e757ebaSAndrew Lunn 994dc30c35bSAndrew Lunn out: 9958e757ebaSAndrew Lunn for (irq = 0; irq < 16; irq++) { 9968e757ebaSAndrew Lunn virq = irq_find_mapping(chip->g2_irq.domain, irq); 9978e757ebaSAndrew Lunn irq_dispose_mapping(virq); 9988e757ebaSAndrew Lunn } 9998e757ebaSAndrew Lunn 10008e757ebaSAndrew Lunn irq_domain_remove(chip->g2_irq.domain); 1001dc30c35bSAndrew Lunn 1002dc30c35bSAndrew Lunn return err; 1003dc30c35bSAndrew Lunn } 1004dc30c35bSAndrew Lunn 1005ec561276SVivien Didelot int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip) 1006ec561276SVivien Didelot { 1007ec561276SVivien Didelot u16 reg; 1008ec561276SVivien Didelot int err; 1009ec561276SVivien Didelot 1010ec561276SVivien Didelot /* Ignore removed tag data on doubly tagged packets, disable 1011ec561276SVivien Didelot * flow control messages, force flow control priority to the 1012ec561276SVivien Didelot * highest, and send all special multicast frames to the CPU 1013ec561276SVivien Didelot * port at the highest priority. 1014ec561276SVivien Didelot */ 1015ec561276SVivien Didelot reg = GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI | (0x7 << 4); 1016ec561276SVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X) || 1017ec561276SVivien Didelot mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X)) 1018ec561276SVivien Didelot reg |= GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x7; 10199fe850fbSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_SWITCH_MGMT, reg); 1020ec561276SVivien Didelot if (err) 1021ec561276SVivien Didelot return err; 1022ec561276SVivien Didelot 1023ec561276SVivien Didelot /* Program the DSA routing table. */ 1024ec561276SVivien Didelot err = mv88e6xxx_g2_set_device_mapping(chip); 1025ec561276SVivien Didelot if (err) 1026ec561276SVivien Didelot return err; 1027ec561276SVivien Didelot 1028ec561276SVivien Didelot /* Clear all trunk masks and mapping. */ 1029ec561276SVivien Didelot err = mv88e6xxx_g2_clear_trunk(chip); 1030ec561276SVivien Didelot if (err) 1031ec561276SVivien Didelot return err; 1032ec561276SVivien Didelot 1033ec561276SVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_IRL)) { 1034ec561276SVivien Didelot /* Disable ingress rate limiting by resetting all per port 1035ec561276SVivien Didelot * ingress rate limit resources to their initial state. 1036ec561276SVivien Didelot */ 1037ec561276SVivien Didelot err = mv88e6xxx_g2_clear_irl(chip); 1038ec561276SVivien Didelot if (err) 1039ec561276SVivien Didelot return err; 1040ec561276SVivien Didelot } 1041ec561276SVivien Didelot 1042ec561276SVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_POT)) { 1043ec561276SVivien Didelot /* Clear the priority override table. */ 1044ec561276SVivien Didelot err = mv88e6xxx_g2_clear_pot(chip); 1045ec561276SVivien Didelot if (err) 1046ec561276SVivien Didelot return err; 1047ec561276SVivien Didelot } 1048ec561276SVivien Didelot 1049ec561276SVivien Didelot return 0; 1050ec561276SVivien Didelot } 1051