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