diff options
Diffstat (limited to 'drivers/net/xilinx_ll_temac_mdio.c')
| -rw-r--r-- | drivers/net/xilinx_ll_temac_mdio.c | 180 | 
1 files changed, 180 insertions, 0 deletions
| diff --git a/drivers/net/xilinx_ll_temac_mdio.c b/drivers/net/xilinx_ll_temac_mdio.c new file mode 100644 index 000000000..c56ff486e --- /dev/null +++ b/drivers/net/xilinx_ll_temac_mdio.c @@ -0,0 +1,180 @@ +/* + * Xilinx xps_ll_temac ethernet driver for u-boot + * + * MDIO bus access + * + * Copyright (C) 2011 - 2012 Stephan Linz <linz@li-pro.net> + * Copyright (C) 2008 - 2011 Michal Simek <monstr@monstr.eu> + * Copyright (C) 2008 - 2011 PetaLogix + * + * Based on Yoshio Kashiwagi kashiwagi@co-nss.co.jp driver + * Copyright (C) 2008 Nissin Systems Co.,Ltd. + * March 2008 created + * + * CREDITS: tsec driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * [0]: http://www.xilinx.com/support/documentation + * + * [S]:	[0]/ip_documentation/xps_ll_temac.pdf + * [A]:	[0]/application_notes/xapp1041.pdf + */ + +#include <config.h> +#include <common.h> +#include <miiphy.h> +#include <phy.h> +#include <malloc.h> +#include <asm/io.h> + +#include "xilinx_ll_temac.h" +#include "xilinx_ll_temac_mdio.h" + +#if !defined(CONFIG_MII) +# error "LL_TEMAC requires MII -- missing CONFIG_MII" +#endif + +#if !defined(CONFIG_PHYLIB) +# error "LL_TEMAC requires PHYLIB -- missing CONFIG_PHYLIB" +#endif + +/* + * Prior to PHY access, the MDIO clock must be setup. This driver will set a + * safe default that should work with PLB bus speeds of up to 150 MHz and keep + * the MDIO clock below 2.5 MHz. If the user wishes faster access to the PHY + * then the clock divisor can be set to a different value by setting the + * correct bus speed value with CONFIG_XILINX_LL_TEMAC_CLK. + */ +#if !defined(CONFIG_XILINX_LL_TEMAC_CLK) +#define MDIO_CLOCK_DIV		MC_CLKDIV_10(150000000) +#else +#define MDIO_CLOCK_DIV		MC_CLKDIV_25(CONFIG_XILINX_LL_TEMAC_CLK) +#endif + +static int ll_temac_mdio_setup(struct mii_dev *bus) +{ +	struct temac_reg *regs = (struct temac_reg *)bus->priv; + +	/* setup MDIO clock */ +	ll_temac_indirect_set(regs, TEMAC_MC, +			MC_MDIOEN | (MDIO_CLOCK_DIV & MC_CLKDIV_MASK)); + +	return 0; +} + +/* + * Indirect MII PHY read via ll_temac. + * + * http://www.xilinx.com/support/documentation/ip_documentation/xps_ll_temac.pdf + * page 67, Using the MII Management to Access PHY Registers + */ +int ll_temac_local_mdio_read(struct temac_reg *regs, int addr, int devad, +				int regnum) +{ +	out_be32(®s->lsw, +		((addr << LSW_PHYAD_POS) & LSW_PHYAD_MASK) | +		(regnum & LSW_REGAD_MASK)); +	out_be32(®s->ctl, TEMAC_MIIMAI); + +	ll_temac_check_status(regs, RSE_MIIM_RR); + +	return in_be32(®s->lsw) & LSW_REGDAT_MASK; +} + +/* + * Indirect MII PHY write via ll_temac. + * + * http://www.xilinx.com/support/documentation/ip_documentation/xps_ll_temac.pdf + * page 67, Using the MII Management to Access PHY Registers + */ +void ll_temac_local_mdio_write(struct temac_reg *regs, int addr, int devad, +				int regnum, u16 value) +{ +	out_be32(®s->lsw, (value & LSW_REGDAT_MASK)); +	out_be32(®s->ctl, CTL_WEN | TEMAC_MIIMWD); + +	out_be32(®s->lsw, +		((addr << LSW_PHYAD_POS) & LSW_PHYAD_MASK) | +		(regnum & LSW_REGAD_MASK)); +	out_be32(®s->ctl, CTL_WEN | TEMAC_MIIMAI); + +	ll_temac_check_status(regs, RSE_MIIM_WR); +} + +int ll_temac_phy_read(struct mii_dev *bus, int addr, int devad, int regnum) +{ +	struct temac_reg *regs = (struct temac_reg *)bus->priv; + +	return ll_temac_local_mdio_read(regs, addr, devad, regnum); +} + +int ll_temac_phy_write(struct mii_dev *bus, int addr, int devad, int regnum, +			u16 value) +{ +	struct temac_reg *regs = (struct temac_reg *)bus->priv; + +	ll_temac_local_mdio_write(regs, addr, devad, regnum, value); + +	return 0; +} + +/* + * Use MII register 1 (MII status register) to detect PHY + * + * A Mask used to verify certain PHY features (register content) + * in the PHY detection register: + *  Auto-negotiation support, 10Mbps half/full duplex support + */ +#define PHY_DETECT_REG		MII_BMSR +#define PHY_DETECT_MASK		(BMSR_10FULL | BMSR_10HALF | BMSR_ANEGCAPABLE) + +/* Looking for a valid PHY address */ +int ll_temac_phy_addr(struct mii_dev *bus) +{ +	struct temac_reg *regs = (struct temac_reg *)bus->priv; +	unsigned short val; +	unsigned int phy; + +	for (phy = PHY_MAX_ADDR; phy >= 0; phy--) { +		val = ll_temac_local_mdio_read(regs, phy, 0, PHY_DETECT_REG); +		if ((val != 0xFFFF) && +		((val & PHY_DETECT_MASK) == PHY_DETECT_MASK)) { +			/* Found a valid PHY address */ +			return phy; +		} +	} + +	return -1; +} + +int xilinx_ll_temac_mdio_initialize(bd_t *bis, struct ll_temac_mdio_info *info) +{ +	struct mii_dev *bus = mdio_alloc(); + +	if (!bus) { +		printf("Failed to allocate LL_TEMAC MDIO bus: %s\n", +				info->name); +		return -1; +	} + +	bus->read = ll_temac_phy_read; +	bus->write = ll_temac_phy_write; +	bus->reset = NULL; + +	/* use given name or generate its own unique name */ +	if (info->name) { +		strncpy(bus->name, info->name, MDIO_NAME_LEN); +	} else { +		snprintf(bus->name, MDIO_NAME_LEN, "lltemii.%p", info->regs); +		info->name = bus->name; +	} + +	bus->priv = info->regs; + +	ll_temac_mdio_setup(bus); +	return mdio_register(bus); +} |