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