linux/drivers/net/dsa/mv88e6xxx/phy.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Marvell 88e6xxx Ethernet switch PHY and PPU support
   4 *
   5 * Copyright (c) 2008 Marvell Semiconductor
   6 *
   7 * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch>
   8 */
   9
  10#include <linux/mdio.h>
  11#include <linux/module.h>
  12
  13#include "chip.h"
  14#include "phy.h"
  15
  16int mv88e6165_phy_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
  17                       int addr, int reg, u16 *val)
  18{
  19        return mv88e6xxx_read(chip, addr, reg, val);
  20}
  21
  22int mv88e6165_phy_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
  23                        int addr, int reg, u16 val)
  24{
  25        return mv88e6xxx_write(chip, addr, reg, val);
  26}
  27
  28int mv88e6xxx_phy_read(struct mv88e6xxx_chip *chip, int phy, int reg, u16 *val)
  29{
  30        int addr = phy; /* PHY devices addresses start at 0x0 */
  31        struct mii_bus *bus;
  32
  33        bus = mv88e6xxx_default_mdio_bus(chip);
  34        if (!bus)
  35                return -EOPNOTSUPP;
  36
  37        if (!chip->info->ops->phy_read)
  38                return -EOPNOTSUPP;
  39
  40        return chip->info->ops->phy_read(chip, bus, addr, reg, val);
  41}
  42
  43int mv88e6xxx_phy_write(struct mv88e6xxx_chip *chip, int phy, int reg, u16 val)
  44{
  45        int addr = phy; /* PHY devices addresses start at 0x0 */
  46        struct mii_bus *bus;
  47
  48        bus = mv88e6xxx_default_mdio_bus(chip);
  49        if (!bus)
  50                return -EOPNOTSUPP;
  51
  52        if (!chip->info->ops->phy_write)
  53                return -EOPNOTSUPP;
  54
  55        return chip->info->ops->phy_write(chip, bus, addr, reg, val);
  56}
  57
  58static int mv88e6xxx_phy_page_get(struct mv88e6xxx_chip *chip, int phy, u8 page)
  59{
  60        return mv88e6xxx_phy_write(chip, phy, MV88E6XXX_PHY_PAGE, page);
  61}
  62
  63static void mv88e6xxx_phy_page_put(struct mv88e6xxx_chip *chip, int phy)
  64{
  65        int err;
  66
  67        /* Restore PHY page Copper 0x0 for access via the registered
  68         * MDIO bus
  69         */
  70        err = mv88e6xxx_phy_write(chip, phy, MV88E6XXX_PHY_PAGE,
  71                                  MV88E6XXX_PHY_PAGE_COPPER);
  72        if (unlikely(err)) {
  73                dev_err(chip->dev,
  74                        "failed to restore PHY %d page Copper (%d)\n",
  75                        phy, err);
  76        }
  77}
  78
  79int mv88e6xxx_phy_page_read(struct mv88e6xxx_chip *chip, int phy,
  80                            u8 page, int reg, u16 *val)
  81{
  82        int err;
  83
  84        /* There is no paging for registers 22 */
  85        if (reg == MV88E6XXX_PHY_PAGE)
  86                return -EINVAL;
  87
  88        err = mv88e6xxx_phy_page_get(chip, phy, page);
  89        if (!err) {
  90                err = mv88e6xxx_phy_read(chip, phy, reg, val);
  91                mv88e6xxx_phy_page_put(chip, phy);
  92        }
  93
  94        return err;
  95}
  96
  97int mv88e6xxx_phy_page_write(struct mv88e6xxx_chip *chip, int phy,
  98                             u8 page, int reg, u16 val)
  99{
 100        int err;
 101
 102        /* There is no paging for registers 22 */
 103        if (reg == MV88E6XXX_PHY_PAGE)
 104                return -EINVAL;
 105
 106        err = mv88e6xxx_phy_page_get(chip, phy, page);
 107        if (!err) {
 108                err = mv88e6xxx_phy_write(chip, phy, MV88E6XXX_PHY_PAGE, page);
 109                if (!err)
 110                        err = mv88e6xxx_phy_write(chip, phy, reg, val);
 111
 112                mv88e6xxx_phy_page_put(chip, phy);
 113        }
 114
 115        return err;
 116}
 117
 118static int mv88e6xxx_phy_ppu_disable(struct mv88e6xxx_chip *chip)
 119{
 120        if (!chip->info->ops->ppu_disable)
 121                return 0;
 122
 123        return chip->info->ops->ppu_disable(chip);
 124}
 125
 126static int mv88e6xxx_phy_ppu_enable(struct mv88e6xxx_chip *chip)
 127{
 128        if (!chip->info->ops->ppu_enable)
 129                return 0;
 130
 131        return chip->info->ops->ppu_enable(chip);
 132}
 133
 134static void mv88e6xxx_phy_ppu_reenable_work(struct work_struct *ugly)
 135{
 136        struct mv88e6xxx_chip *chip;
 137
 138        chip = container_of(ugly, struct mv88e6xxx_chip, ppu_work);
 139
 140        mv88e6xxx_reg_lock(chip);
 141
 142        if (mutex_trylock(&chip->ppu_mutex)) {
 143                if (mv88e6xxx_phy_ppu_enable(chip) == 0)
 144                        chip->ppu_disabled = 0;
 145                mutex_unlock(&chip->ppu_mutex);
 146        }
 147
 148        mv88e6xxx_reg_unlock(chip);
 149}
 150
 151static void mv88e6xxx_phy_ppu_reenable_timer(struct timer_list *t)
 152{
 153        struct mv88e6xxx_chip *chip = from_timer(chip, t, ppu_timer);
 154
 155        schedule_work(&chip->ppu_work);
 156}
 157
 158static int mv88e6xxx_phy_ppu_access_get(struct mv88e6xxx_chip *chip)
 159{
 160        int ret;
 161
 162        mutex_lock(&chip->ppu_mutex);
 163
 164        /* If the PHY polling unit is enabled, disable it so that
 165         * we can access the PHY registers.  If it was already
 166         * disabled, cancel the timer that is going to re-enable
 167         * it.
 168         */
 169        if (!chip->ppu_disabled) {
 170                ret = mv88e6xxx_phy_ppu_disable(chip);
 171                if (ret < 0) {
 172                        mutex_unlock(&chip->ppu_mutex);
 173                        return ret;
 174                }
 175                chip->ppu_disabled = 1;
 176        } else {
 177                del_timer(&chip->ppu_timer);
 178                ret = 0;
 179        }
 180
 181        return ret;
 182}
 183
 184static void mv88e6xxx_phy_ppu_access_put(struct mv88e6xxx_chip *chip)
 185{
 186        /* Schedule a timer to re-enable the PHY polling unit. */
 187        mod_timer(&chip->ppu_timer, jiffies + msecs_to_jiffies(10));
 188        mutex_unlock(&chip->ppu_mutex);
 189}
 190
 191static void mv88e6xxx_phy_ppu_state_init(struct mv88e6xxx_chip *chip)
 192{
 193        mutex_init(&chip->ppu_mutex);
 194        INIT_WORK(&chip->ppu_work, mv88e6xxx_phy_ppu_reenable_work);
 195        timer_setup(&chip->ppu_timer, mv88e6xxx_phy_ppu_reenable_timer, 0);
 196}
 197
 198static void mv88e6xxx_phy_ppu_state_destroy(struct mv88e6xxx_chip *chip)
 199{
 200        del_timer_sync(&chip->ppu_timer);
 201}
 202
 203int mv88e6185_phy_ppu_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
 204                           int addr, int reg, u16 *val)
 205{
 206        int err;
 207
 208        err = mv88e6xxx_phy_ppu_access_get(chip);
 209        if (!err) {
 210                err = mv88e6xxx_read(chip, addr, reg, val);
 211                mv88e6xxx_phy_ppu_access_put(chip);
 212        }
 213
 214        return err;
 215}
 216
 217int mv88e6185_phy_ppu_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
 218                            int addr, int reg, u16 val)
 219{
 220        int err;
 221
 222        err = mv88e6xxx_phy_ppu_access_get(chip);
 223        if (!err) {
 224                err = mv88e6xxx_write(chip, addr, reg, val);
 225                mv88e6xxx_phy_ppu_access_put(chip);
 226        }
 227
 228        return err;
 229}
 230
 231void mv88e6xxx_phy_init(struct mv88e6xxx_chip *chip)
 232{
 233        if (chip->info->ops->ppu_enable && chip->info->ops->ppu_disable)
 234                mv88e6xxx_phy_ppu_state_init(chip);
 235}
 236
 237void mv88e6xxx_phy_destroy(struct mv88e6xxx_chip *chip)
 238{
 239        if (chip->info->ops->ppu_enable && chip->info->ops->ppu_disable)
 240                mv88e6xxx_phy_ppu_state_destroy(chip);
 241}
 242
 243int mv88e6xxx_phy_setup(struct mv88e6xxx_chip *chip)
 244{
 245        return mv88e6xxx_phy_ppu_enable(chip);
 246}
 247