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