#include <sys/param.h>
#include <sys/systm.h>
#include <sys/stdint.h>
#include <dev/ofw/fdt.h>
#include <dev/ofw/openfirm.h>
#include <machine/fdt.h>
#include <machine/octeonvar.h>
#include <octeon/dev/cn30xxsmireg.h>
#include <octeon/dev/cn30xxsmivar.h>
int cn30xxsmi_match(struct device *, void *, void *);
void cn30xxsmi_attach(struct device *, struct device *, void *);
const struct cfattach octsmi_ca = {
sizeof(struct cn30xxsmi_softc), cn30xxsmi_match, cn30xxsmi_attach
};
struct cfdriver octsmi_cd = {
NULL, "octsmi", DV_DULL
};
static SLIST_HEAD(, cn30xxsmi_softc) smi_list =
SLIST_HEAD_INITIALIZER(smi_list);
#define _SMI_RD8(sc, off) \
bus_space_read_8((sc)->sc_regt, (sc)->sc_regh, (off))
#define _SMI_WR8(sc, off, v) \
bus_space_write_8((sc)->sc_regt, (sc)->sc_regh, (off), (v))
int
cn30xxsmi_match(struct device *parent, void *match, void *aux)
{
struct fdt_attach_args *faa = aux;
return OF_is_compatible(faa->fa_node, "cavium,octeon-3860-mdio");
}
void
cn30xxsmi_attach(struct device *parent, struct device *self, void *aux)
{
struct fdt_attach_args *faa = aux;
struct cn30xxsmi_softc *sc = (struct cn30xxsmi_softc *)self;
if (faa->fa_nreg != 1)
return;
sc->sc_node = faa->fa_node;
sc->sc_regt = faa->fa_iot;
if (bus_space_map(sc->sc_regt, faa->fa_reg[0].addr, faa->fa_reg[0].size,
0, &sc->sc_regh)) {
printf(": could not map registers\n");
return;
}
SLIST_INSERT_HEAD(&smi_list, sc, sc_link);
printf("\n");
_SMI_WR8(sc, SMI_CLK_OFFSET, 0x1464);
_SMI_WR8(sc, SMI_EN_OFFSET, SMI_EN_EN);
}
int
cn30xxsmi_read(struct cn30xxsmi_softc *sc, int phy_addr, int reg)
{
uint64_t smi_rd;
int timo;
_SMI_WR8(sc, SMI_CMD_OFFSET, SMI_CMD_PHY_OP |
(phy_addr << SMI_CMD_PHY_ADR_SHIFT) |
(reg << SMI_CMD_REG_ADR_SHIFT));
timo = 10000;
smi_rd = _SMI_RD8(sc, SMI_RD_DAT_OFFSET);
while (ISSET(smi_rd, SMI_RD_DAT_PENDING)) {
if (timo-- == 0)
break;
delay(10);
smi_rd = _SMI_RD8(sc, SMI_RD_DAT_OFFSET);
}
if (ISSET(smi_rd, SMI_RD_DAT_PENDING)) {
return -1;
}
return ISSET(smi_rd, SMI_RD_DAT_VAL) ? (smi_rd & SMI_RD_DAT_DAT) : 0;
}
void
cn30xxsmi_write(struct cn30xxsmi_softc *sc, int phy_addr, int reg, int value)
{
uint64_t smi_wr;
int timo;
smi_wr = 0;
SET(smi_wr, value);
_SMI_WR8(sc, SMI_WR_DAT_OFFSET, smi_wr);
_SMI_WR8(sc, SMI_CMD_OFFSET, (phy_addr << SMI_CMD_PHY_ADR_SHIFT) |
(reg << SMI_CMD_REG_ADR_SHIFT));
timo = 10000;
smi_wr = _SMI_RD8(sc, SMI_WR_DAT_OFFSET);
while (ISSET(smi_wr, SMI_WR_DAT_PENDING)) {
if (timo-- == 0)
break;
delay(10);
smi_wr = _SMI_RD8(sc, SMI_WR_DAT_OFFSET);
}
if (ISSET(smi_wr, SMI_WR_DAT_PENDING)) {
printf("ERROR: cnmac_mii_writereg(0x%x, 0x%x, 0x%x) timed out.\n",
phy_addr, reg, value);
}
}
int
cn30xxsmi_get_phy(int phandle, int port, struct cn30xxsmi_softc **psmi,
int *preg)
{
static const int cam0100_phys[] = {
0x02, 0x03, 0x22
};
static const int cpn100_phys[] = {
0x0c, 0x11, 0x0d
};
static const int nutm25_phys[] = {
0x00, 0x04, 0x09
};
struct cn30xxsmi_softc *smi;
int parent, phynode;
int reg;
if (phandle != 0) {
phynode = OF_getnodebyphandle(phandle);
if (phynode == 0)
return ENOENT;
reg = OF_getpropint(phynode, "reg", UINT32_MAX);
if (reg == UINT32_MAX)
return ENOENT;
parent = OF_parent(phynode);
SLIST_FOREACH(smi, &smi_list, sc_link) {
if (smi->sc_node == parent)
goto found;
}
return ENOENT;
} else {
smi = SLIST_FIRST(&smi_list);
if (smi == NULL)
return ENOENT;
switch (octeon_board) {
case BOARD_CHECKPOINT_N100:
if (port >= nitems(cpn100_phys))
return ENOENT;
reg = cpn100_phys[port];
break;
case BOARD_NETGEAR_UTM25:
if (port >= nitems(nutm25_phys))
return ENOENT;
reg = nutm25_phys[port];
break;
case BOARD_UBIQUITI_E100:
if (port > 1 && octeon_boot_info->board_rev_major == 1)
return ENOENT;
case BOARD_UBIQUITI_E120:
if (port > 2)
return ENOENT;
reg = 7 - port;
break;
case BOARD_CN3010_EVB_HS5:
if (port >= nitems(cam0100_phys))
return ENOENT;
reg = cam0100_phys[port];
break;
default:
return ENOENT;
}
}
found:
*psmi = smi;
*preg = reg;
return 0;
}