Driver for non-standard on-chip ethernet device ARC EMAC 10/100, instantiated in some legacy ARC (Synopsys) FPGA Boards such as ARCAngel4/ML50x. Signed-off-by: Alexey Brodkin <abrodkin@synopsys.com> Cc: Andy Shevchenko <andy.shevchenko@gmail.com> Cc: Francois Romieu <romieu@fr.zoreil.com> Cc: Joe Perches <joe@perches.com> Cc: Vineet Gupta <vgupta@synopsys.com> Cc: Mischa Jonker <mjonker@synopsys.com> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Grant Likely <grant.likely@linaro.org> Cc: Rob Herring <rob.herring@calxeda.com> Cc: Paul Gortmaker <paul.gortmaker@windriver.com> Cc: "David S. Miller" <davem@davemloft.net> Cc: linux-kernel@vger.kernel.org Cc: devicetree-discuss@lists.ozlabs.org Cc: Florian Fainelli <florian@openwrt.org> Cc: David Laight <david.laight@aculab.com> Signed-off-by: David S. Miller <davem@davemloft.net>
		
			
				
	
	
		
			152 lines
		
	
	
	
		
			3.5 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			152 lines
		
	
	
	
		
			3.5 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (C) 2004-2013 Synopsys, Inc. (www.synopsys.com)
 | 
						|
 *
 | 
						|
 * MDIO implementation for ARC EMAC
 | 
						|
 */
 | 
						|
 | 
						|
#include <linux/delay.h>
 | 
						|
#include <linux/of_mdio.h>
 | 
						|
#include <linux/platform_device.h>
 | 
						|
 | 
						|
#include "emac.h"
 | 
						|
 | 
						|
/* Number of seconds we wait for "MDIO complete" flag to appear */
 | 
						|
#define ARC_MDIO_COMPLETE_POLL_COUNT	1
 | 
						|
 | 
						|
/**
 | 
						|
 * arc_mdio_complete_wait - Waits until MDIO transaction is completed.
 | 
						|
 * @priv:	Pointer to ARC EMAC private data structure.
 | 
						|
 *
 | 
						|
 * returns:	0 on success, -ETIMEDOUT on a timeout.
 | 
						|
 */
 | 
						|
