mirror of
				https://xff.cz/git/u-boot/
				synced 2025-10-31 10:26:10 +01:00 
			
		
		
		
	At present ofnode is present in the device even if it is never used. With of-platdata this field is not used, so can be removed. In preparation for this, change the access to go through inline functions. Signed-off-by: Simon Glass <sjg@chromium.org>
		
			
				
	
	
		
			380 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			380 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier:    GPL-2.0
 | |
| /*
 | |
|  * Copyright (C) 2018 Marvell International Ltd.
 | |
|  */
 | |
| 
 | |
| #include <dm.h>
 | |
| #include <malloc.h>
 | |
| #include <miiphy.h>
 | |
| #include <misc.h>
 | |
| #include <pci.h>
 | |
| #include <pci_ids.h>
 | |
| #include <phy.h>
 | |
| #include <asm/io.h>
 | |
| #include <linux/ctype.h>
 | |
| #include <linux/delay.h>
 | |
| 
 | |
| #define PCI_DEVICE_ID_OCTEONTX_SMI 0xA02B
 | |
| 
 | |
| DECLARE_GLOBAL_DATA_PTR;
 | |
| 
 | |
| enum octeontx_smi_mode {
 | |
| 	CLAUSE22 = 0,
 | |
| 	CLAUSE45 = 1,
 | |
| };
 | |
| 
 | |
| enum {
 | |
| 	SMI_OP_C22_WRITE = 0,
 | |
| 	SMI_OP_C22_READ = 1,
 | |
| 
 | |
| 	SMI_OP_C45_ADDR = 0,
 | |
| 	SMI_OP_C45_WRITE = 1,
 | |
| 	SMI_OP_C45_PRIA = 2,
 | |
| 	SMI_OP_C45_READ = 3,
 | |
| };
 | |
| 
 | |
| union smi_x_clk {
 | |
| 	u64 u;
 | |
| 	struct smi_x_clk_s {
 | |
| 		int phase:8;
 | |
| 		int sample:4;
 | |
| 		int preamble:1;
 | |
| 		int clk_idle:1;
 | |
| 		int reserved_14_14:1;
 | |
| 		int sample_mode:1;
 | |
| 		int sample_hi:5;
 | |
| 		int reserved_21_23:3;
 | |
| 		int mode:1;
 | |
| 	} s;
 | |
| };
 | |
| 
 | |
| union smi_x_cmd {
 | |
| 	u64 u;
 | |
| 	struct smi_x_cmd_s {
 | |
| 		int reg_adr:5;
 | |
| 		int reserved_5_7:3;
 | |
| 		int phy_adr:5;
 | |
| 		int reserved_13_15:3;
 | |
| 		int phy_op:2;
 | |
| 	} s;
 | |
| };
 | |
| 
 | |
| union smi_x_wr_dat {
 | |
| 	u64 u;
 | |
| 	struct smi_x_wr_dat_s {
 | |
| 		unsigned int dat:16;
 | |
| 		int val:1;
 | |
| 		int pending:1;
 | |
| 	} s;
 | |
| };
 | |
| 
 | |
| union smi_x_rd_dat {
 | |
| 	u64 u;
 | |
| 	struct smi_x_rd_dat_s {
 | |
| 		unsigned int dat:16;
 | |
| 		int val:1;
 | |
| 		int pending:1;
 | |
| 	} s;
 | |
| };
 | |
| 
 | |
| union smi_x_en {
 | |
| 	u64 u;
 | |
| 	struct smi_x_en_s {
 | |
| 		int en:1;
 | |
| 	} s;
 | |
| };
 | |
| 
 | |
| #define SMI_X_RD_DAT	0x10ull
 | |
| #define SMI_X_WR_DAT	0x08ull
 | |
| #define SMI_X_CMD	0x00ull
 | |
| #define SMI_X_CLK	0x18ull
 | |
| #define SMI_X_EN	0x20ull
 | |
| 
 | |
| struct octeontx_smi_priv {
 | |
| 	void __iomem *baseaddr;
 | |
| 	enum octeontx_smi_mode mode;
 | |
| };
 | |
| 
 | |
| #define MDIO_TIMEOUT 10000
 | |
| 
 | |
| void octeontx_smi_setmode(struct mii_dev *bus, enum octeontx_smi_mode mode)
 | |
