1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2018 Marvell International Ltd.
13 #include <asm/global_data.h>
15 #include <linux/ctype.h>
16 #include <linux/delay.h>
18 #define PCI_DEVICE_ID_OCTEONTX_SMI 0xA02B
20 DECLARE_GLOBAL_DATA_PTR;
22 enum octeontx_smi_mode {
65 struct smi_x_wr_dat_s {
74 struct smi_x_rd_dat_s {
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
94 struct octeontx_smi_priv {
95 void __iomem *baseaddr;
96 enum octeontx_smi_mode mode;
99 #define MDIO_TIMEOUT 10000
101 void octeontx_smi_setmode(struct mii_dev *bus, enum octeontx_smi_mode mode)
103 struct octeontx_smi_priv *priv = bus->priv;
104 union smi_x_clk smix_clk;
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);
114 int octeontx_c45_addr(struct mii_dev *bus, int addr, int devad, int regnum)
116 struct octeontx_smi_priv *priv = bus->priv;
118 union smi_x_cmd smix_cmd;
119 union smi_x_wr_dat smix_wr_dat;
120 unsigned long timeout = MDIO_TIMEOUT;
123 smix_wr_dat.s.dat = regnum;
125 writeq(smix_wr_dat.u, priv->baseaddr + SMI_X_WR_DAT);
128 smix_cmd.s.phy_op = SMI_OP_C45_ADDR;
129 smix_cmd.s.phy_adr = addr;
130 smix_cmd.s.reg_adr = devad;
132 writeq(smix_cmd.u, priv->baseaddr + SMI_X_CMD);
135 smix_wr_dat.u = readq(priv->baseaddr + SMI_X_WR_DAT);
138 } while (smix_wr_dat.s.pending && timeout);
143 int octeontx_phy_read(struct mii_dev *bus, int addr, int devad, int regnum)
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;
151 enum octeontx_smi_mode mode = (devad < 0) ? CLAUSE22 : CLAUSE45;
153 debug("RD: Mode: %u, baseaddr: %p, addr: %d, devad: %d, reg: %d\n",
154 mode, priv->baseaddr, addr, devad, regnum);
156 octeontx_smi_setmode(bus, mode);
158 if (mode == CLAUSE45) {
159 ret = octeontx_c45_addr(bus, addr, devad, regnum);
161 debug("RD: ret: %u\n", ret);
168 smix_cmd.s.phy_adr = addr;
170 if (mode == CLAUSE45) {
171 smix_cmd.s.reg_adr = devad;
172 smix_cmd.s.phy_op = SMI_OP_C45_READ;
174 smix_cmd.s.reg_adr = regnum;
175 smix_cmd.s.phy_op = SMI_OP_C22_READ;
178 writeq(smix_cmd.u, priv->baseaddr + SMI_X_CMD);
181 smix_rd_dat.u = readq(priv->baseaddr + SMI_X_RD_DAT);
184 } while (smix_rd_dat.s.pending && timeout);
186 debug("SMIX_RD_DAT: %lx\n", (unsigned long)smix_rd_dat.u);
188 return smix_rd_dat.s.dat;
191 int octeontx_phy_write(struct mii_dev *bus, int addr, int devad, int regnum,
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;
200 enum octeontx_smi_mode mode = (devad < 0) ? CLAUSE22 : CLAUSE45;
202 debug("WR: Mode: %u, baseaddr: %p, addr: %d, devad: %d, reg: %d\n",
203 mode, priv->baseaddr, addr, devad, regnum);
205 if (mode == CLAUSE45) {
206 ret = octeontx_c45_addr(bus, addr, devad, regnum);
208 debug("WR: ret: %u\n", ret);
215 smix_wr_dat.s.dat = value;
217 writeq(smix_wr_dat.u, priv->baseaddr + SMI_X_WR_DAT);
220 smix_cmd.s.phy_adr = addr;
222 if (mode == CLAUSE45) {
223 smix_cmd.s.reg_adr = devad;
224 smix_cmd.s.phy_op = SMI_OP_C45_WRITE;
226 smix_cmd.s.reg_adr = regnum;
227 smix_cmd.s.phy_op = SMI_OP_C22_WRITE;
230 writeq(smix_cmd.u, priv->baseaddr + SMI_X_CMD);
233 smix_wr_dat.u = readq(priv->baseaddr + SMI_X_WR_DAT);
236 } while (smix_wr_dat.s.pending && timeout);
238 debug("SMIX_WR_DAT: %lx\n", (unsigned long)smix_wr_dat.u);
243 int octeontx_smi_reset(struct mii_dev *bus)
245 struct octeontx_smi_priv *priv = bus->priv;
247 union smi_x_en smi_en;
250 writeq(smi_en.u, priv->baseaddr + SMI_X_EN);
253 writeq(smi_en.u, priv->baseaddr + SMI_X_EN);
255 octeontx_smi_setmode(bus, CLAUSE22);
260 /* PHY XS initialization, primarily for RXAUI
263 int rxaui_phy_xs_init(struct mii_dev *bus, int phy_addr)
267 int phy_id1, phy_id2;
268 int oui, model_number;
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);
276 oui |= (phy_id2 >> 10) & 0x3F;
277 debug("%s oui %x\n", __func__, oui);
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)
287 octeontx_phy_write(bus, phy_addr, 4, 0x0, reg);
289 start_time = get_timer(0);
291 reg = octeontx_phy_read(bus, phy_addr, 4, 0x0);
292 if ((reg & 0xffff) < 0)
294 } while ((reg & 0x8000) && get_timer(start_time) < 500);
296 printf("HW reset for M88X3120 PHY failed");
297 printf("MII_BMCR: 0x%x\n", reg);
300 /* program 4.49155 with 0x5 */
301 octeontx_phy_write(bus, phy_addr, 4, 0xc003, 0x5);
311 debug("M88X3120 PHY config read failed\n");
315 int octeontx_smi_probe(struct udevice *dev)
317 int ret, subnode, cnt = 0, node = dev_ofnode(dev).of_offset;
319 struct octeontx_smi_priv *priv;
320 pci_dev_t bdf = dm_pci_get_bdf(dev);
322 debug("SMI PCI device: %x\n", bdf);
323 if (!dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, PCI_REGION_MEM)) {
324 printf("Failed to map PCI region for bdf %x\n", bdf);
328 fdt_for_each_subnode(subnode, gd->fdt_blob, node) {
329 ret = fdt_node_check_compatible(gd->fdt_blob, subnode,
330 "cavium,thunder-8890-mdio");
335 priv = malloc(sizeof(*priv));
337 printf("Failed to allocate OcteonTX MDIO bus # %u\n",
342 bus->read = octeontx_phy_read;
343 bus->write = octeontx_phy_write;
344 bus->reset = octeontx_smi_reset;
347 priv->mode = CLAUSE22;
348 priv->baseaddr = (void __iomem *)fdtdec_get_addr(gd->fdt_blob,
351 debug("mdio base addr %p\n", priv->baseaddr);
353 /* use given name or generate its own unique name */
354 snprintf(bus->name, MDIO_NAME_LEN, "smi%d", cnt++);
356 ret = mdio_register(bus);
363 static const struct udevice_id octeontx_smi_ids[] = {
364 { .compatible = "cavium,thunder-8890-mdio-nexus" },
368 U_BOOT_DRIVER(octeontx_smi) = {
369 .name = "octeontx_smi",
371 .probe = octeontx_smi_probe,
372 .of_match = octeontx_smi_ids,
375 static struct pci_device_id octeontx_smi_supported[] = {
376 { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_SMI) },
380 U_BOOT_PCI_DEVICE(octeontx_smi, octeontx_smi_supported);