1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) 2018 Marvell International Ltd. 4 */ 5 6#include <dm.h> 7#include <malloc.h> 8#include <miiphy.h> 9#include <misc.h> 10#include <pci.h> 11#include <pci_ids.h> 12#include <phy.h> 13#include <asm/global_data.h> 14#include <asm/io.h> 15#include <linux/ctype.h> 16#include <linux/delay.h> 17 18#define PCI_DEVICE_ID_OCTEONTX_SMI 0xA02B 19 20DECLARE_GLOBAL_DATA_PTR; 21 22enum octeontx_smi_mode { 23 CLAUSE22 = 0, 24 CLAUSE45 = 1, 25}; 26 27enum { 28 SMI_OP_C22_WRITE = 0, 29 SMI_OP_C22_READ = 1, 30 31 SMI_OP_C45_ADDR = 0, 32 SMI_OP_C45_WRITE = 1, 33 SMI_OP_C45_PRIA = 2, 34 SMI_OP_C45_READ = 3, 35}; 36 37union smi_x_clk { 38 u64 u; 39 struct smi_x_clk_s { 40 int phase:8; 41 int sample:4; 42 int preamble:1; 43 int clk_idle:1; 44 int reserved_14_14:1; 45 int sample_mode:1; 46 int sample_hi:5; 47 int reserved_21_23:3; 48 int mode:1; 49 } s; 50}; 51 52union smi_x_cmd { 53 u64 u; 54 struct smi_x_cmd_s { 55 int reg_adr:5; 56 int reserved_5_7:3; 57 int phy_adr:5; 58 int reserved_13_15:3; 59 int phy_op:2; 60 } s; 61}; 62 63union smi_x_wr_dat { 64 u64 u; 65 struct smi_x_wr_dat_s { 66 unsigned int dat:16; 67 int val:1; 68 int pending:1; 69 } s; 70}; 71 72union smi_x_rd_dat { 73 u64 u; 74 struct smi_x_rd_dat_s { 75 unsigned int dat:16; 76 int val:1; 77 int pending:1; 78 } s; 79}; 80 81union smi_x_en { 82 u64 u; 83 struct smi_x_en_s { 84 int en:1; 85 } s; 86}; 87 88#define SMI_X_RD_DAT 0x10ull 89#define SMI_X_WR_DAT 0x08ull 90#define SMI_X_CMD 0x00ull 91#define SMI_X_CLK 0x18ull 92#define SMI_X_EN 0x20ull 93 94struct octeontx_smi_priv { 95 void __iomem *baseaddr; 96 enum octeontx_smi_mode mode; 97}; 98 99#define MDIO_TIMEOUT 10000 100 101void octeontx_smi_setmode(struct mii_dev *bus, enum octeontx_smi_mode mode) 102{ 103 struct octeontx_smi_priv *priv = bus->priv; 104 union smi_x_clk smix_clk; 105 106 smix_clk.u = readq(priv->baseaddr + SMI_X_CLK); 107 smix_clk.s.mode = mode; 108 smix_clk.s.preamble = mode == CLAUSE45; 109 writeq(smix_clk.u, priv->baseaddr + SMI_X_CLK); 110 111 priv->mode = mode; 112} 113 114int octeontx_c45_addr(struct mii_dev *bus, int addr, int devad, int regnum) 115{ 116 struct octeontx_smi_priv *priv = bus->priv; 117 118 union smi_x_cmd smix_cmd; 119 union smi_x_wr_dat smix_wr_dat; 120 unsigned long timeout = MDIO_TIMEOUT; 121 122 smix_wr_dat.u = 0; 123 smix_wr_dat.s.dat = regnum; 124 125 writeq(smix_wr_dat.u, priv->baseaddr + SMI_X_WR_DAT); 126 127 smix_cmd.u = 0; 128 smix_cmd.s.phy_op = SMI_OP_C45_ADDR; 129 smix_cmd.s.phy_adr = addr; 130 smix_cmd.s.reg_adr = devad; 131 132 writeq(smix_cmd.u, priv->baseaddr + SMI_X_CMD); 133 134 do { 135 smix_wr_dat.u = readq(priv->baseaddr + SMI_X_WR_DAT); 136 udelay(100); 137 timeout--; 138 } while (smix_wr_dat.s.pending && timeout); 139 140 return timeout == 0; 141} 142 143int octeontx_phy_read(struct mii_dev *bus, int addr, int devad, int regnum) 144{ 145 struct octeontx_smi_priv *priv = bus->priv; 146 union smi_x_cmd smix_cmd; 147 union smi_x_rd_dat smix_rd_dat; 148 unsigned long timeout = MDIO_TIMEOUT; 149 int ret; 150 151 enum octeontx_smi_mode mode = (devad < 0) ? CLAUSE22 : CLAUSE45; 152 153 debug("RD: Mode: %u, baseaddr: %p, addr: %d, devad: %d, reg: %d\n", 154 mode, priv->baseaddr, addr, devad, regnum); 155 156 octeontx_smi_setmode(bus, mode); 157 158 if (mode == CLAUSE45) { 159 ret = octeontx_c45_addr(bus, addr, devad, regnum); 160 161 debug("RD: ret: %u\n", ret); 162 163 if (ret) 164 return 0; 165 } 166 167 smix_cmd.u = 0; 168 smix_cmd.s.phy_adr = addr; 169 170 if (mode == CLAUSE45) { 171 smix_cmd.s.reg_adr = devad; 172 smix_cmd.s.phy_op = SMI_OP_C45_READ; 173 } else { 174 smix_cmd.s.reg_adr = regnum; 175 smix_cmd.s.phy_op = SMI_OP_C22_READ; 176 } 177 178 writeq(smix_cmd.u, priv->baseaddr + SMI_X_CMD); 179 180 do { 181 smix_rd_dat.u = readq(priv->baseaddr + SMI_X_RD_DAT); 182 udelay(10); 183 timeout--; 184 } while (smix_rd_dat.s.pending && timeout); 185 186 debug("SMIX_RD_DAT: %lx\n", (unsigned long)smix_rd_dat.u); 187 188 return smix_rd_dat.s.dat; 189} 190 191int octeontx_phy_write(struct mii_dev *bus, int addr, int devad, int regnum, 192 u16 value) 193{ 194 struct octeontx_smi_priv *priv = bus->priv; 195 union smi_x_cmd smix_cmd; 196 union smi_x_wr_dat smix_wr_dat; 197 unsigned long timeout = MDIO_TIMEOUT; 198 int ret; 199 200 enum octeontx_smi_mode mode = (devad < 0) ? CLAUSE22 : CLAUSE45; 201 202 debug("WR: Mode: %u, baseaddr: %p, addr: %d, devad: %d, reg: %d\n", 203 mode, priv->baseaddr, addr, devad, regnum); 204 205 if (mode == CLAUSE45) { 206 ret = octeontx_c45_addr(bus, addr, devad, regnum); 207 208 debug("WR: ret: %u\n", ret); 209 210 if (ret) 211 return ret; 212 } 213 214 smix_wr_dat.u = 0; 215 smix_wr_dat.s.dat = value; 216 217 writeq(smix_wr_dat.u, priv->baseaddr + SMI_X_WR_DAT); 218 219 smix_cmd.u = 0; 220 smix_cmd.s.phy_adr = addr; 221 222 if (mode == CLAUSE45) { 223 smix_cmd.s.reg_adr = devad; 224 smix_cmd.s.phy_op = SMI_OP_C45_WRITE; 225 } else { 226 smix_cmd.s.reg_adr = regnum; 227 smix_cmd.s.phy_op = SMI_OP_C22_WRITE; 228 } 229 230 writeq(smix_cmd.u, priv->baseaddr + SMI_X_CMD); 231 232 do { 233 smix_wr_dat.u = readq(priv->baseaddr + SMI_X_WR_DAT); 234 udelay(10); 235 timeout--; 236 } while (smix_wr_dat.s.pending && timeout); 237 238 debug("SMIX_WR_DAT: %lx\n", (unsigned long)smix_wr_dat.u); 239 240 return timeout == 0; 241} 242 243int octeontx_smi_reset(struct mii_dev *bus) 244{ 245 struct octeontx_smi_priv *priv = bus->priv; 246 247 union smi_x_en smi_en; 248 249 smi_en.s.en = 0; 250 writeq(smi_en.u, priv->baseaddr + SMI_X_EN); 251 252 smi_en.s.en = 1; 253 writeq(smi_en.u, priv->baseaddr + SMI_X_EN); 254 255 octeontx_smi_setmode(bus, CLAUSE22); 256 257 return 0; 258} 259 260/* PHY XS initialization, primarily for RXAUI 261 * 262 */ 263int rxaui_phy_xs_init(struct mii_dev *bus, int phy_addr) 264{ 265 int reg; 266 ulong start_time; 267 int phy_id1, phy_id2; 268 int oui, model_number; 269 270 phy_id1 = octeontx_phy_read(bus, phy_addr, 1, 0x2); 271 phy_id2 = octeontx_phy_read(bus, phy_addr, 1, 0x3); 272 model_number = (phy_id2 >> 4) & 0x3F; 273 debug("%s model %x\n", __func__, model_number); 274 oui = phy_id1; 275 oui <<= 6; 276 oui |= (phy_id2 >> 10) & 0x3F; 277 debug("%s oui %x\n", __func__, oui); 278 switch (oui) { 279 case 0x5016: 280 if (model_number == 9) { 281 debug("%s +\n", __func__); 282 /* Perform hardware reset in XGXS control */ 283 reg = octeontx_phy_read(bus, phy_addr, 4, 0x0); 284 if ((reg & 0xffff) < 0) 285 goto read_error; 286 reg |= 0x8000; 287 octeontx_phy_write(bus, phy_addr, 4, 0x0, reg); 288 289 start_time = get_timer(0); 290 do { 291 reg = octeontx_phy_read(bus, phy_addr, 4, 0x0); 292 if ((reg & 0xffff) < 0) 293 goto read_error; 294 } while ((reg & 0x8000) && get_timer(start_time) < 500); 295 if (reg & 0x8000) { 296 printf("HW reset for M88X3120 PHY failed"); 297 printf("MII_BMCR: 0x%x\n", reg); 298 return -1; 299 } 300 /* program 4.49155 with 0x5 */ 301 octeontx_phy_write(bus, phy_addr, 4, 0xc003, 0x5); 302 } 303 break; 304 default: 305 break; 306 } 307 308 return 0; 309 310read_error: 311 debug("M88X3120 PHY config read failed\n"); 312 return -1; 313} 314 315int octeontx_smi_probe(struct udevice *dev) 316{ 317 pci_dev_t bdf = dm_pci_get_bdf(dev); 318 struct octeontx_smi_priv *priv; 319 struct mii_dev *bus; 320 int ret, cnt = 0; 321 ofnode subnode; 322 u64 baseaddr; 323 324 debug("SMI PCI device: %x\n", bdf); 325 if (!dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, 0, 0, PCI_REGION_TYPE, PCI_REGION_MEM)) { 326 printf("Failed to map PCI region for bdf %x\n", bdf); 327 return -1; 328 } 329 330 dev_for_each_subnode(subnode, dev) { 331 if (!ofnode_device_is_compatible(subnode, 332 "cavium,thunder-8890-mdio")) 333 continue; 334 if (ofnode_read_u64(subnode, "reg", &baseaddr)) 335 continue; 336 bus = mdio_alloc(); 337 priv = malloc(sizeof(*priv)); 338 if (!bus || !priv) { 339 printf("Failed to allocate OcteonTX MDIO bus # %u\n", 340 dev_seq(dev)); 341 return -1; 342 } 343 344 bus->read = octeontx_phy_read; 345 bus->write = octeontx_phy_write; 346 bus->reset = octeontx_smi_reset; 347 bus->priv = priv; 348 349 priv->mode = CLAUSE22; 350 priv->baseaddr = (void __iomem *)baseaddr; 351 debug("mdio base addr %p\n", priv->baseaddr); 352 353 /* use given name or generate its own unique name */ 354 snprintf(bus->name, MDIO_NAME_LEN, "smi%d", cnt++); 355 356 ret = mdio_register(bus); 357 if (ret) 358 return ret; 359 } 360 return 0; 361} 362 363static const struct udevice_id octeontx_smi_ids[] = { 364 { .compatible = "cavium,thunder-8890-mdio-nexus" }, 365 {} 366}; 367 368U_BOOT_DRIVER(octeontx_smi) = { 369 .name = "octeontx_smi", 370 .id = UCLASS_MISC, 371 .probe = octeontx_smi_probe, 372 .of_match = octeontx_smi_ids, 373}; 374 375static struct pci_device_id octeontx_smi_supported[] = { 376 { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_SMI) }, 377 {} 378}; 379 380U_BOOT_PCI_DEVICE(octeontx_smi, octeontx_smi_supported); 381