12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 210fa5bfcSAndrew Lunn /* 310fa5bfcSAndrew Lunn * Marvell 88e6xxx Ethernet switch PHY and PPU support 410fa5bfcSAndrew Lunn * 510fa5bfcSAndrew Lunn * Copyright (c) 2008 Marvell Semiconductor 610fa5bfcSAndrew Lunn * 710fa5bfcSAndrew Lunn * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch> 810fa5bfcSAndrew Lunn */ 910fa5bfcSAndrew Lunn 1010fa5bfcSAndrew Lunn #include <linux/mdio.h> 1110fa5bfcSAndrew Lunn #include <linux/module.h> 1210fa5bfcSAndrew Lunn 134d5f2ba7SVivien Didelot #include "chip.h" 1410fa5bfcSAndrew Lunn #include "phy.h" 1510fa5bfcSAndrew Lunn 1610fa5bfcSAndrew Lunn int mv88e6165_phy_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus, 1710fa5bfcSAndrew Lunn int addr, int reg, u16 *val) 1810fa5bfcSAndrew Lunn { 1910fa5bfcSAndrew Lunn return mv88e6xxx_read(chip, addr, reg, val); 2010fa5bfcSAndrew Lunn } 2110fa5bfcSAndrew Lunn 2210fa5bfcSAndrew Lunn int mv88e6165_phy_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus, 2310fa5bfcSAndrew Lunn int addr, int reg, u16 val) 2410fa5bfcSAndrew Lunn { 2510fa5bfcSAndrew Lunn return mv88e6xxx_write(chip, addr, reg, val); 2610fa5bfcSAndrew Lunn } 2710fa5bfcSAndrew Lunn 2810fa5bfcSAndrew Lunn int mv88e6xxx_phy_read(struct mv88e6xxx_chip *chip, int phy, int reg, u16 *val) 2910fa5bfcSAndrew Lunn { 3010fa5bfcSAndrew Lunn int addr = phy; /* PHY devices addresses start at 0x0 */ 3110fa5bfcSAndrew Lunn struct mii_bus *bus; 3210fa5bfcSAndrew Lunn 3310fa5bfcSAndrew Lunn bus = mv88e6xxx_default_mdio_bus(chip); 3410fa5bfcSAndrew Lunn if (!bus) 3510fa5bfcSAndrew Lunn return -EOPNOTSUPP; 3610fa5bfcSAndrew Lunn 3710fa5bfcSAndrew Lunn if (!chip->info->ops->phy_read) 3810fa5bfcSAndrew Lunn return -EOPNOTSUPP; 3910fa5bfcSAndrew Lunn 4010fa5bfcSAndrew Lunn return chip->info->ops->phy_read(chip, bus, addr, reg, val); 4110fa5bfcSAndrew Lunn } 4210fa5bfcSAndrew Lunn 4310fa5bfcSAndrew Lunn int mv88e6xxx_phy_write(struct mv88e6xxx_chip *chip, int phy, int reg, u16 val) 4410fa5bfcSAndrew Lunn { 4510fa5bfcSAndrew Lunn int addr = phy; /* PHY devices addresses start at 0x0 */ 4610fa5bfcSAndrew Lunn struct mii_bus *bus; 4710fa5bfcSAndrew Lunn 4810fa5bfcSAndrew Lunn bus = mv88e6xxx_default_mdio_bus(chip); 4910fa5bfcSAndrew Lunn if (!bus) 5010fa5bfcSAndrew Lunn return -EOPNOTSUPP; 5110fa5bfcSAndrew Lunn 5210fa5bfcSAndrew Lunn if (!chip->info->ops->phy_write) 5310fa5bfcSAndrew Lunn return -EOPNOTSUPP; 5410fa5bfcSAndrew Lunn 5510fa5bfcSAndrew Lunn return chip->info->ops->phy_write(chip, bus, addr, reg, val); 5610fa5bfcSAndrew Lunn } 5710fa5bfcSAndrew Lunn 5810fa5bfcSAndrew Lunn static int mv88e6xxx_phy_page_get(struct mv88e6xxx_chip *chip, int phy, u8 page) 5910fa5bfcSAndrew Lunn { 60bec90b6dSVivien Didelot return mv88e6xxx_phy_write(chip, phy, MV88E6XXX_PHY_PAGE, page); 6110fa5bfcSAndrew Lunn } 6210fa5bfcSAndrew Lunn 6310fa5bfcSAndrew Lunn static void mv88e6xxx_phy_page_put(struct mv88e6xxx_chip *chip, int phy) 6410fa5bfcSAndrew Lunn { 6510fa5bfcSAndrew Lunn int err; 6610fa5bfcSAndrew Lunn 6710fa5bfcSAndrew Lunn /* Restore PHY page Copper 0x0 for access via the registered 6810fa5bfcSAndrew Lunn * MDIO bus 6910fa5bfcSAndrew Lunn */ 70bec90b6dSVivien Didelot err = mv88e6xxx_phy_write(chip, phy, MV88E6XXX_PHY_PAGE, 71bec90b6dSVivien Didelot MV88E6XXX_PHY_PAGE_COPPER); 7210fa5bfcSAndrew Lunn if (unlikely(err)) { 7310fa5bfcSAndrew Lunn dev_err(chip->dev, 7410fa5bfcSAndrew Lunn "failed to restore PHY %d page Copper (%d)\n", 7510fa5bfcSAndrew Lunn phy, err); 7610fa5bfcSAndrew Lunn } 7710fa5bfcSAndrew Lunn } 7810fa5bfcSAndrew Lunn 7910fa5bfcSAndrew Lunn int mv88e6xxx_phy_page_read(struct mv88e6xxx_chip *chip, int phy, 8010fa5bfcSAndrew Lunn u8 page, int reg, u16 *val) 8110fa5bfcSAndrew Lunn { 8210fa5bfcSAndrew Lunn int err; 8310fa5bfcSAndrew Lunn 8410fa5bfcSAndrew Lunn /* There is no paging for registers 22 */ 85bec90b6dSVivien Didelot if (reg == MV88E6XXX_PHY_PAGE) 8610fa5bfcSAndrew Lunn return -EINVAL; 8710fa5bfcSAndrew Lunn 8810fa5bfcSAndrew Lunn err = mv88e6xxx_phy_page_get(chip, phy, page); 8910fa5bfcSAndrew Lunn if (!err) { 9010fa5bfcSAndrew Lunn err = mv88e6xxx_phy_read(chip, phy, reg, val); 9110fa5bfcSAndrew Lunn mv88e6xxx_phy_page_put(chip, phy); 9210fa5bfcSAndrew Lunn } 9310fa5bfcSAndrew Lunn 9410fa5bfcSAndrew Lunn return err; 9510fa5bfcSAndrew Lunn } 9610fa5bfcSAndrew Lunn 9710fa5bfcSAndrew Lunn int mv88e6xxx_phy_page_write(struct mv88e6xxx_chip *chip, int phy, 9810fa5bfcSAndrew Lunn u8 page, int reg, u16 val) 9910fa5bfcSAndrew Lunn { 10010fa5bfcSAndrew Lunn int err; 10110fa5bfcSAndrew Lunn 10210fa5bfcSAndrew Lunn /* There is no paging for registers 22 */ 103bec90b6dSVivien Didelot if (reg == MV88E6XXX_PHY_PAGE) 10410fa5bfcSAndrew Lunn return -EINVAL; 10510fa5bfcSAndrew Lunn 10610fa5bfcSAndrew Lunn err = mv88e6xxx_phy_page_get(chip, phy, page); 10710fa5bfcSAndrew Lunn if (!err) { 108bec90b6dSVivien Didelot err = mv88e6xxx_phy_write(chip, phy, MV88E6XXX_PHY_PAGE, page); 109c309b158SAndrew Lunn if (!err) 110c309b158SAndrew Lunn err = mv88e6xxx_phy_write(chip, phy, reg, val); 111c309b158SAndrew Lunn 11210fa5bfcSAndrew Lunn mv88e6xxx_phy_page_put(chip, phy); 11310fa5bfcSAndrew Lunn } 11410fa5bfcSAndrew Lunn 11510fa5bfcSAndrew Lunn return err; 11610fa5bfcSAndrew Lunn } 11710fa5bfcSAndrew Lunn 118b15a7c03SVivien Didelot static int mv88e6xxx_phy_ppu_disable(struct mv88e6xxx_chip *chip) 11910fa5bfcSAndrew Lunn { 12010fa5bfcSAndrew Lunn if (!chip->info->ops->ppu_disable) 12110fa5bfcSAndrew Lunn return 0; 12210fa5bfcSAndrew Lunn 12310fa5bfcSAndrew Lunn return chip->info->ops->ppu_disable(chip); 12410fa5bfcSAndrew Lunn } 12510fa5bfcSAndrew Lunn 126b15a7c03SVivien Didelot static int mv88e6xxx_phy_ppu_enable(struct mv88e6xxx_chip *chip) 12710fa5bfcSAndrew Lunn { 12810fa5bfcSAndrew Lunn if (!chip->info->ops->ppu_enable) 12910fa5bfcSAndrew Lunn return 0; 13010fa5bfcSAndrew Lunn 13110fa5bfcSAndrew Lunn return chip->info->ops->ppu_enable(chip); 13210fa5bfcSAndrew Lunn } 13310fa5bfcSAndrew Lunn 134b15a7c03SVivien Didelot static void mv88e6xxx_phy_ppu_reenable_work(struct work_struct *ugly) 13510fa5bfcSAndrew Lunn { 13610fa5bfcSAndrew Lunn struct mv88e6xxx_chip *chip; 13710fa5bfcSAndrew Lunn 13810fa5bfcSAndrew Lunn chip = container_of(ugly, struct mv88e6xxx_chip, ppu_work); 13910fa5bfcSAndrew Lunn 140*c9acece0SRasmus Villemoes mv88e6xxx_reg_lock(chip); 14110fa5bfcSAndrew Lunn 14210fa5bfcSAndrew Lunn if (mutex_trylock(&chip->ppu_mutex)) { 143b15a7c03SVivien Didelot if (mv88e6xxx_phy_ppu_enable(chip) == 0) 14410fa5bfcSAndrew Lunn chip->ppu_disabled = 0; 14510fa5bfcSAndrew Lunn mutex_unlock(&chip->ppu_mutex); 14610fa5bfcSAndrew Lunn } 14710fa5bfcSAndrew Lunn 148*c9acece0SRasmus Villemoes mv88e6xxx_reg_unlock(chip); 14910fa5bfcSAndrew Lunn } 15010fa5bfcSAndrew Lunn 151e99e88a9SKees Cook static void mv88e6xxx_phy_ppu_reenable_timer(struct timer_list *t) 15210fa5bfcSAndrew Lunn { 153e99e88a9SKees Cook struct mv88e6xxx_chip *chip = from_timer(chip, t, ppu_timer); 15410fa5bfcSAndrew Lunn 15510fa5bfcSAndrew Lunn schedule_work(&chip->ppu_work); 15610fa5bfcSAndrew Lunn } 15710fa5bfcSAndrew Lunn 158b15a7c03SVivien Didelot static int mv88e6xxx_phy_ppu_access_get(struct mv88e6xxx_chip *chip) 15910fa5bfcSAndrew Lunn { 16010fa5bfcSAndrew Lunn int ret; 16110fa5bfcSAndrew Lunn 16210fa5bfcSAndrew Lunn mutex_lock(&chip->ppu_mutex); 16310fa5bfcSAndrew Lunn 16410fa5bfcSAndrew Lunn /* If the PHY polling unit is enabled, disable it so that 16510fa5bfcSAndrew Lunn * we can access the PHY registers. If it was already 16610fa5bfcSAndrew Lunn * disabled, cancel the timer that is going to re-enable 16710fa5bfcSAndrew Lunn * it. 16810fa5bfcSAndrew Lunn */ 16910fa5bfcSAndrew Lunn if (!chip->ppu_disabled) { 170b15a7c03SVivien Didelot ret = mv88e6xxx_phy_ppu_disable(chip); 17110fa5bfcSAndrew Lunn if (ret < 0) { 17210fa5bfcSAndrew Lunn mutex_unlock(&chip->ppu_mutex); 17310fa5bfcSAndrew Lunn return ret; 17410fa5bfcSAndrew Lunn } 17510fa5bfcSAndrew Lunn chip->ppu_disabled = 1; 17610fa5bfcSAndrew Lunn } else { 17710fa5bfcSAndrew Lunn del_timer(&chip->ppu_timer); 17810fa5bfcSAndrew Lunn ret = 0; 17910fa5bfcSAndrew Lunn } 18010fa5bfcSAndrew Lunn 18110fa5bfcSAndrew Lunn return ret; 18210fa5bfcSAndrew Lunn } 18310fa5bfcSAndrew Lunn 184b15a7c03SVivien Didelot static void mv88e6xxx_phy_ppu_access_put(struct mv88e6xxx_chip *chip) 18510fa5bfcSAndrew Lunn { 18610fa5bfcSAndrew Lunn /* Schedule a timer to re-enable the PHY polling unit. */ 18710fa5bfcSAndrew Lunn mod_timer(&chip->ppu_timer, jiffies + msecs_to_jiffies(10)); 18810fa5bfcSAndrew Lunn mutex_unlock(&chip->ppu_mutex); 18910fa5bfcSAndrew Lunn } 19010fa5bfcSAndrew Lunn 191b15a7c03SVivien Didelot static void mv88e6xxx_phy_ppu_state_init(struct mv88e6xxx_chip *chip) 19210fa5bfcSAndrew Lunn { 19310fa5bfcSAndrew Lunn mutex_init(&chip->ppu_mutex); 194b15a7c03SVivien Didelot INIT_WORK(&chip->ppu_work, mv88e6xxx_phy_ppu_reenable_work); 195e99e88a9SKees Cook timer_setup(&chip->ppu_timer, mv88e6xxx_phy_ppu_reenable_timer, 0); 19610fa5bfcSAndrew Lunn } 19710fa5bfcSAndrew Lunn 198b15a7c03SVivien Didelot static void mv88e6xxx_phy_ppu_state_destroy(struct mv88e6xxx_chip *chip) 19910fa5bfcSAndrew Lunn { 20010fa5bfcSAndrew Lunn del_timer_sync(&chip->ppu_timer); 20110fa5bfcSAndrew Lunn } 20210fa5bfcSAndrew Lunn 2037e20cfb5SVivien Didelot int mv88e6185_phy_ppu_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus, 20410fa5bfcSAndrew Lunn int addr, int reg, u16 *val) 20510fa5bfcSAndrew Lunn { 20610fa5bfcSAndrew Lunn int err; 20710fa5bfcSAndrew Lunn 208b15a7c03SVivien Didelot err = mv88e6xxx_phy_ppu_access_get(chip); 20910fa5bfcSAndrew Lunn if (!err) { 21010fa5bfcSAndrew Lunn err = mv88e6xxx_read(chip, addr, reg, val); 211b15a7c03SVivien Didelot mv88e6xxx_phy_ppu_access_put(chip); 21210fa5bfcSAndrew Lunn } 21310fa5bfcSAndrew Lunn 21410fa5bfcSAndrew Lunn return err; 21510fa5bfcSAndrew Lunn } 21610fa5bfcSAndrew Lunn 2177e20cfb5SVivien Didelot int mv88e6185_phy_ppu_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus, 21810fa5bfcSAndrew Lunn int addr, int reg, u16 val) 21910fa5bfcSAndrew Lunn { 22010fa5bfcSAndrew Lunn int err; 22110fa5bfcSAndrew Lunn 222b15a7c03SVivien Didelot err = mv88e6xxx_phy_ppu_access_get(chip); 22310fa5bfcSAndrew Lunn if (!err) { 22410fa5bfcSAndrew Lunn err = mv88e6xxx_write(chip, addr, reg, val); 225b15a7c03SVivien Didelot mv88e6xxx_phy_ppu_access_put(chip); 22610fa5bfcSAndrew Lunn } 22710fa5bfcSAndrew Lunn 22810fa5bfcSAndrew Lunn return err; 22910fa5bfcSAndrew Lunn } 23010fa5bfcSAndrew Lunn 23110fa5bfcSAndrew Lunn void mv88e6xxx_phy_init(struct mv88e6xxx_chip *chip) 23210fa5bfcSAndrew Lunn { 23310fa5bfcSAndrew Lunn if (chip->info->ops->ppu_enable && chip->info->ops->ppu_disable) 234b15a7c03SVivien Didelot mv88e6xxx_phy_ppu_state_init(chip); 23510fa5bfcSAndrew Lunn } 23610fa5bfcSAndrew Lunn 23710fa5bfcSAndrew Lunn void mv88e6xxx_phy_destroy(struct mv88e6xxx_chip *chip) 23810fa5bfcSAndrew Lunn { 23910fa5bfcSAndrew Lunn if (chip->info->ops->ppu_enable && chip->info->ops->ppu_disable) 240b15a7c03SVivien Didelot mv88e6xxx_phy_ppu_state_destroy(chip); 24110fa5bfcSAndrew Lunn } 2421b17aedfSVivien Didelot 2431b17aedfSVivien Didelot int mv88e6xxx_phy_setup(struct mv88e6xxx_chip *chip) 2441b17aedfSVivien Didelot { 245b15a7c03SVivien Didelot return mv88e6xxx_phy_ppu_enable(chip); 2461b17aedfSVivien Didelot } 247