1*df482650SStephan Linz /* 2*df482650SStephan Linz * Xilinx xps_ll_temac ethernet driver for u-boot 3*df482650SStephan Linz * 4*df482650SStephan Linz * MDIO bus access 5*df482650SStephan Linz * 6*df482650SStephan Linz * Copyright (C) 2011 - 2012 Stephan Linz <linz@li-pro.net> 7*df482650SStephan Linz * Copyright (C) 2008 - 2011 Michal Simek <monstr@monstr.eu> 8*df482650SStephan Linz * Copyright (C) 2008 - 2011 PetaLogix 9*df482650SStephan Linz * 10*df482650SStephan Linz * Based on Yoshio Kashiwagi kashiwagi@co-nss.co.jp driver 11*df482650SStephan Linz * Copyright (C) 2008 Nissin Systems Co.,Ltd. 12*df482650SStephan Linz * March 2008 created 13*df482650SStephan Linz * 14*df482650SStephan Linz * CREDITS: tsec driver 15*df482650SStephan Linz * 16*df482650SStephan Linz * This program is free software; you can redistribute it and/or modify it 17*df482650SStephan Linz * under the terms of the GNU General Public License as published by the 18*df482650SStephan Linz * Free Software Foundation; either version 2 of the License, or (at your 19*df482650SStephan Linz * option) any later version. 20*df482650SStephan Linz * 21*df482650SStephan Linz * [0]: http://www.xilinx.com/support/documentation 22*df482650SStephan Linz * 23*df482650SStephan Linz * [S]: [0]/ip_documentation/xps_ll_temac.pdf 24*df482650SStephan Linz * [A]: [0]/application_notes/xapp1041.pdf 25*df482650SStephan Linz */ 26*df482650SStephan Linz 27*df482650SStephan Linz #include <config.h> 28*df482650SStephan Linz #include <common.h> 29*df482650SStephan Linz #include <miiphy.h> 30*df482650SStephan Linz #include <phy.h> 31*df482650SStephan Linz #include <malloc.h> 32*df482650SStephan Linz #include <asm/io.h> 33*df482650SStephan Linz 34*df482650SStephan Linz #include "xilinx_ll_temac.h" 35*df482650SStephan Linz #include "xilinx_ll_temac_mdio.h" 36*df482650SStephan Linz 37*df482650SStephan Linz #if !defined(CONFIG_MII) 38*df482650SStephan Linz # error "LL_TEMAC requires MII -- missing CONFIG_MII" 39*df482650SStephan Linz #endif 40*df482650SStephan Linz 41*df482650SStephan Linz #if !defined(CONFIG_PHYLIB) 42*df482650SStephan Linz # error "LL_TEMAC requires PHYLIB -- missing CONFIG_PHYLIB" 43*df482650SStephan Linz #endif 44*df482650SStephan Linz 45*df482650SStephan Linz /* 46*df482650SStephan Linz * Prior to PHY access, the MDIO clock must be setup. This driver will set a 47*df482650SStephan Linz * safe default that should work with PLB bus speeds of up to 150 MHz and keep 48*df482650SStephan Linz * the MDIO clock below 2.5 MHz. If the user wishes faster access to the PHY 49*df482650SStephan Linz * then the clock divisor can be set to a different value by setting the 50*df482650SStephan Linz * correct bus speed value with CONFIG_XILINX_LL_TEMAC_CLK. 51*df482650SStephan Linz */ 52*df482650SStephan Linz #if !defined(CONFIG_XILINX_LL_TEMAC_CLK) 53*df482650SStephan Linz #define MDIO_CLOCK_DIV MC_CLKDIV_10(150000000) 54*df482650SStephan Linz #else 55*df482650SStephan Linz #define MDIO_CLOCK_DIV MC_CLKDIV_25(CONFIG_XILINX_LL_TEMAC_CLK) 56*df482650SStephan Linz #endif 57*df482650SStephan Linz 58*df482650SStephan Linz static int ll_temac_mdio_setup(struct mii_dev *bus) 59*df482650SStephan Linz { 60*df482650SStephan Linz struct temac_reg *regs = (struct temac_reg *)bus->priv; 61*df482650SStephan Linz 62*df482650SStephan Linz /* setup MDIO clock */ 63*df482650SStephan Linz ll_temac_indirect_set(regs, TEMAC_MC, 64*df482650SStephan Linz MC_MDIOEN | (MDIO_CLOCK_DIV & MC_CLKDIV_MASK)); 65*df482650SStephan Linz 66*df482650SStephan Linz return 0; 67*df482650SStephan Linz } 68*df482650SStephan Linz 69*df482650SStephan Linz /* 70*df482650SStephan Linz * Indirect MII PHY read via ll_temac. 71*df482650SStephan Linz * 72*df482650SStephan Linz * http://www.xilinx.com/support/documentation/ip_documentation/xps_ll_temac.pdf 73*df482650SStephan Linz * page 67, Using the MII Management to Access PHY Registers 74*df482650SStephan Linz */ 75*df482650SStephan Linz int ll_temac_local_mdio_read(struct temac_reg *regs, int addr, int devad, 76*df482650SStephan Linz int regnum) 77*df482650SStephan Linz { 78*df482650SStephan Linz out_be32(®s->lsw, 79*df482650SStephan Linz ((addr << LSW_PHYAD_POS) & LSW_PHYAD_MASK) | 80*df482650SStephan Linz (regnum & LSW_REGAD_MASK)); 81*df482650SStephan Linz out_be32(®s->ctl, TEMAC_MIIMAI); 82*df482650SStephan Linz 83*df482650SStephan Linz ll_temac_check_status(regs, RSE_MIIM_RR); 84*df482650SStephan Linz 85*df482650SStephan Linz return in_be32(®s->lsw) & LSW_REGDAT_MASK; 86*df482650SStephan Linz } 87*df482650SStephan Linz 88*df482650SStephan Linz /* 89*df482650SStephan Linz * Indirect MII PHY write via ll_temac. 90*df482650SStephan Linz * 91*df482650SStephan Linz * http://www.xilinx.com/support/documentation/ip_documentation/xps_ll_temac.pdf 92*df482650SStephan Linz * page 67, Using the MII Management to Access PHY Registers 93*df482650SStephan Linz */ 94*df482650SStephan Linz void ll_temac_local_mdio_write(struct temac_reg *regs, int addr, int devad, 95*df482650SStephan Linz int regnum, u16 value) 96*df482650SStephan Linz { 97*df482650SStephan Linz out_be32(®s->lsw, (value & LSW_REGDAT_MASK)); 98*df482650SStephan Linz out_be32(®s->ctl, CTL_WEN | TEMAC_MIIMWD); 99*df482650SStephan Linz 100*df482650SStephan Linz out_be32(®s->lsw, 101*df482650SStephan Linz ((addr << LSW_PHYAD_POS) & LSW_PHYAD_MASK) | 102*df482650SStephan Linz (regnum & LSW_REGAD_MASK)); 103*df482650SStephan Linz out_be32(®s->ctl, CTL_WEN | TEMAC_MIIMAI); 104*df482650SStephan Linz 105*df482650SStephan Linz ll_temac_check_status(regs, RSE_MIIM_WR); 106*df482650SStephan Linz } 107*df482650SStephan Linz 108*df482650SStephan Linz int ll_temac_phy_read(struct mii_dev *bus, int addr, int devad, int regnum) 109*df482650SStephan Linz { 110*df482650SStephan Linz struct temac_reg *regs = (struct temac_reg *)bus->priv; 111*df482650SStephan Linz 112*df482650SStephan Linz return ll_temac_local_mdio_read(regs, addr, devad, regnum); 113*df482650SStephan Linz } 114*df482650SStephan Linz 115*df482650SStephan Linz int ll_temac_phy_write(struct mii_dev *bus, int addr, int devad, int regnum, 116*df482650SStephan Linz u16 value) 117*df482650SStephan Linz { 118*df482650SStephan Linz struct temac_reg *regs = (struct temac_reg *)bus->priv; 119*df482650SStephan Linz 120*df482650SStephan Linz ll_temac_local_mdio_write(regs, addr, devad, regnum, value); 121*df482650SStephan Linz 122*df482650SStephan Linz return 0; 123*df482650SStephan Linz } 124*df482650SStephan Linz 125*df482650SStephan Linz /* 126*df482650SStephan Linz * Use MII register 1 (MII status register) to detect PHY 127*df482650SStephan Linz * 128*df482650SStephan Linz * A Mask used to verify certain PHY features (register content) 129*df482650SStephan Linz * in the PHY detection register: 130*df482650SStephan Linz * Auto-negotiation support, 10Mbps half/full duplex support 131*df482650SStephan Linz */ 132*df482650SStephan Linz #define PHY_DETECT_REG MII_BMSR 133*df482650SStephan Linz #define PHY_DETECT_MASK (BMSR_10FULL | BMSR_10HALF | BMSR_ANEGCAPABLE) 134*df482650SStephan Linz 135*df482650SStephan Linz /* Looking for a valid PHY address */ 136*df482650SStephan Linz int ll_temac_phy_addr(struct mii_dev *bus) 137*df482650SStephan Linz { 138*df482650SStephan Linz struct temac_reg *regs = (struct temac_reg *)bus->priv; 139*df482650SStephan Linz unsigned short val; 140*df482650SStephan Linz unsigned int phy; 141*df482650SStephan Linz 142*df482650SStephan Linz for (phy = PHY_MAX_ADDR; phy >= 0; phy--) { 143*df482650SStephan Linz val = ll_temac_local_mdio_read(regs, phy, 0, PHY_DETECT_REG); 144*df482650SStephan Linz if ((val != 0xFFFF) && 145*df482650SStephan Linz ((val & PHY_DETECT_MASK) == PHY_DETECT_MASK)) { 146*df482650SStephan Linz /* Found a valid PHY address */ 147*df482650SStephan Linz return phy; 148*df482650SStephan Linz } 149*df482650SStephan Linz } 150*df482650SStephan Linz 151*df482650SStephan Linz return -1; 152*df482650SStephan Linz } 153*df482650SStephan Linz 154*df482650SStephan Linz int xilinx_ll_temac_mdio_initialize(bd_t *bis, struct ll_temac_mdio_info *info) 155*df482650SStephan Linz { 156*df482650SStephan Linz struct mii_dev *bus = mdio_alloc(); 157*df482650SStephan Linz 158*df482650SStephan Linz if (!bus) { 159*df482650SStephan Linz printf("Failed to allocate LL_TEMAC MDIO bus: %s\n", 160*df482650SStephan Linz info->name); 161*df482650SStephan Linz return -1; 162*df482650SStephan Linz } 163*df482650SStephan Linz 164*df482650SStephan Linz bus->read = ll_temac_phy_read; 165*df482650SStephan Linz bus->write = ll_temac_phy_write; 166*df482650SStephan Linz bus->reset = NULL; 167*df482650SStephan Linz 168*df482650SStephan Linz /* use given name or generate its own unique name */ 169*df482650SStephan Linz if (info->name) { 170*df482650SStephan Linz strncpy(bus->name, info->name, MDIO_NAME_LEN); 171*df482650SStephan Linz } else { 172*df482650SStephan Linz snprintf(bus->name, MDIO_NAME_LEN, "lltemii.%p", info->regs); 173*df482650SStephan Linz info->name = bus->name; 174*df482650SStephan Linz } 175*df482650SStephan Linz 176*df482650SStephan Linz bus->priv = info->regs; 177*df482650SStephan Linz 178*df482650SStephan Linz ll_temac_mdio_setup(bus); 179*df482650SStephan Linz return mdio_register(bus); 180*df482650SStephan Linz } 181