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
|
// SPDX-License-Identifier: GPL-2.0+
#include <asm/io.h>
#include <dm.h>
#include <time.h>
#include <regmap.h>
#include <miiphy.h>
#include <linux/bitfield.h>
#define MSCC_MIIM_REG_STATUS 0x0
#define MSCC_MIIM_STATUS_STAT_PENDING BIT(2)
#define MSCC_MIIM_STATUS_STAT_BUSY BIT(3)
#define MSCC_MIIM_REG_CMD 0x8
#define MSCC_MIIM_CMD_OPR_WRITE BIT(1)
#define MSCC_MIIM_CMD_OPR_READ BIT(2)
#define MSCC_MIIM_CMD_WRDATA_SHIFT 4
#define MSCC_MIIM_CMD_REGAD_SHIFT 20
#define MSCC_MIIM_CMD_PHYAD_SHIFT 25
#define MSCC_MIIM_CMD_VLD BIT(31)
#define MSCC_MIIM_REG_DATA 0xC
#define MSCC_MIIM_DATA_ERROR (BIT(16) | BIT(17))
#define MSCC_MIIM_DATA_MASK GENMASK(15, 0)
#define MSCC_MIIM_REG_CFG 0x10
#define MSCC_MIIM_CFG_PRESCALE_MASK GENMASK(7, 0)
/* 01 = Clause 22, 00 = Clause 45 */
#define MSCC_MIIM_CFG_ST_CFG_MASK GENMASK(10, 9)
#define MSCC_MIIM_C22 1
#define MSCC_MIIM_C45 0
#define MSCC_MDIO_TIMEOUT 10000
#define MSCC_MDIO_SLEEP 50
struct mscc_mdio_priv {
struct regmap *map;
};
static int mscc_mdio_wait_busy(struct mscc_mdio_priv *priv)
{
u32 busy;
return regmap_read_poll_timeout(priv->map, MSCC_MIIM_REG_STATUS, busy,
(busy & MSCC_MIIM_STATUS_STAT_BUSY) == 0,
MSCC_MDIO_SLEEP,
MSCC_MDIO_TIMEOUT);
}
static int mscc_mdio_read(struct udevice *dev, int addr, int devad, int reg)
{
struct mscc_mdio_priv *priv = dev_get_priv(dev);
u32 val;
int ret;
if (mscc_mdio_wait_busy(priv))
return -ETIMEDOUT;
ret = regmap_write(priv->map, MSCC_MIIM_REG_CMD,
MSCC_MIIM_CMD_VLD |
(addr << MSCC_MIIM_CMD_PHYAD_SHIFT) |
(reg << MSCC_MIIM_CMD_REGAD_SHIFT) |
MSCC_MIIM_CMD_OPR_READ);
if (ret)
return ret;
if (mscc_mdio_wait_busy(priv))
return -ETIMEDOUT;
regmap_read(priv->map, MSCC_MIIM_REG_DATA, &val);
if (val & MSCC_MIIM_DATA_ERROR)
return -EIO;
return FIELD_GET(MSCC_MIIM_DATA_MASK, val);
}
int mscc_mdio_write(struct udevice *dev, int addr, int devad, int reg, u16 val)
{
struct mscc_mdio_priv *priv = dev_get_priv(dev);
int ret;
if (mscc_mdio_wait_busy(priv))
return -ETIMEDOUT;
ret = regmap_write(priv->map, MSCC_MIIM_REG_CMD,
MSCC_MIIM_CMD_VLD |
(addr << MSCC_MIIM_CMD_PHYAD_SHIFT) |
(reg << MSCC_MIIM_CMD_REGAD_SHIFT) |
(val << MSCC_MIIM_CMD_WRDATA_SHIFT) |
MSCC_MIIM_CMD_OPR_WRITE);
return ret;
}
static const struct mdio_ops mscc_mdio_ops = {
.read = mscc_mdio_read,
.write = mscc_mdio_write,
};
static int mscc_mdio_bind(struct udevice *dev)
{
if (ofnode_valid(dev_ofnode(dev)))
device_set_name(dev, ofnode_get_name(dev_ofnode(dev)));
return 0;
}
static int mscc_mdio_probe(struct udevice *dev)
{
struct mscc_mdio_priv *priv = dev_get_priv(dev);
int ret;
ret = regmap_init_mem(dev_ofnode(dev), &priv->map);
if (ret)
return -EINVAL;
/* Enter Clause 22 mode */
ret = regmap_update_bits(priv->map, MSCC_MIIM_REG_CFG,
MSCC_MIIM_CFG_ST_CFG_MASK,
FIELD_PREP(MSCC_MIIM_CFG_ST_CFG_MASK,
MSCC_MIIM_C22));
return ret;
}
static const struct udevice_id mscc_mdio_ids[] = {
{ .compatible = "mscc,ocelot-miim", },
{ }
};
U_BOOT_DRIVER(mscc_mdio) = {
.name = "mscc_mdio",
.id = UCLASS_MDIO,
.of_match = mscc_mdio_ids,
.bind = mscc_mdio_bind,
.probe = mscc_mdio_probe,
.ops = &mscc_mdio_ops,
.priv_auto = sizeof(struct mscc_mdio_priv),
};
|