xref: /openbmc/linux/drivers/net/dsa/mv88e6xxx/phy.c (revision 743a19e3)
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 
mv88e6165_phy_read(struct mv88e6xxx_chip * chip,struct mii_bus * bus,int addr,int reg,u16 * val)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 
mv88e6165_phy_write(struct mv88e6xxx_chip * chip,struct mii_bus * bus,int addr,int reg,u16 val)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 
mv88e6xxx_phy_read(struct mv88e6xxx_chip * chip,int phy,int reg,u16 * val)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 
mv88e6xxx_phy_write(struct mv88e6xxx_chip * chip,int phy,int reg,u16 val)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 
mv88e6xxx_phy_read_c45(struct mv88e6xxx_chip * chip,int phy,int devad,int reg,u16 * val)58*743a19e3SAndrew Lunn int mv88e6xxx_phy_read_c45(struct mv88e6xxx_chip *chip, int phy, int devad,
59*743a19e3SAndrew Lunn 			   int reg, u16 *val)
60*743a19e3SAndrew Lunn {
61*743a19e3SAndrew Lunn 	int addr = phy; /* PHY devices addresses start at 0x0 */
62*743a19e3SAndrew Lunn 	struct mii_bus *bus;
63*743a19e3SAndrew Lunn 
64*743a19e3SAndrew Lunn 	bus = mv88e6xxx_default_mdio_bus(chip);
65*743a19e3SAndrew Lunn 	if (!bus)
66*743a19e3SAndrew Lunn 		return -EOPNOTSUPP;
67*743a19e3SAndrew Lunn 
68*743a19e3SAndrew Lunn 	if (!chip->info->ops->phy_read_c45)
69*743a19e3SAndrew Lunn 		return -EOPNOTSUPP;
70*743a19e3SAndrew Lunn 
71*743a19e3SAndrew Lunn 	return chip->info->ops->phy_read_c45(chip, bus, addr, devad, reg, val);
72*743a19e3SAndrew Lunn }
73*743a19e3SAndrew Lunn 
mv88e6xxx_phy_write_c45(struct mv88e6xxx_chip * chip,int phy,int devad,int reg,u16 val)74*743a19e3SAndrew Lunn int mv88e6xxx_phy_write_c45(struct mv88e6xxx_chip *chip, int phy, int devad,
75*743a19e3SAndrew Lunn 			    int reg, u16 val)
76*743a19e3SAndrew Lunn {
77*743a19e3SAndrew Lunn 	int addr = phy; /* PHY devices addresses start at 0x0 */
78*743a19e3SAndrew Lunn 	struct mii_bus *bus;
79*743a19e3SAndrew Lunn 
80*743a19e3SAndrew Lunn 	bus = mv88e6xxx_default_mdio_bus(chip);
81*743a19e3SAndrew Lunn 	if (!bus)
82*743a19e3SAndrew Lunn 		return -EOPNOTSUPP;
83*743a19e3SAndrew Lunn 
84*743a19e3SAndrew Lunn 	if (!chip->info->ops->phy_write_c45)
85*743a19e3SAndrew Lunn 		return -EOPNOTSUPP;
86*743a19e3SAndrew Lunn 
87*743a19e3SAndrew Lunn 	return chip->info->ops->phy_write_c45(chip, bus, addr, devad, reg, val);
88*743a19e3SAndrew Lunn }
89*743a19e3SAndrew Lunn 
mv88e6xxx_phy_page_get(struct mv88e6xxx_chip * chip,int phy,u8 page)9010fa5bfcSAndrew Lunn static int mv88e6xxx_phy_page_get(struct mv88e6xxx_chip *chip, int phy, u8 page)
9110fa5bfcSAndrew Lunn {
92bec90b6dSVivien Didelot 	return mv88e6xxx_phy_write(chip, phy, MV88E6XXX_PHY_PAGE, page);
9310fa5bfcSAndrew Lunn }
9410fa5bfcSAndrew Lunn 
mv88e6xxx_phy_page_put(struct mv88e6xxx_chip * chip,int phy)9510fa5bfcSAndrew Lunn static void mv88e6xxx_phy_page_put(struct mv88e6xxx_chip *chip, int phy)
9610fa5bfcSAndrew Lunn {
9710fa5bfcSAndrew Lunn 	int err;
9810fa5bfcSAndrew Lunn 
9910fa5bfcSAndrew Lunn 	/* Restore PHY page Copper 0x0 for access via the registered
10010fa5bfcSAndrew Lunn 	 * MDIO bus
10110fa5bfcSAndrew Lunn 	 */
102bec90b6dSVivien Didelot 	err = mv88e6xxx_phy_write(chip, phy, MV88E6XXX_PHY_PAGE,
103bec90b6dSVivien Didelot 				  MV88E6XXX_PHY_PAGE_COPPER);
10410fa5bfcSAndrew Lunn 	if (unlikely(err)) {
10510fa5bfcSAndrew Lunn 		dev_err(chip->dev,
10610fa5bfcSAndrew Lunn 			"failed to restore PHY %d page Copper (%d)\n",
10710fa5bfcSAndrew Lunn 			phy, err);
10810fa5bfcSAndrew Lunn 	}
10910fa5bfcSAndrew Lunn }
11010fa5bfcSAndrew Lunn 
mv88e6xxx_phy_page_read(struct mv88e6xxx_chip * chip,int phy,u8 page,int reg,u16 * val)11110fa5bfcSAndrew Lunn int mv88e6xxx_phy_page_read(struct mv88e6xxx_chip *chip, int phy,
11210fa5bfcSAndrew Lunn 			    u8 page, int reg, u16 *val)
11310fa5bfcSAndrew Lunn {
11410fa5bfcSAndrew Lunn 	int err;
11510fa5bfcSAndrew Lunn 
11610fa5bfcSAndrew Lunn 	/* There is no paging for registers 22 */
117bec90b6dSVivien Didelot 	if (reg == MV88E6XXX_PHY_PAGE)
11810fa5bfcSAndrew Lunn 		return -EINVAL;
11910fa5bfcSAndrew Lunn 
12010fa5bfcSAndrew Lunn 	err = mv88e6xxx_phy_page_get(chip, phy, page);
12110fa5bfcSAndrew Lunn 	if (!err) {
12210fa5bfcSAndrew Lunn 		err = mv88e6xxx_phy_read(chip, phy, reg, val);
12310fa5bfcSAndrew Lunn 		mv88e6xxx_phy_page_put(chip, phy);
12410fa5bfcSAndrew Lunn 	}
12510fa5bfcSAndrew Lunn 
12610fa5bfcSAndrew Lunn 	return err;
12710fa5bfcSAndrew Lunn }
12810fa5bfcSAndrew Lunn 
mv88e6xxx_phy_page_write(struct mv88e6xxx_chip * chip,int phy,u8 page,int reg,u16 val)12910fa5bfcSAndrew Lunn int mv88e6xxx_phy_page_write(struct mv88e6xxx_chip *chip, int phy,
13010fa5bfcSAndrew Lunn 			     u8 page, int reg, u16 val)
13110fa5bfcSAndrew Lunn {
13210fa5bfcSAndrew Lunn 	int err;
13310fa5bfcSAndrew Lunn 
13410fa5bfcSAndrew Lunn 	/* There is no paging for registers 22 */
135bec90b6dSVivien Didelot 	if (reg == MV88E6XXX_PHY_PAGE)
13610fa5bfcSAndrew Lunn 		return -EINVAL;
13710fa5bfcSAndrew Lunn 
13810fa5bfcSAndrew Lunn 	err = mv88e6xxx_phy_page_get(chip, phy, page);
13910fa5bfcSAndrew Lunn 	if (!err) {
140bec90b6dSVivien Didelot 		err = mv88e6xxx_phy_write(chip, phy, MV88E6XXX_PHY_PAGE, page);
141c309b158SAndrew Lunn 		if (!err)
142c309b158SAndrew Lunn 			err = mv88e6xxx_phy_write(chip, phy, reg, val);
143c309b158SAndrew Lunn 
14410fa5bfcSAndrew Lunn 		mv88e6xxx_phy_page_put(chip, phy);
14510fa5bfcSAndrew Lunn 	}
14610fa5bfcSAndrew Lunn 
14710fa5bfcSAndrew Lunn 	return err;
14810fa5bfcSAndrew Lunn }
14910fa5bfcSAndrew Lunn 
mv88e6xxx_phy_ppu_disable(struct mv88e6xxx_chip * chip)150b15a7c03SVivien Didelot static int mv88e6xxx_phy_ppu_disable(struct mv88e6xxx_chip *chip)
15110fa5bfcSAndrew Lunn {
15210fa5bfcSAndrew Lunn 	if (!chip->info->ops->ppu_disable)
15310fa5bfcSAndrew Lunn 		return 0;
15410fa5bfcSAndrew Lunn 
15510fa5bfcSAndrew Lunn 	return chip->info->ops->ppu_disable(chip);
15610fa5bfcSAndrew Lunn }
15710fa5bfcSAndrew Lunn 
mv88e6xxx_phy_ppu_enable(struct mv88e6xxx_chip * chip)158b15a7c03SVivien Didelot static int mv88e6xxx_phy_ppu_enable(struct mv88e6xxx_chip *chip)
15910fa5bfcSAndrew Lunn {
16010fa5bfcSAndrew Lunn 	if (!chip->info->ops->ppu_enable)
16110fa5bfcSAndrew Lunn 		return 0;
16210fa5bfcSAndrew Lunn 
16310fa5bfcSAndrew Lunn 	return chip->info->ops->ppu_enable(chip);
16410fa5bfcSAndrew Lunn }
16510fa5bfcSAndrew Lunn 
mv88e6xxx_phy_ppu_reenable_work(struct work_struct * ugly)166b15a7c03SVivien Didelot static void mv88e6xxx_phy_ppu_reenable_work(struct work_struct *ugly)
16710fa5bfcSAndrew Lunn {
16810fa5bfcSAndrew Lunn 	struct mv88e6xxx_chip *chip;
16910fa5bfcSAndrew Lunn 
17010fa5bfcSAndrew Lunn 	chip = container_of(ugly, struct mv88e6xxx_chip, ppu_work);
17110fa5bfcSAndrew Lunn 
172c9acece0SRasmus Villemoes 	mv88e6xxx_reg_lock(chip);
17310fa5bfcSAndrew Lunn 
17410fa5bfcSAndrew Lunn 	if (mutex_trylock(&chip->ppu_mutex)) {
175b15a7c03SVivien Didelot 		if (mv88e6xxx_phy_ppu_enable(chip) == 0)
17610fa5bfcSAndrew Lunn 			chip->ppu_disabled = 0;
17710fa5bfcSAndrew Lunn 		mutex_unlock(&chip->ppu_mutex);
17810fa5bfcSAndrew Lunn 	}
17910fa5bfcSAndrew Lunn 
180c9acece0SRasmus Villemoes 	mv88e6xxx_reg_unlock(chip);
18110fa5bfcSAndrew Lunn }
18210fa5bfcSAndrew Lunn 
mv88e6xxx_phy_ppu_reenable_timer(struct timer_list * t)183e99e88a9SKees Cook static void mv88e6xxx_phy_ppu_reenable_timer(struct timer_list *t)
18410fa5bfcSAndrew Lunn {
185e99e88a9SKees Cook 	struct mv88e6xxx_chip *chip = from_timer(chip, t, ppu_timer);
18610fa5bfcSAndrew Lunn 
18710fa5bfcSAndrew Lunn 	schedule_work(&chip->ppu_work);
18810fa5bfcSAndrew Lunn }
18910fa5bfcSAndrew Lunn 
mv88e6xxx_phy_ppu_access_get(struct mv88e6xxx_chip * chip)190b15a7c03SVivien Didelot static int mv88e6xxx_phy_ppu_access_get(struct mv88e6xxx_chip *chip)
19110fa5bfcSAndrew Lunn {
19210fa5bfcSAndrew Lunn 	int ret;
19310fa5bfcSAndrew Lunn 
19410fa5bfcSAndrew Lunn 	mutex_lock(&chip->ppu_mutex);
19510fa5bfcSAndrew Lunn 
19610fa5bfcSAndrew Lunn 	/* If the PHY polling unit is enabled, disable it so that
19710fa5bfcSAndrew Lunn 	 * we can access the PHY registers.  If it was already
19810fa5bfcSAndrew Lunn 	 * disabled, cancel the timer that is going to re-enable
19910fa5bfcSAndrew Lunn 	 * it.
20010fa5bfcSAndrew Lunn 	 */
20110fa5bfcSAndrew Lunn 	if (!chip->ppu_disabled) {
202b15a7c03SVivien Didelot 		ret = mv88e6xxx_phy_ppu_disable(chip);
20310fa5bfcSAndrew Lunn 		if (ret < 0) {
20410fa5bfcSAndrew Lunn 			mutex_unlock(&chip->ppu_mutex);
20510fa5bfcSAndrew Lunn 			return ret;
20610fa5bfcSAndrew Lunn 		}
20710fa5bfcSAndrew Lunn 		chip->ppu_disabled = 1;
20810fa5bfcSAndrew Lunn 	} else {
20910fa5bfcSAndrew Lunn 		del_timer(&chip->ppu_timer);
21010fa5bfcSAndrew Lunn 		ret = 0;
21110fa5bfcSAndrew Lunn 	}
21210fa5bfcSAndrew Lunn 
21310fa5bfcSAndrew Lunn 	return ret;
21410fa5bfcSAndrew Lunn }
21510fa5bfcSAndrew Lunn 
mv88e6xxx_phy_ppu_access_put(struct mv88e6xxx_chip * chip)216b15a7c03SVivien Didelot static void mv88e6xxx_phy_ppu_access_put(struct mv88e6xxx_chip *chip)
21710fa5bfcSAndrew Lunn {
21810fa5bfcSAndrew Lunn 	/* Schedule a timer to re-enable the PHY polling unit. */
21910fa5bfcSAndrew Lunn 	mod_timer(&chip->ppu_timer, jiffies + msecs_to_jiffies(10));
22010fa5bfcSAndrew Lunn 	mutex_unlock(&chip->ppu_mutex);
22110fa5bfcSAndrew Lunn }
22210fa5bfcSAndrew Lunn 
mv88e6xxx_phy_ppu_state_init(struct mv88e6xxx_chip * chip)223b15a7c03SVivien Didelot static void mv88e6xxx_phy_ppu_state_init(struct mv88e6xxx_chip *chip)
22410fa5bfcSAndrew Lunn {
22510fa5bfcSAndrew Lunn 	mutex_init(&chip->ppu_mutex);
226b15a7c03SVivien Didelot 	INIT_WORK(&chip->ppu_work, mv88e6xxx_phy_ppu_reenable_work);
227e99e88a9SKees Cook 	timer_setup(&chip->ppu_timer, mv88e6xxx_phy_ppu_reenable_timer, 0);
22810fa5bfcSAndrew Lunn }
22910fa5bfcSAndrew Lunn 
mv88e6xxx_phy_ppu_state_destroy(struct mv88e6xxx_chip * chip)230b15a7c03SVivien Didelot static void mv88e6xxx_phy_ppu_state_destroy(struct mv88e6xxx_chip *chip)
23110fa5bfcSAndrew Lunn {
23210fa5bfcSAndrew Lunn 	del_timer_sync(&chip->ppu_timer);
23310fa5bfcSAndrew Lunn }
23410fa5bfcSAndrew Lunn 
mv88e6185_phy_ppu_read(struct mv88e6xxx_chip * chip,struct mii_bus * bus,int addr,int reg,u16 * val)2357e20cfb5SVivien Didelot int mv88e6185_phy_ppu_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
23610fa5bfcSAndrew Lunn 			   int addr, int reg, u16 *val)
23710fa5bfcSAndrew Lunn {
23810fa5bfcSAndrew Lunn 	int err;
23910fa5bfcSAndrew Lunn 
240b15a7c03SVivien Didelot 	err = mv88e6xxx_phy_ppu_access_get(chip);
24110fa5bfcSAndrew Lunn 	if (!err) {
24210fa5bfcSAndrew Lunn 		err = mv88e6xxx_read(chip, addr, reg, val);
243b15a7c03SVivien Didelot 		mv88e6xxx_phy_ppu_access_put(chip);
24410fa5bfcSAndrew Lunn 	}
24510fa5bfcSAndrew Lunn 
24610fa5bfcSAndrew Lunn 	return err;
24710fa5bfcSAndrew Lunn }
24810fa5bfcSAndrew Lunn 
mv88e6185_phy_ppu_write(struct mv88e6xxx_chip * chip,struct mii_bus * bus,int addr,int reg,u16 val)2497e20cfb5SVivien Didelot int mv88e6185_phy_ppu_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
25010fa5bfcSAndrew Lunn 			    int addr, int reg, u16 val)
25110fa5bfcSAndrew Lunn {
25210fa5bfcSAndrew Lunn 	int err;
25310fa5bfcSAndrew Lunn 
254b15a7c03SVivien Didelot 	err = mv88e6xxx_phy_ppu_access_get(chip);
25510fa5bfcSAndrew Lunn 	if (!err) {
25610fa5bfcSAndrew Lunn 		err = mv88e6xxx_write(chip, addr, reg, val);
257b15a7c03SVivien Didelot 		mv88e6xxx_phy_ppu_access_put(chip);
25810fa5bfcSAndrew Lunn 	}
25910fa5bfcSAndrew Lunn 
26010fa5bfcSAndrew Lunn 	return err;
26110fa5bfcSAndrew Lunn }
26210fa5bfcSAndrew Lunn 
mv88e6xxx_phy_init(struct mv88e6xxx_chip * chip)26310fa5bfcSAndrew Lunn void mv88e6xxx_phy_init(struct mv88e6xxx_chip *chip)
26410fa5bfcSAndrew Lunn {
26510fa5bfcSAndrew Lunn 	if (chip->info->ops->ppu_enable && chip->info->ops->ppu_disable)
266b15a7c03SVivien Didelot 		mv88e6xxx_phy_ppu_state_init(chip);
26710fa5bfcSAndrew Lunn }
26810fa5bfcSAndrew Lunn 
mv88e6xxx_phy_destroy(struct mv88e6xxx_chip * chip)26910fa5bfcSAndrew Lunn void mv88e6xxx_phy_destroy(struct mv88e6xxx_chip *chip)
27010fa5bfcSAndrew Lunn {
27110fa5bfcSAndrew Lunn 	if (chip->info->ops->ppu_enable && chip->info->ops->ppu_disable)
272b15a7c03SVivien Didelot 		mv88e6xxx_phy_ppu_state_destroy(chip);
27310fa5bfcSAndrew Lunn }
2741b17aedfSVivien Didelot 
mv88e6xxx_phy_setup(struct mv88e6xxx_chip * chip)2751b17aedfSVivien Didelot int mv88e6xxx_phy_setup(struct mv88e6xxx_chip *chip)
2761b17aedfSVivien Didelot {
277b15a7c03SVivien Didelot 	return mv88e6xxx_phy_ppu_enable(chip);
2781b17aedfSVivien Didelot }
279