]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - drivers/net/phy/mv88e6352.c
socfpga: Move board/socfpga_cyclone5 to board/socfpga
[karo-tx-uboot.git] / drivers / net / phy / mv88e6352.c
1 /*
2  * (C) Copyright 2012
3  * Valentin Lontgchamp, Keymile AG, valentin.longchamp@keymile.com
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21  * MA 02110-1301 USA
22  */
23
24 #include <common.h>
25 #include <miiphy.h>
26 #include <asm/errno.h>
27 #include <mv88e6352.h>
28
29 #define SMI_HDR         ((0x8 | 0x1) << 12)
30 #define SMI_BUSY_MASK   (0x8000)
31 #define SMIRD_OP        (0x2 << 10)
32 #define SMIWR_OP        (0x1 << 10)
33 #define SMI_MASK        0x1f
34 #define PORT_SHIFT      5
35
36 #define COMMAND_REG     0
37 #define DATA_REG        1
38
39 /* global registers */
40 #define GLOBAL          0x1b
41
42 #define GLOBAL_STATUS   0x00
43 #define PPU_STATE       0x8000
44
45 #define GLOBAL_CTRL     0x04
46 #define SW_RESET        0x8000
47 #define PPU_ENABLE      0x4000
48
49 static int sw_wait_rdy(const char *devname, u8 phy_addr)
50 {
51         u16 command;
52         u32 timeout = 100;
53         int ret;
54
55         /* wait till the SMI is not busy */
56         do {
57                 /* read command register */
58                 ret = miiphy_read(devname, phy_addr, COMMAND_REG, &command);
59                 if (ret < 0) {
60                         printf("%s: Error reading command register\n",
61                                 __func__);
62                         return ret;
63                 }
64                 if (timeout-- == 0) {
65                         printf("Err..(%s) SMI busy timeout\n", __func__);
66                         return -EFAULT;
67                 }
68         } while (command & SMI_BUSY_MASK);
69
70         return 0;
71 }
72
73 static int sw_reg_read(const char *devname, u8 phy_addr, u8 port,
74         u8 reg, u16 *data)
75 {
76         int ret;
77         u16 command;
78
79         ret = sw_wait_rdy(devname, phy_addr);
80         if (ret)
81                 return ret;
82
83         command = SMI_HDR | SMIRD_OP | ((port&SMI_MASK) << PORT_SHIFT) |
84                         (reg & SMI_MASK);
85         debug("%s: write to command: %#x\n", __func__, command);
86         ret = miiphy_write(devname, phy_addr, COMMAND_REG, command);
87         if (ret)
88                 return ret;
89
90         ret = sw_wait_rdy(devname, phy_addr);
91         if (ret)
92                 return ret;
93
94         ret = miiphy_read(devname, phy_addr, DATA_REG, data);
95
96         return ret;
97 }
98
99 static int sw_reg_write(const char *devname, u8 phy_addr, u8 port,
100         u8 reg, u16 data)
101 {
102         int ret;
103         u16 value;
104
105         ret = sw_wait_rdy(devname, phy_addr);
106         if (ret)
107                 return ret;
108
109         debug("%s: write to data: %#x\n", __func__, data);
110         ret = miiphy_write(devname, phy_addr, DATA_REG, data);
111         if (ret)
112                 return ret;
113
114         value = SMI_HDR | SMIWR_OP | ((port & SMI_MASK) << PORT_SHIFT) |
115                         (reg & SMI_MASK);
116         debug("%s: write to command: %#x\n", __func__, value);
117         ret = miiphy_write(devname, phy_addr, COMMAND_REG, value);
118         if (ret)
119                 return ret;
120
121         ret = sw_wait_rdy(devname, phy_addr);
122         if (ret)
123                 return ret;
124
125         return 0;
126 }
127
128 static int ppu_enable(const char *devname, u8 phy_addr)
129 {
130         int i, ret = 0;
131         u16 reg;
132
133         ret = sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_CTRL, &reg);
134         if (ret) {
135                 printf("%s: Error reading global ctrl reg\n", __func__);
136                 return ret;
137         }
138
139         reg |= PPU_ENABLE;
140
141         ret = sw_reg_write(devname, phy_addr, GLOBAL, GLOBAL_CTRL, reg);
142         if (ret) {
143                 printf("%s: Error writing global ctrl reg\n", __func__);
144                 return ret;
145         }
146
147         for (i = 0; i < 1000; i++) {
148                 sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_STATUS,
149                         &reg);
150                 if ((reg & 0xc000) == 0xc000)
151                         return 0;
152                 udelay(1000);
153         }
154
155         return -ETIMEDOUT;
156 }
157
158 static int ppu_disable(const char *devname, u8 phy_addr)
159 {
160         int i, ret = 0;
161         u16 reg;
162
163         ret = sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_CTRL, &reg);
164         if (ret) {
165                 printf("%s: Error reading global ctrl reg\n", __func__);
166                 return ret;
167         }
168
169         reg &= ~PPU_ENABLE;
170
171         ret = sw_reg_write(devname, phy_addr, GLOBAL, GLOBAL_CTRL, reg);
172         if (ret) {
173                 printf("%s: Error writing global ctrl reg\n", __func__);
174                 return ret;
175         }
176
177         for (i = 0; i < 1000; i++) {
178                 sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_STATUS,
179                         &reg);
180                 if ((reg & 0xc000) != 0xc000)
181                         return 0;
182                 udelay(1000);
183         }
184
185         return -ETIMEDOUT;
186 }
187
188 int mv88e_sw_program(const char *devname, u8 phy_addr,
189         struct mv88e_sw_reg *regs, int regs_nb)
190 {
191         int i, ret = 0;
192
193         /* first we need to disable the PPU */
194         ret = ppu_disable(devname, phy_addr);
195         if (ret) {
196                 printf("%s: Error disabling PPU\n", __func__);
197                 return ret;
198         }
199
200         for (i = 0; i < regs_nb; i++) {
201                 ret = sw_reg_write(devname, phy_addr, regs[i].port,
202                         regs[i].reg, regs[i].value);
203                 if (ret) {
204                         printf("%s: Error configuring switch\n", __func__);
205                         ppu_enable(devname, phy_addr);
206                         return ret;
207                 }
208         }
209
210         /* re-enable the PPU */
211         ret = ppu_enable(devname, phy_addr);
212         if (ret) {
213                 printf("%s: Error enabling PPU\n", __func__);
214                 return ret;
215         }
216
217         return 0;
218 }
219
220 int mv88e_sw_reset(const char *devname, u8 phy_addr)
221 {
222         int i, ret = 0;
223         u16 reg;
224
225         ret = sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_CTRL, &reg);
226         if (ret) {
227                 printf("%s: Error reading global ctrl reg\n", __func__);
228                 return ret;
229         }
230
231         reg = SW_RESET | PPU_ENABLE | 0x0400;
232
233         ret = sw_reg_write(devname, phy_addr, GLOBAL, GLOBAL_CTRL, reg);
234         if (ret) {
235                 printf("%s: Error writing global ctrl reg\n", __func__);
236                 return ret;
237         }
238
239         for (i = 0; i < 1000; i++) {
240                 sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_STATUS,
241                         &reg);
242                 if ((reg & 0xc800) != 0xc800)
243                         return 0;
244                 udelay(1000);
245         }
246
247         return -ETIMEDOUT;
248 }
249
250 int do_mvsw_reg_read(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
259         ret = sw_reg_read(name, phyaddr, port, reg, &value);
260         printf("%#x\n", value);
261
262         return ret;
263 }
264
265 int do_mvsw_reg_write(const char *name, int argc, char * const argv[])
266 {
267         u16 value = 0, phyaddr, reg, port;
268         int ret;
269
270         phyaddr = simple_strtoul(argv[1], NULL, 10);
271         port = simple_strtoul(argv[2], NULL, 10);
272         reg = simple_strtoul(argv[3], NULL, 10);
273         value = simple_strtoul(argv[4], NULL, 16);
274
275         ret = sw_reg_write(name, phyaddr, port, reg, value);
276
277         return ret;
278 }
279
280
281 int do_mvsw_reg(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
282 {
283         int ret;
284         const char *cmd, *ethname;
285
286         if (argc < 2)
287                 return cmd_usage(cmdtp);
288
289         cmd = argv[1];
290         --argc;
291         ++argv;
292
293         if (strcmp(cmd, "read") == 0) {
294                 if (argc < 5)
295                         return cmd_usage(cmdtp);
296                 ethname = argv[1];
297                 --argc;
298                 ++argv;
299                 ret = do_mvsw_reg_read(ethname, argc, argv);
300         } else if (strcmp(cmd, "write") == 0) {
301                 if (argc < 6)
302                         return cmd_usage(cmdtp);
303                 ethname = argv[1];
304                 --argc;
305                 ++argv;
306                 ret = do_mvsw_reg_write(ethname, argc, argv);
307         } else
308                 return cmd_usage(cmdtp);
309
310         return ret;
311 }
312
313 U_BOOT_CMD(
314         mvsw_reg,       7,      1,      do_mvsw_reg,
315         "marvell 88e6352 switch register access",
316         "write ethname phyaddr port reg value\n"
317         "mvsw_reg read  ethname phyaddr port reg\n"
318         );