root/drivers/net/phy/mdio-mscc-miim.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mscc_miim_wait_ready
  2. mscc_miim_read
  3. mscc_miim_write
  4. mscc_miim_reset
  5. mscc_miim_probe
  6. mscc_miim_remove

   1 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
   2 /*
   3  * Driver for the MDIO interface of Microsemi network switches.
   4  *
   5  * Author: Alexandre Belloni <alexandre.belloni@bootlin.com>
   6  * Copyright (c) 2017 Microsemi Corporation
   7  */
   8 
   9 #include <linux/kernel.h>
  10 #include <linux/module.h>
  11 #include <linux/phy.h>
  12 #include <linux/platform_device.h>
  13 #include <linux/bitops.h>
  14 #include <linux/io.h>
  15 #include <linux/iopoll.h>
  16 #include <linux/of_mdio.h>
  17 
  18 #define MSCC_MIIM_REG_STATUS            0x0
  19 #define         MSCC_MIIM_STATUS_STAT_BUSY      BIT(3)
  20 #define MSCC_MIIM_REG_CMD               0x8
  21 #define         MSCC_MIIM_CMD_OPR_WRITE         BIT(1)
  22 #define         MSCC_MIIM_CMD_OPR_READ          BIT(2)
  23 #define         MSCC_MIIM_CMD_WRDATA_SHIFT      4
  24 #define         MSCC_MIIM_CMD_REGAD_SHIFT       20
  25 #define         MSCC_MIIM_CMD_PHYAD_SHIFT       25
  26 #define         MSCC_MIIM_CMD_VLD               BIT(31)
  27 #define MSCC_MIIM_REG_DATA              0xC
  28 #define         MSCC_MIIM_DATA_ERROR            (BIT(16) | BIT(17))
  29 
  30 #define MSCC_PHY_REG_PHY_CFG    0x0
  31 #define         PHY_CFG_PHY_ENA         (BIT(0) | BIT(1) | BIT(2) | BIT(3))
  32 #define         PHY_CFG_PHY_COMMON_RESET BIT(4)
  33 #define         PHY_CFG_PHY_RESET       (BIT(5) | BIT(6) | BIT(7) | BIT(8))
  34 #define MSCC_PHY_REG_PHY_STATUS 0x4
  35 
  36 struct mscc_miim_dev {
  37         void __iomem *regs;
  38         void __iomem *phy_regs;
  39 };
  40 
  41 static int mscc_miim_wait_ready(struct mii_bus *bus)
  42 {
  43         struct mscc_miim_dev *miim = bus->priv;
  44         u32 val;
  45 
  46         readl_poll_timeout(miim->regs + MSCC_MIIM_REG_STATUS, val,
  47                            !(val & MSCC_MIIM_STATUS_STAT_BUSY), 100, 250000);
  48         if (val & MSCC_MIIM_STATUS_STAT_BUSY)
  49                 return -ETIMEDOUT;
  50 
  51         return 0;
  52 }
  53 
  54 static int mscc_miim_read(struct mii_bus *bus, int mii_id, int regnum)
  55 {
  56         struct mscc_miim_dev *miim = bus->priv;
  57         u32 val;
  58         int ret;
  59 
  60         ret = mscc_miim_wait_ready(bus);
  61         if (ret)
  62                 goto out;
  63 
  64         writel(MSCC_MIIM_CMD_VLD | (mii_id << MSCC_MIIM_CMD_PHYAD_SHIFT) |
  65                (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) | MSCC_MIIM_CMD_OPR_READ,
  66                miim->regs + MSCC_MIIM_REG_CMD);
  67 
  68         ret = mscc_miim_wait_ready(bus);
  69         if (ret)
  70                 goto out;
  71 
  72         val = readl(miim->regs + MSCC_MIIM_REG_DATA);
  73         if (val & MSCC_MIIM_DATA_ERROR) {
  74                 ret = -EIO;
  75                 goto out;
  76         }
  77 
  78         ret = val & 0xFFFF;
  79 out:
  80         return ret;
  81 }
  82 
  83 static int mscc_miim_write(struct mii_bus *bus, int mii_id,
  84                            int regnum, u16 value)
  85 {
  86         struct mscc_miim_dev *miim = bus->priv;
  87         int ret;
  88 
  89         ret = mscc_miim_wait_ready(bus);
  90         if (ret < 0)
  91                 goto out;
  92 
  93         writel(MSCC_MIIM_CMD_VLD | (mii_id << MSCC_MIIM_CMD_PHYAD_SHIFT) |
  94                (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) |
  95                (value << MSCC_MIIM_CMD_WRDATA_SHIFT) |
  96                MSCC_MIIM_CMD_OPR_WRITE,
  97                miim->regs + MSCC_MIIM_REG_CMD);
  98 
  99 out:
 100         return ret;
 101 }
 102 
 103 static int mscc_miim_reset(struct mii_bus *bus)
 104 {
 105         struct mscc_miim_dev *miim = bus->priv;
 106 
 107         if (miim->phy_regs) {
 108                 writel(0, miim->phy_regs + MSCC_PHY_REG_PHY_CFG);
 109                 writel(0x1ff, miim->phy_regs + MSCC_PHY_REG_PHY_CFG);
 110                 mdelay(500);
 111         }
 112 
 113         return 0;
 114 }
 115 
 116 static int mscc_miim_probe(struct platform_device *pdev)
 117 {
 118         struct resource *res;
 119         struct mii_bus *bus;
 120         struct mscc_miim_dev *dev;
 121         int ret;
 122 
 123         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 124         if (!res)
 125                 return -ENODEV;
 126 
 127         bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*dev));
 128         if (!bus)
 129                 return -ENOMEM;
 130 
 131         bus->name = "mscc_miim";
 132         bus->read = mscc_miim_read;
 133         bus->write = mscc_miim_write;
 134         bus->reset = mscc_miim_reset;
 135         snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
 136         bus->parent = &pdev->dev;
 137 
 138         dev = bus->priv;
 139         dev->regs = devm_ioremap_resource(&pdev->dev, res);
 140         if (IS_ERR(dev->regs)) {
 141                 dev_err(&pdev->dev, "Unable to map MIIM registers\n");
 142                 return PTR_ERR(dev->regs);
 143         }
 144 
 145         res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 146         if (res) {
 147                 dev->phy_regs = devm_ioremap_resource(&pdev->dev, res);
 148                 if (IS_ERR(dev->phy_regs)) {
 149                         dev_err(&pdev->dev, "Unable to map internal phy registers\n");
 150                         return PTR_ERR(dev->phy_regs);
 151                 }
 152         }
 153 
 154         ret = of_mdiobus_register(bus, pdev->dev.of_node);
 155         if (ret < 0) {
 156                 dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
 157                 return ret;
 158         }
 159 
 160         platform_set_drvdata(pdev, bus);
 161 
 162         return 0;
 163 }
 164 
 165 static int mscc_miim_remove(struct platform_device *pdev)
 166 {
 167         struct mii_bus *bus = platform_get_drvdata(pdev);
 168 
 169         mdiobus_unregister(bus);
 170 
 171         return 0;
 172 }
 173 
 174 static const struct of_device_id mscc_miim_match[] = {
 175         { .compatible = "mscc,ocelot-miim" },
 176         { }
 177 };
 178 MODULE_DEVICE_TABLE(of, mscc_miim_match);
 179 
 180 static struct platform_driver mscc_miim_driver = {
 181         .probe = mscc_miim_probe,
 182         .remove = mscc_miim_remove,
 183         .driver = {
 184                 .name = "mscc-miim",
 185                 .of_match_table = mscc_miim_match,
 186         },
 187 };
 188 
 189 module_platform_driver(mscc_miim_driver);
 190 
 191 MODULE_DESCRIPTION("Microsemi MIIM driver");
 192 MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>");
 193 MODULE_LICENSE("Dual MIT/GPL");

/* [<][>][^][v][top][bottom][index][help] */