1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2018 Marvell International Ltd.
4 * Author: Ken Ma<make@marvell.com>
5 */
6
7#include <common.h>
8#include <dm.h>
9#include <dm/device-internal.h>
10#include <dm/lists.h>
11#include <miiphy.h>
12#include <phy.h>
13#include <asm/io.h>
14#include <wait_bit.h>
15#include <linux/bitops.h>
16#include <linux/printk.h>
17
18#define MVMDIO_SMI_DATA_SHIFT		0
19#define MVMDIO_SMI_PHY_ADDR_SHIFT	16
20#define MVMDIO_SMI_PHY_REG_SHIFT	21
21#define MVMDIO_SMI_READ_OPERATION	BIT(26)
22#define MVMDIO_SMI_WRITE_OPERATION	0
23#define MVMDIO_SMI_READ_VALID		BIT(27)
24#define MVMDIO_SMI_BUSY			BIT(28)
25
26#define MVMDIO_XSMI_MGNT_REG		0x0
27#define MVMDIO_XSMI_PHYADDR_SHIFT	16
28#define MVMDIO_XSMI_DEVADDR_SHIFT	21
29#define MVMDIO_XSMI_WRITE_OPERATION	(0x5 << 26)
30#define MVMDIO_XSMI_READ_OPERATION	(0x7 << 26)
31#define MVMDIO_XSMI_READ_VALID		BIT(29)
32#define MVMDIO_XSMI_BUSY		BIT(30)
33#define MVMDIO_XSMI_ADDR_REG		0x8
34
35enum mvmdio_bus_type {
36	BUS_TYPE_SMI,
37	BUS_TYPE_XSMI
38};
39
40struct mvmdio_priv {
41	void *mdio_base;
42	enum mvmdio_bus_type type;
43};
44
45static int mvmdio_smi_read(struct udevice *dev, int addr,
46			   int devad, int reg)
47{
48	struct mvmdio_priv *priv = dev_get_priv(dev);
49	u32 val;
50	int ret;
51
52	if (devad != MDIO_DEVAD_NONE)
53		return -EOPNOTSUPP;
54
55	ret = wait_for_bit_le32(priv->mdio_base, MVMDIO_SMI_BUSY,
56				false, CONFIG_SYS_HZ, false);
57	if (ret < 0)
58		return ret;
59
60	writel(((addr << MVMDIO_SMI_PHY_ADDR_SHIFT) |
61		(reg << MVMDIO_SMI_PHY_REG_SHIFT)  |
62		MVMDIO_SMI_READ_OPERATION),
63	       priv->mdio_base);
64
65	ret = wait_for_bit_le32(priv->mdio_base, MVMDIO_SMI_BUSY,
66				false, CONFIG_SYS_HZ, false);
67	if (ret < 0)
68		return ret;
69
70	val = readl(priv->mdio_base);
71	if (!(val & MVMDIO_SMI_READ_VALID)) {
72		pr_err("SMI bus read not valid\n");
73		return -ENODEV;
74	}
75
76	return val & GENMASK(15, 0);
77}
78
79static int mvmdio_smi_write(struct udevice *dev, int addr, int devad,
80			    int reg, u16 value)
81{
82	struct mvmdio_priv *priv = dev_get_priv(dev);
83	int ret;
84
85	if (devad != MDIO_DEVAD_NONE)
86		return -EOPNOTSUPP;
87
88	ret = wait_for_bit_le32(priv->mdio_base, MVMDIO_SMI_BUSY,
89				false, CONFIG_SYS_HZ, false);
90	if (ret < 0)
91		return ret;
92
93	writel(((addr << MVMDIO_SMI_PHY_ADDR_SHIFT) |
94		(reg << MVMDIO_SMI_PHY_REG_SHIFT)  |
95		MVMDIO_SMI_WRITE_OPERATION            |
96		(value << MVMDIO_SMI_DATA_SHIFT)),
97	       priv->mdio_base);
98
99	return 0;
100}
101
102static int mvmdio_xsmi_read(struct udevice *dev, int addr,
103			    int devad, int reg)
104{
105	struct mvmdio_priv *priv = dev_get_priv(dev);
106	int ret;
107
108	if (devad == MDIO_DEVAD_NONE)
109		return -EOPNOTSUPP;
110
111	ret = wait_for_bit_le32(priv->mdio_base, MVMDIO_XSMI_BUSY,
112				false, CONFIG_SYS_HZ, false);
113	if (ret < 0)
114		return ret;
115
116	writel(reg & GENMASK(15, 0), priv->mdio_base + MVMDIO_XSMI_ADDR_REG);
117	writel(((addr << MVMDIO_XSMI_PHYADDR_SHIFT) |
118		(devad << MVMDIO_XSMI_DEVADDR_SHIFT) |
119		MVMDIO_XSMI_READ_OPERATION),
120	       priv->mdio_base + MVMDIO_XSMI_MGNT_REG);
121
122	ret = wait_for_bit_le32(priv->mdio_base, MVMDIO_XSMI_BUSY,
123				false, CONFIG_SYS_HZ, false);
124	if (ret < 0)
125		return ret;
126
127	if (!(readl(priv->mdio_base + MVMDIO_XSMI_MGNT_REG) &
128	      MVMDIO_XSMI_READ_VALID)) {
129		pr_err("XSMI bus read not valid\n");
130		return -ENODEV;
131	}
132
133	return readl(priv->mdio_base + MVMDIO_XSMI_MGNT_REG) & GENMASK(15, 0);
134}
135
136static int mvmdio_xsmi_write(struct udevice *dev, int addr, int devad,
137			     int reg, u16 value)
138{
139	struct mvmdio_priv *priv = dev_get_priv(dev);
140	int ret;
141
142	if (devad == MDIO_DEVAD_NONE)
143		return -EOPNOTSUPP;
144
145	ret = wait_for_bit_le32(priv->mdio_base, MVMDIO_XSMI_BUSY,
146				false, CONFIG_SYS_HZ, false);
147	if (ret < 0)
148		return ret;
149
150	writel(reg & GENMASK(15, 0), priv->mdio_base + MVMDIO_XSMI_ADDR_REG);
151	writel(((addr << MVMDIO_XSMI_PHYADDR_SHIFT) |
152		(devad << MVMDIO_XSMI_DEVADDR_SHIFT) |
153		MVMDIO_XSMI_WRITE_OPERATION | value),
154	       priv->mdio_base + MVMDIO_XSMI_MGNT_REG);
155
156	return 0;
157}
158
159static int mvmdio_read(struct udevice *dev, int addr, int devad, int reg)
160{
161	struct mvmdio_priv *priv = dev_get_priv(dev);
162	int err = -ENOTSUPP;
163
164	switch (priv->type) {
165	case BUS_TYPE_SMI:
166		err = mvmdio_smi_read(dev, addr, devad, reg);
167		break;
168	case BUS_TYPE_XSMI:
169		err = mvmdio_xsmi_read(dev, addr, devad, reg);
170		break;
171	}
172
173	return err;
174}
175
176static int mvmdio_write(struct udevice *dev, int addr, int devad, int reg,
177			u16 value)
178{
179	struct mvmdio_priv *priv = dev_get_priv(dev);
180	int err = -ENOTSUPP;
181
182	switch (priv->type) {
183	case BUS_TYPE_SMI:
184		err = mvmdio_smi_write(dev, addr, devad, reg, value);
185		break;
186	case BUS_TYPE_XSMI:
187		err = mvmdio_xsmi_write(dev, addr, devad, reg, value);
188		break;
189	}
190
191	return err;
192}
193
194/*
195 * Name the device, we use the device tree node name.
196 * This can be overwritten by MDIO class code if device-name property is
197 * present.
198 */
199static int mvmdio_bind(struct udevice *dev)
200{
201	if (ofnode_valid(dev_ofnode(dev)))
202		device_set_name(dev, ofnode_get_name(dev_ofnode(dev)));
203
204	return 0;
205}
206
207/* Get device base address and type, either C22 SMII or C45 XSMI */
208static int mvmdio_probe(struct udevice *dev)
209{
210	struct mvmdio_priv *priv = dev_get_priv(dev);
211
212	priv->mdio_base = dev_read_addr_ptr(dev);
213	priv->type = (enum mvmdio_bus_type)dev_get_driver_data(dev);
214
215	return 0;
216}
217
218static const struct mdio_ops mvmdio_ops = {
219	.read = mvmdio_read,
220	.write = mvmdio_write,
221};
222
223static const struct udevice_id mvmdio_ids[] = {
224	{ .compatible = "marvell,orion-mdio", .data = BUS_TYPE_SMI },
225	{ .compatible = "marvell,xmdio", .data = BUS_TYPE_XSMI },
226	{ }
227};
228
229U_BOOT_DRIVER(mvmdio) = {
230	.name			= "mvmdio",
231	.id			= UCLASS_MDIO,
232	.of_match		= mvmdio_ids,
233	.bind			= mvmdio_bind,
234	.probe			= mvmdio_probe,
235	.ops			= &mvmdio_ops,
236	.priv_auto	= sizeof(struct mvmdio_priv),
237};
238