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