110fa5bfcSAndrew Lunn /* 210fa5bfcSAndrew Lunn * Marvell 88e6xxx Ethernet switch PHY and PPU support 310fa5bfcSAndrew Lunn * 410fa5bfcSAndrew Lunn * Copyright (c) 2008 Marvell Semiconductor 510fa5bfcSAndrew Lunn * 610fa5bfcSAndrew Lunn * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch> 710fa5bfcSAndrew Lunn * 810fa5bfcSAndrew Lunn * This program is free software; you can redistribute it and/or modify 910fa5bfcSAndrew Lunn * it under the terms of the GNU General Public License as published by 1010fa5bfcSAndrew Lunn * the Free Software Foundation; either version 2 of the License, or 1110fa5bfcSAndrew Lunn * (at your option) any later version. 1210fa5bfcSAndrew Lunn */ 1310fa5bfcSAndrew Lunn 1410fa5bfcSAndrew Lunn #include <linux/mdio.h> 1510fa5bfcSAndrew Lunn #include <linux/module.h> 1610fa5bfcSAndrew Lunn #include <net/dsa.h> 1710fa5bfcSAndrew Lunn 184d5f2ba7SVivien Didelot #include "chip.h" 1910fa5bfcSAndrew Lunn #include "phy.h" 2010fa5bfcSAndrew Lunn 2110fa5bfcSAndrew Lunn int mv88e6165_phy_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus, 2210fa5bfcSAndrew Lunn int addr, int reg, u16 *val) 2310fa5bfcSAndrew Lunn { 2410fa5bfcSAndrew Lunn return mv88e6xxx_read(chip, addr, reg, val); 2510fa5bfcSAndrew Lunn } 2610fa5bfcSAndrew Lunn 2710fa5bfcSAndrew Lunn int mv88e6165_phy_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus, 2810fa5bfcSAndrew Lunn int addr, int reg, u16 val) 2910fa5bfcSAndrew Lunn { 3010fa5bfcSAndrew Lunn return mv88e6xxx_write(chip, addr, reg, val); 3110fa5bfcSAndrew Lunn } 3210fa5bfcSAndrew Lunn 3310fa5bfcSAndrew Lunn int mv88e6xxx_phy_read(struct mv88e6xxx_chip *chip, int phy, int reg, u16 *val) 3410fa5bfcSAndrew Lunn { 3510fa5bfcSAndrew Lunn int addr = phy; /* PHY devices addresses start at 0x0 */ 3610fa5bfcSAndrew Lunn struct mii_bus *bus; 3710fa5bfcSAndrew Lunn 3810fa5bfcSAndrew Lunn bus = mv88e6xxx_default_mdio_bus(chip); 3910fa5bfcSAndrew Lunn if (!bus) 4010fa5bfcSAndrew Lunn return -EOPNOTSUPP; 4110fa5bfcSAndrew Lunn 4210fa5bfcSAndrew Lunn if (!chip->info->ops->phy_read) 4310fa5bfcSAndrew Lunn return -EOPNOTSUPP; 4410fa5bfcSAndrew Lunn 4510fa5bfcSAndrew Lunn return chip->info->ops->phy_read(chip, bus, addr, reg, val); 4610fa5bfcSAndrew Lunn } 4710fa5bfcSAndrew Lunn 4810fa5bfcSAndrew Lunn int mv88e6xxx_phy_write(struct mv88e6xxx_chip *chip, int phy, int reg, u16 val) 4910fa5bfcSAndrew Lunn { 5010fa5bfcSAndrew Lunn int addr = phy; /* PHY devices addresses start at 0x0 */ 5110fa5bfcSAndrew Lunn struct mii_bus *bus; 5210fa5bfcSAndrew Lunn 5310fa5bfcSAndrew Lunn bus = mv88e6xxx_default_mdio_bus(chip); 5410fa5bfcSAndrew Lunn if (!bus) 5510fa5bfcSAndrew Lunn return -EOPNOTSUPP; 5610fa5bfcSAndrew Lunn 5710fa5bfcSAndrew Lunn if (!chip->info->ops->phy_write) 5810fa5bfcSAndrew Lunn return -EOPNOTSUPP; 5910fa5bfcSAndrew Lunn 6010fa5bfcSAndrew Lunn return chip->info->ops->phy_write(chip, bus, addr, reg, val); 6110fa5bfcSAndrew Lunn } 6210fa5bfcSAndrew Lunn 6310fa5bfcSAndrew Lunn static int mv88e6xxx_phy_page_get(struct mv88e6xxx_chip *chip, int phy, u8 page) 6410fa5bfcSAndrew Lunn { 6510fa5bfcSAndrew Lunn return mv88e6xxx_phy_write(chip, phy, PHY_PAGE, page); 6610fa5bfcSAndrew Lunn } 6710fa5bfcSAndrew Lunn 6810fa5bfcSAndrew Lunn static void mv88e6xxx_phy_page_put(struct mv88e6xxx_chip *chip, int phy) 6910fa5bfcSAndrew Lunn { 7010fa5bfcSAndrew Lunn int err; 7110fa5bfcSAndrew Lunn 7210fa5bfcSAndrew Lunn /* Restore PHY page Copper 0x0 for access via the registered 7310fa5bfcSAndrew Lunn * MDIO bus 7410fa5bfcSAndrew Lunn */ 7510fa5bfcSAndrew Lunn err = mv88e6xxx_phy_write(chip, phy, PHY_PAGE, PHY_PAGE_COPPER); 7610fa5bfcSAndrew Lunn if (unlikely(err)) { 7710fa5bfcSAndrew Lunn dev_err(chip->dev, 7810fa5bfcSAndrew Lunn "failed to restore PHY %d page Copper (%d)\n", 7910fa5bfcSAndrew Lunn phy, err); 8010fa5bfcSAndrew Lunn } 8110fa5bfcSAndrew Lunn } 8210fa5bfcSAndrew Lunn 8310fa5bfcSAndrew Lunn int mv88e6xxx_phy_page_read(struct mv88e6xxx_chip *chip, int phy, 8410fa5bfcSAndrew Lunn u8 page, int reg, u16 *val) 8510fa5bfcSAndrew Lunn { 8610fa5bfcSAndrew Lunn int err; 8710fa5bfcSAndrew Lunn 8810fa5bfcSAndrew Lunn /* There is no paging for registers 22 */ 8910fa5bfcSAndrew Lunn if (reg == PHY_PAGE) 9010fa5bfcSAndrew Lunn return -EINVAL; 9110fa5bfcSAndrew Lunn 9210fa5bfcSAndrew Lunn err = mv88e6xxx_phy_page_get(chip, phy, page); 9310fa5bfcSAndrew Lunn if (!err) { 9410fa5bfcSAndrew Lunn err = mv88e6xxx_phy_read(chip, phy, reg, val); 9510fa5bfcSAndrew Lunn mv88e6xxx_phy_page_put(chip, phy); 9610fa5bfcSAndrew Lunn } 9710fa5bfcSAndrew Lunn 9810fa5bfcSAndrew Lunn return err; 9910fa5bfcSAndrew Lunn } 10010fa5bfcSAndrew Lunn 10110fa5bfcSAndrew Lunn int mv88e6xxx_phy_page_write(struct mv88e6xxx_chip *chip, int phy, 10210fa5bfcSAndrew Lunn u8 page, int reg, u16 val) 10310fa5bfcSAndrew Lunn { 10410fa5bfcSAndrew Lunn int err; 10510fa5bfcSAndrew Lunn 10610fa5bfcSAndrew Lunn /* There is no paging for registers 22 */ 10710fa5bfcSAndrew Lunn if (reg == PHY_PAGE) 10810fa5bfcSAndrew Lunn return -EINVAL; 10910fa5bfcSAndrew Lunn 11010fa5bfcSAndrew Lunn err = mv88e6xxx_phy_page_get(chip, phy, page); 11110fa5bfcSAndrew Lunn if (!err) { 11210fa5bfcSAndrew Lunn err = mv88e6xxx_phy_write(chip, phy, PHY_PAGE, page); 11310fa5bfcSAndrew Lunn mv88e6xxx_phy_page_put(chip, phy); 11410fa5bfcSAndrew Lunn } 11510fa5bfcSAndrew Lunn 11610fa5bfcSAndrew Lunn return err; 11710fa5bfcSAndrew Lunn } 11810fa5bfcSAndrew Lunn 119b15a7c03SVivien Didelot static int mv88e6xxx_phy_ppu_disable(struct mv88e6xxx_chip *chip) 12010fa5bfcSAndrew Lunn { 12110fa5bfcSAndrew Lunn if (!chip->info->ops->ppu_disable) 12210fa5bfcSAndrew Lunn return 0; 12310fa5bfcSAndrew Lunn 12410fa5bfcSAndrew Lunn return chip->info->ops->ppu_disable(chip); 12510fa5bfcSAndrew Lunn } 12610fa5bfcSAndrew Lunn 127b15a7c03SVivien Didelot static int mv88e6xxx_phy_ppu_enable(struct mv88e6xxx_chip *chip) 12810fa5bfcSAndrew Lunn { 12910fa5bfcSAndrew Lunn if (!chip->info->ops->ppu_enable) 13010fa5bfcSAndrew Lunn return 0; 13110fa5bfcSAndrew Lunn 13210fa5bfcSAndrew Lunn return chip->info->ops->ppu_enable(chip); 13310fa5bfcSAndrew Lunn } 13410fa5bfcSAndrew Lunn 135b15a7c03SVivien Didelot static void mv88e6xxx_phy_ppu_reenable_work(struct work_struct *ugly) 13610fa5bfcSAndrew Lunn { 13710fa5bfcSAndrew Lunn struct mv88e6xxx_chip *chip; 13810fa5bfcSAndrew Lunn 13910fa5bfcSAndrew Lunn chip = container_of(ugly, struct mv88e6xxx_chip, ppu_work); 14010fa5bfcSAndrew Lunn 14110fa5bfcSAndrew Lunn mutex_lock(&chip->reg_lock); 14210fa5bfcSAndrew Lunn 14310fa5bfcSAndrew Lunn if (mutex_trylock(&chip->ppu_mutex)) { 144b15a7c03SVivien Didelot if (mv88e6xxx_phy_ppu_enable(chip) == 0) 14510fa5bfcSAndrew Lunn chip->ppu_disabled = 0; 14610fa5bfcSAndrew Lunn mutex_unlock(&chip->ppu_mutex); 14710fa5bfcSAndrew Lunn } 14810fa5bfcSAndrew Lunn 14910fa5bfcSAndrew Lunn mutex_unlock(&chip->reg_lock); 15010fa5bfcSAndrew Lunn } 15110fa5bfcSAndrew Lunn 152b15a7c03SVivien Didelot static void mv88e6xxx_phy_ppu_reenable_timer(unsigned long _ps) 15310fa5bfcSAndrew Lunn { 15410fa5bfcSAndrew Lunn struct mv88e6xxx_chip *chip = (void *)_ps; 15510fa5bfcSAndrew Lunn 15610fa5bfcSAndrew Lunn schedule_work(&chip->ppu_work); 15710fa5bfcSAndrew Lunn } 15810fa5bfcSAndrew Lunn 159b15a7c03SVivien Didelot static int mv88e6xxx_phy_ppu_access_get(struct mv88e6xxx_chip *chip) 16010fa5bfcSAndrew Lunn { 16110fa5bfcSAndrew Lunn int ret; 16210fa5bfcSAndrew Lunn 16310fa5bfcSAndrew Lunn mutex_lock(&chip->ppu_mutex); 16410fa5bfcSAndrew Lunn 16510fa5bfcSAndrew Lunn /* If the PHY polling unit is enabled, disable it so that 16610fa5bfcSAndrew Lunn * we can access the PHY registers. If it was already 16710fa5bfcSAndrew Lunn * disabled, cancel the timer that is going to re-enable 16810fa5bfcSAndrew Lunn * it. 16910fa5bfcSAndrew Lunn */ 17010fa5bfcSAndrew Lunn if (!chip->ppu_disabled) { 171b15a7c03SVivien Didelot ret = mv88e6xxx_phy_ppu_disable(chip); 17210fa5bfcSAndrew Lunn if (ret < 0) { 17310fa5bfcSAndrew Lunn mutex_unlock(&chip->ppu_mutex); 17410fa5bfcSAndrew Lunn return ret; 17510fa5bfcSAndrew Lunn } 17610fa5bfcSAndrew Lunn chip->ppu_disabled = 1; 17710fa5bfcSAndrew Lunn } else { 17810fa5bfcSAndrew Lunn del_timer(&chip->ppu_timer); 17910fa5bfcSAndrew Lunn ret = 0; 18010fa5bfcSAndrew Lunn } 18110fa5bfcSAndrew Lunn 18210fa5bfcSAndrew Lunn return ret; 18310fa5bfcSAndrew Lunn } 18410fa5bfcSAndrew Lunn 185b15a7c03SVivien Didelot static void mv88e6xxx_phy_ppu_access_put(struct mv88e6xxx_chip *chip) 18610fa5bfcSAndrew Lunn { 18710fa5bfcSAndrew Lunn /* Schedule a timer to re-enable the PHY polling unit. */ 18810fa5bfcSAndrew Lunn mod_timer(&chip->ppu_timer, jiffies + msecs_to_jiffies(10)); 18910fa5bfcSAndrew Lunn mutex_unlock(&chip->ppu_mutex); 19010fa5bfcSAndrew Lunn } 19110fa5bfcSAndrew Lunn 192b15a7c03SVivien Didelot static void mv88e6xxx_phy_ppu_state_init(struct mv88e6xxx_chip *chip) 19310fa5bfcSAndrew Lunn { 19410fa5bfcSAndrew Lunn mutex_init(&chip->ppu_mutex); 195b15a7c03SVivien Didelot INIT_WORK(&chip->ppu_work, mv88e6xxx_phy_ppu_reenable_work); 196b15a7c03SVivien Didelot setup_timer(&chip->ppu_timer, mv88e6xxx_phy_ppu_reenable_timer, 19710fa5bfcSAndrew Lunn (unsigned long)chip); 19810fa5bfcSAndrew Lunn } 19910fa5bfcSAndrew Lunn 200b15a7c03SVivien Didelot static void mv88e6xxx_phy_ppu_state_destroy(struct mv88e6xxx_chip *chip) 20110fa5bfcSAndrew Lunn { 20210fa5bfcSAndrew Lunn del_timer_sync(&chip->ppu_timer); 20310fa5bfcSAndrew Lunn } 20410fa5bfcSAndrew Lunn 2057e20cfb5SVivien Didelot int mv88e6185_phy_ppu_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus, 20610fa5bfcSAndrew Lunn int addr, int reg, u16 *val) 20710fa5bfcSAndrew Lunn { 20810fa5bfcSAndrew Lunn int err; 20910fa5bfcSAndrew Lunn 210b15a7c03SVivien Didelot err = mv88e6xxx_phy_ppu_access_get(chip); 21110fa5bfcSAndrew Lunn if (!err) { 21210fa5bfcSAndrew Lunn err = mv88e6xxx_read(chip, addr, reg, val); 213b15a7c03SVivien Didelot mv88e6xxx_phy_ppu_access_put(chip); 21410fa5bfcSAndrew Lunn } 21510fa5bfcSAndrew Lunn 21610fa5bfcSAndrew Lunn return err; 21710fa5bfcSAndrew Lunn } 21810fa5bfcSAndrew Lunn 2197e20cfb5SVivien Didelot int mv88e6185_phy_ppu_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus, 22010fa5bfcSAndrew Lunn int addr, int reg, u16 val) 22110fa5bfcSAndrew Lunn { 22210fa5bfcSAndrew Lunn int err; 22310fa5bfcSAndrew Lunn 224b15a7c03SVivien Didelot err = mv88e6xxx_phy_ppu_access_get(chip); 22510fa5bfcSAndrew Lunn if (!err) { 22610fa5bfcSAndrew Lunn err = mv88e6xxx_write(chip, addr, reg, val); 227b15a7c03SVivien Didelot mv88e6xxx_phy_ppu_access_put(chip); 22810fa5bfcSAndrew Lunn } 22910fa5bfcSAndrew Lunn 23010fa5bfcSAndrew Lunn return err; 23110fa5bfcSAndrew Lunn } 23210fa5bfcSAndrew Lunn 23310fa5bfcSAndrew Lunn void mv88e6xxx_phy_init(struct mv88e6xxx_chip *chip) 23410fa5bfcSAndrew Lunn { 23510fa5bfcSAndrew Lunn if (chip->info->ops->ppu_enable && chip->info->ops->ppu_disable) 236b15a7c03SVivien Didelot mv88e6xxx_phy_ppu_state_init(chip); 23710fa5bfcSAndrew Lunn } 23810fa5bfcSAndrew Lunn 23910fa5bfcSAndrew Lunn void mv88e6xxx_phy_destroy(struct mv88e6xxx_chip *chip) 24010fa5bfcSAndrew Lunn { 24110fa5bfcSAndrew Lunn if (chip->info->ops->ppu_enable && chip->info->ops->ppu_disable) 242b15a7c03SVivien Didelot mv88e6xxx_phy_ppu_state_destroy(chip); 24310fa5bfcSAndrew Lunn } 2441b17aedfSVivien Didelot 2451b17aedfSVivien Didelot int mv88e6xxx_phy_setup(struct mv88e6xxx_chip *chip) 2461b17aedfSVivien Didelot { 247b15a7c03SVivien Didelot return mv88e6xxx_phy_ppu_enable(chip); 2481b17aedfSVivien Didelot } 249