xref: /linux/drivers/net/dsa/mv88e6xxx/phy.c (revision 0898782247ae533d1f4e47a06bc5d4870931b284)
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