2020-02-06 08:48:16 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
|
|
|
/*
|
|
|
|
* QE UEC ethernet phy controller driver
|
|
|
|
*
|
|
|
|
* based on phy parts of drivers/qe/uec.c and drivers/qe/uec_phy.c
|
|
|
|
* from NXP
|
|
|
|
*
|
|
|
|
* Copyright (C) 2020 Heiko Schocher <hs@denx.de>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <common.h>
|
|
|
|
#include <dm.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <miiphy.h>
|
|
|
|
#include <phy.h>
|
|
|
|
#include <asm/io.h>
|
|
|
|
#include <linux/ioport.h>
|
|
|
|
|
|
|
|
#include "dm_qe_uec.h"
|
|
|
|
|
|
|
|
struct qe_uec_mdio_priv {
|
|
|
|
struct ucc_mii_mng *base;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
qe_uec_mdio_read(struct udevice *dev, int addr, int devad, int reg)
|
|
|
|
{
|
|
|
|
struct qe_uec_mdio_priv *priv = dev_get_priv(dev);
|
|
|
|
struct ucc_mii_mng *regs = priv->base;
|
|
|
|
u32 tmp_reg;
|
|
|
|
u16 value;
|
|
|
|
|
|
|
|
debug("%s: regs: %p addr: %x devad: %x reg: %x\n", __func__, regs,
|
|
|
|
addr, devad, reg);
|
|
|
|
/* Setting up the MII management Address Register */
|
|
|
|
tmp_reg = ((u32)addr << MIIMADD_PHY_ADDRESS_SHIFT) | reg;
|
|
|
|
out_be32(®s->miimadd, tmp_reg);
|
|
|
|
|
|
|
|
/* clear MII management command cycle */
|
|
|
|
out_be32(®s->miimcom, 0);
|
|
|
|
sync();
|
|
|
|
|
|
|
|
/* Perform an MII management read cycle */
|
|
|
|
out_be32(®s->miimcom, MIIMCOM_READ_CYCLE);
|
|
|
|
|
|
|
|
/* Wait till MII management write is complete */
|
|
|
|
while ((in_be32(®s->miimind)) &
|
|
|
|
(MIIMIND_NOT_VALID | MIIMIND_BUSY))
|
|
|
|
;
|
|
|
|
|
|
|
|
/* Read MII management status */
|
|
|
|
value = (u16)in_be32(®s->miimstat);
|
|
|
|
if (value == 0xffff)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return value;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
qe_uec_mdio_write(struct udevice *dev, int addr, int devad, int reg,
|
|
|
|
u16 value)
|
|
|
|
{
|
|
|
|
struct qe_uec_mdio_priv *priv = dev_get_priv(dev);
|
|
|
|
struct ucc_mii_mng *regs = priv->base;
|
|
|
|
u32 tmp_reg;
|
|
|
|
|
|
|
|
debug("%s: regs: %p addr: %x devad: %x reg: %x val: %x\n", __func__,
|
|
|
|
regs, addr, devad, reg, value);
|
|
|
|
|
|
|
|
/* Stop the MII management read cycle */
|
|
|
|
out_be32(®s->miimcom, 0);
|
|
|
|
/* Setting up the MII management Address Register */
|
|
|
|
tmp_reg = ((u32)addr << MIIMADD_PHY_ADDRESS_SHIFT) | reg;
|
|
|
|
out_be32(®s->miimadd, tmp_reg);
|
|
|
|
|
|
|
|
/* Setting up the MII management Control Register with the value */
|
|
|
|
out_be32(®s->miimcon, (u32)value);
|
|
|
|
sync();
|
|
|
|
|
|
|
|
/* Wait till MII management write is complete */
|
|
|
|
while ((in_be32(®s->miimind)) & MIIMIND_BUSY)
|
|
|
|
;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct mdio_ops qe_uec_mdio_ops = {
|
|
|
|
.read = qe_uec_mdio_read,
|
|
|
|
.write = qe_uec_mdio_write,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int qe_uec_mdio_probe(struct udevice *dev)
|
|
|
|
{
|
|
|
|
struct qe_uec_mdio_priv *priv = dev_get_priv(dev);
|
|
|
|
fdt_size_t base;
|
|
|
|
ofnode node;
|
|
|
|
u32 num = 0;
|
|
|
|
int ret = -ENODEV;
|
|
|
|
|
|
|
|
priv->base = (struct ucc_mii_mng *)dev_read_addr(dev);
|
|
|
|
base = (fdt_size_t)priv->base;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* idea from linux:
|
|
|
|
* drivers/net/ethernet/freescale/fsl_pq_mdio.c
|
|
|
|
*
|
|
|
|
* Find the UCC node that controls the given MDIO node
|
|
|
|
*
|
|
|
|
* For some reason, the QE MDIO nodes are not children of the UCC
|
|
|
|
* devices that control them. Therefore, we need to scan all UCC
|
|
|
|
* nodes looking for the one that encompases the given MDIO node.
|
|
|
|
* We do this by comparing physical addresses. The 'start' and
|
|
|
|
* 'end' addresses of the MDIO node are passed, and the correct
|
|
|
|
* UCC node will cover the entire address range.
|
|
|
|
*/
|
|
|
|
node = ofnode_by_compatible(ofnode_null(), "ucc_geth");
|
|
|
|
while (ofnode_valid(node)) {
|
|
|
|
fdt_size_t size;
|
|
|
|
fdt_addr_t addr;
|
|
|
|
|
|
|
|
addr = ofnode_get_addr_index(node, 0);
|
|
|
|
ret = ofnode_get_addr_size_index(node, 0, &size);
|
|
|
|
|
|
|
|
if (addr == FDT_ADDR_T_NONE) {
|
|
|
|
node = ofnode_by_compatible(node, "ucc_geth");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check if priv->base in start end */
|
|
|
|
if (base > addr && base < (addr + size)) {
|
|
|
|
ret = ofnode_read_u32(node, "cell-index", &num);
|
|
|
|
if (ret)
|
|
|
|
ret = ofnode_read_u32(node, "device-id",
|
|
|
|
&num);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
node = ofnode_by_compatible(node, "ucc_geth");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
printf("%s: no cell-index nor device-id found!", __func__);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Setup MII master clock source */
|
|
|
|
qe_set_mii_clk_src(num - 1);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct udevice_id qe_uec_mdio_ids[] = {
|
|
|
|
{ .compatible = "fsl,ucc-mdio" },
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
|
|
|
|
U_BOOT_DRIVER(mvmdio) = {
|
|
|
|
.name = "qe_uec_mdio",
|
|
|
|
.id = UCLASS_MDIO,
|
|
|
|
.of_match = qe_uec_mdio_ids,
|
|
|
|
.probe = qe_uec_mdio_probe,
|
|
|
|
.ops = &qe_uec_mdio_ops,
|
2020-12-03 23:55:17 +00:00
|
|
|
.priv_auto = sizeof(struct qe_uec_mdio_priv),
|
2020-02-06 08:48:16 +00:00
|
|
|
};
|