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>
		
			
				
	
	
		
			153 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			153 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;
 | |
| }
 |