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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
|
// SPDX-License-Identifier: GPL-2.0+
/*
* CPSW MDIO generic driver for TI AMxx/K2x/EMAC devices.
*
* Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
*/
#include <common.h>
#include <asm/io.h>
#include <miiphy.h>
#include <wait_bit.h>
struct cpsw_mdio_regs {
u32 version;
u32 control;
#define CONTROL_IDLE BIT(31)
#define CONTROL_ENABLE BIT(30)
#define CONTROL_FAULT BIT(19)
#define CONTROL_FAULT_ENABLE BIT(18)
#define CONTROL_DIV_MASK GENMASK(15, 0)
u32 alive;
u32 link;
u32 linkintraw;
u32 linkintmasked;
u32 __reserved_0[2];
u32 userintraw;
u32 userintmasked;
u32 userintmaskset;
u32 userintmaskclr;
u32 __reserved_1[20];
struct {
u32 access;
u32 physel;
#define USERACCESS_GO BIT(31)
#define USERACCESS_WRITE BIT(30)
#define USERACCESS_ACK BIT(29)
#define USERACCESS_READ (0)
#define USERACCESS_PHY_REG_SHIFT (21)
#define USERACCESS_PHY_ADDR_SHIFT (16)
#define USERACCESS_DATA GENMASK(15, 0)
} user[0];
};
#define CPSW_MDIO_DIV_DEF 0xff
#define PHY_REG_MASK 0x1f
#define PHY_ID_MASK 0x1f
/*
* This timeout definition is a worst-case ultra defensive measure against
* unexpected controller lock ups. Ideally, we should never ever hit this
* scenario in practice.
*/
#define CPSW_MDIO_TIMEOUT 100 /* msecs */
struct cpsw_mdio {
struct cpsw_mdio_regs *regs;
struct mii_dev *bus;
int div;
};
/* wait until hardware is ready for another user access */
static int cpsw_mdio_wait_for_user_access(struct cpsw_mdio *mdio)
{
return wait_for_bit_le32(&mdio->regs->user[0].access,
USERACCESS_GO, false,
CPSW_MDIO_TIMEOUT, false);
}
static int cpsw_mdio_read(struct mii_dev *bus, int phy_id,
int dev_addr, int phy_reg)
{
struct cpsw_mdio *mdio = bus->priv;
int data, ret;
u32 reg;
if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
return -EINVAL;
ret = cpsw_mdio_wait_for_user_access(mdio);
if (ret)
return ret;
reg = (USERACCESS_GO | USERACCESS_READ |
(phy_reg << USERACCESS_PHY_REG_SHIFT) |
(phy_id << USERACCESS_PHY_ADDR_SHIFT));
writel(reg, &mdio->regs->user[0].access);
ret = cpsw_mdio_wait_for_user_access(mdio);
if (ret)
return ret;
reg = readl(&mdio->regs->user[0].access);
data = (reg & USERACCESS_ACK) ? (reg & USERACCESS_DATA) : -1;
return data;
}
static int cpsw_mdio_write(struct mii_dev *bus, int phy_id, int dev_addr,
int phy_reg, u16 data)
{
struct cpsw_mdio *mdio = bus->priv;
u32 reg;
int ret;
if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
return -EINVAL;
ret = cpsw_mdio_wait_for_user_access(mdio);
if (ret)
return ret;
reg = (USERACCESS_GO | USERACCESS_WRITE |
(phy_reg << USERACCESS_PHY_REG_SHIFT) |
(phy_id << USERACCESS_PHY_ADDR_SHIFT) |
(data & USERACCESS_DATA));
writel(reg, &mdio->regs->user[0].access);
return cpsw_mdio_wait_for_user_access(mdio);
}
u32 cpsw_mdio_get_alive(struct mii_dev *bus)
{
struct cpsw_mdio *mdio = bus->priv;
u32 val;
val = readl(&mdio->regs->control);
return val & GENMASK(15, 0);
}
struct mii_dev *cpsw_mdio_init(const char *name, phys_addr_t mdio_base,
u32 bus_freq, int fck_freq)
{
struct cpsw_mdio *cpsw_mdio;
int ret;
cpsw_mdio = calloc(1, sizeof(*cpsw_mdio));
if (!cpsw_mdio) {
debug("failed to alloc cpsw_mdio\n");
return NULL;
}
cpsw_mdio->bus = mdio_alloc();
if (!cpsw_mdio->bus) {
debug("failed to alloc mii bus\n");
free(cpsw_mdio);
return NULL;
}
cpsw_mdio->regs = (struct cpsw_mdio_regs *)(uintptr_t)mdio_base;
if (!bus_freq || !fck_freq)
cpsw_mdio->div = CPSW_MDIO_DIV_DEF;
else
cpsw_mdio->div = (fck_freq / bus_freq) - 1;
cpsw_mdio->div &= CONTROL_DIV_MASK;
/* set enable and clock divider */
writel(cpsw_mdio->div | CONTROL_ENABLE | CONTROL_FAULT |
CONTROL_FAULT_ENABLE, &cpsw_mdio->regs->control);
wait_for_bit_le32(&cpsw_mdio->regs->control,
CONTROL_IDLE, false, CPSW_MDIO_TIMEOUT, true);
/*
* wait for scan logic to settle:
* the scan time consists of (a) a large fixed component, and (b) a
* small component that varies with the mii bus frequency. These
* were estimated using measurements at 1.1 and 2.2 MHz on tnetv107x
* silicon. Since the effect of (b) was found to be largely
* negligible, we keep things simple here.
*/
mdelay(1);
cpsw_mdio->bus->read = cpsw_mdio_read;
cpsw_mdio->bus->write = cpsw_mdio_write;
cpsw_mdio->bus->priv = cpsw_mdio;
snprintf(cpsw_mdio->bus->name, sizeof(cpsw_mdio->bus->name), name);
ret = mdio_register(cpsw_mdio->bus);
if (ret < 0) {
debug("failed to register mii bus\n");
goto free_bus;
}
return cpsw_mdio->bus;
free_bus:
mdio_free(cpsw_mdio->bus);
free(cpsw_mdio);
return NULL;
}
void cpsw_mdio_free(struct mii_dev *bus)
{
struct cpsw_mdio *mdio = bus->priv;
u32 reg;
/* disable mdio */
reg = readl(&mdio->regs->control);
reg &= ~CONTROL_ENABLE;
writel(reg, &mdio->regs->control);
mdio_unregister(bus);
mdio_free(bus);
free(mdio);
}
|