58436419f1b946e1fc84c11f894047e74eb414fe
[platform/kernel/u-boot.git] / drivers / net / octeontx / smi.c
1 // 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
19 DECLARE_GLOBAL_DATA_PTR;
20
21 enum octeontx_smi_mode {
22         CLAUSE22 = 0,
23         CLAUSE45 = 1,
24 };
25
26 enum {
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
36 union 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
51 union 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
62 union 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
71 union 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
80 union 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
93 struct octeontx_smi_priv {
94         void __iomem *baseaddr;
95         enum octeontx_smi_mode mode;
96 };
97
98 #define MDIO_TIMEOUT 10000
99
100 void 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
113 int 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
142 int 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
190 int 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
242 int 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  */
262 int 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
309 read_error:
310         debug("M88X3120 PHY config read failed\n");
311         return -1;
312 }
313
314 int octeontx_smi_probe(struct udevice *dev)
315 {
316         int ret, subnode, cnt = 0, node = dev_ofnode(dev).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         if (!dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, PCI_REGION_MEM)) {
323                 printf("Failed to map PCI region for bdf %x\n", bdf);
324                 return -1;
325         }
326
327         fdt_for_each_subnode(subnode, gd->fdt_blob, node) {
328                 ret = fdt_node_check_compatible(gd->fdt_blob, subnode,
329                                                 "cavium,thunder-8890-mdio");
330                 if (ret)
331                         continue;
332
333                 bus = mdio_alloc();
334                 priv = malloc(sizeof(*priv));
335                 if (!bus || !priv) {
336                         printf("Failed to allocate OcteonTX MDIO bus # %u\n",
337                                dev_seq(dev));
338                         return -1;
339                 }
340
341                 bus->read = octeontx_phy_read;
342                 bus->write = octeontx_phy_write;
343                 bus->reset = octeontx_smi_reset;
344                 bus->priv = priv;
345
346                 priv->mode = CLAUSE22;
347                 priv->baseaddr = (void __iomem *)fdtdec_get_addr(gd->fdt_blob,
348                                                                  subnode,
349                                                                  "reg");
350                 debug("mdio base addr %p\n", priv->baseaddr);
351
352                 /* use given name or generate its own unique name */
353                 snprintf(bus->name, MDIO_NAME_LEN, "smi%d", cnt++);
354
355                 ret = mdio_register(bus);
356                 if (ret)
357                         return ret;
358         }
359         return 0;
360 }
361
362 static const struct udevice_id octeontx_smi_ids[] = {
363         { .compatible = "cavium,thunder-8890-mdio-nexus" },
364         {}
365 };
366
367 U_BOOT_DRIVER(octeontx_smi) = {
368         .name   = "octeontx_smi",
369         .id     = UCLASS_MISC,
370         .probe  = octeontx_smi_probe,
371         .of_match = octeontx_smi_ids,
372 };
373
374 static struct pci_device_id octeontx_smi_supported[] = {
375         { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_SMI) },
376         {}
377 };
378
379 U_BOOT_PCI_DEVICE(octeontx_smi, octeontx_smi_supported);