| {
 | |
| 	struct octeontx_smi_priv *priv = bus->priv;
 | |
| 	union smi_x_clk smix_clk;
 | |
| 
 | |
| 	smix_clk.u = readq(priv->baseaddr + SMI_X_CLK);
 | |
| 	smix_clk.s.mode = mode;
 | |
| 	smix_clk.s.preamble = mode == CLAUSE45;
 | |
| 	writeq(smix_clk.u, priv->baseaddr + SMI_X_CLK);
 | |
| 
 | |
| 	priv->mode = mode;
 | |
| }
 | |
| 
 | |
| int octeontx_c45_addr(struct mii_dev *bus, int addr, int devad, int regnum)
 | |
| {
 | |
| 	struct octeontx_smi_priv *priv = bus->priv;
 | |
| 
 | |
| 	union smi_x_cmd smix_cmd;
 | |
| 	union smi_x_wr_dat smix_wr_dat;
 | |
| 	unsigned long timeout = MDIO_TIMEOUT;
 | |
| 
 | |
| 	smix_wr_dat.u = 0;
 | |
| 	smix_wr_dat.s.dat = regnum;
 | |
| 
 | |
| 	writeq(smix_wr_dat.u, priv->baseaddr + SMI_X_WR_DAT);
 | |
| 
 | |
| 	smix_cmd.u = 0;
 | |
| 	smix_cmd.s.phy_op = SMI_OP_C45_ADDR;
 | |
| 	smix_cmd.s.phy_adr = addr;
 | |
| 	smix_cmd.s.reg_adr = devad;
 | |
| 
 | |
| 	writeq(smix_cmd.u, priv->baseaddr + SMI_X_CMD);
 | |
| 
 | |
| 	do {
 | |
| 		smix_wr_dat.u = readq(priv->baseaddr + SMI_X_WR_DAT);
 | |
| 		udelay(100);
 | |
| 		timeout--;
 | |
| 	} while (smix_wr_dat.s.pending && timeout);
 | |
| 
 | |
| 	return timeout == 0;
 | |
| }
 | |
| 
 | |
| int octeontx_phy_read(struct mii_dev *bus, int addr, int devad, int regnum)
 | |
| {
 | |
| 	struct octeontx_smi_priv *priv = bus->priv;
 | |
| 	union smi_x_cmd smix_cmd;
 | |
| 	union smi_x_rd_dat smix_rd_dat;
 | |
| 	unsigned long timeout = MDIO_TIMEOUT;
 | |
| 	int ret;
 | |
| 
 | |
| 	enum octeontx_smi_mode mode = (devad < 0) ? CLAUSE22 : CLAUSE45;
 | |
| 
 | |
| 	debug("RD: Mode: %u, baseaddr: %p, addr: %d, devad: %d, reg: %d\n",
 | |
| 	      mode, priv->baseaddr, addr, devad, regnum);
 | |
| 
 | |
| 	octeontx_smi_setmode(bus, mode);
 | |
| 
 | |
| 	if (mode == CLAUSE45) {
 | |
| 		ret = octeontx_c45_addr(bus, addr, devad, regnum);
 | |
| 
 | |
| 		debug("RD: ret: %u\n", ret);
 | |
| 
 | |
| 		if (ret)
 | |
| 			return 0;
 | |
| 	}
 | |
| 
 | |
| 	smix_cmd.u = 0;
 | |
| 	smix_cmd.s.phy_adr = addr;
 | |
| 
 | |
| 	if (mode == CLAUSE45) {
 | |
| 		smix_cmd.s.reg_adr = devad;
 | |
| 		smix_cmd.s.phy_op = SMI_OP_C45_READ;
 | |
| 	} else {
 | |
| 		smix_cmd.s.reg_adr = regnum;
 | |
| 		smix_cmd.s.phy_op = SMI_OP_C22_READ;
 | |
| 	}
 | |
| 
 | |
| 	writeq(smix_cmd.u, priv->baseaddr + SMI_X_CMD);
 | |
| 
 | |
| 	do {
 | |
| 		smix_rd_dat.u = readq(priv->baseaddr + SMI_X_RD_DAT);
 | |
| 		udelay(10);
 | |
| 		timeout--;
 | |
| 	} while (smix_rd_dat.s.pending && timeout);
 | |
| 
 | |
| 	debug("SMIX_RD_DAT: %lx\n", (unsigned long)smix_rd_dat.u);
 | |
| 
 | |
| 	return smix_rd_dat.s.dat;
 | |
| }
 | |
