blob: a8e93e80fc08eb438a0028c274022429d0717799 [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>
Alex Margineande3e55b2019-07-25 12:33:19 +030016
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->node))
201 device_set_name(dev, ofnode_get_name(dev->node));
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 = (void *)dev_read_addr(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_alloc_size = sizeof(struct mvmdio_priv),
236};
237