blob: 5ebcfe14b7fb6d4634a3b711ee9f623b70f03b84 [file] [log] [blame]
Alex Margineande3e55b2019-07-25 12:33:19 +03001// 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>
Simon Glass4dcacfc2020-05-10 11:40:13 -060015#include <linux/bitops.h>
Simon Glassbdd5f812023-09-14 18:21:46 -060016#include <linux/printk.h>
Alex Margineande3e55b2019-07-25 12:33:19 +030017
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{
Simon Glassa7ece582020-12-19 10:40:14 -0700201 if (ofnode_valid(dev_ofnode(dev)))
202 device_set_name(dev, ofnode_get_name(dev_ofnode(dev)));
Alex Margineande3e55b2019-07-25 12:33:19 +0300203
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
Johan Jonker8d5d8e02023-03-13 01:32:04 +0100212 priv->mdio_base = dev_read_addr_ptr(dev);
Alex Margineande3e55b2019-07-25 12:33:19 +0300213 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,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700236 .priv_auto = sizeof(struct mvmdio_priv),
Alex Margineande3e55b2019-07-25 12:33:19 +0300237};