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