381 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			381 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/global_data.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)
 | 
						|
{
 | 
						|
	pci_dev_t bdf = dm_pci_get_bdf(dev);
 | 
						|
	struct octeontx_smi_priv *priv;
 | 
						|
	struct mii_dev *bus;
 | 
						|
	int ret, cnt = 0;
 | 
						|
	ofnode subnode;
 | 
						|
	u64 baseaddr;
 | 
						|
 | 
						|
	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;
 | 
						|
	}
 | 
						|
 | 
						|
	dev_for_each_subnode(subnode, dev) {
 | 
						|
		if (!ofnode_device_is_compatible(subnode,
 | 
						|
						 "cavium,thunder-8890-mdio"))
 | 
						|
			continue;
 | 
						|
		if (ofnode_read_u64(subnode, "reg", &baseaddr))
 | 
						|
			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 *)baseaddr;
 | 
						|
		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);
 |