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