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