blob: 27f4423c6ac53988c3deb52687f6b5544ed414f2 [file] [log] [blame]
Suneel Garapati0008e9a2020-08-26 14:37:33 +02001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2018 Marvell International Ltd.
4 */
5
6#include <dm.h>
7#include <malloc.h>
8#include <miiphy.h>
9#include <misc.h>
10#include <pci.h>
11#include <pci_ids.h>
12#include <phy.h>
Simon Glass401d1c42020-10-30 21:38:53 -060013#include <asm/global_data.h>
Suneel Garapati0008e9a2020-08-26 14:37:33 +020014#include <asm/io.h>
15#include <linux/ctype.h>
16#include <linux/delay.h>
17
18#define PCI_DEVICE_ID_OCTEONTX_SMI 0xA02B
19
20DECLARE_GLOBAL_DATA_PTR;
21
22enum octeontx_smi_mode {
23 CLAUSE22 = 0,
24 CLAUSE45 = 1,
25};
26
27enum {
28 SMI_OP_C22_WRITE = 0,
29 SMI_OP_C22_READ = 1,
30
31 SMI_OP_C45_ADDR = 0,
32 SMI_OP_C45_WRITE = 1,
33 SMI_OP_C45_PRIA = 2,
34 SMI_OP_C45_READ = 3,
35};
36
37union smi_x_clk {
38 u64 u;
39 struct smi_x_clk_s {
40 int phase:8;
41 int sample:4;
42 int preamble:1;
43 int clk_idle:1;
44 int reserved_14_14:1;
45 int sample_mode:1;
46 int sample_hi:5;
47 int reserved_21_23:3;
48 int mode:1;
49 } s;
50};
51
52union smi_x_cmd {
53 u64 u;
54 struct smi_x_cmd_s {
55 int reg_adr:5;
56 int reserved_5_7:3;
57 int phy_adr:5;
58 int reserved_13_15:3;
59 int phy_op:2;
60 } s;
61};
62
63union smi_x_wr_dat {
64 u64 u;
65 struct smi_x_wr_dat_s {
66 unsigned int dat:16;
67 int val:1;
68 int pending:1;
69 } s;
70};
71
72union smi_x_rd_dat {
73 u64 u;
74 struct smi_x_rd_dat_s {
75 unsigned int dat:16;
76 int val:1;
77 int pending:1;
78 } s;
79};
80
81union smi_x_en {
82 u64 u;
83 struct smi_x_en_s {
84 int en:1;
85 } s;
86};
87
88#define SMI_X_RD_DAT 0x10ull
89#define SMI_X_WR_DAT 0x08ull
90#define SMI_X_CMD 0x00ull
91#define SMI_X_CLK 0x18ull
92#define SMI_X_EN 0x20ull
93
94struct octeontx_smi_priv {
95 void __iomem *baseaddr;
96 enum octeontx_smi_mode mode;
97};
98
99#define MDIO_TIMEOUT 10000
100
101void octeontx_smi_setmode(struct mii_dev *bus, enum octeontx_smi_mode mode)
102{
103 struct octeontx_smi_priv *priv = bus->priv;
104 union smi_x_clk smix_clk;
105
106 smix_clk.u = readq(priv->baseaddr + SMI_X_CLK);
107 smix_clk.s.mode = mode;
108 smix_clk.s.preamble = mode == CLAUSE45;
109 writeq(smix_clk.u, priv->baseaddr + SMI_X_CLK);
110
111 priv->mode = mode;
112}
113
114int octeontx_c45_addr(struct mii_dev *bus, int addr, int devad, int regnum)
115{
116 struct octeontx_smi_priv *priv = bus->priv;
117
118 union smi_x_cmd smix_cmd;
119 union smi_x_wr_dat smix_wr_dat;
120 unsigned long timeout = MDIO_TIMEOUT;
121
122 smix_wr_dat.u = 0;
123 smix_wr_dat.s.dat = regnum;
124
125 writeq(smix_wr_dat.u, priv->baseaddr + SMI_X_WR_DAT);
126
127 smix_cmd.u = 0;
128 smix_cmd.s.phy_op = SMI_OP_C45_ADDR;
129 smix_cmd.s.phy_adr = addr;
130 smix_cmd.s.reg_adr = devad;
131
132 writeq(smix_cmd.u, priv->baseaddr + SMI_X_CMD);
133
134 do {
135 smix_wr_dat.u = readq(priv->baseaddr + SMI_X_WR_DAT);
136 udelay(100);
137 timeout--;
138 } while (smix_wr_dat.s.pending && timeout);
139
140 return timeout == 0;
141}
142
143int octeontx_phy_read(struct mii_dev *bus, int addr, int devad, int regnum)
144{
145 struct octeontx_smi_priv *priv = bus->priv;
146 union smi_x_cmd smix_cmd;
147 union smi_x_rd_dat smix_rd_dat;
148 unsigned long timeout = MDIO_TIMEOUT;
149 int ret;
150
151 enum octeontx_smi_mode mode = (devad < 0) ? CLAUSE22 : CLAUSE45;
152
153 debug("RD: Mode: %u, baseaddr: %p, addr: %d, devad: %d, reg: %d\n",
154 mode, priv->baseaddr, addr, devad, regnum);
155
156 octeontx_smi_setmode(bus, mode);
157
158 if (mode == CLAUSE45) {
159 ret = octeontx_c45_addr(bus, addr, devad, regnum);
160
161 debug("RD: ret: %u\n", ret);
162
163 if (ret)
164 return 0;
165 }
166
167 smix_cmd.u = 0;
168 smix_cmd.s.phy_adr = addr;
169
170 if (mode == CLAUSE45) {
171 smix_cmd.s.reg_adr = devad;
172 smix_cmd.s.phy_op = SMI_OP_C45_READ;
173 } else {
174 smix_cmd.s.reg_adr = regnum;
175 smix_cmd.s.phy_op = SMI_OP_C22_READ;
176 }
177
178 writeq(smix_cmd.u, priv->baseaddr + SMI_X_CMD);
179
180 do {
181 smix_rd_dat.u = readq(priv->baseaddr + SMI_X_RD_DAT);
182 udelay(10);
183 timeout--;
184 } while (smix_rd_dat.s.pending && timeout);
185
186 debug("SMIX_RD_DAT: %lx\n", (unsigned long)smix_rd_dat.u);
187
188 return smix_rd_dat.s.dat;
189}
190
191int octeontx_phy_write(struct mii_dev *bus, int addr, int devad, int regnum,
192 u16 value)
193{
194 struct octeontx_smi_priv *priv = bus->priv;
195 union smi_x_cmd smix_cmd;
196 union smi_x_wr_dat smix_wr_dat;
197 unsigned long timeout = MDIO_TIMEOUT;
198 int ret;
199
200 enum octeontx_smi_mode mode = (devad < 0) ? CLAUSE22 : CLAUSE45;
201
202 debug("WR: Mode: %u, baseaddr: %p, addr: %d, devad: %d, reg: %d\n",
203 mode, priv->baseaddr, addr, devad, regnum);
204
205 if (mode == CLAUSE45) {
206 ret = octeontx_c45_addr(bus, addr, devad, regnum);
207
208 debug("WR: ret: %u\n", ret);
209
210 if (ret)
211 return ret;
212 }
213
214 smix_wr_dat.u = 0;
215 smix_wr_dat.s.dat = value;
216
217 writeq(smix_wr_dat.u, priv->baseaddr + SMI_X_WR_DAT);
218
219 smix_cmd.u = 0;
220 smix_cmd.s.phy_adr = addr;
221
222 if (mode == CLAUSE45) {
223 smix_cmd.s.reg_adr = devad;
224 smix_cmd.s.phy_op = SMI_OP_C45_WRITE;
225 } else {
226 smix_cmd.s.reg_adr = regnum;
227 smix_cmd.s.phy_op = SMI_OP_C22_WRITE;
228 }
229
230 writeq(smix_cmd.u, priv->baseaddr + SMI_X_CMD);
231
232 do {
233 smix_wr_dat.u = readq(priv->baseaddr + SMI_X_WR_DAT);
234 udelay(10);
235 timeout--;
236 } while (smix_wr_dat.s.pending && timeout);
237
238 debug("SMIX_WR_DAT: %lx\n", (unsigned long)smix_wr_dat.u);
239
240 return timeout == 0;
241}
242
243int octeontx_smi_reset(struct mii_dev *bus)
244{
245 struct octeontx_smi_priv *priv = bus->priv;
246
247 union smi_x_en smi_en;
248
249 smi_en.s.en = 0;
250 writeq(smi_en.u, priv->baseaddr + SMI_X_EN);
251
252 smi_en.s.en = 1;
253 writeq(smi_en.u, priv->baseaddr + SMI_X_EN);
254
255 octeontx_smi_setmode(bus, CLAUSE22);
256
257 return 0;
258}
259
260/* PHY XS initialization, primarily for RXAUI
261 *
262 */
263int rxaui_phy_xs_init(struct mii_dev *bus, int phy_addr)
264{
265 int reg;
266 ulong start_time;
267 int phy_id1, phy_id2;
268 int oui, model_number;
269
270 phy_id1 = octeontx_phy_read(bus, phy_addr, 1, 0x2);
271 phy_id2 = octeontx_phy_read(bus, phy_addr, 1, 0x3);
272 model_number = (phy_id2 >> 4) & 0x3F;
273 debug("%s model %x\n", __func__, model_number);
274 oui = phy_id1;
275 oui <<= 6;
276 oui |= (phy_id2 >> 10) & 0x3F;
277 debug("%s oui %x\n", __func__, oui);
278 switch (oui) {
279 case 0x5016:
280 if (model_number == 9) {
281 debug("%s +\n", __func__);
282 /* Perform hardware reset in XGXS control */
283 reg = octeontx_phy_read(bus, phy_addr, 4, 0x0);
284 if ((reg & 0xffff) < 0)
285 goto read_error;
286 reg |= 0x8000;
287 octeontx_phy_write(bus, phy_addr, 4, 0x0, reg);
288
289 start_time = get_timer(0);
290 do {
291 reg = octeontx_phy_read(bus, phy_addr, 4, 0x0);
292 if ((reg & 0xffff) < 0)
293 goto read_error;
294 } while ((reg & 0x8000) && get_timer(start_time) < 500);
295 if (reg & 0x8000) {
296 printf("HW reset for M88X3120 PHY failed");
297 printf("MII_BMCR: 0x%x\n", reg);
298 return -1;
299 }
300 /* program 4.49155 with 0x5 */
301 octeontx_phy_write(bus, phy_addr, 4, 0xc003, 0x5);
302 }
303 break;
304 default:
305 break;
306 }
307
308 return 0;
309
310read_error:
311 debug("M88X3120 PHY config read failed\n");
312 return -1;
313}
314
315int octeontx_smi_probe(struct udevice *dev)
316{
Simon Glassf10643c2020-12-19 10:40:14 -0700317 int ret, subnode, cnt = 0, node = dev_ofnode(dev).of_offset;
Suneel Garapati0008e9a2020-08-26 14:37:33 +0200318 struct mii_dev *bus;
319 struct octeontx_smi_priv *priv;
320 pci_dev_t bdf = dm_pci_get_bdf(dev);
321
322 debug("SMI PCI device: %x\n", bdf);
Suneel Garapati0008e9a2020-08-26 14:37:33 +0200323 if (!dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, PCI_REGION_MEM)) {
324 printf("Failed to map PCI region for bdf %x\n", bdf);
325 return -1;
326 }
327
Tim Harvey935e0b02021-03-25 17:07:37 -0700328 node = fdt_node_offset_by_compatible(gd->fdt_blob, -1,
329 "cavium,thunder-8890-mdio-nexus");
Suneel Garapati0008e9a2020-08-26 14:37:33 +0200330 fdt_for_each_subnode(subnode, gd->fdt_blob, node) {
331 ret = fdt_node_check_compatible(gd->fdt_blob, subnode,
332 "cavium,thunder-8890-mdio");
333 if (ret)
334 continue;
335
336 bus = mdio_alloc();
337 priv = malloc(sizeof(*priv));
338 if (!bus || !priv) {
339 printf("Failed to allocate OcteonTX MDIO bus # %u\n",
Simon Glass8b85dfc2020-12-16 21:20:07 -0700340 dev_seq(dev));
Suneel Garapati0008e9a2020-08-26 14:37:33 +0200341 return -1;
342 }
343
344 bus->read = octeontx_phy_read;
345 bus->write = octeontx_phy_write;
346 bus->reset = octeontx_smi_reset;
347 bus->priv = priv;
348
349 priv->mode = CLAUSE22;
350 priv->baseaddr = (void __iomem *)fdtdec_get_addr(gd->fdt_blob,
351 subnode,
352 "reg");
353 debug("mdio base addr %p\n", priv->baseaddr);
354
355 /* use given name or generate its own unique name */
356 snprintf(bus->name, MDIO_NAME_LEN, "smi%d", cnt++);
357
358 ret = mdio_register(bus);
359 if (ret)
360 return ret;
361 }
362 return 0;
363}
364
365static const struct udevice_id octeontx_smi_ids[] = {
366 { .compatible = "cavium,thunder-8890-mdio-nexus" },
367 {}
368};
369
370U_BOOT_DRIVER(octeontx_smi) = {
371 .name = "octeontx_smi",
372 .id = UCLASS_MISC,
373 .probe = octeontx_smi_probe,
374 .of_match = octeontx_smi_ids,
375};
376
377static struct pci_device_id octeontx_smi_supported[] = {
378 { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_SMI) },
379 {}
380};
381
382U_BOOT_PCI_DEVICE(octeontx_smi, octeontx_smi_supported);