static int arc_mdio_complete_wait(struct arc_emac_priv *priv)
 | 
						|
{
 | 
						|
	unsigned int i;
 | 
						|
 | 
						|
	for (i = 0; i < ARC_MDIO_COMPLETE_POLL_COUNT * 40; i++) {
 | 
						|
		unsigned int status = arc_reg_get(priv, R_STATUS);
 | 
						|
 | 
						|
		status &= MDIO_MASK;
 | 
						|
 | 
						|
		if (status) {
 | 
						|
			/* Reset "MDIO complete" flag */
 | 
						|
			arc_reg_set(priv, R_STATUS, status);
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
 | 
						|
		msleep(25);
 | 
						|
	}
 | 
						|
 | 
						|
	return -ETIMEDOUT;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * arc_mdio_read - MDIO interface read function.
 | 
						|
 * @bus:	Pointer to MII bus structure.
 | 
						|
 * @phy_addr:	Address of the PHY device.
 | 
						|
 * @reg_num:	PHY register to read.
 | 
						|
 *
 | 
						|
 * returns:	The register contents on success, -ETIMEDOUT on a timeout.
 | 
						|
 *
 | 
						|
 * Reads the contents of the requested register from the requested PHY
 | 
						|
 * address.
 | 
						|
 */
 | 
						|
static int arc_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)
 | 
						|
{
 | 
						|
	struct arc_emac_priv *priv = bus->priv;
 | 
						|
	unsigned int value;
 | 
						|
	int error;
 | 
						|
 | 
						|
	arc_reg_set(priv, R_MDIO,
 | 
						|
		    0x60020000 | (phy_addr << 23) | (reg_num << 18));
 | 
						|
 | 
						|
	error = arc_mdio_complete_wait(priv);
 | 
						|
	if (error < 0)
 | 
						|
		return error;
 | 
						|
 | 
						|
	value = arc_reg_get(priv, R_MDIO) & 0xffff;
 | 
						|
 | 
						|
	dev_dbg(priv->dev, "arc_mdio_read(phy_addr=%i, reg_num=%x) = %x\n",
 | 
						|
		phy_addr, reg_num, value);
 | 
						|
 | 
						|
	return value;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * arc_mdio_write - MDIO interface write function.
 | 
						|
 * @bus:	Pointer to MII bus structure.
 | 
						|
 * @phy_addr:	Address of the PHY device.
 | 
						|
 * @reg_num:	PHY register to write to.
 | 
						|
 * @value:	Value to be written into the register.
 | 
						|
 *
 | 
						|
 * returns:	0 on success, -ETIMEDOUT on a timeout.
 | 
						|
 *
 | 
						|
 * Writes the value to the requested register.
 | 
						|
 */
 | 
						|
static int arc_mdio_write(struct mii_bus *bus, int phy_addr,
 | 
						|
			  int reg_num, u16 value)
 | 
						|
{
 | 
						|
	struct arc_emac_priv *priv = bus->priv;
 | 
						|
 | 
						|
	dev_dbg(priv->dev,
 | 
						|
		"arc_mdio_write(phy_addr=%i, reg_num=%x, value=%x)\n",
 | 
						|
		phy_addr, reg_num, value);
 | 
						|
 | 
						|
	arc_reg_set(priv, R_MDIO,
 | 
						|
		     0x50020000 | (phy_addr << 23) | (reg_num << 18) | value);
 | 
						|
 | 
						|
	return arc_mdio_complete_wait(priv);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * arc_mdio_probe - MDIO probe function.
 | 
						|
 * @pdev:	Pointer to platform device.
 | 
						|
 * @priv:	Pointer to ARC EMAC private data structure.
 | 
						|
 *
 | 
						|
 * returns:	0 on success, -ENOMEM when mdiobus_alloc
 | 
						|
 * (to allocate memory for MII bus structure) fails.
 | 
						|
 *
 | 
						|
 * Sets up and registers the MDIO interface.
 | 
						|
 */
 | 
						|
int arc_mdio_probe(struct platform_device *pdev, struct arc_emac_priv *priv)
 | 
						|
{
 | 
						|
	struct mii_bus *bus;
 | 
						|
	int error;
 | 
						|
 | 
						|
	bus = mdiobus_alloc();
 | 
						|
	if (!bus)
 | 
						|
		return -ENOMEM;
 | 
						|
 | 
						|
	priv->bus = bus;
 | 
						|
	bus->priv = priv;
 | 
						|
	bus->parent = priv->dev;
 | 
						|
	bus->name = "Synopsys MII Bus",
 | 
						|
	bus->read = &arc_mdio_read;
 | 
						|
	bus->write = &arc_mdio_write;
 | 
						|
 | 
						|
	snprintf(bus->id, MII_BUS_ID_SIZE, "%s", pdev->name);
 | 
						|
 | 
						|
	error = of_mdiobus_register(bus, pdev->dev.of_node);
 | 
						|
	if (error) {
 | 
						|
		dev_err(priv->dev, "cannot register MDIO bus %s\n", bus->name);
 | 
						|
		mdiobus_free(bus);
 | 
						|
		return error;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * arc_mdio_remove - MDIO remove function.
 | 
						|
 * @priv:	Pointer to ARC EMAC private data structure.
 | 
						|
 *
 | 
						|
 * Unregisters the MDIO and frees any associate memory for MII bus.
 | 
						|
 */
 | 
						|
int arc_mdio_remove(struct arc_emac_priv *priv)
 | 
						|
{
 | 
						|
	mdiobus_unregister(priv->bus);
 | 
						|
	mdiobus_free(priv->bus);
 | 
						|
	priv->bus = NULL;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 |