summaryrefslogtreecommitdiff
path: root/drivers/net/mdio-mscc-miim.c
blob: 5700b872586c6a52a917509ba223dec3a0b0516c (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
// 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),
};