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 16e289ef0dSVivien Didelot #include <linux/bitfield.h> 17282ccf6eSFlorian Westphal #include <linux/interrupt.h> 18dc30c35bSAndrew Lunn #include <linux/irqdomain.h> 194d5f2ba7SVivien Didelot 204d5f2ba7SVivien Didelot #include "chip.h" 2182466921SVivien Didelot #include "global1.h" /* for MV88E6XXX_G1_STS_IRQ_DEVICE */ 22ec561276SVivien Didelot #include "global2.h" 23ec561276SVivien Didelot 249fe850fbSVivien Didelot static int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val) 259fe850fbSVivien Didelot { 269fe850fbSVivien Didelot return mv88e6xxx_read(chip, ADDR_GLOBAL2, reg, val); 279fe850fbSVivien Didelot } 289fe850fbSVivien Didelot 299fe850fbSVivien Didelot static int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val) 309fe850fbSVivien Didelot { 319fe850fbSVivien Didelot return mv88e6xxx_write(chip, ADDR_GLOBAL2, reg, val); 329fe850fbSVivien Didelot } 339fe850fbSVivien Didelot 349fe850fbSVivien Didelot static int mv88e6xxx_g2_update(struct mv88e6xxx_chip *chip, int reg, u16 update) 359fe850fbSVivien Didelot { 369fe850fbSVivien Didelot return mv88e6xxx_update(chip, ADDR_GLOBAL2, reg, update); 379fe850fbSVivien Didelot } 389fe850fbSVivien Didelot 399fe850fbSVivien Didelot static int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask) 409fe850fbSVivien Didelot { 419fe850fbSVivien Didelot return mv88e6xxx_wait(chip, ADDR_GLOBAL2, reg, mask); 429fe850fbSVivien Didelot } 439fe850fbSVivien Didelot 446e55f698SAndrew Lunn /* Offset 0x02: Management Enable 2x */ 456e55f698SAndrew Lunn /* Offset 0x03: Management Enable 0x */ 466e55f698SAndrew Lunn 476e55f698SAndrew Lunn int mv88e6095_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip) 486e55f698SAndrew Lunn { 496e55f698SAndrew Lunn int err; 506e55f698SAndrew Lunn 516e55f698SAndrew Lunn /* Consider the frames with reserved multicast destination 526e55f698SAndrew Lunn * addresses matching 01:80:c2:00:00:2x as MGMT. 536e55f698SAndrew Lunn */ 546e55f698SAndrew Lunn if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X)) { 556e55f698SAndrew Lunn err = mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_2X, 0xffff); 566e55f698SAndrew Lunn if (err) 576e55f698SAndrew Lunn return err; 586e55f698SAndrew Lunn } 596e55f698SAndrew Lunn 606e55f698SAndrew Lunn /* Consider the frames with reserved multicast destination 616e55f698SAndrew Lunn * addresses matching 01:80:c2:00:00:0x as MGMT. 626e55f698SAndrew Lunn */ 636e55f698SAndrew Lunn if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X)) 646e55f698SAndrew Lunn return mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_0X, 0xffff); 656e55f698SAndrew Lunn 666e55f698SAndrew Lunn return 0; 676e55f698SAndrew Lunn } 686e55f698SAndrew Lunn 69ec561276SVivien Didelot /* Offset 0x06: Device Mapping Table register */ 70ec561276SVivien Didelot 71ec561276SVivien Didelot static int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip, 72ec561276SVivien Didelot int target, int port) 73ec561276SVivien Didelot { 74ec561276SVivien Didelot u16 val = (target << 8) | (port & 0xf); 75ec561276SVivien Didelot 76067e474aSVivien Didelot return mv88e6xxx_g2_update(chip, MV88E6XXX_G2_DEVICE_MAPPING, val); 77ec561276SVivien Didelot } 78ec561276SVivien Didelot 79ec561276SVivien Didelot static int mv88e6xxx_g2_set_device_mapping(struct mv88e6xxx_chip *chip) 80ec561276SVivien Didelot { 81ec561276SVivien Didelot int target, port; 82ec561276SVivien Didelot int err; 83ec561276SVivien Didelot 84ec561276SVivien Didelot /* Initialize the routing port to the 32 possible target devices */ 85ec561276SVivien Didelot for (target = 0; target < 32; ++target) { 86ec561276SVivien Didelot port = 0xf; 87ec561276SVivien Didelot 88ec561276SVivien Didelot if (target < DSA_MAX_SWITCHES) { 89ec561276SVivien Didelot port = chip->ds->rtable[target]; 90ec561276SVivien Didelot if (port == DSA_RTABLE_NONE) 91ec561276SVivien Didelot port = 0xf; 92ec561276SVivien Didelot } 93ec561276SVivien Didelot 94ec561276SVivien Didelot err = mv88e6xxx_g2_device_mapping_write(chip, target, port); 95ec561276SVivien Didelot if (err) 96ec561276SVivien Didelot break; 97ec561276SVivien Didelot } 98ec561276SVivien Didelot 99ec561276SVivien Didelot return err; 100ec561276SVivien Didelot } 101ec561276SVivien Didelot 102ec561276SVivien Didelot /* Offset 0x07: Trunk Mask Table register */ 103ec561276SVivien Didelot 104ec561276SVivien Didelot static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num, 10556dc7347SVivien Didelot bool hash, u16 mask) 106ec561276SVivien Didelot { 10756dc7347SVivien Didelot u16 val = (num << 12) | (mask & mv88e6xxx_port_mask(chip)); 108ec561276SVivien Didelot 10956dc7347SVivien Didelot if (hash) 11056dc7347SVivien Didelot val |= MV88E6XXX_G2_TRUNK_MASK_HASH; 111ec561276SVivien Didelot 11256dc7347SVivien Didelot return mv88e6xxx_g2_update(chip, MV88E6XXX_G2_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 12356dc7347SVivien Didelot return mv88e6xxx_g2_update(chip, MV88E6XXX_G2_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 152cd8da8bbSVivien Didelot static int mv88e6xxx_g2_irl_wait(struct mv88e6xxx_chip *chip) 153ec561276SVivien Didelot { 154cd8da8bbSVivien Didelot return mv88e6xxx_g2_wait(chip, MV88E6XXX_G2_IRL_CMD, 155cd8da8bbSVivien Didelot MV88E6XXX_G2_IRL_CMD_BUSY); 156ec561276SVivien Didelot } 157ec561276SVivien Didelot 158cd8da8bbSVivien Didelot static int mv88e6xxx_g2_irl_op(struct mv88e6xxx_chip *chip, u16 op, int port, 159cd8da8bbSVivien Didelot int res, int reg) 160cd8da8bbSVivien Didelot { 161cd8da8bbSVivien Didelot int err; 162cd8da8bbSVivien Didelot 163cd8da8bbSVivien Didelot err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_IRL_CMD, 164cd8da8bbSVivien Didelot MV88E6XXX_G2_IRL_CMD_BUSY | op | (port << 8) | 165cd8da8bbSVivien Didelot (res << 5) | reg); 166cd8da8bbSVivien Didelot if (err) 167ec561276SVivien Didelot return err; 168cd8da8bbSVivien Didelot 169cd8da8bbSVivien Didelot return mv88e6xxx_g2_irl_wait(chip); 170cd8da8bbSVivien Didelot } 171cd8da8bbSVivien Didelot 172cd8da8bbSVivien Didelot int mv88e6352_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port) 173cd8da8bbSVivien Didelot { 174cd8da8bbSVivien Didelot return mv88e6xxx_g2_irl_op(chip, MV88E6352_G2_IRL_CMD_OP_INIT_ALL, port, 175cd8da8bbSVivien Didelot 0, 0); 176cd8da8bbSVivien Didelot } 177cd8da8bbSVivien Didelot 178cd8da8bbSVivien Didelot int mv88e6390_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port) 179cd8da8bbSVivien Didelot { 180cd8da8bbSVivien Didelot return mv88e6xxx_g2_irl_op(chip, MV88E6390_G2_IRL_CMD_OP_INIT_ALL, port, 181cd8da8bbSVivien Didelot 0, 0); 182ec561276SVivien Didelot } 183ec561276SVivien Didelot 18417a1594eSVivien Didelot /* Offset 0x0B: Cross-chip Port VLAN (Addr) Register 18517a1594eSVivien Didelot * Offset 0x0C: Cross-chip Port VLAN Data Register 18617a1594eSVivien Didelot */ 18717a1594eSVivien Didelot 18817a1594eSVivien Didelot static int mv88e6xxx_g2_pvt_op_wait(struct mv88e6xxx_chip *chip) 18917a1594eSVivien Didelot { 19017a1594eSVivien Didelot return mv88e6xxx_g2_wait(chip, GLOBAL2_PVT_ADDR, GLOBAL2_PVT_ADDR_BUSY); 19117a1594eSVivien Didelot } 19217a1594eSVivien Didelot 19317a1594eSVivien Didelot static int mv88e6xxx_g2_pvt_op(struct mv88e6xxx_chip *chip, int src_dev, 19417a1594eSVivien Didelot int src_port, u16 op) 19517a1594eSVivien Didelot { 19617a1594eSVivien Didelot int err; 19717a1594eSVivien Didelot 19817a1594eSVivien Didelot /* 9-bit Cross-chip PVT pointer: with GLOBAL2_MISC_5_BIT_PORT cleared, 19917a1594eSVivien Didelot * source device is 5-bit, source port is 4-bit. 20017a1594eSVivien Didelot */ 20117a1594eSVivien Didelot op |= (src_dev & 0x1f) << 4; 20217a1594eSVivien Didelot op |= (src_port & 0xf); 20317a1594eSVivien Didelot 20417a1594eSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_ADDR, op); 20517a1594eSVivien Didelot if (err) 20617a1594eSVivien Didelot return err; 20717a1594eSVivien Didelot 20817a1594eSVivien Didelot return mv88e6xxx_g2_pvt_op_wait(chip); 20917a1594eSVivien Didelot } 21017a1594eSVivien Didelot 21117a1594eSVivien Didelot int mv88e6xxx_g2_pvt_write(struct mv88e6xxx_chip *chip, int src_dev, 21217a1594eSVivien Didelot int src_port, u16 data) 21317a1594eSVivien Didelot { 21417a1594eSVivien Didelot int err; 21517a1594eSVivien Didelot 21617a1594eSVivien Didelot err = mv88e6xxx_g2_pvt_op_wait(chip); 21717a1594eSVivien Didelot if (err) 21817a1594eSVivien Didelot return err; 21917a1594eSVivien Didelot 22017a1594eSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_DATA, data); 22117a1594eSVivien Didelot if (err) 22217a1594eSVivien Didelot return err; 22317a1594eSVivien Didelot 22417a1594eSVivien Didelot return mv88e6xxx_g2_pvt_op(chip, src_dev, src_port, 22517a1594eSVivien Didelot GLOBAL2_PVT_ADDR_OP_WRITE_PVLAN); 22617a1594eSVivien Didelot } 22717a1594eSVivien Didelot 228ec561276SVivien Didelot /* Offset 0x0D: Switch MAC/WoL/WoF register */ 229ec561276SVivien Didelot 230ec561276SVivien Didelot static int mv88e6xxx_g2_switch_mac_write(struct mv88e6xxx_chip *chip, 231ec561276SVivien Didelot unsigned int pointer, u8 data) 232ec561276SVivien Didelot { 233ec561276SVivien Didelot u16 val = (pointer << 8) | data; 234ec561276SVivien Didelot 2359fe850fbSVivien Didelot return mv88e6xxx_g2_update(chip, GLOBAL2_SWITCH_MAC, val); 236ec561276SVivien Didelot } 237ec561276SVivien Didelot 238ec561276SVivien Didelot int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr) 239ec561276SVivien Didelot { 240ec561276SVivien Didelot int i, err; 241ec561276SVivien Didelot 242ec561276SVivien Didelot for (i = 0; i < 6; i++) { 243ec561276SVivien Didelot err = mv88e6xxx_g2_switch_mac_write(chip, i, addr[i]); 244ec561276SVivien Didelot if (err) 245ec561276SVivien Didelot break; 246ec561276SVivien Didelot } 247ec561276SVivien Didelot 248ec561276SVivien Didelot return err; 249ec561276SVivien Didelot } 250ec561276SVivien Didelot 251ec561276SVivien Didelot /* Offset 0x0F: Priority Override Table */ 252ec561276SVivien Didelot 253ec561276SVivien Didelot static int mv88e6xxx_g2_pot_write(struct mv88e6xxx_chip *chip, int pointer, 254ec561276SVivien Didelot u8 data) 255ec561276SVivien Didelot { 256ec561276SVivien Didelot u16 val = (pointer << 8) | (data & 0x7); 257ec561276SVivien Didelot 2589fe850fbSVivien Didelot return mv88e6xxx_g2_update(chip, GLOBAL2_PRIO_OVERRIDE, val); 259ec561276SVivien Didelot } 260ec561276SVivien Didelot 261ec561276SVivien Didelot static int mv88e6xxx_g2_clear_pot(struct mv88e6xxx_chip *chip) 262ec561276SVivien Didelot { 263ec561276SVivien Didelot int i, err; 264ec561276SVivien Didelot 265ec561276SVivien Didelot /* Clear all sixteen possible Priority Override entries */ 266ec561276SVivien Didelot for (i = 0; i < 16; i++) { 267ec561276SVivien Didelot err = mv88e6xxx_g2_pot_write(chip, i, 0); 268ec561276SVivien Didelot if (err) 269ec561276SVivien Didelot break; 270ec561276SVivien Didelot } 271ec561276SVivien Didelot 272ec561276SVivien Didelot return err; 273ec561276SVivien Didelot } 274ec561276SVivien Didelot 275ec561276SVivien Didelot /* Offset 0x14: EEPROM Command 27698fc3c6fSVivien Didelot * Offset 0x15: EEPROM Data (for 16-bit data access) 27798fc3c6fSVivien Didelot * Offset 0x15: EEPROM Addr (for 8-bit data access) 278ec561276SVivien Didelot */ 279ec561276SVivien Didelot 280ec561276SVivien Didelot static int mv88e6xxx_g2_eeprom_wait(struct mv88e6xxx_chip *chip) 281ec561276SVivien Didelot { 2829fe850fbSVivien Didelot return mv88e6xxx_g2_wait(chip, GLOBAL2_EEPROM_CMD, 283ec561276SVivien Didelot GLOBAL2_EEPROM_CMD_BUSY | 284ec561276SVivien Didelot GLOBAL2_EEPROM_CMD_RUNNING); 285ec561276SVivien Didelot } 286ec561276SVivien Didelot 287ec561276SVivien Didelot static int mv88e6xxx_g2_eeprom_cmd(struct mv88e6xxx_chip *chip, u16 cmd) 288ec561276SVivien Didelot { 289ec561276SVivien Didelot int err; 290ec561276SVivien Didelot 2919fe850fbSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_CMD, cmd); 292ec561276SVivien Didelot if (err) 293ec561276SVivien Didelot return err; 294ec561276SVivien Didelot 295ec561276SVivien Didelot return mv88e6xxx_g2_eeprom_wait(chip); 296ec561276SVivien Didelot } 297ec561276SVivien Didelot 29898fc3c6fSVivien Didelot static int mv88e6xxx_g2_eeprom_read8(struct mv88e6xxx_chip *chip, 29998fc3c6fSVivien Didelot u16 addr, u8 *data) 30098fc3c6fSVivien Didelot { 30198fc3c6fSVivien Didelot u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ; 30298fc3c6fSVivien Didelot int err; 30398fc3c6fSVivien Didelot 30498fc3c6fSVivien Didelot err = mv88e6xxx_g2_eeprom_wait(chip); 30598fc3c6fSVivien Didelot if (err) 30698fc3c6fSVivien Didelot return err; 30798fc3c6fSVivien Didelot 30898fc3c6fSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_ADDR, addr); 30998fc3c6fSVivien Didelot if (err) 31098fc3c6fSVivien Didelot return err; 31198fc3c6fSVivien Didelot 31298fc3c6fSVivien Didelot err = mv88e6xxx_g2_eeprom_cmd(chip, cmd); 31398fc3c6fSVivien Didelot if (err) 31498fc3c6fSVivien Didelot return err; 31598fc3c6fSVivien Didelot 31698fc3c6fSVivien Didelot err = mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_CMD, &cmd); 31798fc3c6fSVivien Didelot if (err) 31898fc3c6fSVivien Didelot return err; 31998fc3c6fSVivien Didelot 32098fc3c6fSVivien Didelot *data = cmd & 0xff; 32198fc3c6fSVivien Didelot 32298fc3c6fSVivien Didelot return 0; 32398fc3c6fSVivien Didelot } 32498fc3c6fSVivien Didelot 32598fc3c6fSVivien Didelot static int mv88e6xxx_g2_eeprom_write8(struct mv88e6xxx_chip *chip, 32698fc3c6fSVivien Didelot u16 addr, u8 data) 32798fc3c6fSVivien Didelot { 32898fc3c6fSVivien Didelot u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | GLOBAL2_EEPROM_CMD_WRITE_EN; 32998fc3c6fSVivien Didelot int err; 33098fc3c6fSVivien Didelot 33198fc3c6fSVivien Didelot err = mv88e6xxx_g2_eeprom_wait(chip); 33298fc3c6fSVivien Didelot if (err) 33398fc3c6fSVivien Didelot return err; 33498fc3c6fSVivien Didelot 33598fc3c6fSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_ADDR, addr); 33698fc3c6fSVivien Didelot if (err) 33798fc3c6fSVivien Didelot return err; 33898fc3c6fSVivien Didelot 33998fc3c6fSVivien Didelot return mv88e6xxx_g2_eeprom_cmd(chip, cmd | data); 34098fc3c6fSVivien Didelot } 34198fc3c6fSVivien Didelot 342ec561276SVivien Didelot static int mv88e6xxx_g2_eeprom_read16(struct mv88e6xxx_chip *chip, 343ec561276SVivien Didelot u8 addr, u16 *data) 344ec561276SVivien Didelot { 345ec561276SVivien Didelot u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ | addr; 346ec561276SVivien Didelot int err; 347ec561276SVivien Didelot 348ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_wait(chip); 349ec561276SVivien Didelot if (err) 350ec561276SVivien Didelot return err; 351ec561276SVivien Didelot 352ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_cmd(chip, cmd); 353ec561276SVivien Didelot if (err) 354ec561276SVivien Didelot return err; 355ec561276SVivien Didelot 3569fe850fbSVivien Didelot return mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_DATA, data); 357ec561276SVivien Didelot } 358ec561276SVivien Didelot 359ec561276SVivien Didelot static int mv88e6xxx_g2_eeprom_write16(struct mv88e6xxx_chip *chip, 360ec561276SVivien Didelot u8 addr, u16 data) 361ec561276SVivien Didelot { 362ec561276SVivien Didelot u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | addr; 363ec561276SVivien Didelot int err; 364ec561276SVivien Didelot 365ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_wait(chip); 366ec561276SVivien Didelot if (err) 367ec561276SVivien Didelot return err; 368ec561276SVivien Didelot 3699fe850fbSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_DATA, data); 370ec561276SVivien Didelot if (err) 371ec561276SVivien Didelot return err; 372ec561276SVivien Didelot 373ec561276SVivien Didelot return mv88e6xxx_g2_eeprom_cmd(chip, cmd); 374ec561276SVivien Didelot } 375ec561276SVivien Didelot 37698fc3c6fSVivien Didelot int mv88e6xxx_g2_get_eeprom8(struct mv88e6xxx_chip *chip, 37798fc3c6fSVivien Didelot struct ethtool_eeprom *eeprom, u8 *data) 37898fc3c6fSVivien Didelot { 37998fc3c6fSVivien Didelot unsigned int offset = eeprom->offset; 38098fc3c6fSVivien Didelot unsigned int len = eeprom->len; 38198fc3c6fSVivien Didelot int err; 38298fc3c6fSVivien Didelot 38398fc3c6fSVivien Didelot eeprom->len = 0; 38498fc3c6fSVivien Didelot 38598fc3c6fSVivien Didelot while (len) { 38698fc3c6fSVivien Didelot err = mv88e6xxx_g2_eeprom_read8(chip, offset, data); 38798fc3c6fSVivien Didelot if (err) 38898fc3c6fSVivien Didelot return err; 38998fc3c6fSVivien Didelot 39098fc3c6fSVivien Didelot eeprom->len++; 39198fc3c6fSVivien Didelot offset++; 39298fc3c6fSVivien Didelot data++; 39398fc3c6fSVivien Didelot len--; 39498fc3c6fSVivien Didelot } 39598fc3c6fSVivien Didelot 39698fc3c6fSVivien Didelot return 0; 39798fc3c6fSVivien Didelot } 39898fc3c6fSVivien Didelot 39998fc3c6fSVivien Didelot int mv88e6xxx_g2_set_eeprom8(struct mv88e6xxx_chip *chip, 40098fc3c6fSVivien Didelot struct ethtool_eeprom *eeprom, u8 *data) 40198fc3c6fSVivien Didelot { 40298fc3c6fSVivien Didelot unsigned int offset = eeprom->offset; 40398fc3c6fSVivien Didelot unsigned int len = eeprom->len; 40498fc3c6fSVivien Didelot int err; 40598fc3c6fSVivien Didelot 40698fc3c6fSVivien Didelot eeprom->len = 0; 40798fc3c6fSVivien Didelot 40898fc3c6fSVivien Didelot while (len) { 40998fc3c6fSVivien Didelot err = mv88e6xxx_g2_eeprom_write8(chip, offset, *data); 41098fc3c6fSVivien Didelot if (err) 41198fc3c6fSVivien Didelot return err; 41298fc3c6fSVivien Didelot 41398fc3c6fSVivien Didelot eeprom->len++; 41498fc3c6fSVivien Didelot offset++; 41598fc3c6fSVivien Didelot data++; 41698fc3c6fSVivien Didelot len--; 41798fc3c6fSVivien Didelot } 41898fc3c6fSVivien Didelot 41998fc3c6fSVivien Didelot return 0; 42098fc3c6fSVivien Didelot } 42198fc3c6fSVivien Didelot 422ec561276SVivien Didelot int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip, 423ec561276SVivien Didelot struct ethtool_eeprom *eeprom, u8 *data) 424ec561276SVivien Didelot { 425ec561276SVivien Didelot unsigned int offset = eeprom->offset; 426ec561276SVivien Didelot unsigned int len = eeprom->len; 427ec561276SVivien Didelot u16 val; 428ec561276SVivien Didelot int err; 429ec561276SVivien Didelot 430ec561276SVivien Didelot eeprom->len = 0; 431ec561276SVivien Didelot 432ec561276SVivien Didelot if (offset & 1) { 433ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); 434ec561276SVivien Didelot if (err) 435ec561276SVivien Didelot return err; 436ec561276SVivien Didelot 437ec561276SVivien Didelot *data++ = (val >> 8) & 0xff; 438ec561276SVivien Didelot 439ec561276SVivien Didelot offset++; 440ec561276SVivien Didelot len--; 441ec561276SVivien Didelot eeprom->len++; 442ec561276SVivien Didelot } 443ec561276SVivien Didelot 444ec561276SVivien Didelot while (len >= 2) { 445ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); 446ec561276SVivien Didelot if (err) 447ec561276SVivien Didelot return err; 448ec561276SVivien Didelot 449ec561276SVivien Didelot *data++ = val & 0xff; 450ec561276SVivien Didelot *data++ = (val >> 8) & 0xff; 451ec561276SVivien Didelot 452ec561276SVivien Didelot offset += 2; 453ec561276SVivien Didelot len -= 2; 454ec561276SVivien Didelot eeprom->len += 2; 455ec561276SVivien Didelot } 456ec561276SVivien Didelot 457ec561276SVivien Didelot if (len) { 458ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); 459ec561276SVivien Didelot if (err) 460ec561276SVivien Didelot return err; 461ec561276SVivien Didelot 462ec561276SVivien Didelot *data++ = val & 0xff; 463ec561276SVivien Didelot 464ec561276SVivien Didelot offset++; 465ec561276SVivien Didelot len--; 466ec561276SVivien Didelot eeprom->len++; 467ec561276SVivien Didelot } 468ec561276SVivien Didelot 469ec561276SVivien Didelot return 0; 470ec561276SVivien Didelot } 471ec561276SVivien Didelot 472ec561276SVivien Didelot int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip, 473ec561276SVivien Didelot struct ethtool_eeprom *eeprom, u8 *data) 474ec561276SVivien Didelot { 475ec561276SVivien Didelot unsigned int offset = eeprom->offset; 476ec561276SVivien Didelot unsigned int len = eeprom->len; 477ec561276SVivien Didelot u16 val; 478ec561276SVivien Didelot int err; 479ec561276SVivien Didelot 480ec561276SVivien Didelot /* Ensure the RO WriteEn bit is set */ 4819fe850fbSVivien Didelot err = mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_CMD, &val); 482ec561276SVivien Didelot if (err) 483ec561276SVivien Didelot return err; 484ec561276SVivien Didelot 485ec561276SVivien Didelot if (!(val & GLOBAL2_EEPROM_CMD_WRITE_EN)) 486ec561276SVivien Didelot return -EROFS; 487ec561276SVivien Didelot 488ec561276SVivien Didelot eeprom->len = 0; 489ec561276SVivien Didelot 490ec561276SVivien Didelot if (offset & 1) { 491ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); 492ec561276SVivien Didelot if (err) 493ec561276SVivien Didelot return err; 494ec561276SVivien Didelot 495ec561276SVivien Didelot val = (*data++ << 8) | (val & 0xff); 496ec561276SVivien Didelot 497ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val); 498ec561276SVivien Didelot if (err) 499ec561276SVivien Didelot return err; 500ec561276SVivien Didelot 501ec561276SVivien Didelot offset++; 502ec561276SVivien Didelot len--; 503ec561276SVivien Didelot eeprom->len++; 504ec561276SVivien Didelot } 505ec561276SVivien Didelot 506ec561276SVivien Didelot while (len >= 2) { 507ec561276SVivien Didelot val = *data++; 508ec561276SVivien Didelot val |= *data++ << 8; 509ec561276SVivien Didelot 510ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val); 511ec561276SVivien Didelot if (err) 512ec561276SVivien Didelot return err; 513ec561276SVivien Didelot 514ec561276SVivien Didelot offset += 2; 515ec561276SVivien Didelot len -= 2; 516ec561276SVivien Didelot eeprom->len += 2; 517ec561276SVivien Didelot } 518ec561276SVivien Didelot 519ec561276SVivien Didelot if (len) { 520ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); 521ec561276SVivien Didelot if (err) 522ec561276SVivien Didelot return err; 523ec561276SVivien Didelot 524ec561276SVivien Didelot val = (val & 0xff00) | *data++; 525ec561276SVivien Didelot 526ec561276SVivien Didelot err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val); 527ec561276SVivien Didelot if (err) 528ec561276SVivien Didelot return err; 529ec561276SVivien Didelot 530ec561276SVivien Didelot offset++; 531ec561276SVivien Didelot len--; 532ec561276SVivien Didelot eeprom->len++; 533ec561276SVivien Didelot } 534ec561276SVivien Didelot 535ec561276SVivien Didelot return 0; 536ec561276SVivien Didelot } 537ec561276SVivien Didelot 538ec561276SVivien Didelot /* Offset 0x18: SMI PHY Command Register 539ec561276SVivien Didelot * Offset 0x19: SMI PHY Data Register 540ec561276SVivien Didelot */ 541ec561276SVivien Didelot 542ec561276SVivien Didelot static int mv88e6xxx_g2_smi_phy_wait(struct mv88e6xxx_chip *chip) 543ec561276SVivien Didelot { 544e289ef0dSVivien Didelot return mv88e6xxx_g2_wait(chip, MV88E6XXX_G2_SMI_PHY_CMD, 545e289ef0dSVivien Didelot MV88E6XXX_G2_SMI_PHY_CMD_BUSY); 546ec561276SVivien Didelot } 547ec561276SVivien Didelot 548ec561276SVivien Didelot static int mv88e6xxx_g2_smi_phy_cmd(struct mv88e6xxx_chip *chip, u16 cmd) 549ec561276SVivien Didelot { 550ec561276SVivien Didelot int err; 551ec561276SVivien Didelot 552e289ef0dSVivien Didelot err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_CMD, 553e289ef0dSVivien Didelot MV88E6XXX_G2_SMI_PHY_CMD_BUSY | cmd); 554ec561276SVivien Didelot if (err) 555ec561276SVivien Didelot return err; 556ec561276SVivien Didelot 557ec561276SVivien Didelot return mv88e6xxx_g2_smi_phy_wait(chip); 558ec561276SVivien Didelot } 559ec561276SVivien Didelot 560e289ef0dSVivien Didelot static int mv88e6xxx_g2_smi_phy_access(struct mv88e6xxx_chip *chip, 561e289ef0dSVivien Didelot bool external, bool c45, u16 op, int dev, 562e289ef0dSVivien Didelot int reg) 563ec561276SVivien Didelot { 564e289ef0dSVivien Didelot u16 cmd = op; 565ec561276SVivien Didelot 566cf3e80dfSAndrew Lunn if (external) 567e289ef0dSVivien Didelot cmd |= MV88E6390_G2_SMI_PHY_CMD_FUNC_EXTERNAL; 568e289ef0dSVivien Didelot else 569e289ef0dSVivien Didelot cmd |= MV88E6390_G2_SMI_PHY_CMD_FUNC_INTERNAL; /* empty mask */ 570cf3e80dfSAndrew Lunn 571e289ef0dSVivien Didelot if (c45) 572e289ef0dSVivien Didelot cmd |= MV88E6XXX_G2_SMI_PHY_CMD_MODE_45; /* empty mask */ 573e289ef0dSVivien Didelot else 574e289ef0dSVivien Didelot cmd |= MV88E6XXX_G2_SMI_PHY_CMD_MODE_22; 575cf3e80dfSAndrew Lunn 576e289ef0dSVivien Didelot dev <<= __bf_shf(MV88E6XXX_G2_SMI_PHY_CMD_DEV_ADDR_MASK); 577e289ef0dSVivien Didelot cmd |= dev & MV88E6XXX_G2_SMI_PHY_CMD_DEV_ADDR_MASK; 578e289ef0dSVivien Didelot cmd |= reg & MV88E6XXX_G2_SMI_PHY_CMD_REG_ADDR_MASK; 579cf3e80dfSAndrew Lunn 580cf3e80dfSAndrew Lunn return mv88e6xxx_g2_smi_phy_cmd(chip, cmd); 581cf3e80dfSAndrew Lunn } 582cf3e80dfSAndrew Lunn 583e289ef0dSVivien Didelot static int mv88e6xxx_g2_smi_phy_access_c22(struct mv88e6xxx_chip *chip, 584e289ef0dSVivien Didelot bool external, u16 op, int dev, 585e289ef0dSVivien Didelot int reg) 586cf3e80dfSAndrew Lunn { 587e289ef0dSVivien Didelot return mv88e6xxx_g2_smi_phy_access(chip, external, false, op, dev, reg); 588cf3e80dfSAndrew Lunn } 589cf3e80dfSAndrew Lunn 590e289ef0dSVivien Didelot /* IEEE 802.3 Clause 22 Read Data Register */ 591e289ef0dSVivien Didelot static int mv88e6xxx_g2_smi_phy_read_data_c22(struct mv88e6xxx_chip *chip, 592e289ef0dSVivien Didelot bool external, int dev, int reg, 593e289ef0dSVivien Didelot u16 *data) 594cf3e80dfSAndrew Lunn { 595e289ef0dSVivien Didelot u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_22_READ_DATA; 596cf3e80dfSAndrew Lunn int err; 597cf3e80dfSAndrew Lunn 598ec561276SVivien Didelot err = mv88e6xxx_g2_smi_phy_wait(chip); 599ec561276SVivien Didelot if (err) 600ec561276SVivien Didelot return err; 601ec561276SVivien Didelot 602e289ef0dSVivien Didelot err = mv88e6xxx_g2_smi_phy_access_c22(chip, external, op, dev, reg); 603ec561276SVivien Didelot if (err) 604ec561276SVivien Didelot return err; 605ec561276SVivien Didelot 606e289ef0dSVivien Didelot return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SMI_PHY_DATA, data); 607ec561276SVivien Didelot } 608ec561276SVivien Didelot 609e289ef0dSVivien Didelot /* IEEE 802.3 Clause 22 Write Data Register */ 610e289ef0dSVivien Didelot static int mv88e6xxx_g2_smi_phy_write_data_c22(struct mv88e6xxx_chip *chip, 611e289ef0dSVivien Didelot bool external, int dev, int reg, 612e289ef0dSVivien Didelot u16 data) 613e289ef0dSVivien Didelot { 614e289ef0dSVivien Didelot u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_22_WRITE_DATA; 615e289ef0dSVivien Didelot int err; 616e289ef0dSVivien Didelot 617e289ef0dSVivien Didelot err = mv88e6xxx_g2_smi_phy_wait(chip); 618e289ef0dSVivien Didelot if (err) 619e289ef0dSVivien Didelot return err; 620e289ef0dSVivien Didelot 621e289ef0dSVivien Didelot err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, data); 622e289ef0dSVivien Didelot if (err) 623e289ef0dSVivien Didelot return err; 624e289ef0dSVivien Didelot 625e289ef0dSVivien Didelot return mv88e6xxx_g2_smi_phy_access_c22(chip, external, op, dev, reg); 626e289ef0dSVivien Didelot } 627e289ef0dSVivien Didelot 628e289ef0dSVivien Didelot static int mv88e6xxx_g2_smi_phy_access_c45(struct mv88e6xxx_chip *chip, 629e289ef0dSVivien Didelot bool external, u16 op, int port, 630e289ef0dSVivien Didelot int dev) 631e289ef0dSVivien Didelot { 632e289ef0dSVivien Didelot return mv88e6xxx_g2_smi_phy_access(chip, external, true, op, port, dev); 633e289ef0dSVivien Didelot } 634e289ef0dSVivien Didelot 635e289ef0dSVivien Didelot /* IEEE 802.3 Clause 45 Write Address Register */ 636e289ef0dSVivien Didelot static int mv88e6xxx_g2_smi_phy_write_addr_c45(struct mv88e6xxx_chip *chip, 637e289ef0dSVivien Didelot bool external, int port, int dev, 638e289ef0dSVivien Didelot int addr) 639e289ef0dSVivien Didelot { 640e289ef0dSVivien Didelot u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_45_WRITE_ADDR; 641e289ef0dSVivien Didelot int err; 642e289ef0dSVivien Didelot 643e289ef0dSVivien Didelot err = mv88e6xxx_g2_smi_phy_wait(chip); 644e289ef0dSVivien Didelot if (err) 645e289ef0dSVivien Didelot return err; 646e289ef0dSVivien Didelot 647e289ef0dSVivien Didelot err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, addr); 648e289ef0dSVivien Didelot if (err) 649e289ef0dSVivien Didelot return err; 650e289ef0dSVivien Didelot 651e289ef0dSVivien Didelot return mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev); 652e289ef0dSVivien Didelot } 653e289ef0dSVivien Didelot 654e289ef0dSVivien Didelot /* IEEE 802.3 Clause 45 Read Data Register */ 655e289ef0dSVivien Didelot static int mv88e6xxx_g2_smi_phy_read_data_c45(struct mv88e6xxx_chip *chip, 656e289ef0dSVivien Didelot bool external, int port, int dev, 657e289ef0dSVivien Didelot u16 *data) 658e289ef0dSVivien Didelot { 659e289ef0dSVivien Didelot u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_45_READ_DATA; 660e289ef0dSVivien Didelot int err; 661e289ef0dSVivien Didelot 662e289ef0dSVivien Didelot err = mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev); 663e289ef0dSVivien Didelot if (err) 664e289ef0dSVivien Didelot return err; 665e289ef0dSVivien Didelot 666e289ef0dSVivien Didelot return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SMI_PHY_DATA, data); 667e289ef0dSVivien Didelot } 668e289ef0dSVivien Didelot 669e289ef0dSVivien Didelot static int mv88e6xxx_g2_smi_phy_read_c45(struct mv88e6xxx_chip *chip, 670e289ef0dSVivien Didelot bool external, int port, int reg, 671e289ef0dSVivien Didelot u16 *data) 672e289ef0dSVivien Didelot { 673e289ef0dSVivien Didelot int dev = (reg >> 16) & 0x1f; 674e289ef0dSVivien Didelot int addr = reg & 0xffff; 675e289ef0dSVivien Didelot int err; 676e289ef0dSVivien Didelot 677e289ef0dSVivien Didelot err = mv88e6xxx_g2_smi_phy_write_addr_c45(chip, external, port, dev, 678e289ef0dSVivien Didelot addr); 679e289ef0dSVivien Didelot if (err) 680e289ef0dSVivien Didelot return err; 681e289ef0dSVivien Didelot 682e289ef0dSVivien Didelot return mv88e6xxx_g2_smi_phy_read_data_c45(chip, external, port, dev, 683e289ef0dSVivien Didelot data); 684e289ef0dSVivien Didelot } 685e289ef0dSVivien Didelot 686e289ef0dSVivien Didelot /* IEEE 802.3 Clause 45 Write Data Register */ 687e289ef0dSVivien Didelot static int mv88e6xxx_g2_smi_phy_write_data_c45(struct mv88e6xxx_chip *chip, 688e289ef0dSVivien Didelot bool external, int port, int dev, 689e289ef0dSVivien Didelot u16 data) 690e289ef0dSVivien Didelot { 691e289ef0dSVivien Didelot u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_45_WRITE_DATA; 692e289ef0dSVivien Didelot int err; 693e289ef0dSVivien Didelot 694e289ef0dSVivien Didelot err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, data); 695e289ef0dSVivien Didelot if (err) 696e289ef0dSVivien Didelot return err; 697e289ef0dSVivien Didelot 698e289ef0dSVivien Didelot return mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev); 699e289ef0dSVivien Didelot } 700e289ef0dSVivien Didelot 701e289ef0dSVivien Didelot static int mv88e6xxx_g2_smi_phy_write_c45(struct mv88e6xxx_chip *chip, 702e289ef0dSVivien Didelot bool external, int port, int reg, 703e289ef0dSVivien Didelot u16 data) 704e289ef0dSVivien Didelot { 705e289ef0dSVivien Didelot int dev = (reg >> 16) & 0x1f; 706e289ef0dSVivien Didelot int addr = reg & 0xffff; 707e289ef0dSVivien Didelot int err; 708e289ef0dSVivien Didelot 709e289ef0dSVivien Didelot err = mv88e6xxx_g2_smi_phy_write_addr_c45(chip, external, port, dev, 710e289ef0dSVivien Didelot addr); 711e289ef0dSVivien Didelot if (err) 712e289ef0dSVivien Didelot return err; 713e289ef0dSVivien Didelot 714e289ef0dSVivien Didelot return mv88e6xxx_g2_smi_phy_write_data_c45(chip, external, port, dev, 715e289ef0dSVivien Didelot data); 716e289ef0dSVivien Didelot } 717e289ef0dSVivien Didelot 718e289ef0dSVivien Didelot int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus, 719cf3e80dfSAndrew Lunn int addr, int reg, u16 *val) 720cf3e80dfSAndrew Lunn { 721cf3e80dfSAndrew Lunn struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv; 722cf3e80dfSAndrew Lunn bool external = mdio_bus->external; 723cf3e80dfSAndrew Lunn 724cf3e80dfSAndrew Lunn if (reg & MII_ADDR_C45) 725e289ef0dSVivien Didelot return mv88e6xxx_g2_smi_phy_read_c45(chip, external, addr, reg, 726e289ef0dSVivien Didelot val); 727e289ef0dSVivien Didelot 728e289ef0dSVivien Didelot return mv88e6xxx_g2_smi_phy_read_data_c22(chip, external, addr, reg, 729e289ef0dSVivien Didelot val); 730cf3e80dfSAndrew Lunn } 731cf3e80dfSAndrew Lunn 732e289ef0dSVivien Didelot int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus, 733cf3e80dfSAndrew Lunn int addr, int reg, u16 val) 734cf3e80dfSAndrew Lunn { 735cf3e80dfSAndrew Lunn struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv; 736cf3e80dfSAndrew Lunn bool external = mdio_bus->external; 737cf3e80dfSAndrew Lunn 738cf3e80dfSAndrew Lunn if (reg & MII_ADDR_C45) 739e289ef0dSVivien Didelot return mv88e6xxx_g2_smi_phy_write_c45(chip, external, addr, reg, 740e289ef0dSVivien Didelot val); 741cf3e80dfSAndrew Lunn 742e289ef0dSVivien Didelot return mv88e6xxx_g2_smi_phy_write_data_c22(chip, external, addr, reg, 743e289ef0dSVivien Didelot val); 744cf3e80dfSAndrew Lunn } 745cf3e80dfSAndrew Lunn 746fcd25166SAndrew Lunn static int mv88e6097_watchdog_action(struct mv88e6xxx_chip *chip, int irq) 747fcd25166SAndrew Lunn { 748fcd25166SAndrew Lunn u16 reg; 749fcd25166SAndrew Lunn 750fcd25166SAndrew Lunn mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, ®); 751fcd25166SAndrew Lunn 752fcd25166SAndrew Lunn dev_info(chip->dev, "Watchdog event: 0x%04x", reg); 753fcd25166SAndrew Lunn 754fcd25166SAndrew Lunn return IRQ_HANDLED; 755fcd25166SAndrew Lunn } 756fcd25166SAndrew Lunn 757fcd25166SAndrew Lunn static void mv88e6097_watchdog_free(struct mv88e6xxx_chip *chip) 758fcd25166SAndrew Lunn { 759fcd25166SAndrew Lunn u16 reg; 760fcd25166SAndrew Lunn 761fcd25166SAndrew Lunn mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, ®); 762fcd25166SAndrew Lunn 763fcd25166SAndrew Lunn reg &= ~(GLOBAL2_WDOG_CONTROL_EGRESS_ENABLE | 764fcd25166SAndrew Lunn GLOBAL2_WDOG_CONTROL_QC_ENABLE); 765fcd25166SAndrew Lunn 766fcd25166SAndrew Lunn mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, reg); 767fcd25166SAndrew Lunn } 768fcd25166SAndrew Lunn 769fcd25166SAndrew Lunn static int mv88e6097_watchdog_setup(struct mv88e6xxx_chip *chip) 770fcd25166SAndrew Lunn { 771fcd25166SAndrew Lunn return mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, 772fcd25166SAndrew Lunn GLOBAL2_WDOG_CONTROL_EGRESS_ENABLE | 773fcd25166SAndrew Lunn GLOBAL2_WDOG_CONTROL_QC_ENABLE | 774fcd25166SAndrew Lunn GLOBAL2_WDOG_CONTROL_SWRESET); 775fcd25166SAndrew Lunn } 776fcd25166SAndrew Lunn 777fcd25166SAndrew Lunn const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = { 778fcd25166SAndrew Lunn .irq_action = mv88e6097_watchdog_action, 779fcd25166SAndrew Lunn .irq_setup = mv88e6097_watchdog_setup, 780fcd25166SAndrew Lunn .irq_free = mv88e6097_watchdog_free, 781fcd25166SAndrew Lunn }; 782fcd25166SAndrew Lunn 78361303736SAndrew Lunn static int mv88e6390_watchdog_setup(struct mv88e6xxx_chip *chip) 78461303736SAndrew Lunn { 78561303736SAndrew Lunn return mv88e6xxx_g2_update(chip, GLOBAL2_WDOG_CONTROL, 78661303736SAndrew Lunn GLOBAL2_WDOG_INT_ENABLE | 78761303736SAndrew Lunn GLOBAL2_WDOG_CUT_THROUGH | 78861303736SAndrew Lunn GLOBAL2_WDOG_QUEUE_CONTROLLER | 78961303736SAndrew Lunn GLOBAL2_WDOG_EGRESS | 79061303736SAndrew Lunn GLOBAL2_WDOG_FORCE_IRQ); 79161303736SAndrew Lunn } 79261303736SAndrew Lunn 79361303736SAndrew Lunn static int mv88e6390_watchdog_action(struct mv88e6xxx_chip *chip, int irq) 79461303736SAndrew Lunn { 79561303736SAndrew Lunn int err; 79661303736SAndrew Lunn u16 reg; 79761303736SAndrew Lunn 79861303736SAndrew Lunn mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, GLOBAL2_WDOG_EVENT); 79961303736SAndrew Lunn err = mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, ®); 80061303736SAndrew Lunn 80161303736SAndrew Lunn dev_info(chip->dev, "Watchdog event: 0x%04x", 80261303736SAndrew Lunn reg & GLOBAL2_WDOG_DATA_MASK); 80361303736SAndrew Lunn 80461303736SAndrew Lunn mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, GLOBAL2_WDOG_HISTORY); 80561303736SAndrew Lunn err = mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, ®); 80661303736SAndrew Lunn 80761303736SAndrew Lunn dev_info(chip->dev, "Watchdog history: 0x%04x", 80861303736SAndrew Lunn reg & GLOBAL2_WDOG_DATA_MASK); 80961303736SAndrew Lunn 81061303736SAndrew Lunn /* Trigger a software reset to try to recover the switch */ 81161303736SAndrew Lunn if (chip->info->ops->reset) 81261303736SAndrew Lunn chip->info->ops->reset(chip); 81361303736SAndrew Lunn 81461303736SAndrew Lunn mv88e6390_watchdog_setup(chip); 81561303736SAndrew Lunn 81661303736SAndrew Lunn return IRQ_HANDLED; 81761303736SAndrew Lunn } 81861303736SAndrew Lunn 81961303736SAndrew Lunn static void mv88e6390_watchdog_free(struct mv88e6xxx_chip *chip) 82061303736SAndrew Lunn { 82161303736SAndrew Lunn mv88e6xxx_g2_update(chip, GLOBAL2_WDOG_CONTROL, 82261303736SAndrew Lunn GLOBAL2_WDOG_INT_ENABLE); 82361303736SAndrew Lunn } 82461303736SAndrew Lunn 82561303736SAndrew Lunn const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = { 82661303736SAndrew Lunn .irq_action = mv88e6390_watchdog_action, 82761303736SAndrew Lunn .irq_setup = mv88e6390_watchdog_setup, 82861303736SAndrew Lunn .irq_free = mv88e6390_watchdog_free, 82961303736SAndrew Lunn }; 83061303736SAndrew Lunn 831fcd25166SAndrew Lunn static irqreturn_t mv88e6xxx_g2_watchdog_thread_fn(int irq, void *dev_id) 832fcd25166SAndrew Lunn { 833fcd25166SAndrew Lunn struct mv88e6xxx_chip *chip = dev_id; 834fcd25166SAndrew Lunn irqreturn_t ret = IRQ_NONE; 835fcd25166SAndrew Lunn 836fcd25166SAndrew Lunn mutex_lock(&chip->reg_lock); 837fcd25166SAndrew Lunn if (chip->info->ops->watchdog_ops->irq_action) 838fcd25166SAndrew Lunn ret = chip->info->ops->watchdog_ops->irq_action(chip, irq); 839fcd25166SAndrew Lunn mutex_unlock(&chip->reg_lock); 840fcd25166SAndrew Lunn 841fcd25166SAndrew Lunn return ret; 842fcd25166SAndrew Lunn } 843fcd25166SAndrew Lunn 844fcd25166SAndrew Lunn static void mv88e6xxx_g2_watchdog_free(struct mv88e6xxx_chip *chip) 845fcd25166SAndrew Lunn { 846fcd25166SAndrew Lunn mutex_lock(&chip->reg_lock); 847fcd25166SAndrew Lunn if (chip->info->ops->watchdog_ops->irq_free) 848fcd25166SAndrew Lunn chip->info->ops->watchdog_ops->irq_free(chip); 849fcd25166SAndrew Lunn mutex_unlock(&chip->reg_lock); 850fcd25166SAndrew Lunn 851fcd25166SAndrew Lunn free_irq(chip->watchdog_irq, chip); 852fcd25166SAndrew Lunn irq_dispose_mapping(chip->watchdog_irq); 853fcd25166SAndrew Lunn } 854fcd25166SAndrew Lunn 855fcd25166SAndrew Lunn static int mv88e6xxx_g2_watchdog_setup(struct mv88e6xxx_chip *chip) 856fcd25166SAndrew Lunn { 857fcd25166SAndrew Lunn int err; 858fcd25166SAndrew Lunn 859fcd25166SAndrew Lunn chip->watchdog_irq = irq_find_mapping(chip->g2_irq.domain, 860fcd25166SAndrew Lunn GLOBAL2_INT_SOURCE_WATCHDOG); 861fcd25166SAndrew Lunn if (chip->watchdog_irq < 0) 862fcd25166SAndrew Lunn return chip->watchdog_irq; 863fcd25166SAndrew Lunn 864fcd25166SAndrew Lunn err = request_threaded_irq(chip->watchdog_irq, NULL, 865fcd25166SAndrew Lunn mv88e6xxx_g2_watchdog_thread_fn, 866fcd25166SAndrew Lunn IRQF_ONESHOT | IRQF_TRIGGER_FALLING, 867fcd25166SAndrew Lunn "mv88e6xxx-watchdog", chip); 868fcd25166SAndrew Lunn if (err) 869fcd25166SAndrew Lunn return err; 870fcd25166SAndrew Lunn 871fcd25166SAndrew Lunn mutex_lock(&chip->reg_lock); 872fcd25166SAndrew Lunn if (chip->info->ops->watchdog_ops->irq_setup) 873fcd25166SAndrew Lunn err = chip->info->ops->watchdog_ops->irq_setup(chip); 874fcd25166SAndrew Lunn mutex_unlock(&chip->reg_lock); 875fcd25166SAndrew Lunn 876fcd25166SAndrew Lunn return err; 877fcd25166SAndrew Lunn } 878fcd25166SAndrew Lunn 87981228996SVivien Didelot /* Offset 0x1D: Misc Register */ 88081228996SVivien Didelot 88181228996SVivien Didelot static int mv88e6xxx_g2_misc_5_bit_port(struct mv88e6xxx_chip *chip, 88281228996SVivien Didelot bool port_5_bit) 88381228996SVivien Didelot { 88481228996SVivien Didelot u16 val; 88581228996SVivien Didelot int err; 88681228996SVivien Didelot 88781228996SVivien Didelot err = mv88e6xxx_g2_read(chip, GLOBAL2_MISC, &val); 88881228996SVivien Didelot if (err) 88981228996SVivien Didelot return err; 89081228996SVivien Didelot 89181228996SVivien Didelot if (port_5_bit) 89281228996SVivien Didelot val |= GLOBAL2_MISC_5_BIT_PORT; 89381228996SVivien Didelot else 89481228996SVivien Didelot val &= ~GLOBAL2_MISC_5_BIT_PORT; 89581228996SVivien Didelot 89681228996SVivien Didelot return mv88e6xxx_g2_write(chip, GLOBAL2_MISC, val); 89781228996SVivien Didelot } 89881228996SVivien Didelot 89981228996SVivien Didelot int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip) 90081228996SVivien Didelot { 90181228996SVivien Didelot return mv88e6xxx_g2_misc_5_bit_port(chip, false); 90281228996SVivien Didelot } 90381228996SVivien Didelot 904dc30c35bSAndrew Lunn static void mv88e6xxx_g2_irq_mask(struct irq_data *d) 905dc30c35bSAndrew Lunn { 906dc30c35bSAndrew Lunn struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); 907dc30c35bSAndrew Lunn unsigned int n = d->hwirq; 908dc30c35bSAndrew Lunn 909dc30c35bSAndrew Lunn chip->g2_irq.masked |= (1 << n); 910dc30c35bSAndrew Lunn } 911dc30c35bSAndrew Lunn 912dc30c35bSAndrew Lunn static void mv88e6xxx_g2_irq_unmask(struct irq_data *d) 913dc30c35bSAndrew Lunn { 914dc30c35bSAndrew Lunn struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); 915dc30c35bSAndrew Lunn unsigned int n = d->hwirq; 916dc30c35bSAndrew Lunn 917dc30c35bSAndrew Lunn chip->g2_irq.masked &= ~(1 << n); 918dc30c35bSAndrew Lunn } 919dc30c35bSAndrew Lunn 920dc30c35bSAndrew Lunn static irqreturn_t mv88e6xxx_g2_irq_thread_fn(int irq, void *dev_id) 921dc30c35bSAndrew Lunn { 922dc30c35bSAndrew Lunn struct mv88e6xxx_chip *chip = dev_id; 923dc30c35bSAndrew Lunn unsigned int nhandled = 0; 924dc30c35bSAndrew Lunn unsigned int sub_irq; 925dc30c35bSAndrew Lunn unsigned int n; 926dc30c35bSAndrew Lunn int err; 927dc30c35bSAndrew Lunn u16 reg; 928dc30c35bSAndrew Lunn 929dc30c35bSAndrew Lunn mutex_lock(&chip->reg_lock); 930dc30c35bSAndrew Lunn err = mv88e6xxx_g2_read(chip, GLOBAL2_INT_SOURCE, ®); 931dc30c35bSAndrew Lunn mutex_unlock(&chip->reg_lock); 932dc30c35bSAndrew Lunn if (err) 933dc30c35bSAndrew Lunn goto out; 934dc30c35bSAndrew Lunn 935dc30c35bSAndrew Lunn for (n = 0; n < 16; ++n) { 936dc30c35bSAndrew Lunn if (reg & (1 << n)) { 937dc30c35bSAndrew Lunn sub_irq = irq_find_mapping(chip->g2_irq.domain, n); 938dc30c35bSAndrew Lunn handle_nested_irq(sub_irq); 939dc30c35bSAndrew Lunn ++nhandled; 940dc30c35bSAndrew Lunn } 941dc30c35bSAndrew Lunn } 942dc30c35bSAndrew Lunn out: 943dc30c35bSAndrew Lunn return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE); 944dc30c35bSAndrew Lunn } 945dc30c35bSAndrew Lunn 946dc30c35bSAndrew Lunn static void mv88e6xxx_g2_irq_bus_lock(struct irq_data *d) 947dc30c35bSAndrew Lunn { 948dc30c35bSAndrew Lunn struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); 949dc30c35bSAndrew Lunn 950dc30c35bSAndrew Lunn mutex_lock(&chip->reg_lock); 951dc30c35bSAndrew Lunn } 952dc30c35bSAndrew Lunn 953dc30c35bSAndrew Lunn static void mv88e6xxx_g2_irq_bus_sync_unlock(struct irq_data *d) 954dc30c35bSAndrew Lunn { 955dc30c35bSAndrew Lunn struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); 956dc30c35bSAndrew Lunn 957dc30c35bSAndrew Lunn mv88e6xxx_g2_write(chip, GLOBAL2_INT_MASK, ~chip->g2_irq.masked); 958dc30c35bSAndrew Lunn 959dc30c35bSAndrew Lunn mutex_unlock(&chip->reg_lock); 960dc30c35bSAndrew Lunn } 961dc30c35bSAndrew Lunn 962dc30c35bSAndrew Lunn static struct irq_chip mv88e6xxx_g2_irq_chip = { 963dc30c35bSAndrew Lunn .name = "mv88e6xxx-g2", 964dc30c35bSAndrew Lunn .irq_mask = mv88e6xxx_g2_irq_mask, 965dc30c35bSAndrew Lunn .irq_unmask = mv88e6xxx_g2_irq_unmask, 966dc30c35bSAndrew Lunn .irq_bus_lock = mv88e6xxx_g2_irq_bus_lock, 967dc30c35bSAndrew Lunn .irq_bus_sync_unlock = mv88e6xxx_g2_irq_bus_sync_unlock, 968dc30c35bSAndrew Lunn }; 969dc30c35bSAndrew Lunn 970dc30c35bSAndrew Lunn static int mv88e6xxx_g2_irq_domain_map(struct irq_domain *d, 971dc30c35bSAndrew Lunn unsigned int irq, 972dc30c35bSAndrew Lunn irq_hw_number_t hwirq) 973dc30c35bSAndrew Lunn { 974dc30c35bSAndrew Lunn struct mv88e6xxx_chip *chip = d->host_data; 975dc30c35bSAndrew Lunn 976dc30c35bSAndrew Lunn irq_set_chip_data(irq, d->host_data); 977dc30c35bSAndrew Lunn irq_set_chip_and_handler(irq, &chip->g2_irq.chip, handle_level_irq); 978dc30c35bSAndrew Lunn irq_set_noprobe(irq); 979dc30c35bSAndrew Lunn 980dc30c35bSAndrew Lunn return 0; 981dc30c35bSAndrew Lunn } 982dc30c35bSAndrew Lunn 983dc30c35bSAndrew Lunn static const struct irq_domain_ops mv88e6xxx_g2_irq_domain_ops = { 984dc30c35bSAndrew Lunn .map = mv88e6xxx_g2_irq_domain_map, 985dc30c35bSAndrew Lunn .xlate = irq_domain_xlate_twocell, 986dc30c35bSAndrew Lunn }; 987dc30c35bSAndrew Lunn 988dc30c35bSAndrew Lunn void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip) 989dc30c35bSAndrew Lunn { 990dc30c35bSAndrew Lunn int irq, virq; 991dc30c35bSAndrew Lunn 992fcd25166SAndrew Lunn mv88e6xxx_g2_watchdog_free(chip); 993fcd25166SAndrew Lunn 9948e757ebaSAndrew Lunn free_irq(chip->device_irq, chip); 9958e757ebaSAndrew Lunn irq_dispose_mapping(chip->device_irq); 9968e757ebaSAndrew Lunn 997dc30c35bSAndrew Lunn for (irq = 0; irq < 16; irq++) { 998dc30c35bSAndrew Lunn virq = irq_find_mapping(chip->g2_irq.domain, irq); 999dc30c35bSAndrew Lunn irq_dispose_mapping(virq); 1000dc30c35bSAndrew Lunn } 1001dc30c35bSAndrew Lunn 1002dc30c35bSAndrew Lunn irq_domain_remove(chip->g2_irq.domain); 1003dc30c35bSAndrew Lunn } 1004dc30c35bSAndrew Lunn 1005dc30c35bSAndrew Lunn int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip) 1006dc30c35bSAndrew Lunn { 10078e757ebaSAndrew Lunn int err, irq, virq; 1008dc30c35bSAndrew Lunn 1009dc30c35bSAndrew Lunn if (!chip->dev->of_node) 1010dc30c35bSAndrew Lunn return -EINVAL; 1011dc30c35bSAndrew Lunn 1012dc30c35bSAndrew Lunn chip->g2_irq.domain = irq_domain_add_simple( 1013dc30c35bSAndrew Lunn chip->dev->of_node, 16, 0, &mv88e6xxx_g2_irq_domain_ops, chip); 1014dc30c35bSAndrew Lunn if (!chip->g2_irq.domain) 1015dc30c35bSAndrew Lunn return -ENOMEM; 1016dc30c35bSAndrew Lunn 1017dc30c35bSAndrew Lunn for (irq = 0; irq < 16; irq++) 1018dc30c35bSAndrew Lunn irq_create_mapping(chip->g2_irq.domain, irq); 1019dc30c35bSAndrew Lunn 1020dc30c35bSAndrew Lunn chip->g2_irq.chip = mv88e6xxx_g2_irq_chip; 1021dc30c35bSAndrew Lunn chip->g2_irq.masked = ~0; 1022dc30c35bSAndrew Lunn 10238e757ebaSAndrew Lunn chip->device_irq = irq_find_mapping(chip->g1_irq.domain, 102482466921SVivien Didelot MV88E6XXX_G1_STS_IRQ_DEVICE); 10258e757ebaSAndrew Lunn if (chip->device_irq < 0) { 10268e757ebaSAndrew Lunn err = chip->device_irq; 1027dc30c35bSAndrew Lunn goto out; 1028dc30c35bSAndrew Lunn } 1029dc30c35bSAndrew Lunn 10308e757ebaSAndrew Lunn err = request_threaded_irq(chip->device_irq, NULL, 1031dc30c35bSAndrew Lunn mv88e6xxx_g2_irq_thread_fn, 1032dc30c35bSAndrew Lunn IRQF_ONESHOT, "mv88e6xxx-g1", chip); 1033dc30c35bSAndrew Lunn if (err) 1034dc30c35bSAndrew Lunn goto out; 1035dc30c35bSAndrew Lunn 1036fcd25166SAndrew Lunn return mv88e6xxx_g2_watchdog_setup(chip); 10378e757ebaSAndrew Lunn 1038dc30c35bSAndrew Lunn out: 10398e757ebaSAndrew Lunn for (irq = 0; irq < 16; irq++) { 10408e757ebaSAndrew Lunn virq = irq_find_mapping(chip->g2_irq.domain, irq); 10418e757ebaSAndrew Lunn irq_dispose_mapping(virq); 10428e757ebaSAndrew Lunn } 10438e757ebaSAndrew Lunn 10448e757ebaSAndrew Lunn irq_domain_remove(chip->g2_irq.domain); 1045dc30c35bSAndrew Lunn 1046dc30c35bSAndrew Lunn return err; 1047dc30c35bSAndrew Lunn } 1048dc30c35bSAndrew Lunn 1049ec561276SVivien Didelot int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip) 1050ec561276SVivien Didelot { 1051ec561276SVivien Didelot u16 reg; 1052ec561276SVivien Didelot int err; 1053ec561276SVivien Didelot 1054ec561276SVivien Didelot /* Ignore removed tag data on doubly tagged packets, disable 1055ec561276SVivien Didelot * flow control messages, force flow control priority to the 1056ec561276SVivien Didelot * highest, and send all special multicast frames to the CPU 1057ec561276SVivien Didelot * port at the highest priority. 1058ec561276SVivien Didelot */ 1059ec561276SVivien Didelot reg = GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI | (0x7 << 4); 1060ec561276SVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X) || 1061ec561276SVivien Didelot mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X)) 1062ec561276SVivien Didelot reg |= GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x7; 10639fe850fbSVivien Didelot err = mv88e6xxx_g2_write(chip, GLOBAL2_SWITCH_MGMT, reg); 1064ec561276SVivien Didelot if (err) 1065ec561276SVivien Didelot return err; 1066ec561276SVivien Didelot 1067ec561276SVivien Didelot /* Program the DSA routing table. */ 1068ec561276SVivien Didelot err = mv88e6xxx_g2_set_device_mapping(chip); 1069ec561276SVivien Didelot if (err) 1070ec561276SVivien Didelot return err; 1071ec561276SVivien Didelot 1072ec561276SVivien Didelot /* Clear all trunk masks and mapping. */ 1073ec561276SVivien Didelot err = mv88e6xxx_g2_clear_trunk(chip); 1074ec561276SVivien Didelot if (err) 1075ec561276SVivien Didelot return err; 1076ec561276SVivien Didelot 1077ec561276SVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_POT)) { 1078ec561276SVivien Didelot /* Clear the priority override table. */ 1079ec561276SVivien Didelot err = mv88e6xxx_g2_clear_pot(chip); 1080ec561276SVivien Didelot if (err) 1081ec561276SVivien Didelot return err; 1082ec561276SVivien Didelot } 1083ec561276SVivien Didelot 1084ec561276SVivien Didelot return 0; 1085ec561276SVivien Didelot } 1086