| 
 | |
| int octeontx_phy_write(struct mii_dev *bus, int addr, int devad, int regnum,
 | |
| 		       u16 value)
 | |
| {
 | |
| 	struct octeontx_smi_priv *priv = bus->priv;
 | |
| 	union smi_x_cmd smix_cmd;
 | |
| 	union smi_x_wr_dat smix_wr_dat;
 | |
| 	unsigned long timeout = MDIO_TIMEOUT;
 | |
| 	int ret;
 | |
| 
 | |
| 	enum octeontx_smi_mode mode = (devad < 0) ? CLAUSE22 : CLAUSE45;
 | |
| 
 | |
| 	debug("WR: Mode: %u, baseaddr: %p, addr: %d, devad: %d, reg: %d\n",
 | |
| 	      mode, priv->baseaddr, addr, devad, regnum);
 | |
| 
 | |
| 	if (mode == CLAUSE45) {
 | |
| 		ret = octeontx_c45_addr(bus, addr, devad, regnum);
 | |
| 
 | |
| 		debug("WR: ret: %u\n", ret);
 | |
| 
 | |
| 		if (ret)
 | |
| 			return ret;
 | |
| 	}
 | |
| 
 | |
| 	smix_wr_dat.u = 0;
 | |
| 	smix_wr_dat.s.dat = value;
 | |
| 
 | |
| 	writeq(smix_wr_dat.u, priv->baseaddr + SMI_X_WR_DAT);
 | |
| 
 | |
| 	smix_cmd.u = 0;
 | |
| 	smix_cmd.s.phy_adr = addr;
 | |
| 
 | |
| 	if (mode == CLAUSE45) {
 | |
| 		smix_cmd.s.reg_adr = devad;
 | |
| 		smix_cmd.s.phy_op = SMI_OP_C45_WRITE;
 | |
| 	} else {
 | |
| 		smix_cmd.s.reg_adr = regnum;
 | |
| 		smix_cmd.s.phy_op = SMI_OP_C22_WRITE;
 | |
| 	}
 | |
| 
 | |
| 	writeq(smix_cmd.u, priv->baseaddr + SMI_X_CMD);
 | |
| 
 | |
| 	do {
 | |
| 		smix_wr_dat.u = readq(priv->baseaddr + SMI_X_WR_DAT);
 | |
| 		udelay(10);
 | |
| 		timeout--;
 | |
| 	} while (smix_wr_dat.s.pending && timeout);
 | |
| 
 | |
| 	debug("SMIX_WR_DAT: %lx\n", (unsigned long)smix_wr_dat.u);
 | |
| 
 | |
| 	return timeout == 0;
 | |
| }
 | |
| 
 | |
| int octeontx_smi_reset(struct mii_dev *bus)
 | |
