1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
|
/*
* 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
*
* 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 <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);
}
|