12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 26d91782fSAndrew Lunn /* 36d91782fSAndrew Lunn * Marvell 88E6xxx SERDES manipulation, via SMI bus 46d91782fSAndrew Lunn * 56d91782fSAndrew Lunn * Copyright (c) 2008 Marvell Semiconductor 66d91782fSAndrew Lunn * 76d91782fSAndrew Lunn * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch> 86d91782fSAndrew Lunn */ 96d91782fSAndrew Lunn 10efd1ba6aSAndrew Lunn #include <linux/interrupt.h> 11efd1ba6aSAndrew Lunn #include <linux/irqdomain.h> 126d91782fSAndrew Lunn #include <linux/mii.h> 136d91782fSAndrew Lunn 144d5f2ba7SVivien Didelot #include "chip.h" 156335e9f2SAndrew Lunn #include "global2.h" 166d91782fSAndrew Lunn #include "phy.h" 176d91782fSAndrew Lunn #include "port.h" 186d91782fSAndrew Lunn #include "serdes.h" 196d91782fSAndrew Lunn 206d91782fSAndrew Lunn static int mv88e6352_serdes_read(struct mv88e6xxx_chip *chip, int reg, 216d91782fSAndrew Lunn u16 *val) 226d91782fSAndrew Lunn { 236d91782fSAndrew Lunn return mv88e6xxx_phy_page_read(chip, MV88E6352_ADDR_SERDES, 246d91782fSAndrew Lunn MV88E6352_SERDES_PAGE_FIBER, 256d91782fSAndrew Lunn reg, val); 266d91782fSAndrew Lunn } 276d91782fSAndrew Lunn 286d91782fSAndrew Lunn static int mv88e6352_serdes_write(struct mv88e6xxx_chip *chip, int reg, 296d91782fSAndrew Lunn u16 val) 306d91782fSAndrew Lunn { 316d91782fSAndrew Lunn return mv88e6xxx_phy_page_write(chip, MV88E6352_ADDR_SERDES, 326d91782fSAndrew Lunn MV88E6352_SERDES_PAGE_FIBER, 336d91782fSAndrew Lunn reg, val); 346d91782fSAndrew Lunn } 356d91782fSAndrew Lunn 36e6891c76SAndrew Lunn static int mv88e6390_serdes_read(struct mv88e6xxx_chip *chip, 37e6891c76SAndrew Lunn int lane, int device, int reg, u16 *val) 38e6891c76SAndrew Lunn { 39e6891c76SAndrew Lunn int reg_c45 = MII_ADDR_C45 | device << 16 | reg; 40e6891c76SAndrew Lunn 41e6891c76SAndrew Lunn return mv88e6xxx_phy_read(chip, lane, reg_c45, val); 42e6891c76SAndrew Lunn } 43e6891c76SAndrew Lunn 44e6891c76SAndrew Lunn static int mv88e6390_serdes_write(struct mv88e6xxx_chip *chip, 45e6891c76SAndrew Lunn int lane, int device, int reg, u16 val) 46e6891c76SAndrew Lunn { 47e6891c76SAndrew Lunn int reg_c45 = MII_ADDR_C45 | device << 16 | reg; 48e6891c76SAndrew Lunn 49e6891c76SAndrew Lunn return mv88e6xxx_phy_write(chip, lane, reg_c45, val); 50e6891c76SAndrew Lunn } 51e6891c76SAndrew Lunn 526d91782fSAndrew Lunn static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on) 536d91782fSAndrew Lunn { 546d91782fSAndrew Lunn u16 val, new_val; 556d91782fSAndrew Lunn int err; 566d91782fSAndrew Lunn 576d91782fSAndrew Lunn err = mv88e6352_serdes_read(chip, MII_BMCR, &val); 586d91782fSAndrew Lunn if (err) 596d91782fSAndrew Lunn return err; 606d91782fSAndrew Lunn 616d91782fSAndrew Lunn if (on) 626d91782fSAndrew Lunn new_val = val & ~BMCR_PDOWN; 636d91782fSAndrew Lunn else 646d91782fSAndrew Lunn new_val = val | BMCR_PDOWN; 656d91782fSAndrew Lunn 666d91782fSAndrew Lunn if (val != new_val) 676d91782fSAndrew Lunn err = mv88e6352_serdes_write(chip, MII_BMCR, new_val); 686d91782fSAndrew Lunn 696d91782fSAndrew Lunn return err; 706d91782fSAndrew Lunn } 716d91782fSAndrew Lunn 72eb755c3fSAndrew Lunn static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port) 736d91782fSAndrew Lunn { 742d2e1dd2SAndrew Lunn u8 cmode = chip->ports[port].cmode; 756d91782fSAndrew Lunn 765f83dc93SVivien Didelot if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASE_X) || 775f83dc93SVivien Didelot (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) || 78eb755c3fSAndrew Lunn (cmode == MV88E6XXX_PORT_STS_CMODE_SGMII)) 79b1312b85SFengguang Wu return true; 80eb755c3fSAndrew Lunn 81b1312b85SFengguang Wu return false; 82eb755c3fSAndrew Lunn } 83eb755c3fSAndrew Lunn 84eb755c3fSAndrew Lunn int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) 85eb755c3fSAndrew Lunn { 86eb755c3fSAndrew Lunn int err; 87eb755c3fSAndrew Lunn 88eb755c3fSAndrew Lunn if (mv88e6352_port_has_serdes(chip, port)) { 896d91782fSAndrew Lunn err = mv88e6352_serdes_power_set(chip, on); 906d91782fSAndrew Lunn if (err < 0) 916d91782fSAndrew Lunn return err; 926d91782fSAndrew Lunn } 936d91782fSAndrew Lunn 946d91782fSAndrew Lunn return 0; 956d91782fSAndrew Lunn } 966335e9f2SAndrew Lunn 97cda9f4aaSAndrew Lunn struct mv88e6352_serdes_hw_stat { 98cda9f4aaSAndrew Lunn char string[ETH_GSTRING_LEN]; 99cda9f4aaSAndrew Lunn int sizeof_stat; 100cda9f4aaSAndrew Lunn int reg; 101cda9f4aaSAndrew Lunn }; 102cda9f4aaSAndrew Lunn 103cda9f4aaSAndrew Lunn static struct mv88e6352_serdes_hw_stat mv88e6352_serdes_hw_stats[] = { 104cda9f4aaSAndrew Lunn { "serdes_fibre_rx_error", 16, 21 }, 105cda9f4aaSAndrew Lunn { "serdes_PRBS_error", 32, 24 }, 106cda9f4aaSAndrew Lunn }; 107cda9f4aaSAndrew Lunn 108cda9f4aaSAndrew Lunn int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port) 109cda9f4aaSAndrew Lunn { 110cda9f4aaSAndrew Lunn if (mv88e6352_port_has_serdes(chip, port)) 111cda9f4aaSAndrew Lunn return ARRAY_SIZE(mv88e6352_serdes_hw_stats); 112cda9f4aaSAndrew Lunn 113cda9f4aaSAndrew Lunn return 0; 114cda9f4aaSAndrew Lunn } 115cda9f4aaSAndrew Lunn 11665f60e45SAndrew Lunn int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip, 117cda9f4aaSAndrew Lunn int port, uint8_t *data) 118cda9f4aaSAndrew Lunn { 119cda9f4aaSAndrew Lunn struct mv88e6352_serdes_hw_stat *stat; 120cda9f4aaSAndrew Lunn int i; 121cda9f4aaSAndrew Lunn 122cda9f4aaSAndrew Lunn if (!mv88e6352_port_has_serdes(chip, port)) 12365f60e45SAndrew Lunn return 0; 124cda9f4aaSAndrew Lunn 125cda9f4aaSAndrew Lunn for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) { 126cda9f4aaSAndrew Lunn stat = &mv88e6352_serdes_hw_stats[i]; 127cda9f4aaSAndrew Lunn memcpy(data + i * ETH_GSTRING_LEN, stat->string, 128cda9f4aaSAndrew Lunn ETH_GSTRING_LEN); 129cda9f4aaSAndrew Lunn } 13065f60e45SAndrew Lunn return ARRAY_SIZE(mv88e6352_serdes_hw_stats); 131cda9f4aaSAndrew Lunn } 132cda9f4aaSAndrew Lunn 133cda9f4aaSAndrew Lunn static uint64_t mv88e6352_serdes_get_stat(struct mv88e6xxx_chip *chip, 134cda9f4aaSAndrew Lunn struct mv88e6352_serdes_hw_stat *stat) 135cda9f4aaSAndrew Lunn { 136cda9f4aaSAndrew Lunn u64 val = 0; 137cda9f4aaSAndrew Lunn u16 reg; 138cda9f4aaSAndrew Lunn int err; 139cda9f4aaSAndrew Lunn 140cda9f4aaSAndrew Lunn err = mv88e6352_serdes_read(chip, stat->reg, ®); 141cda9f4aaSAndrew Lunn if (err) { 142cda9f4aaSAndrew Lunn dev_err(chip->dev, "failed to read statistic\n"); 143cda9f4aaSAndrew Lunn return 0; 144cda9f4aaSAndrew Lunn } 145cda9f4aaSAndrew Lunn 146cda9f4aaSAndrew Lunn val = reg; 147cda9f4aaSAndrew Lunn 148cda9f4aaSAndrew Lunn if (stat->sizeof_stat == 32) { 149cda9f4aaSAndrew Lunn err = mv88e6352_serdes_read(chip, stat->reg + 1, ®); 150cda9f4aaSAndrew Lunn if (err) { 151cda9f4aaSAndrew Lunn dev_err(chip->dev, "failed to read statistic\n"); 152cda9f4aaSAndrew Lunn return 0; 153cda9f4aaSAndrew Lunn } 154cda9f4aaSAndrew Lunn val = val << 16 | reg; 155cda9f4aaSAndrew Lunn } 156cda9f4aaSAndrew Lunn 157cda9f4aaSAndrew Lunn return val; 158cda9f4aaSAndrew Lunn } 159cda9f4aaSAndrew Lunn 16065f60e45SAndrew Lunn int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, 161cda9f4aaSAndrew Lunn uint64_t *data) 162cda9f4aaSAndrew Lunn { 163cda9f4aaSAndrew Lunn struct mv88e6xxx_port *mv88e6xxx_port = &chip->ports[port]; 164cda9f4aaSAndrew Lunn struct mv88e6352_serdes_hw_stat *stat; 165cda9f4aaSAndrew Lunn u64 value; 166cda9f4aaSAndrew Lunn int i; 167cda9f4aaSAndrew Lunn 168cda9f4aaSAndrew Lunn if (!mv88e6352_port_has_serdes(chip, port)) 16965f60e45SAndrew Lunn return 0; 170cda9f4aaSAndrew Lunn 171cda9f4aaSAndrew Lunn BUILD_BUG_ON(ARRAY_SIZE(mv88e6352_serdes_hw_stats) > 172cda9f4aaSAndrew Lunn ARRAY_SIZE(mv88e6xxx_port->serdes_stats)); 173cda9f4aaSAndrew Lunn 174cda9f4aaSAndrew Lunn for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) { 175cda9f4aaSAndrew Lunn stat = &mv88e6352_serdes_hw_stats[i]; 176cda9f4aaSAndrew Lunn value = mv88e6352_serdes_get_stat(chip, stat); 177cda9f4aaSAndrew Lunn mv88e6xxx_port->serdes_stats[i] += value; 178cda9f4aaSAndrew Lunn data[i] = mv88e6xxx_port->serdes_stats[i]; 179cda9f4aaSAndrew Lunn } 18065f60e45SAndrew Lunn 18165f60e45SAndrew Lunn return ARRAY_SIZE(mv88e6352_serdes_hw_stats); 182cda9f4aaSAndrew Lunn } 183cda9f4aaSAndrew Lunn 1844382172fSAndrew Lunn static void mv88e6352_serdes_irq_link(struct mv88e6xxx_chip *chip, int port) 1854382172fSAndrew Lunn { 1864382172fSAndrew Lunn struct dsa_switch *ds = chip->ds; 1874382172fSAndrew Lunn u16 status; 1884382172fSAndrew Lunn bool up; 1894382172fSAndrew Lunn 1904382172fSAndrew Lunn mv88e6352_serdes_read(chip, MII_BMSR, &status); 1914382172fSAndrew Lunn 1924382172fSAndrew Lunn /* Status must be read twice in order to give the current link 1934382172fSAndrew Lunn * status. Otherwise the change in link status since the last 1944382172fSAndrew Lunn * read of the register is returned. 1954382172fSAndrew Lunn */ 1964382172fSAndrew Lunn mv88e6352_serdes_read(chip, MII_BMSR, &status); 1974382172fSAndrew Lunn 1984382172fSAndrew Lunn up = status & BMSR_LSTATUS; 1994382172fSAndrew Lunn 2004382172fSAndrew Lunn dsa_port_phylink_mac_change(ds, port, up); 2014382172fSAndrew Lunn } 2024382172fSAndrew Lunn 2034382172fSAndrew Lunn static irqreturn_t mv88e6352_serdes_thread_fn(int irq, void *dev_id) 2044382172fSAndrew Lunn { 2054382172fSAndrew Lunn struct mv88e6xxx_port *port = dev_id; 2064382172fSAndrew Lunn struct mv88e6xxx_chip *chip = port->chip; 2074382172fSAndrew Lunn irqreturn_t ret = IRQ_NONE; 2084382172fSAndrew Lunn u16 status; 2094382172fSAndrew Lunn int err; 2104382172fSAndrew Lunn 211c9acece0SRasmus Villemoes mv88e6xxx_reg_lock(chip); 2124382172fSAndrew Lunn 2134382172fSAndrew Lunn err = mv88e6352_serdes_read(chip, MV88E6352_SERDES_INT_STATUS, &status); 2144382172fSAndrew Lunn if (err) 2154382172fSAndrew Lunn goto out; 2164382172fSAndrew Lunn 2174382172fSAndrew Lunn if (status & MV88E6352_SERDES_INT_LINK_CHANGE) { 2184382172fSAndrew Lunn ret = IRQ_HANDLED; 2194382172fSAndrew Lunn mv88e6352_serdes_irq_link(chip, port->port); 2204382172fSAndrew Lunn } 2214382172fSAndrew Lunn out: 222c9acece0SRasmus Villemoes mv88e6xxx_reg_unlock(chip); 2234382172fSAndrew Lunn 2244382172fSAndrew Lunn return ret; 2254382172fSAndrew Lunn } 2264382172fSAndrew Lunn 2274382172fSAndrew Lunn static int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip) 2284382172fSAndrew Lunn { 2294382172fSAndrew Lunn return mv88e6352_serdes_write(chip, MV88E6352_SERDES_INT_ENABLE, 2304382172fSAndrew Lunn MV88E6352_SERDES_INT_LINK_CHANGE); 2314382172fSAndrew Lunn } 2324382172fSAndrew Lunn 2334382172fSAndrew Lunn static int mv88e6352_serdes_irq_disable(struct mv88e6xxx_chip *chip) 2344382172fSAndrew Lunn { 2354382172fSAndrew Lunn return mv88e6352_serdes_write(chip, MV88E6352_SERDES_INT_ENABLE, 0); 2364382172fSAndrew Lunn } 2374382172fSAndrew Lunn 2384382172fSAndrew Lunn int mv88e6352_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port) 2394382172fSAndrew Lunn { 2404382172fSAndrew Lunn int err; 2414382172fSAndrew Lunn 2424382172fSAndrew Lunn if (!mv88e6352_port_has_serdes(chip, port)) 2434382172fSAndrew Lunn return 0; 2444382172fSAndrew Lunn 2454382172fSAndrew Lunn chip->ports[port].serdes_irq = irq_find_mapping(chip->g2_irq.domain, 2464382172fSAndrew Lunn MV88E6352_SERDES_IRQ); 2474382172fSAndrew Lunn if (chip->ports[port].serdes_irq < 0) { 2484382172fSAndrew Lunn dev_err(chip->dev, "Unable to map SERDES irq: %d\n", 2494382172fSAndrew Lunn chip->ports[port].serdes_irq); 2504382172fSAndrew Lunn return chip->ports[port].serdes_irq; 2514382172fSAndrew Lunn } 2524382172fSAndrew Lunn 2534382172fSAndrew Lunn /* Requesting the IRQ will trigger irq callbacks. So we cannot 2544382172fSAndrew Lunn * hold the reg_lock. 2554382172fSAndrew Lunn */ 256c9acece0SRasmus Villemoes mv88e6xxx_reg_unlock(chip); 2574382172fSAndrew Lunn err = request_threaded_irq(chip->ports[port].serdes_irq, NULL, 2584382172fSAndrew Lunn mv88e6352_serdes_thread_fn, 2594382172fSAndrew Lunn IRQF_ONESHOT, "mv88e6xxx-serdes", 2604382172fSAndrew Lunn &chip->ports[port]); 261c9acece0SRasmus Villemoes mv88e6xxx_reg_lock(chip); 2624382172fSAndrew Lunn 2634382172fSAndrew Lunn if (err) { 2644382172fSAndrew Lunn dev_err(chip->dev, "Unable to request SERDES interrupt: %d\n", 2654382172fSAndrew Lunn err); 2664382172fSAndrew Lunn return err; 2674382172fSAndrew Lunn } 2684382172fSAndrew Lunn 2694382172fSAndrew Lunn return mv88e6352_serdes_irq_enable(chip); 2704382172fSAndrew Lunn } 2714382172fSAndrew Lunn 2724382172fSAndrew Lunn void mv88e6352_serdes_irq_free(struct mv88e6xxx_chip *chip, int port) 2734382172fSAndrew Lunn { 2744382172fSAndrew Lunn if (!mv88e6352_port_has_serdes(chip, port)) 2754382172fSAndrew Lunn return; 2764382172fSAndrew Lunn 2774382172fSAndrew Lunn mv88e6352_serdes_irq_disable(chip); 2784382172fSAndrew Lunn 2794382172fSAndrew Lunn /* Freeing the IRQ will trigger irq callbacks. So we cannot 2804382172fSAndrew Lunn * hold the reg_lock. 2814382172fSAndrew Lunn */ 282c9acece0SRasmus Villemoes mv88e6xxx_reg_unlock(chip); 2834382172fSAndrew Lunn free_irq(chip->ports[port].serdes_irq, &chip->ports[port]); 284c9acece0SRasmus Villemoes mv88e6xxx_reg_lock(chip); 2854382172fSAndrew Lunn 2864382172fSAndrew Lunn chip->ports[port].serdes_irq = 0; 2874382172fSAndrew Lunn } 2884382172fSAndrew Lunn 289d3cf7d8fSMarek Behún int mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port, u8 *lane) 290d3cf7d8fSMarek Behún { 291d3cf7d8fSMarek Behún u8 cmode = chip->ports[port].cmode; 292d3cf7d8fSMarek Behún 293d3cf7d8fSMarek Behún if (port != 5) 294d3cf7d8fSMarek Behún return -ENODEV; 295d3cf7d8fSMarek Behún 296d3cf7d8fSMarek Behún if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 297d3cf7d8fSMarek Behún cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || 298d3cf7d8fSMarek Behún cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) { 299d3cf7d8fSMarek Behún *lane = MV88E6341_PORT5_LANE; 300d3cf7d8fSMarek Behún return 0; 301d3cf7d8fSMarek Behún } 302d3cf7d8fSMarek Behún 303d3cf7d8fSMarek Behún return -ENODEV; 304d3cf7d8fSMarek Behún } 305d3cf7d8fSMarek Behún 30617deaf5cSMarek Behún int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port, u8 *lane) 30707ffbd74SAndrew Lunn { 3082d2e1dd2SAndrew Lunn u8 cmode = chip->ports[port].cmode; 30907ffbd74SAndrew Lunn 31007ffbd74SAndrew Lunn switch (port) { 31107ffbd74SAndrew Lunn case 9: 31207ffbd74SAndrew Lunn if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 31307ffbd74SAndrew Lunn cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || 31417deaf5cSMarek Behún cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) { 31517deaf5cSMarek Behún *lane = MV88E6390_PORT9_LANE0; 31617deaf5cSMarek Behún return 0; 31717deaf5cSMarek Behún } 31817deaf5cSMarek Behún break; 31907ffbd74SAndrew Lunn case 10: 32007ffbd74SAndrew Lunn if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 32107ffbd74SAndrew Lunn cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || 32217deaf5cSMarek Behún cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) { 32317deaf5cSMarek Behún *lane = MV88E6390_PORT10_LANE0; 32417deaf5cSMarek Behún return 0; 32507ffbd74SAndrew Lunn } 32617deaf5cSMarek Behún break; 32717deaf5cSMarek Behún default: 32817deaf5cSMarek Behún break; 32907ffbd74SAndrew Lunn } 33007ffbd74SAndrew Lunn 33117deaf5cSMarek Behún return -ENODEV; 33217deaf5cSMarek Behún } 33317deaf5cSMarek Behún 33417deaf5cSMarek Behún int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port, u8 *lane) 335a8c01c0dSAndrew Lunn { 336a8c01c0dSAndrew Lunn u8 cmode_port9, cmode_port10, cmode_port; 337a8c01c0dSAndrew Lunn 3382d2e1dd2SAndrew Lunn cmode_port9 = chip->ports[9].cmode; 3392d2e1dd2SAndrew Lunn cmode_port10 = chip->ports[10].cmode; 3402d2e1dd2SAndrew Lunn cmode_port = chip->ports[port].cmode; 341a8c01c0dSAndrew Lunn 342a8c01c0dSAndrew Lunn switch (port) { 343a8c01c0dSAndrew Lunn case 2: 344a8c01c0dSAndrew Lunn if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 345a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || 34617deaf5cSMarek Behún cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX) { 34717deaf5cSMarek Behún if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) { 34817deaf5cSMarek Behún *lane = MV88E6390_PORT9_LANE1; 34917deaf5cSMarek Behún return 0; 35017deaf5cSMarek Behún } 35117deaf5cSMarek Behún } 35217deaf5cSMarek Behún break; 353a8c01c0dSAndrew Lunn case 3: 354a8c01c0dSAndrew Lunn if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 355a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || 356a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || 35717deaf5cSMarek Behún cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) { 35817deaf5cSMarek Behún if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) { 35917deaf5cSMarek Behún *lane = MV88E6390_PORT9_LANE2; 36017deaf5cSMarek Behún return 0; 36117deaf5cSMarek Behún } 36217deaf5cSMarek Behún } 36317deaf5cSMarek Behún break; 364a8c01c0dSAndrew Lunn case 4: 365a8c01c0dSAndrew Lunn if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 366a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || 367a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || 36817deaf5cSMarek Behún cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) { 36917deaf5cSMarek Behún if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) { 37017deaf5cSMarek Behún *lane = MV88E6390_PORT9_LANE3; 37117deaf5cSMarek Behún return 0; 37217deaf5cSMarek Behún } 37317deaf5cSMarek Behún } 37417deaf5cSMarek Behún break; 375a8c01c0dSAndrew Lunn case 5: 376a8c01c0dSAndrew Lunn if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 377a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || 37817deaf5cSMarek Behún cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX) { 37917deaf5cSMarek Behún if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) { 38017deaf5cSMarek Behún *lane = MV88E6390_PORT10_LANE1; 38117deaf5cSMarek Behún return 0; 38217deaf5cSMarek Behún } 38317deaf5cSMarek Behún } 38417deaf5cSMarek Behún break; 385a8c01c0dSAndrew Lunn case 6: 386a8c01c0dSAndrew Lunn if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 387a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || 388a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || 38917deaf5cSMarek Behún cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) { 39017deaf5cSMarek Behún if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) { 39117deaf5cSMarek Behún *lane = MV88E6390_PORT10_LANE2; 39217deaf5cSMarek Behún return 0; 39317deaf5cSMarek Behún } 39417deaf5cSMarek Behún } 39517deaf5cSMarek Behún break; 396a8c01c0dSAndrew Lunn case 7: 397a8c01c0dSAndrew Lunn if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 398a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || 399a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || 40017deaf5cSMarek Behún cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) { 40117deaf5cSMarek Behún if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) { 40217deaf5cSMarek Behún *lane = MV88E6390_PORT10_LANE3; 40317deaf5cSMarek Behún return 0; 40417deaf5cSMarek Behún } 40517deaf5cSMarek Behún } 40617deaf5cSMarek Behún break; 407a8c01c0dSAndrew Lunn case 9: 408a8c01c0dSAndrew Lunn if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 409a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || 410a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || 411a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_XAUI || 41217deaf5cSMarek Behún cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) { 41317deaf5cSMarek Behún *lane = MV88E6390_PORT9_LANE0; 41417deaf5cSMarek Behún return 0; 41517deaf5cSMarek Behún } 41617deaf5cSMarek Behún break; 417a8c01c0dSAndrew Lunn case 10: 418a8c01c0dSAndrew Lunn if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 419a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || 420a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || 421a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_XAUI || 42217deaf5cSMarek Behún cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) { 42317deaf5cSMarek Behún *lane = MV88E6390_PORT10_LANE0; 42417deaf5cSMarek Behún return 0; 425a8c01c0dSAndrew Lunn } 42617deaf5cSMarek Behún break; 42717deaf5cSMarek Behún default: 42817deaf5cSMarek Behún break; 42917deaf5cSMarek Behún } 43017deaf5cSMarek Behún 43117deaf5cSMarek Behún return -ENODEV; 432a8c01c0dSAndrew Lunn } 433a8c01c0dSAndrew Lunn 4346335e9f2SAndrew Lunn /* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */ 43517deaf5cSMarek Behún static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, u8 lane, 43623ef57d8SAndrew Lunn bool on) 4376335e9f2SAndrew Lunn { 4386335e9f2SAndrew Lunn u16 val, new_val; 4396335e9f2SAndrew Lunn int err; 4406335e9f2SAndrew Lunn 441e6891c76SAndrew Lunn err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, 442e6891c76SAndrew Lunn MV88E6390_PCS_CONTROL_1, &val); 443e6891c76SAndrew Lunn 4446335e9f2SAndrew Lunn if (err) 4456335e9f2SAndrew Lunn return err; 4466335e9f2SAndrew Lunn 4476335e9f2SAndrew Lunn if (on) 4486335e9f2SAndrew Lunn new_val = val & ~(MV88E6390_PCS_CONTROL_1_RESET | 4496335e9f2SAndrew Lunn MV88E6390_PCS_CONTROL_1_LOOPBACK | 4506335e9f2SAndrew Lunn MV88E6390_PCS_CONTROL_1_PDOWN); 4516335e9f2SAndrew Lunn else 4526335e9f2SAndrew Lunn new_val = val | MV88E6390_PCS_CONTROL_1_PDOWN; 4536335e9f2SAndrew Lunn 4546335e9f2SAndrew Lunn if (val != new_val) 455e6891c76SAndrew Lunn err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, 456e6891c76SAndrew Lunn MV88E6390_PCS_CONTROL_1, new_val); 4576335e9f2SAndrew Lunn 4586335e9f2SAndrew Lunn return err; 4596335e9f2SAndrew Lunn } 4606335e9f2SAndrew Lunn 461a8c01c0dSAndrew Lunn /* Set the power on/off for SGMII and 1000Base-X */ 46217deaf5cSMarek Behún static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, u8 lane, 4636335e9f2SAndrew Lunn bool on) 4646335e9f2SAndrew Lunn { 4656335e9f2SAndrew Lunn u16 val, new_val; 4666335e9f2SAndrew Lunn int err; 4676335e9f2SAndrew Lunn 468e6891c76SAndrew Lunn err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, 469e6891c76SAndrew Lunn MV88E6390_SGMII_CONTROL, &val); 4706335e9f2SAndrew Lunn if (err) 4716335e9f2SAndrew Lunn return err; 4726335e9f2SAndrew Lunn 4736335e9f2SAndrew Lunn if (on) 4746335e9f2SAndrew Lunn new_val = val & ~(MV88E6390_SGMII_CONTROL_RESET | 4756335e9f2SAndrew Lunn MV88E6390_SGMII_CONTROL_LOOPBACK | 4766335e9f2SAndrew Lunn MV88E6390_SGMII_CONTROL_PDOWN); 4776335e9f2SAndrew Lunn else 4786335e9f2SAndrew Lunn new_val = val | MV88E6390_SGMII_CONTROL_PDOWN; 4796335e9f2SAndrew Lunn 4806335e9f2SAndrew Lunn if (val != new_val) 481e6891c76SAndrew Lunn err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, 482e6891c76SAndrew Lunn MV88E6390_SGMII_CONTROL, new_val); 4836335e9f2SAndrew Lunn 4846335e9f2SAndrew Lunn return err; 4856335e9f2SAndrew Lunn } 4866335e9f2SAndrew Lunn 487d3cf7d8fSMarek Behún int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) 4886335e9f2SAndrew Lunn { 4892d2e1dd2SAndrew Lunn u8 cmode = chip->ports[port].cmode; 490d3cf7d8fSMarek Behún u8 lane; 491d3cf7d8fSMarek Behún int err; 492d3cf7d8fSMarek Behún 493d3cf7d8fSMarek Behún err = mv88e6xxx_serdes_get_lane(chip, port, &lane); 494d3cf7d8fSMarek Behún if (err) { 495d3cf7d8fSMarek Behún if (err == -ENODEV) 496d3cf7d8fSMarek Behún err = 0; 497d3cf7d8fSMarek Behún return err; 498d3cf7d8fSMarek Behún } 4996335e9f2SAndrew Lunn 500a8c01c0dSAndrew Lunn switch (cmode) { 501a8c01c0dSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_SGMII: 502a8c01c0dSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: 503f8236a08SAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_2500BASEX: 50423ef57d8SAndrew Lunn return mv88e6390_serdes_power_sgmii(chip, lane, on); 505a8c01c0dSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_XAUI: 506a8c01c0dSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_RXAUI: 50723ef57d8SAndrew Lunn return mv88e6390_serdes_power_10g(chip, lane, on); 508a8c01c0dSAndrew Lunn } 509a8c01c0dSAndrew Lunn 510a8c01c0dSAndrew Lunn return 0; 511a8c01c0dSAndrew Lunn } 512a8c01c0dSAndrew Lunn 513efd1ba6aSAndrew Lunn static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip, 51417deaf5cSMarek Behún int port, u8 lane) 515efd1ba6aSAndrew Lunn { 5164e6da796SMarek Behún u8 cmode = chip->ports[port].cmode; 517efd1ba6aSAndrew Lunn struct dsa_switch *ds = chip->ds; 51872d8b4fdSHeiner Kallweit int duplex = DUPLEX_UNKNOWN; 51972d8b4fdSHeiner Kallweit int speed = SPEED_UNKNOWN; 5204e6da796SMarek Behún phy_interface_t mode; 52172d8b4fdSHeiner Kallweit int link, err; 522efd1ba6aSAndrew Lunn u16 status; 523efd1ba6aSAndrew Lunn 52472d8b4fdSHeiner Kallweit err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, 52572d8b4fdSHeiner Kallweit MV88E6390_SGMII_PHY_STATUS, &status); 52672d8b4fdSHeiner Kallweit if (err) { 52772d8b4fdSHeiner Kallweit dev_err(chip->dev, "can't read SGMII PHY status: %d\n", err); 52872d8b4fdSHeiner Kallweit return; 52972d8b4fdSHeiner Kallweit } 530efd1ba6aSAndrew Lunn 53172d8b4fdSHeiner Kallweit link = status & MV88E6390_SGMII_PHY_STATUS_LINK ? 53272d8b4fdSHeiner Kallweit LINK_FORCED_UP : LINK_FORCED_DOWN; 533efd1ba6aSAndrew Lunn 53472d8b4fdSHeiner Kallweit if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) { 53572d8b4fdSHeiner Kallweit duplex = status & MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL ? 53672d8b4fdSHeiner Kallweit DUPLEX_FULL : DUPLEX_HALF; 53772d8b4fdSHeiner Kallweit 53872d8b4fdSHeiner Kallweit switch (status & MV88E6390_SGMII_PHY_STATUS_SPEED_MASK) { 53972d8b4fdSHeiner Kallweit case MV88E6390_SGMII_PHY_STATUS_SPEED_1000: 5404e6da796SMarek Behún if (cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) 5414e6da796SMarek Behún speed = SPEED_2500; 5424e6da796SMarek Behún else 54372d8b4fdSHeiner Kallweit speed = SPEED_1000; 54472d8b4fdSHeiner Kallweit break; 54572d8b4fdSHeiner Kallweit case MV88E6390_SGMII_PHY_STATUS_SPEED_100: 54672d8b4fdSHeiner Kallweit speed = SPEED_100; 54772d8b4fdSHeiner Kallweit break; 54872d8b4fdSHeiner Kallweit case MV88E6390_SGMII_PHY_STATUS_SPEED_10: 54972d8b4fdSHeiner Kallweit speed = SPEED_10; 55072d8b4fdSHeiner Kallweit break; 55172d8b4fdSHeiner Kallweit default: 55272d8b4fdSHeiner Kallweit dev_err(chip->dev, "invalid PHY speed\n"); 55372d8b4fdSHeiner Kallweit return; 55472d8b4fdSHeiner Kallweit } 55572d8b4fdSHeiner Kallweit } 55672d8b4fdSHeiner Kallweit 5574e6da796SMarek Behún switch (cmode) { 5584e6da796SMarek Behún case MV88E6XXX_PORT_STS_CMODE_SGMII: 5594e6da796SMarek Behún mode = PHY_INTERFACE_MODE_SGMII; 5604e6da796SMarek Behún break; 5614e6da796SMarek Behún case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: 5624e6da796SMarek Behún mode = PHY_INTERFACE_MODE_1000BASEX; 5634e6da796SMarek Behún break; 5644e6da796SMarek Behún case MV88E6XXX_PORT_STS_CMODE_2500BASEX: 5654e6da796SMarek Behún mode = PHY_INTERFACE_MODE_2500BASEX; 5664e6da796SMarek Behún break; 5674e6da796SMarek Behún default: 5684e6da796SMarek Behún mode = PHY_INTERFACE_MODE_NA; 5694e6da796SMarek Behún } 5704e6da796SMarek Behún 57172d8b4fdSHeiner Kallweit err = mv88e6xxx_port_setup_mac(chip, port, link, speed, duplex, 5724e6da796SMarek Behún PAUSE_OFF, mode); 57372d8b4fdSHeiner Kallweit if (err) 57472d8b4fdSHeiner Kallweit dev_err(chip->dev, "can't propagate PHY settings to MAC: %d\n", 57572d8b4fdSHeiner Kallweit err); 57672d8b4fdSHeiner Kallweit else 57772d8b4fdSHeiner Kallweit dsa_port_phylink_mac_change(ds, port, link == LINK_FORCED_UP); 578efd1ba6aSAndrew Lunn } 579efd1ba6aSAndrew Lunn 580efd1ba6aSAndrew Lunn static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip, 58117deaf5cSMarek Behún u8 lane) 582efd1ba6aSAndrew Lunn { 583efd1ba6aSAndrew Lunn return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, 584efd1ba6aSAndrew Lunn MV88E6390_SGMII_INT_ENABLE, 585efd1ba6aSAndrew Lunn MV88E6390_SGMII_INT_LINK_DOWN | 586efd1ba6aSAndrew Lunn MV88E6390_SGMII_INT_LINK_UP); 587efd1ba6aSAndrew Lunn } 588efd1ba6aSAndrew Lunn 589efd1ba6aSAndrew Lunn static int mv88e6390_serdes_irq_disable_sgmii(struct mv88e6xxx_chip *chip, 59017deaf5cSMarek Behún u8 lane) 591efd1ba6aSAndrew Lunn { 592efd1ba6aSAndrew Lunn return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, 593efd1ba6aSAndrew Lunn MV88E6390_SGMII_INT_ENABLE, 0); 594efd1ba6aSAndrew Lunn } 595efd1ba6aSAndrew Lunn 596efd1ba6aSAndrew Lunn int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, 59717deaf5cSMarek Behún u8 lane) 598efd1ba6aSAndrew Lunn { 599efd1ba6aSAndrew Lunn u8 cmode = chip->ports[port].cmode; 600efd1ba6aSAndrew Lunn int err = 0; 601efd1ba6aSAndrew Lunn 602efd1ba6aSAndrew Lunn switch (cmode) { 603efd1ba6aSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_SGMII: 604efd1ba6aSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: 605efd1ba6aSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_2500BASEX: 606efd1ba6aSAndrew Lunn err = mv88e6390_serdes_irq_enable_sgmii(chip, lane); 607efd1ba6aSAndrew Lunn } 608efd1ba6aSAndrew Lunn 609efd1ba6aSAndrew Lunn return err; 610efd1ba6aSAndrew Lunn } 611efd1ba6aSAndrew Lunn 612efd1ba6aSAndrew Lunn int mv88e6390_serdes_irq_disable(struct mv88e6xxx_chip *chip, int port, 61317deaf5cSMarek Behún u8 lane) 614efd1ba6aSAndrew Lunn { 615efd1ba6aSAndrew Lunn u8 cmode = chip->ports[port].cmode; 616efd1ba6aSAndrew Lunn int err = 0; 617efd1ba6aSAndrew Lunn 618efd1ba6aSAndrew Lunn switch (cmode) { 619efd1ba6aSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_SGMII: 620efd1ba6aSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: 621efd1ba6aSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_2500BASEX: 622efd1ba6aSAndrew Lunn err = mv88e6390_serdes_irq_disable_sgmii(chip, lane); 623efd1ba6aSAndrew Lunn } 624efd1ba6aSAndrew Lunn 625efd1ba6aSAndrew Lunn return err; 626efd1ba6aSAndrew Lunn } 627efd1ba6aSAndrew Lunn 628efd1ba6aSAndrew Lunn static int mv88e6390_serdes_irq_status_sgmii(struct mv88e6xxx_chip *chip, 62917deaf5cSMarek Behún u8 lane, u16 *status) 630efd1ba6aSAndrew Lunn { 631efd1ba6aSAndrew Lunn int err; 632efd1ba6aSAndrew Lunn 633efd1ba6aSAndrew Lunn err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, 634efd1ba6aSAndrew Lunn MV88E6390_SGMII_INT_STATUS, status); 635efd1ba6aSAndrew Lunn 636efd1ba6aSAndrew Lunn return err; 637efd1ba6aSAndrew Lunn } 638efd1ba6aSAndrew Lunn 639efd1ba6aSAndrew Lunn static irqreturn_t mv88e6390_serdes_thread_fn(int irq, void *dev_id) 640efd1ba6aSAndrew Lunn { 641efd1ba6aSAndrew Lunn struct mv88e6xxx_port *port = dev_id; 642efd1ba6aSAndrew Lunn struct mv88e6xxx_chip *chip = port->chip; 643efd1ba6aSAndrew Lunn irqreturn_t ret = IRQ_NONE; 644efd1ba6aSAndrew Lunn u8 cmode = port->cmode; 645efd1ba6aSAndrew Lunn u16 status; 646efd1ba6aSAndrew Lunn int err; 64717deaf5cSMarek Behún u8 lane; 648efd1ba6aSAndrew Lunn 64917deaf5cSMarek Behún mv88e6xxx_serdes_get_lane(chip, port->port, &lane); 650efd1ba6aSAndrew Lunn 651c9acece0SRasmus Villemoes mv88e6xxx_reg_lock(chip); 652efd1ba6aSAndrew Lunn 653efd1ba6aSAndrew Lunn switch (cmode) { 654efd1ba6aSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_SGMII: 655efd1ba6aSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: 656efd1ba6aSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_2500BASEX: 657efd1ba6aSAndrew Lunn err = mv88e6390_serdes_irq_status_sgmii(chip, lane, &status); 658efd1ba6aSAndrew Lunn if (err) 659efd1ba6aSAndrew Lunn goto out; 6606feddb49SDan Carpenter if (status & (MV88E6390_SGMII_INT_LINK_DOWN | 661efd1ba6aSAndrew Lunn MV88E6390_SGMII_INT_LINK_UP)) { 662efd1ba6aSAndrew Lunn ret = IRQ_HANDLED; 663efd1ba6aSAndrew Lunn mv88e6390_serdes_irq_link_sgmii(chip, port->port, lane); 664efd1ba6aSAndrew Lunn } 665efd1ba6aSAndrew Lunn } 666efd1ba6aSAndrew Lunn out: 667c9acece0SRasmus Villemoes mv88e6xxx_reg_unlock(chip); 668efd1ba6aSAndrew Lunn 669efd1ba6aSAndrew Lunn return ret; 670efd1ba6aSAndrew Lunn } 671efd1ba6aSAndrew Lunn 672d3cf7d8fSMarek Behún int mv88e6390_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port) 673efd1ba6aSAndrew Lunn { 674efd1ba6aSAndrew Lunn int err; 67517deaf5cSMarek Behún u8 lane; 676efd1ba6aSAndrew Lunn 67717deaf5cSMarek Behún err = mv88e6xxx_serdes_get_lane(chip, port, &lane); 67817deaf5cSMarek Behún if (err) { 67917deaf5cSMarek Behún if (err == -ENODEV) 68017deaf5cSMarek Behún err = 0; 68117deaf5cSMarek Behún return err; 68217deaf5cSMarek Behún } 683efd1ba6aSAndrew Lunn 684efd1ba6aSAndrew Lunn chip->ports[port].serdes_irq = irq_find_mapping(chip->g2_irq.domain, 685efd1ba6aSAndrew Lunn port); 686efd1ba6aSAndrew Lunn if (chip->ports[port].serdes_irq < 0) { 687efd1ba6aSAndrew Lunn dev_err(chip->dev, "Unable to map SERDES irq: %d\n", 688efd1ba6aSAndrew Lunn chip->ports[port].serdes_irq); 689efd1ba6aSAndrew Lunn return chip->ports[port].serdes_irq; 690efd1ba6aSAndrew Lunn } 691efd1ba6aSAndrew Lunn 692efd1ba6aSAndrew Lunn /* Requesting the IRQ will trigger irq callbacks. So we cannot 693efd1ba6aSAndrew Lunn * hold the reg_lock. 694efd1ba6aSAndrew Lunn */ 695c9acece0SRasmus Villemoes mv88e6xxx_reg_unlock(chip); 696efd1ba6aSAndrew Lunn err = request_threaded_irq(chip->ports[port].serdes_irq, NULL, 697efd1ba6aSAndrew Lunn mv88e6390_serdes_thread_fn, 698efd1ba6aSAndrew Lunn IRQF_ONESHOT, "mv88e6xxx-serdes", 699efd1ba6aSAndrew Lunn &chip->ports[port]); 700c9acece0SRasmus Villemoes mv88e6xxx_reg_lock(chip); 701efd1ba6aSAndrew Lunn 702efd1ba6aSAndrew Lunn if (err) { 703efd1ba6aSAndrew Lunn dev_err(chip->dev, "Unable to request SERDES interrupt: %d\n", 704efd1ba6aSAndrew Lunn err); 705efd1ba6aSAndrew Lunn return err; 706efd1ba6aSAndrew Lunn } 707efd1ba6aSAndrew Lunn 708efd1ba6aSAndrew Lunn return mv88e6390_serdes_irq_enable(chip, port, lane); 709efd1ba6aSAndrew Lunn } 710efd1ba6aSAndrew Lunn 711d3cf7d8fSMarek Behún void mv88e6390_serdes_irq_free(struct mv88e6xxx_chip *chip, int port) 712efd1ba6aSAndrew Lunn { 71317deaf5cSMarek Behún int err; 71417deaf5cSMarek Behún u8 lane; 715efd1ba6aSAndrew Lunn 71617deaf5cSMarek Behún err = mv88e6xxx_serdes_get_lane(chip, port, &lane); 71717deaf5cSMarek Behún if (err) { 71817deaf5cSMarek Behún if (err != -ENODEV) 71917deaf5cSMarek Behún dev_err(chip->dev, "Unable to free SERDES irq: %d\n", 72017deaf5cSMarek Behún err); 721efd1ba6aSAndrew Lunn return; 72217deaf5cSMarek Behún } 723efd1ba6aSAndrew Lunn 724efd1ba6aSAndrew Lunn mv88e6390_serdes_irq_disable(chip, port, lane); 725efd1ba6aSAndrew Lunn 726efd1ba6aSAndrew Lunn /* Freeing the IRQ will trigger irq callbacks. So we cannot 727efd1ba6aSAndrew Lunn * hold the reg_lock. 728efd1ba6aSAndrew Lunn */ 729c9acece0SRasmus Villemoes mv88e6xxx_reg_unlock(chip); 730efd1ba6aSAndrew Lunn free_irq(chip->ports[port].serdes_irq, &chip->ports[port]); 731c9acece0SRasmus Villemoes mv88e6xxx_reg_lock(chip); 732734447d4SAndrew Lunn 733734447d4SAndrew Lunn chip->ports[port].serdes_irq = 0; 734efd1ba6aSAndrew Lunn } 735