164 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			164 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			C
		
	
	
	
// 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,
 | 
						|
	.priv_auto	= sizeof(struct qe_uec_mdio_priv),
 | 
						|
};
 |