mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-24 21:54:01 +00:00
2635e3b50f
Add a mask parameter to control the lookup of the PCI region from which the mapping can be made. Signed-off-by: Andrew Scull <ascull@google.com> Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
380 lines
7.9 KiB
C
380 lines
7.9 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, 0, 0, PCI_REGION_TYPE, 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);
|