summaryrefslogtreecommitdiff
path: root/drivers/net/mdio-mt7531-mmio.c
blob: 930454a9b0e6fd1457da7cc3a61cea51a46b3bba (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
// SPDX-License-Identifier: GPL-2.0+

#include <asm/io.h>
#include <dm.h>
#include <linux/bitfield.h>
#include <linux/iopoll.h>
#include <miiphy.h>

#include "mdio-mt7531-mmio.h"

#define MT7531_PHY_IAC			0x701c
#define   MT7531_PHY_ACS_ST		BIT(31)
#define   MT7531_MDIO_REG_ADDR_CL22	GENMASK(29, 25)
#define   MT7531_MDIO_DEV_ADDR		MT7531_MDIO_REG_ADDR_CL22
#define   MT7531_MDIO_PHY_ADDR		GENMASK(24, 20)
#define   MT7531_MDIO_CMD		GENMASK(19, 18)
#define   MT7531_MDIO_CMD_READ_CL45	FIELD_PREP_CONST(MT7531_MDIO_CMD, 0x3)
#define   MT7531_MDIO_CMD_READ_CL22	FIELD_PREP_CONST(MT7531_MDIO_CMD, 0x2)
#define   MT7531_MDIO_CMD_WRITE		FIELD_PREP_CONST(MT7531_MDIO_CMD, 0x1)
#define   MT7531_MDIO_CMD_ADDR		FIELD_PREP_CONST(MT7531_MDIO_CMD, 0x0)
#define   MT7531_MDIO_ST		GENMASK(17, 16)
#define   MT7531_MDIO_ST_CL22		FIELD_PREP_CONST(MT7531_MDIO_ST, 0x1)
#define   MT7531_MDIO_ST_CL45		FIELD_PREP_CONST(MT7531_MDIO_ST, 0x0)
#define   MT7531_MDIO_RW_DATA		GENMASK(15, 0)
#define   MT7531_MDIO_REG_ADDR_CL45	MT7531_MDIO_RW_DATA

#define MT7531_MDIO_TIMEOUT		100000
#define MT7531_MDIO_SLEEP		20

static int mt7531_mdio_wait_busy(struct mt7531_mdio_mmio_priv *priv)
{
	unsigned int busy;

	return readl_poll_sleep_timeout(priv->switch_regs + MT7531_PHY_IAC,
					busy, (busy & MT7531_PHY_ACS_ST) == 0,
					MT7531_MDIO_SLEEP, MT7531_MDIO_TIMEOUT);
}

static int mt7531_mdio_read(struct mt7531_mdio_mmio_priv *priv, int addr, int devad, int reg)
{
	u32 val;

	if (devad != MDIO_DEVAD_NONE) {
		if (mt7531_mdio_wait_busy(priv))
			return -ETIMEDOUT;

		val = MT7531_PHY_ACS_ST |
		      MT7531_MDIO_ST_CL45 | MT7531_MDIO_CMD_ADDR |
		      FIELD_PREP(MT7531_MDIO_PHY_ADDR, addr) |
		      FIELD_PREP(MT7531_MDIO_DEV_ADDR, devad) |
		      FIELD_PREP(MT7531_MDIO_REG_ADDR_CL45, reg);

		writel(val, priv->switch_regs + MT7531_PHY_IAC);
	}

	if (mt7531_mdio_wait_busy(priv))
		return -ETIMEDOUT;

	val = MT7531_PHY_ACS_ST | FIELD_PREP(MT7531_MDIO_PHY_ADDR, addr);
	if (devad != MDIO_DEVAD_NONE)
		val |= MT7531_MDIO_ST_CL45 | MT7531_MDIO_CMD_READ_CL45 |
		       FIELD_PREP(MT7531_MDIO_DEV_ADDR, devad);
	else
		val |= MT7531_MDIO_ST_CL22 | MT7531_MDIO_CMD_READ_CL22 |
		       FIELD_PREP(MT7531_MDIO_REG_ADDR_CL22, reg);

	writel(val, priv->switch_regs + MT7531_PHY_IAC);

	if (mt7531_mdio_wait_busy(priv))
		return -ETIMEDOUT;

	val = readl(priv->switch_regs + MT7531_PHY_IAC);
	return val & MT7531_MDIO_RW_DATA;
}

static int mt7531_mdio_write(struct mt7531_mdio_mmio_priv *priv, int addr, int devad,
			     int reg, u16 value)
{
	u32 val;

	if (devad != MDIO_DEVAD_NONE) {
		if (mt7531_mdio_wait_busy(priv))
			return -ETIMEDOUT;

		val = MT7531_PHY_ACS_ST |
		      MT7531_MDIO_ST_CL45 | MT7531_MDIO_CMD_ADDR |
		      FIELD_PREP(MT7531_MDIO_PHY_ADDR, addr) |
		      FIELD_PREP(MT7531_MDIO_DEV_ADDR, devad) |
		      FIELD_PREP(MT7531_MDIO_REG_ADDR_CL45, reg);

		writel(val, priv->switch_regs + MT7531_PHY_IAC);
	}

	if (mt7531_mdio_wait_busy(priv))
		return -ETIMEDOUT;

	val = MT7531_PHY_ACS_ST | FIELD_PREP(MT7531_MDIO_PHY_ADDR, addr) |
	      MT7531_MDIO_CMD_WRITE | FIELD_PREP(MT7531_MDIO_RW_DATA, value);
	if (devad != MDIO_DEVAD_NONE)
		val |= MT7531_MDIO_ST_CL45 |
		       FIELD_PREP(MT7531_MDIO_DEV_ADDR, devad);
	else
		val |= MT7531_MDIO_ST_CL22 |
		       FIELD_PREP(MT7531_MDIO_REG_ADDR_CL22, reg);

	writel(val, priv->switch_regs + MT7531_PHY_IAC);

	if (mt7531_mdio_wait_busy(priv))
		return -ETIMEDOUT;

	return 0;
}

int mt7531_mdio_mmio_read(struct mii_dev *bus, int addr, int devad, int reg)
{
	struct mt7531_mdio_mmio_priv *priv = bus->priv;

	return mt7531_mdio_read(priv, addr, devad, reg);
}

int mt7531_mdio_mmio_write(struct mii_dev *bus, int addr, int devad,
			   int reg, u16 value)
{
	struct mt7531_mdio_mmio_priv *priv = bus->priv;

	return mt7531_mdio_write(priv, addr, devad, reg, value);
}

static int dm_mt7531_mdio_read(struct udevice *dev, int addr, int devad, int reg)
{
	struct mt7531_mdio_mmio_priv *priv = dev_get_priv(dev);

	return mt7531_mdio_read(priv, addr, devad, reg);
}

static int dm_mt7531_mdio_write(struct udevice *dev, int addr, int devad,
				int reg, u16 value)
{
	struct mt7531_mdio_mmio_priv *priv = dev_get_priv(dev);

	return mt7531_mdio_write(priv, addr, devad, reg, value);
}

static const struct mdio_ops mt7531_mdio_ops = {
	.read = dm_mt7531_mdio_read,
	.write = dm_mt7531_mdio_write,
};

static int mt7531_mdio_probe(struct udevice *dev)
{
	struct mt7531_mdio_mmio_priv *priv = dev_get_priv(dev);
	ofnode switch_node;

	switch_node = ofnode_get_parent(dev_ofnode(dev));
	if (!ofnode_valid(switch_node))
		return -EINVAL;

	priv->switch_regs = ofnode_get_addr(switch_node);
	if (priv->switch_regs == FDT_ADDR_T_NONE)
		return -EINVAL;

	return 0;
}

U_BOOT_DRIVER(mt7531_mdio) = {
	.name           = "mt7531-mdio-mmio",
	.id             = UCLASS_MDIO,
	.probe          = mt7531_mdio_probe,
	.ops            = &mt7531_mdio_ops,
	.priv_auto	  = sizeof(struct mt7531_mdio_mmio_priv),
};