sxgbe_mdio.c (6232B)
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 29static 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 42static 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 52static void sxgbe_mdio_c45(struct sxgbe_priv_data *sp, u32 cmd, int phyaddr, 53 int phyreg, u16 phydata) 54{ 55 u32 reg; 56 57 /* set mdio address register */ 58 reg = ((phyreg >> 16) & 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 65static 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 79static int sxgbe_mdio_access(struct sxgbe_priv_data *sp, u32 cmd, int phyaddr, 80 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 if (phyreg & MII_ADDR_C45) { 90 sxgbe_mdio_c45(sp, cmd, phyaddr, phyreg, phydata); 91 } else { 92 /* Ports 0-3 only support C22. */ 93 if (phyaddr >= 4) 94 return -ENODEV; 95 96 sxgbe_mdio_c22(sp, cmd, phyaddr, phyreg, phydata); 97 } 98 99 return sxgbe_mdio_busy_wait(sp->ioaddr, mii->data); 100} 101 102/** 103 * sxgbe_mdio_read 104 * @bus: points to the mii_bus structure 105 * @phyaddr: address of phy port 106 * @phyreg: address of register with in phy register 107 * Description: this function used for C45 and C22 MDIO Read 108 */ 109static int sxgbe_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg) 110{ 111 struct net_device *ndev = bus->priv; 112 struct sxgbe_priv_data *priv = netdev_priv(ndev); 113 int rc; 114 115 rc = sxgbe_mdio_access(priv, SXGBE_SMA_READ_CMD, phyaddr, phyreg, 0); 116 if (rc < 0) 117 return rc; 118 119 return readl(priv->ioaddr + priv->hw->mii.data) & 0xffff; 120} 121 122/** 123 * sxgbe_mdio_write 124 * @bus: points to the mii_bus structure 125 * @phyaddr: address of phy port 126 * @phyreg: address of phy registers 127 * @phydata: data to be written into phy register 128 * Description: this function is used for C45 and C22 MDIO write 129 */ 130static int sxgbe_mdio_write(struct mii_bus *bus, int phyaddr, int phyreg, 131 u16 phydata) 132{ 133 struct net_device *ndev = bus->priv; 134 struct sxgbe_priv_data *priv = netdev_priv(ndev); 135 136 return sxgbe_mdio_access(priv, SXGBE_SMA_WRITE_CMD, phyaddr, phyreg, 137 phydata); 138} 139 140int sxgbe_mdio_register(struct net_device *ndev) 141{ 142 struct mii_bus *mdio_bus; 143 struct sxgbe_priv_data *priv = netdev_priv(ndev); 144 struct sxgbe_mdio_bus_data *mdio_data = priv->plat->mdio_bus_data; 145 int err, phy_addr; 146 int *irqlist; 147 bool phy_found = false; 148 bool act; 149 150 /* allocate the new mdio bus */ 151 mdio_bus = mdiobus_alloc(); 152 if (!mdio_bus) { 153 netdev_err(ndev, "%s: mii bus allocation failed\n", __func__); 154 return -ENOMEM; 155 } 156 157 if (mdio_data->irqs) 158 irqlist = mdio_data->irqs; 159 else 160 irqlist = priv->mii_irq; 161 162 /* assign mii bus fields */ 163 mdio_bus->name = "sxgbe"; 164 mdio_bus->read = &sxgbe_mdio_read; 165 mdio_bus->write = &sxgbe_mdio_write; 166 snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s-%x", 167 mdio_bus->name, priv->plat->bus_id); 168 mdio_bus->priv = ndev; 169 mdio_bus->phy_mask = mdio_data->phy_mask; 170 mdio_bus->parent = priv->device; 171 172 /* register with kernel subsystem */ 173 err = mdiobus_register(mdio_bus); 174 if (err != 0) { 175 netdev_err(ndev, "mdiobus register failed\n"); 176 goto mdiobus_err; 177 } 178 179 for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { 180 struct phy_device *phy = mdiobus_get_phy(mdio_bus, phy_addr); 181 182 if (phy) { 183 char irq_num[4]; 184 char *irq_str; 185 /* If an IRQ was provided to be assigned after 186 * the bus probe, do it here. 187 */ 188 if ((mdio_data->irqs == NULL) && 189 (mdio_data->probed_phy_irq > 0)) { 190 irqlist[phy_addr] = mdio_data->probed_phy_irq; 191 phy->irq = mdio_data->probed_phy_irq; 192 } 193 194 /* If we're going to bind the MAC to this PHY bus, 195 * and no PHY number was provided to the MAC, 196 * use the one probed here. 197 */ 198 if (priv->plat->phy_addr == -1) 199 priv->plat->phy_addr = phy_addr; 200 201 act = (priv->plat->phy_addr == phy_addr); 202 switch (phy->irq) { 203 case PHY_POLL: 204 irq_str = "POLL"; 205 break; 206 case PHY_MAC_INTERRUPT: 207 irq_str = "MAC"; 208 break; 209 default: 210 sprintf(irq_num, "%d", phy->irq); 211 irq_str = irq_num; 212 break; 213 } 214 netdev_info(ndev, "PHY ID %08x at %d IRQ %s (%s)%s\n", 215 phy->phy_id, phy_addr, irq_str, 216 phydev_name(phy), act ? " active" : ""); 217 phy_found = true; 218 } 219 } 220 221 if (!phy_found) { 222 netdev_err(ndev, "PHY not found\n"); 223 goto phyfound_err; 224 } 225 226 priv->mii = mdio_bus; 227 228 return 0; 229 230phyfound_err: 231 err = -ENODEV; 232 mdiobus_unregister(mdio_bus); 233mdiobus_err: 234 mdiobus_free(mdio_bus); 235 return err; 236} 237 238int sxgbe_mdio_unregister(struct net_device *ndev) 239{ 240 struct sxgbe_priv_data *priv = netdev_priv(ndev); 241 242 if (!priv->mii) 243 return 0; 244 245 mdiobus_unregister(priv->mii); 246 priv->mii->priv = NULL; 247 mdiobus_free(priv->mii); 248 priv->mii = NULL; 249 250 return 0; 251}