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/global_data.h>
14 #include <asm/io.h>
15 #include <linux/ctype.h>
16 #include <linux/delay.h>
17 
18 #define PCI_DEVICE_ID_OCTEONTX_SMI 0xA02B
19 
20 DECLARE_GLOBAL_DATA_PTR;
21 
22 enum octeontx_smi_mode {
23 	CLAUSE22 = 0,
24 	CLAUSE45 = 1,
25 };
26 
27 enum {
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 
37 union 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 
52 union 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 
63 union 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 
72 union 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 
81 union 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 
94 struct octeontx_smi_priv {
95 	void __iomem *baseaddr;
96 	enum octeontx_smi_mode mode;
97 };
98 
99 #define MDIO_TIMEOUT 10000
100 
octeontx_smi_setmode(struct mii_dev * bus,enum octeontx_smi_mode mode)101 void 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 
octeontx_c45_addr(struct mii_dev * bus,int addr,int devad,int regnum)114 int 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 
octeontx_phy_read(struct mii_dev * bus,int addr,int devad,int regnum)143 int 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 
octeontx_phy_write(struct mii_dev * bus,int addr,int devad,int regnum,u16 value)191 int 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 
octeontx_smi_reset(struct mii_dev * bus)243 int 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  */
rxaui_phy_xs_init(struct mii_dev * bus,int phy_addr)263 int 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 
310 read_error:
311 	debug("M88X3120 PHY config read failed\n");
312 	return -1;
313 }
314 
octeontx_smi_probe(struct udevice * dev)315 int octeontx_smi_probe(struct udevice *dev)
316 {
317 	pci_dev_t bdf = dm_pci_get_bdf(dev);
318 	struct octeontx_smi_priv *priv;
319 	struct mii_dev *bus;
320 	int ret, cnt = 0;
321 	ofnode subnode;
322 	u64 baseaddr;
323 
324 	debug("SMI PCI device: %x\n", bdf);
325 	if (!dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, PCI_REGION_MEM)) {
326 		printf("Failed to map PCI region for bdf %x\n", bdf);
327 		return -1;
328 	}
329 
330 	dev_for_each_subnode(subnode, dev) {
331 		if (!ofnode_device_is_compatible(subnode,
332 						 "cavium,thunder-8890-mdio"))
333 			continue;
334 		if (ofnode_read_u64(subnode, "reg", &baseaddr))
335 			continue;
336 		bus = mdio_alloc();
337 		priv = malloc(sizeof(*priv));
338 		if (!bus || !priv) {
339 			printf("Failed to allocate OcteonTX MDIO bus # %u\n",
340 			       dev_seq(dev));
341 			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 *)baseaddr;
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 
363 static const struct udevice_id octeontx_smi_ids[] = {
364 	{ .compatible = "cavium,thunder-8890-mdio-nexus" },
365 	{}
366 };
367 
368 U_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 
375 static struct pci_device_id octeontx_smi_supported[] = {
376 	{ PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_SMI) },
377 	{}
378 };
379 
380 U_BOOT_PCI_DEVICE(octeontx_smi, octeontx_smi_supported);
381