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
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{
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, 0, 0, PCI_REGION_TYPE, 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
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);
381