156 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			156 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			C
		
	
	
	
// SPDX-License-Identifier: GPL-2.0+
 | 
						|
/*
 | 
						|
 * ENETC ethernet controller driver
 | 
						|
 * Copyright 2019 NXP
 | 
						|
 */
 | 
						|
 | 
						|
#include <common.h>
 | 
						|
#include <dm.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <pci.h>
 | 
						|
#include <miiphy.h>
 | 
						|
#include <asm/io.h>
 | 
						|
#include <asm/processor.h>
 | 
						|
#include <miiphy.h>
 | 
						|
 | 
						|
#include "fsl_enetc.h"
 | 
						|
 | 
						|
static void enetc_mdio_wait_bsy(struct enetc_mdio_priv *priv)
 | 
						|
{
 | 
						|
	int to = 10000;
 | 
						|
 | 
						|
	while ((enetc_read(priv, ENETC_MDIO_CFG) & ENETC_EMDIO_CFG_BSY) &&
 | 
						|
	       --to)
 | 
						|
		cpu_relax();
 | 
						|
	if (!to)
 | 
						|
		printf("T");
 | 
						|
}
 | 
						|
 | 
						|
int enetc_mdio_read_priv(struct enetc_mdio_priv *priv, int addr, int devad,
 | 
						|
			 int reg)
 | 
						|
{
 | 
						|
	if (devad == MDIO_DEVAD_NONE)
 | 
						|
		enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22);
 | 
						|
	else
 | 
						|
		enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C45);
 | 
						|
	enetc_mdio_wait_bsy(priv);
 | 
						|
 | 
						|
	if (devad == MDIO_DEVAD_NONE) {
 | 
						|
		enetc_write(priv, ENETC_MDIO_CTL, ENETC_MDIO_CTL_READ |
 | 
						|
			    (addr << 5) | reg);
 | 
						|
	} else {
 | 
						|
		enetc_write(priv, ENETC_MDIO_CTL, (addr << 5) + devad);
 | 
						|
		enetc_mdio_wait_bsy(priv);
 | 
						|
 | 
						|
		enetc_write(priv, ENETC_MDIO_STAT, reg);
 | 
						|
		enetc_mdio_wait_bsy(priv);
 | 
						|
 | 
						|
		enetc_write(priv, ENETC_MDIO_CTL, ENETC_MDIO_CTL_READ |
 | 
						|
			    (addr << 5) | devad);
 | 
						|
	}
 | 
						|
 | 
						|
	enetc_mdio_wait_bsy(priv);
 | 
						|
	if (enetc_read(priv, ENETC_MDIO_CFG) & ENETC_EMDIO_CFG_RD_ER)
 | 
						|
		return ENETC_MDIO_READ_ERR;
 | 
						|
 | 
						|
	return enetc_read(priv, ENETC_MDIO_DATA);
 | 
						|
}
 | 
						|
 | 
						|
int enetc_mdio_write_priv(struct enetc_mdio_priv *priv, int addr, int devad,
 | 
						|
			  int reg, u16 val)
 | 
						|
{
 | 
						|
	if (devad == MDIO_DEVAD_NONE)
 | 
						|
		enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22);
 | 
						|
	else
 | 
						|
		enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C45);
 | 
						|
	enetc_mdio_wait_bsy(priv);
 | 
						|
 | 
						|
	if (devad != MDIO_DEVAD_NONE) {
 | 
						|
		enetc_write(priv, ENETC_MDIO_CTL, (addr << 5) + devad);
 | 
						|
		enetc_write(priv, ENETC_MDIO_STAT, reg);
 | 
						|
	} else {
 | 
						|
		enetc_write(priv, ENETC_MDIO_CTL, (addr << 5) + reg);
 | 
						|
	}
 | 
						|
	enetc_mdio_wait_bsy(priv);
 | 
						|
 | 
						|
	enetc_write(priv, ENETC_MDIO_DATA, val);
 | 
						|
	enetc_mdio_wait_bsy(priv);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* DM wrappers */
 | 
						|
static int dm_enetc_mdio_read(struct udevice *dev, int addr, int devad, int reg)
 | 
						|
{
 | 
						|
	struct enetc_mdio_priv *priv = dev_get_priv(dev);
 | 
						|
 | 
						|
	return enetc_mdio_read_priv(priv, addr, devad, reg);
 | 
						|
}
 | 
						|
 | 
						|
static int dm_enetc_mdio_write(struct udevice *dev, int addr, int devad,
 | 
						|
			       int reg, u16 val)
 | 
						|
{
 | 
						|
	struct enetc_mdio_priv *priv = dev_get_priv(dev);
 | 
						|
 | 
						|
	return enetc_mdio_write_priv(priv, addr, devad, reg, val);
 | 
						|
}
 | 
						|
 | 
						|
static const struct mdio_ops enetc_mdio_ops = {
 | 
						|
	.read = dm_enetc_mdio_read,
 | 
						|
	.write = dm_enetc_mdio_write,
 | 
						|
};
 | 
						|
 | 
						|
static int enetc_mdio_bind(struct udevice *dev)
 | 
						|
{
 | 
						|
	char name[16];
 | 
						|
	static int eth_num_devices;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * prefer using PCI function numbers to number interfaces, but these
 | 
						|
	 * are only available if dts nodes are present.  For PCI they are
 | 
						|
	 * optional, handle that case too.  Just in case some nodes are present
 | 
						|
	 * and some are not, use different naming scheme - enetc-N based on
 | 
						|
	 * PCI function # and enetc#N based on interface count
 | 
						|
	 */
 | 
						|
	if (ofnode_valid(dev->node))
 | 
						|
		sprintf(name, "emdio-%u", PCI_FUNC(pci_get_devfn(dev)));
 | 
						|
	else
 | 
						|
		sprintf(name, "emdio#%u", eth_num_devices++);
 | 
						|
	device_set_name(dev, name);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int enetc_mdio_probe(struct udevice *dev)
 | 
						|
{
 | 
						|
	struct enetc_mdio_priv *priv = dev_get_priv(dev);
 | 
						|
 | 
						|
	priv->regs_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, 0);
 | 
						|
	if (!priv->regs_base) {
 | 
						|
		enetc_dbg(dev, "failed to map BAR0\n");
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
 | 
						|
	priv->regs_base += ENETC_MDIO_BASE;
 | 
						|
 | 
						|
	dm_pci_clrset_config16(dev, PCI_COMMAND, 0, PCI_COMMAND_MEMORY);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
U_BOOT_DRIVER(enetc_mdio) = {
 | 
						|
	.name	= "enetc_mdio",
 | 
						|
	.id	= UCLASS_MDIO,
 | 
						|
	.bind	= enetc_mdio_bind,
 | 
						|
	.probe	= enetc_mdio_probe,
 | 
						|
	.ops	= &enetc_mdio_ops,
 | 
						|
	.priv_auto_alloc_size = sizeof(struct enetc_mdio_priv),
 | 
						|
};
 | 
						|
 | 
						|
static struct pci_device_id enetc_mdio_ids[] = {
 | 
						|
	{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_MDIO) },
 | 
						|
	{ }
 | 
						|
};
 | 
						|
 | 
						|
U_BOOT_PCI_DEVICE(enetc_mdio, enetc_mdio_ids);
 |