| {
 | |
| 	struct octeontx_smi_priv *priv = bus->priv;
 | |
| 
 | |
| 	union smi_x_en smi_en;
 | |
| 
 | |
| 	smi_en.s.en = 0;
 | |
| 	writeq(smi_en.u, priv->baseaddr + SMI_X_EN);
 | |
| 
 | |
| 	smi_en.s.en = 1;
 | |
| 	writeq(smi_en.u, priv->baseaddr + SMI_X_EN);
 | |
| 
 | |
| 	octeontx_smi_setmode(bus, CLAUSE22);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* PHY XS initialization, primarily for RXAUI
 | |
|  *
 | |
|  */
 | |
| int rxaui_phy_xs_init(struct mii_dev *bus, int phy_addr)
 | |
| {
 | |
| 	int reg;
 | |
| 	ulong start_time;
 | |
| 	int phy_id1, phy_id2;
 | |
| 	int oui, model_number;
 | |
| 
 | |
| 	phy_id1 = octeontx_phy_read(bus, phy_addr, 1, 0x2);
 | |
| 	phy_id2 = octeontx_phy_read(bus, phy_addr, 1, 0x3);
 | |
| 	model_number = (phy_id2 >> 4) & 0x3F;
 | |
| 	debug("%s model %x\n", __func__, model_number);
 | |
| 	oui = phy_id1;
 | |
| 	oui <<= 6;
 | |
| 	oui |= (phy_id2 >> 10) & 0x3F;
 | |
| 	debug("%s oui %x\n", __func__, oui);
 | |
| 	switch (oui) {
 | |
| 	case 0x5016:
 | |
| 		if (model_number == 9) {
 | |
| 			debug("%s +\n", __func__);
 | |
| 			/* Perform hardware reset in XGXS control */
 | |
| 			reg = octeontx_phy_read(bus, phy_addr, 4, 0x0);
 | |
| 			if ((reg & 0xffff) < 0)
 | |
| 				goto read_error;
 | |
| 			reg |= 0x8000;
 | |
| 			octeontx_phy_write(bus, phy_addr, 4, 0x0, reg);
 | |
| 
 | |
| 			start_time = get_timer(0);
 | |
| 			do {
 | |
| 				reg = octeontx_phy_read(bus, phy_addr, 4, 0x0);
 | |
| 				if ((reg & 0xffff) < 0)
 | |
| 					goto read_error;
 | |
| 			} while ((reg & 0x8000) && get_timer(start_time) < 500);
 | |
| 			if (reg & 0x8000) {
 | |
| 				printf("HW reset for M88X3120 PHY failed");
 | |
| 				printf("MII_BMCR: 0x%x\n", reg);
 | |
| 				return -1;
 | |
| 			}
 | |
| 			/* program 4.49155 with 0x5 */
 | |
| 			octeontx_phy_write(bus, phy_addr, 4, 0xc003, 0x5);
 | |
| 		}
 | |
| 		break;
 | |
| 	default:
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| read_error:
 | |
| 	debug("M88X3120 PHY config read failed\n");
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| int octeontx_smi_probe(struct udevice *dev)
 | |
| {
 | |
| 	int ret, subnode, cnt = 0, node = dev_ofnode(dev).of_offset;
 | |
| 	struct mii_dev *bus;
 | |
| 	struct octeontx_smi_priv *priv;
 | |
| 	pci_dev_t bdf = dm_pci_get_bdf(dev);
 | |
| 
 | |
| 	debug("SMI PCI device: %x\n", bdf);
 | |
| 	if (!dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, PCI_REGION_MEM)) {
 | |
| 		printf("Failed to map PCI region for bdf %x\n", bdf);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	fdt_for_each_subnode(subnode, gd->fdt_blob, node) {
 | |
| 		ret = fdt_node_check_compatible(gd->fdt_blob, subnode,
 | |
| 						"cavium,thunder-8890-mdio");
 | |
| 		if (ret)
 | |
| 			continue;
 | |
| 
 | |
| 		bus = mdio_alloc();
 | |
| 		priv = malloc(sizeof(*priv));
 | |
| 		if (!bus || !priv) {
 | |
| 			printf("Failed to allocate OcteonTX MDIO bus # %u\n",
 | |
| 			       dev_seq(dev));
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		bus->read = octeontx_phy_read;
 | |
| 		bus->write = octeontx_phy_write;
 | |
| 		bus->reset = octeontx_smi_reset;
 | |
| 		bus->priv = priv;
 | |
| 
 | |
| 		priv->mode = CLAUSE22;
 | |
| 		priv->baseaddr = (void __iomem *)fdtdec_get_addr(gd->fdt_blob,
 | |
| 								 subnode,
 | |
| 								 "reg");
 | |
| 		debug("mdio base addr %p\n", priv->baseaddr);
 | |
| 
 | |
| 		/* use given name or generate its own unique name */
 | |
| 		snprintf(bus->name, MDIO_NAME_LEN, "smi%d", cnt++);
 | |
| 
 | |
| 		ret = mdio_register(bus);
 | |
| 		if (ret)
 | |
| 			return ret;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static const struct udevice_id octeontx_smi_ids[] = {
 | |
| 	{ .compatible = "cavium,thunder-8890-mdio-nexus" },
 | |
| 	{}
 | |
| };
 | |
| 
 | |
| U_BOOT_DRIVER(octeontx_smi) = {
 | |
| 	.name	= "octeontx_smi",
 | |
| 	.id	= UCLASS_MISC,
 | |
| 	.probe	= octeontx_smi_probe,
 | |
| 	.of_match = octeontx_smi_ids,
 | |
| };
 | |
| 
 | |
| static struct pci_device_id octeontx_smi_supported[] = {
 | |
| 	{ PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_SMI) },
 | |
| 	{}
 | |
| };
 | |
| 
 | |
| U_BOOT_PCI_DEVICE(octeontx_smi, octeontx_smi_supported);
 |