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 0x00800000 /* 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 act; 151 152 /* allocate the new mdio bus */ 153 mdio_bus = mdiobus_alloc(); 154 if (!mdio_bus) { 155 netdev_err(ndev, "%s: mii bus allocation failed\n", __func__); 156 return -ENOMEM; 157 } 158 159 if (mdio_data->irqs) 160 irqlist = mdio_data->irqs; 161 else 162 irqlist = priv->mii_irq; 163 164 /* assign mii bus fields */ 165 mdio_bus->name = "samsxgbe"; 166 mdio_bus->read = &sxgbe_mdio_read; 167 mdio_bus->write = &sxgbe_mdio_write; 168 snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s-%x", 169 mdio_bus->name, priv->plat->bus_id); 170 mdio_bus->priv = ndev; 171 mdio_bus->phy_mask = mdio_data->phy_mask; 172 mdio_bus->parent = priv->device; 173 174 /* register with kernel subsystem */ 175 err = mdiobus_register(mdio_bus); 176 if (err != 0) { 177 netdev_err(ndev, "mdiobus register failed\n"); 178 goto mdiobus_err; 179 } 180 181 for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { 182 struct phy_device *phy = mdio_bus->phy_map[phy_addr]; 183 184 if (phy) { 185 char irq_num[4]; 186 char *irq_str; 187 /* If an IRQ was provided to be assigned after 188 * the bus probe, do it here. 189 */ 190 if ((mdio_data->irqs == NULL) && 191 (mdio_data->probed_phy_irq > 0)) { 192 irqlist[phy_addr] = mdio_data->probed_phy_irq; 193 phy->irq = mdio_data->probed_phy_irq; 194 } 195 196 /* If we're going to bind the MAC to this PHY bus, 197 * and no PHY number was provided to the MAC, 198 * use the one probed here. 199 */ 200 if (priv->plat->phy_addr == -1) 201 priv->plat->phy_addr = phy_addr; 202 203 act = (priv->plat->phy_addr == phy_addr); 204 switch (phy->irq) { 205 case PHY_POLL: 206 irq_str = "POLL"; 207 break; 208 case PHY_IGNORE_INTERRUPT: 209 irq_str = "IGNORE"; 210 break; 211 default: 212 sprintf(irq_num, "%d", phy->irq); 213 irq_str = irq_num; 214 break; 215 } 216 netdev_info(ndev, "PHY ID %08x at %d IRQ %s (%s)%s\n", 217 phy->phy_id, phy_addr, irq_str, 218 dev_name(&phy->dev), act ? " active" : ""); 219 } 220 } 221 222 priv->mii = mdio_bus; 223 224 return 0; 225 226 mdiobus_err: 227 mdiobus_free(mdio_bus); 228 return err; 229 } 230 231 int sxgbe_mdio_unregister(struct net_device *ndev) 232 { 233 struct sxgbe_priv_data *priv = netdev_priv(ndev); 234 235 if (!priv->mii) 236 return 0; 237 238 mdiobus_unregister(priv->mii); 239 priv->mii->priv = NULL; 240 mdiobus_free(priv->mii); 241 priv->mii = NULL; 242 243 return 0; 244 } 245