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