1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * (C) Copyright 2012
4 * Valentin Lontgchamp, Keymile AG, valentin.longchamp@keymile.com
5 */
6
7#include <common.h>
8#include <command.h>
9#include <log.h>
10#include <miiphy.h>
11#include <linux/delay.h>
12#include <linux/errno.h>
13#include <mv88e6352.h>
14
15#define SMI_HDR		((0x8 | 0x1) << 12)
16#define SMI_BUSY_MASK	(0x8000)
17#define SMIRD_OP	(0x2 << 10)
18#define SMIWR_OP	(0x1 << 10)
19#define SMI_MASK	0x1f
20#define PORT_SHIFT	5
21
22#define COMMAND_REG	0
23#define DATA_REG	1
24
25/* global registers */
26#define GLOBAL		0x1b
27
28#define GLOBAL_STATUS	0x00
29#define PPU_STATE	0x8000
30
31#define GLOBAL_CTRL	0x04
32#define SW_RESET	0x8000
33#define PPU_ENABLE	0x4000
34
35static int sw_wait_rdy(const char *devname, u8 phy_addr)
36{
37	u16 command;
38	u32 timeout = 100;
39	int ret;
40
41	/* wait till the SMI is not busy */
42	do {
43		/* read command register */
44		ret = miiphy_read(devname, phy_addr, COMMAND_REG, &command);
45		if (ret < 0) {
46			printf("%s: Error reading command register\n",
47				__func__);
48			return ret;
49		}
50		if (timeout-- == 0) {
51			printf("Err..(%s) SMI busy timeout\n", __func__);
52			return -EFAULT;
53		}
54	} while (command & SMI_BUSY_MASK);
55
56	return 0;
57}
58
59static int sw_reg_read(const char *devname, u8 phy_addr, u8 port,
60	u8 reg, u16 *data)
61{
62	int ret;
63	u16 command;
64
65	ret = sw_wait_rdy(devname, phy_addr);
66	if (ret)
67		return ret;
68
69	command = SMI_HDR | SMIRD_OP | ((port&SMI_MASK) << PORT_SHIFT) |
70			(reg & SMI_MASK);
71	debug("%s: write to command: %#x\n", __func__, command);
72	ret = miiphy_write(devname, phy_addr, COMMAND_REG, command);
73	if (ret)
74		return ret;
75
76	ret = sw_wait_rdy(devname, phy_addr);
77	if (ret)
78		return ret;
79
80	ret = miiphy_read(devname, phy_addr, DATA_REG, data);
81
82	return ret;
83}
84
85static int sw_reg_write(const char *devname, u8 phy_addr, u8 port,
86	u8 reg, u16 data)
87{
88	int ret;
89	u16 value;
90
91	ret = sw_wait_rdy(devname, phy_addr);
92	if (ret)
93		return ret;
94
95	debug("%s: write to data: %#x\n", __func__, data);
96	ret = miiphy_write(devname, phy_addr, DATA_REG, data);
97	if (ret)
98		return ret;
99
100	value = SMI_HDR | SMIWR_OP | ((port & SMI_MASK) << PORT_SHIFT) |
101			(reg & SMI_MASK);
102	debug("%s: write to command: %#x\n", __func__, value);
103	ret = miiphy_write(devname, phy_addr, COMMAND_REG, value);
104	if (ret)
105		return ret;
106
107	ret = sw_wait_rdy(devname, phy_addr);
108	if (ret)
109		return ret;
110
111	return 0;
112}
113
114static int ppu_enable(const char *devname, u8 phy_addr)
115{
116	int i, ret = 0;
117	u16 reg;
118
119	ret = sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_CTRL, &reg);
120	if (ret) {
121		printf("%s: Error reading global ctrl reg\n", __func__);
122		return ret;
123	}
124
125	reg |= PPU_ENABLE;
126
127	ret = sw_reg_write(devname, phy_addr, GLOBAL, GLOBAL_CTRL, reg);
128	if (ret) {
129		printf("%s: Error writing global ctrl reg\n", __func__);
130		return ret;
131	}
132
133	for (i = 0; i < 1000; i++) {
134		sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_STATUS,
135			&reg);
136		if ((reg & 0xc000) == 0xc000)
137			return 0;
138		udelay(1000);
139	}
140
141	return -ETIMEDOUT;
142}
143
144static int ppu_disable(const char *devname, u8 phy_addr)
145{
146	int i, ret = 0;
147	u16 reg;
148
149	ret = sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_CTRL, &reg);
150	if (ret) {
151		printf("%s: Error reading global ctrl reg\n", __func__);
152		return ret;
153	}
154
155	reg &= ~PPU_ENABLE;
156
157	ret = sw_reg_write(devname, phy_addr, GLOBAL, GLOBAL_CTRL, reg);
158	if (ret) {
159		printf("%s: Error writing global ctrl reg\n", __func__);
160		return ret;
161	}
162
163	for (i = 0; i < 1000; i++) {
164		sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_STATUS,
165			&reg);
166		if ((reg & 0xc000) != 0xc000)
167			return 0;
168		udelay(1000);
169	}
170
171	return -ETIMEDOUT;
172}
173
174int mv88e_sw_program(const char *devname, u8 phy_addr,
175	struct mv88e_sw_reg *regs, int regs_nb)
176{
177	int i, ret = 0;
178
179	/* first we need to disable the PPU */
180	ret = ppu_disable(devname, phy_addr);
181	if (ret) {
182		printf("%s: Error disabling PPU\n", __func__);
183		return ret;
184	}
185
186	for (i = 0; i < regs_nb; i++) {
187		ret = sw_reg_write(devname, phy_addr, regs[i].port,
188			regs[i].reg, regs[i].value);
189		if (ret) {
190			printf("%s: Error configuring switch\n", __func__);
191			ppu_enable(devname, phy_addr);
192			return ret;
193		}
194	}
195
196	/* re-enable the PPU */
197	ret = ppu_enable(devname, phy_addr);
198	if (ret) {
199		printf("%s: Error enabling PPU\n", __func__);
200		return ret;
201	}
202
203	return 0;
204}
205
206int mv88e_sw_reset(const char *devname, u8 phy_addr)
207{
208	int i, ret = 0;
209	u16 reg;
210
211	ret = sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_CTRL, &reg);
212	if (ret) {
213		printf("%s: Error reading global ctrl reg\n", __func__);
214		return ret;
215	}
216
217	reg = SW_RESET | PPU_ENABLE | 0x0400;
218
219	ret = sw_reg_write(devname, phy_addr, GLOBAL, GLOBAL_CTRL, reg);
220	if (ret) {
221		printf("%s: Error writing global ctrl reg\n", __func__);
222		return ret;
223	}
224
225	for (i = 0; i < 1000; i++) {
226		sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_STATUS,
227			&reg);
228		if ((reg & 0xc800) != 0xc800)
229			return 0;
230		udelay(1000);
231	}
232
233	return -ETIMEDOUT;
234}
235
236int do_mvsw_reg_read(const char *name, int argc, char *const argv[])
237{
238	u16 value = 0, phyaddr, reg, port;
239	int ret;
240
241	phyaddr = dectoul(argv[1], NULL);
242	port = dectoul(argv[2], NULL);
243	reg = dectoul(argv[3], NULL);
244
245	ret = sw_reg_read(name, phyaddr, port, reg, &value);
246	printf("%#x\n", value);
247
248	return ret;
249}
250
251int do_mvsw_reg_write(const char *name, int argc, char *const argv[])
252{
253	u16 value = 0, phyaddr, reg, port;
254	int ret;
255
256	phyaddr = dectoul(argv[1], NULL);
257	port = dectoul(argv[2], NULL);
258	reg = dectoul(argv[3], NULL);
259	value = hextoul(argv[4], NULL);
260
261	ret = sw_reg_write(name, phyaddr, port, reg, value);
262
263	return ret;
264}
265
266
267int do_mvsw_reg(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
268{
269	int ret;
270	const char *cmd, *ethname;
271
272	if (argc < 2)
273		return cmd_usage(cmdtp);
274
275	cmd = argv[1];
276	--argc;
277	++argv;
278
279	if (strcmp(cmd, "read") == 0) {
280		if (argc < 5)
281			return cmd_usage(cmdtp);
282		ethname = argv[1];
283		--argc;
284		++argv;
285		ret = do_mvsw_reg_read(ethname, argc, argv);
286	} else if (strcmp(cmd, "write") == 0) {
287		if (argc < 6)
288			return cmd_usage(cmdtp);
289		ethname = argv[1];
290		--argc;
291		++argv;
292		ret = do_mvsw_reg_write(ethname, argc, argv);
293	} else
294		return cmd_usage(cmdtp);
295
296	return ret;
297}
298
299U_BOOT_CMD(
300	mvsw_reg,	7,	1,	do_mvsw_reg,
301	"marvell 88e6352 switch register access",
302	"write ethname phyaddr port reg value\n"
303	"mvsw_reg read  ethname phyaddr port reg\n"
304	);
305