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