summaryrefslogtreecommitdiffstats
path: root/drivers/net/xilinx_ll_temac_mdio.c
blob: c56ff486ee779db7e2127940d2c812e116c6f8c2 (plain)
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
178
179
180
/*
 * 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
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * [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(&regs->lsw,
		((addr << LSW_PHYAD_POS) & LSW_PHYAD_MASK) |
		(regnum & LSW_REGAD_MASK));
	out_be32(&regs->ctl, TEMAC_MIIMAI);

	ll_temac_check_status(regs, RSE_MIIM_RR);

	return in_be32(&regs->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(&regs->lsw, (value & LSW_REGDAT_MASK));
	out_be32(&regs->ctl, CTL_WEN | TEMAC_MIIMWD);

	out_be32(&regs->lsw,
		((addr << LSW_PHYAD_POS) & LSW_PHYAD_MASK) |
		(regnum & LSW_REGAD_MASK));
	out_be32(&regs->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);
}