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