/* * Xilinx xps_ll_temac ethernet driver for u-boot * * MDIO bus access * * Copyright (C) 2011 - 2012 Stephan Linz * Copyright (C) 2008 - 2011 Michal Simek * 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 * * SPDX-License-Identifier: GPL-2.0+ * * [0]: http://www.xilinx.com/support/documentation * * [S]: [0]/ip_documentation/xps_ll_temac.pdf * [A]: [0]/application_notes/xapp1041.pdf */ #include #include #include #include #include #include #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); }