1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 10G controller driver for Samsung SoCs 3 * 4 * Copyright (C) 2013 Samsung Electronics Co., Ltd. 5 * http://www.samsung.com 6 * 7 * Author: Siva Reddy Kallam <siva.kallam@samsung.com> 8 */ 9 10 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 11 12 #include <linux/io.h> 13 #include <linux/mii.h> 14 #include <linux/netdevice.h> 15 #include <linux/platform_device.h> 16 #include <linux/phy.h> 17 #include <linux/slab.h> 18 #include <linux/sxgbe_platform.h> 19 20 #include "sxgbe_common.h" 21 #include "sxgbe_reg.h" 22 23 #define SXGBE_SMA_WRITE_CMD 0x01 /* write command */ 24 #define SXGBE_SMA_PREAD_CMD 0x02 /* post read increament address */ 25 #define SXGBE_SMA_READ_CMD 0x03 /* read command */ 26 #define SXGBE_SMA_SKIP_ADDRFRM 0x00040000 /* skip the address frame */ 27 #define SXGBE_MII_BUSY 0x00400000 /* mii busy */ 28 29 static int sxgbe_mdio_busy_wait(void __iomem *ioaddr, unsigned int mii_data) 30 { 31 unsigned long fin_time = jiffies + 3 * HZ; /* 3 seconds */ 32 33 while (!time_after(jiffies, fin_time)) { 34 if (!(readl(ioaddr + mii_data) & SXGBE_MII_BUSY)) 35 return 0; 36 cpu_relax(); 37 } 38 39 return -EBUSY; 40 } 41 42 static void sxgbe_mdio_ctrl_data(struct sxgbe_priv_data *sp, u32 cmd, 43 u16 phydata) 44 { 45 u32 reg = phydata; 46 47 reg |= (cmd << 16) | SXGBE_SMA_SKIP_ADDRFRM | 48 ((sp->clk_csr & 0x7) << 19) | SXGBE_MII_BUSY; 49 writel(reg, sp->ioaddr + sp->hw->mii.data); 50 } 51 52 static void sxgbe_mdio_c45(struct sxgbe_priv_data *sp, u32 cmd, int phyaddr, 53 int devad, int phyreg, u16 phydata) 54 { 55 u32 reg; 56 57 /* set mdio address register */ 58 reg = (devad & 0x1f) << 21; 59 reg |= (phyaddr << 16) | (phyreg & 0xffff); 60 writel(reg, sp->ioaddr + sp->hw->mii.addr); 61 62 sxgbe_mdio_ctrl_data(sp, cmd, phydata); 63 } 64 65 static void sxgbe_mdio_c22(struct sxgbe_priv_data *sp, u32 cmd, int phyaddr, 66 int phyreg, u16 phydata) 67 { 68 u32 reg; 69 70 writel(1 << phyaddr, sp->ioaddr + SXGBE_MDIO_CLAUSE22_PORT_REG); 71 72 /* set mdio address register */ 73 reg = (phyaddr << 16) | (phyreg & 0x1f); 74 writel(reg, sp->ioaddr + sp->hw->mii.addr); 75 76 sxgbe_mdio_ctrl_data(sp, cmd, phydata); 77 } 78 79 static int sxgbe_mdio_access_c22(struct sxgbe_priv_data *sp, u32 cmd, 80 int phyaddr, int phyreg, u16 phydata) 81 { 82 const struct mii_regs *mii = &sp->hw->mii; 83 int rc; 84 85 rc = sxgbe_mdio_busy_wait(sp->ioaddr, mii->data); 86 if (rc < 0) 87 return rc; 88 89 /* Ports 0-3 only support C22. */ 90 if (phyaddr >= 4) 91 return -ENODEV; 92 93 sxgbe_mdio_c22(sp, cmd, phyaddr, phyreg, phydata); 94 95 return sxgbe_mdio_busy_wait(sp->ioaddr, mii->data); 96 } 97 98 static int sxgbe_mdio_access_c45(struct sxgbe_priv_data *sp, u32 cmd, 99 int phyaddr, int devad, int phyreg, 100 u16 phydata) 101 { 102 const struct mii_regs *mii = &sp->hw->mii; 103 int rc; 104 105 rc = sxgbe_mdio_busy_wait(sp->ioaddr, mii->data); 106 if (rc < 0) 107 return rc; 108 109 sxgbe_mdio_c45(sp, cmd, phyaddr, devad, phyreg, phydata); 110 111 return sxgbe_mdio_busy_wait(sp->ioaddr, mii->data); 112 } 113 114 /** 115 * sxgbe_mdio_read_c22 116 * @bus: points to the mii_bus structure 117 * @phyaddr: address of phy port 118 * @phyreg: address of register with in phy register 119 * Description: this function used for C22 MDIO Read 120 */ 121 static int sxgbe_mdio_read_c22(struct mii_bus *bus, int phyaddr, int phyreg) 122 { 123 struct net_device *ndev = bus->priv; 124 struct sxgbe_priv_data *priv = netdev_priv(ndev); 125 int rc; 126 127 rc = sxgbe_mdio_access_c22(priv, SXGBE_SMA_READ_CMD, phyaddr, 128 phyreg, 0); 129 if (rc < 0) 130 return rc; 131 132 return readl(priv->ioaddr + priv->hw->mii.data) & 0xffff; 133 } 134 135 /** 136 * sxgbe_mdio_read_c45 137 * @bus: points to the mii_bus structure 138 * @phyaddr: address of phy port 139 * @devad: device (MMD) address 140 * @phyreg: address of register with in phy register 141 * Description: this function used for C45 MDIO Read 142 */ 143 static int sxgbe_mdio_read_c45(struct mii_bus *bus, int phyaddr, int devad, 144 int phyreg) 145 { 146 struct net_device *ndev = bus->priv; 147 struct sxgbe_priv_data *priv = netdev_priv(ndev); 148 int rc; 149 150 rc = sxgbe_mdio_access_c45(priv, SXGBE_SMA_READ_CMD, phyaddr, 151 devad, phyreg, 0); 152 if (rc < 0) 153 return rc; 154 155 return readl(priv->ioaddr + priv->hw->mii.data) & 0xffff; 156 } 157 158 /** 159 * sxgbe_mdio_write_c22 160 * @bus: points to the mii_bus structure 161 * @phyaddr: address of phy port 162 * @phyreg: address of phy registers 163 * @phydata: data to be written into phy register 164 * Description: this function is used for C22 MDIO write 165 */ 166 static int sxgbe_mdio_write_c22(struct mii_bus *bus, int phyaddr, int phyreg, 167 u16 phydata) 168 { 169 struct net_device *ndev = bus->priv; 170 struct sxgbe_priv_data *priv = netdev_priv(ndev); 171 172 return sxgbe_mdio_access_c22(priv, SXGBE_SMA_WRITE_CMD, phyaddr, phyreg, 173 phydata); 174 } 175 176 /** 177 * sxgbe_mdio_write_c45 178 * @bus: points to the mii_bus structure 179 * @phyaddr: address of phy port 180 * @phyreg: address of phy registers 181 * @devad: device (MMD) address 182 * @phydata: data to be written into phy register 183 * Description: this function is used for C45 MDIO write 184 */ 185 static int sxgbe_mdio_write_c45(struct mii_bus *bus, int phyaddr, int devad, 186 int phyreg, u16 phydata) 187 { 188 struct net_device *ndev = bus->priv; 189 struct sxgbe_priv_data *priv = netdev_priv(ndev); 190 191 return sxgbe_mdio_access_c45(priv, SXGBE_SMA_WRITE_CMD, phyaddr, 192 devad, phyreg, phydata); 193 } 194 195 int sxgbe_mdio_register(struct net_device *ndev) 196 { 197 struct mii_bus *mdio_bus; 198 struct sxgbe_priv_data *priv = netdev_priv(ndev); 199 struct sxgbe_mdio_bus_data *mdio_data = priv->plat->mdio_bus_data; 200 int err, phy_addr; 201 int *irqlist; 202 bool phy_found = false; 203 bool act; 204 205 /* allocate the new mdio bus */ 206 mdio_bus = mdiobus_alloc(); 207 if (!mdio_bus) { 208 netdev_err(ndev, "%s: mii bus allocation failed\n", __func__); 209 return -ENOMEM; 210 } 211 212 if (mdio_data->irqs) 213 irqlist = mdio_data->irqs; 214 else 215 irqlist = priv->mii_irq; 216 217 /* assign mii bus fields */ 218 mdio_bus->name = "sxgbe"; 219 mdio_bus->read = sxgbe_mdio_read_c22; 220 mdio_bus->write = sxgbe_mdio_write_c22; 221 mdio_bus->read_c45 = sxgbe_mdio_read_c45; 222 mdio_bus->write_c45 = sxgbe_mdio_write_c45; 223 snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s-%x", 224 mdio_bus->name, priv->plat->bus_id); 225 mdio_bus->priv = ndev; 226 mdio_bus->phy_mask = mdio_data->phy_mask; 227 mdio_bus->parent = priv->device; 228 229 /* register with kernel subsystem */ 230 err = mdiobus_register(mdio_bus); 231 if (err != 0) { 232 netdev_err(ndev, "mdiobus register failed\n"); 233 goto mdiobus_err; 234 } 235 236 for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { 237 struct phy_device *phy = mdiobus_get_phy(mdio_bus, phy_addr); 238 239 if (phy) { 240 char irq_num[4]; 241 char *irq_str; 242 /* If an IRQ was provided to be assigned after 243 * the bus probe, do it here. 244 */ 245 if ((mdio_data->irqs == NULL) && 246 (mdio_data->probed_phy_irq > 0)) { 247 irqlist[phy_addr] = mdio_data->probed_phy_irq; 248 phy->irq = mdio_data->probed_phy_irq; 249 } 250 251 /* If we're going to bind the MAC to this PHY bus, 252 * and no PHY number was provided to the MAC, 253 * use the one probed here. 254 */ 255 if (priv->plat->phy_addr == -1) 256 priv->plat->phy_addr = phy_addr; 257 258 act = (priv->plat->phy_addr == phy_addr); 259 switch (phy->irq) { 260 case PHY_POLL: 261 irq_str = "POLL"; 262 break; 263 case PHY_MAC_INTERRUPT: 264 irq_str = "MAC"; 265 break; 266 default: 267 sprintf(irq_num, "%d", phy->irq); 268 irq_str = irq_num; 269 break; 270 } 271 netdev_info(ndev, "PHY ID %08x at %d IRQ %s (%s)%s\n", 272 phy->phy_id, phy_addr, irq_str, 273 phydev_name(phy), act ? " active" : ""); 274 phy_found = true; 275 } 276 } 277 278 if (!phy_found) { 279 netdev_err(ndev, "PHY not found\n"); 280 goto phyfound_err; 281 } 282 283 priv->mii = mdio_bus; 284 285 return 0; 286 287 phyfound_err: 288 err = -ENODEV; 289 mdiobus_unregister(mdio_bus); 290 mdiobus_err: 291 mdiobus_free(mdio_bus); 292 return err; 293 } 294 295 int sxgbe_mdio_unregister(struct net_device *ndev) 296 { 297 struct sxgbe_priv_data *priv = netdev_priv(ndev); 298 299 if (!priv->mii) 300 return 0; 301 302 mdiobus_unregister(priv->mii); 303 priv->mii->priv = NULL; 304 mdiobus_free(priv->mii); 305 priv->mii = NULL; 306 307 return 0; 308 } 309