]>
Commit | Line | Data |
---|---|---|
2b62997c CC |
1 | /* |
2 | * CPSW Ethernet Switch Driver | |
3 | * | |
4 | * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License as | |
8 | * published by the Free Software Foundation version 2. | |
9 | * | |
10 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | |
11 | * kind, whether express or implied; without even the implied warranty | |
12 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | */ | |
15 | ||
16 | #include <common.h> | |
17 | #include <command.h> | |
18 | #include <net.h> | |
19 | #include <miiphy.h> | |
20 | #include <malloc.h> | |
21 | #include <net.h> | |
22 | #include <netdev.h> | |
23 | #include <cpsw.h> | |
24 | #include <asm/errno.h> | |
25 | #include <asm/io.h> | |
26 | #include <phy.h> | |
98f92001 | 27 | #include <asm/arch/cpu.h> |
4cc77895 M |
28 | #include <dm.h> |
29 | ||
30 | DECLARE_GLOBAL_DATA_PTR; | |
2b62997c CC |
31 | |
32 | #define BITMASK(bits) (BIT(bits) - 1) | |
33 | #define PHY_REG_MASK 0x1f | |
34 | #define PHY_ID_MASK 0x1f | |
35 | #define NUM_DESCS (PKTBUFSRX * 2) | |
36 | #define PKT_MIN 60 | |
37 | #define PKT_MAX (1500 + 14 + 4 + 4) | |
38 | #define CLEAR_BIT 1 | |
39 | #define GIGABITEN BIT(7) | |
40 | #define FULLDUPLEXEN BIT(0) | |
41 | #define MIIEN BIT(15) | |
42 | ||
4cc77895 M |
43 | /* reg offset */ |
44 | #define CPSW_HOST_PORT_OFFSET 0x108 | |
45 | #define CPSW_SLAVE0_OFFSET 0x208 | |
46 | #define CPSW_SLAVE1_OFFSET 0x308 | |
47 | #define CPSW_SLAVE_SIZE 0x100 | |
48 | #define CPSW_CPDMA_OFFSET 0x800 | |
49 | #define CPSW_HW_STATS 0x900 | |
50 | #define CPSW_STATERAM_OFFSET 0xa00 | |
51 | #define CPSW_CPTS_OFFSET 0xc00 | |
52 | #define CPSW_ALE_OFFSET 0xd00 | |
53 | #define CPSW_SLIVER0_OFFSET 0xd80 | |
54 | #define CPSW_SLIVER1_OFFSET 0xdc0 | |
55 | #define CPSW_BD_OFFSET 0x2000 | |
56 | #define CPSW_MDIO_DIV 0xff | |
57 | ||
58 | #define AM335X_GMII_SEL_OFFSET 0x630 | |
59 | ||
2b62997c CC |
60 | /* DMA Registers */ |
61 | #define CPDMA_TXCONTROL 0x004 | |
62 | #define CPDMA_RXCONTROL 0x014 | |
63 | #define CPDMA_SOFTRESET 0x01c | |
64 | #define CPDMA_RXFREE 0x0e0 | |
65 | #define CPDMA_TXHDP_VER1 0x100 | |
66 | #define CPDMA_TXHDP_VER2 0x200 | |
67 | #define CPDMA_RXHDP_VER1 0x120 | |
68 | #define CPDMA_RXHDP_VER2 0x220 | |
69 | #define CPDMA_TXCP_VER1 0x140 | |
70 | #define CPDMA_TXCP_VER2 0x240 | |
71 | #define CPDMA_RXCP_VER1 0x160 | |
72 | #define CPDMA_RXCP_VER2 0x260 | |
73 | ||
2b62997c CC |
74 | /* Descriptor mode bits */ |
75 | #define CPDMA_DESC_SOP BIT(31) | |
76 | #define CPDMA_DESC_EOP BIT(30) | |
77 | #define CPDMA_DESC_OWNER BIT(29) | |
78 | #define CPDMA_DESC_EOQ BIT(28) | |
79 | ||
80 | /* | |
81 | * This timeout definition is a worst-case ultra defensive measure against | |
82 | * unexpected controller lock ups. Ideally, we should never ever hit this | |
83 | * scenario in practice. | |
84 | */ | |
85 | #define MDIO_TIMEOUT 100 /* msecs */ | |
86 | #define CPDMA_TIMEOUT 100 /* msecs */ | |
87 | ||
88 | struct cpsw_mdio_regs { | |
89 | u32 version; | |
90 | u32 control; | |
91 | #define CONTROL_IDLE BIT(31) | |
92 | #define CONTROL_ENABLE BIT(30) | |
93 | ||
94 | u32 alive; | |
95 | u32 link; | |
96 | u32 linkintraw; | |
97 | u32 linkintmasked; | |
98 | u32 __reserved_0[2]; | |
99 | u32 userintraw; | |
100 | u32 userintmasked; | |
101 | u32 userintmaskset; | |
102 | u32 userintmaskclr; | |
103 | u32 __reserved_1[20]; | |
104 | ||
105 | struct { | |
106 | u32 access; | |
107 | u32 physel; | |
108 | #define USERACCESS_GO BIT(31) | |
109 | #define USERACCESS_WRITE BIT(30) | |
110 | #define USERACCESS_ACK BIT(29) | |
111 | #define USERACCESS_READ (0) | |
112 | #define USERACCESS_DATA (0xffff) | |
113 | } user[0]; | |
114 | }; | |
115 | ||
116 | struct cpsw_regs { | |
117 | u32 id_ver; | |
118 | u32 control; | |
119 | u32 soft_reset; | |
120 | u32 stat_port_en; | |
121 | u32 ptype; | |
122 | }; | |
123 | ||
124 | struct cpsw_slave_regs { | |
125 | u32 max_blks; | |
126 | u32 blk_cnt; | |
127 | u32 flow_thresh; | |
128 | u32 port_vlan; | |
129 | u32 tx_pri_map; | |
f6f86a64 | 130 | #ifdef CONFIG_AM33XX |
2b62997c | 131 | u32 gap_thresh; |
f6f86a64 MP |
132 | #elif defined(CONFIG_TI814X) |
133 | u32 ts_ctl; | |
134 | u32 ts_seq_ltype; | |
135 | u32 ts_vlan; | |
136 | #endif | |
2b62997c CC |
137 | u32 sa_lo; |
138 | u32 sa_hi; | |
139 | }; | |
140 | ||
141 | struct cpsw_host_regs { | |
142 | u32 max_blks; | |
143 | u32 blk_cnt; | |
144 | u32 flow_thresh; | |
145 | u32 port_vlan; | |
146 | u32 tx_pri_map; | |
147 | u32 cpdma_tx_pri_map; | |
148 | u32 cpdma_rx_chan_map; | |
149 | }; | |
150 | ||
151 | struct cpsw_sliver_regs { | |
152 | u32 id_ver; | |
153 | u32 mac_control; | |
154 | u32 mac_status; | |
155 | u32 soft_reset; | |
156 | u32 rx_maxlen; | |
157 | u32 __reserved_0; | |
158 | u32 rx_pause; | |
159 | u32 tx_pause; | |
160 | u32 __reserved_1; | |
161 | u32 rx_pri_map; | |
162 | }; | |
163 | ||
164 | #define ALE_ENTRY_BITS 68 | |
165 | #define ALE_ENTRY_WORDS DIV_ROUND_UP(ALE_ENTRY_BITS, 32) | |
166 | ||
167 | /* ALE Registers */ | |
168 | #define ALE_CONTROL 0x08 | |
169 | #define ALE_UNKNOWNVLAN 0x18 | |
170 | #define ALE_TABLE_CONTROL 0x20 | |
171 | #define ALE_TABLE 0x34 | |
172 | #define ALE_PORTCTL 0x40 | |
173 | ||
174 | #define ALE_TABLE_WRITE BIT(31) | |
175 | ||
176 | #define ALE_TYPE_FREE 0 | |
177 | #define ALE_TYPE_ADDR 1 | |
178 | #define ALE_TYPE_VLAN 2 | |
179 | #define ALE_TYPE_VLAN_ADDR 3 | |
180 | ||
181 | #define ALE_UCAST_PERSISTANT 0 | |
182 | #define ALE_UCAST_UNTOUCHED 1 | |
183 | #define ALE_UCAST_OUI 2 | |
184 | #define ALE_UCAST_TOUCHED 3 | |
185 | ||
186 | #define ALE_MCAST_FWD 0 | |
187 | #define ALE_MCAST_BLOCK_LEARN_FWD 1 | |
188 | #define ALE_MCAST_FWD_LEARN 2 | |
189 | #define ALE_MCAST_FWD_2 3 | |
190 | ||
191 | enum cpsw_ale_port_state { | |
192 | ALE_PORT_STATE_DISABLE = 0x00, | |
193 | ALE_PORT_STATE_BLOCK = 0x01, | |
194 | ALE_PORT_STATE_LEARN = 0x02, | |
195 | ALE_PORT_STATE_FORWARD = 0x03, | |
196 | }; | |
197 | ||
198 | /* ALE unicast entry flags - passed into cpsw_ale_add_ucast() */ | |
199 | #define ALE_SECURE 1 | |
200 | #define ALE_BLOCKED 2 | |
201 | ||
202 | struct cpsw_slave { | |
203 | struct cpsw_slave_regs *regs; | |
204 | struct cpsw_sliver_regs *sliver; | |
205 | int slave_num; | |
206 | u32 mac_control; | |
207 | struct cpsw_slave_data *data; | |
208 | }; | |
209 | ||
210 | struct cpdma_desc { | |
211 | /* hardware fields */ | |
212 | u32 hw_next; | |
213 | u32 hw_buffer; | |
214 | u32 hw_len; | |
215 | u32 hw_mode; | |
216 | /* software fields */ | |
217 | u32 sw_buffer; | |
218 | u32 sw_len; | |
219 | }; | |
220 | ||
221 | struct cpdma_chan { | |
222 | struct cpdma_desc *head, *tail; | |
223 | void *hdp, *cp, *rxfree; | |
224 | }; | |
225 | ||
226 | #define desc_write(desc, fld, val) __raw_writel((u32)(val), &(desc)->fld) | |
227 | #define desc_read(desc, fld) __raw_readl(&(desc)->fld) | |
228 | #define desc_read_ptr(desc, fld) ((void *)__raw_readl(&(desc)->fld)) | |
229 | ||
230 | #define chan_write(chan, fld, val) __raw_writel((u32)(val), (chan)->fld) | |
231 | #define chan_read(chan, fld) __raw_readl((chan)->fld) | |
232 | #define chan_read_ptr(chan, fld) ((void *)__raw_readl((chan)->fld)) | |
233 | ||
7a022753 M |
234 | #define for_active_slave(slave, priv) \ |
235 | slave = (priv)->slaves + (priv)->data.active_slave; if (slave) | |
2b62997c CC |
236 | #define for_each_slave(slave, priv) \ |
237 | for (slave = (priv)->slaves; slave != (priv)->slaves + \ | |
238 | (priv)->data.slaves; slave++) | |
239 | ||
240 | struct cpsw_priv { | |
4cc77895 M |
241 | #ifdef CONFIG_DM_ETH |
242 | struct udevice *dev; | |
243 | #else | |
2b62997c | 244 | struct eth_device *dev; |
4cc77895 | 245 | #endif |
2b62997c CC |
246 | struct cpsw_platform_data data; |
247 | int host_port; | |
248 | ||
249 | struct cpsw_regs *regs; | |
250 | void *dma_regs; | |
251 | struct cpsw_host_regs *host_port_regs; | |
252 | void *ale_regs; | |
253 | ||
254 | struct cpdma_desc *descs; | |
255 | struct cpdma_desc *desc_free; | |
256 | struct cpdma_chan rx_chan, tx_chan; | |
257 | ||
258 | struct cpsw_slave *slaves; | |
259 | struct phy_device *phydev; | |
260 | struct mii_dev *bus; | |
48ec5291 | 261 | |
48ec5291 | 262 | u32 phy_mask; |
2b62997c CC |
263 | }; |
264 | ||
265 | static inline int cpsw_ale_get_field(u32 *ale_entry, u32 start, u32 bits) | |
266 | { | |
267 | int idx; | |
268 | ||
269 | idx = start / 32; | |
270 | start -= idx * 32; | |
271 | idx = 2 - idx; /* flip */ | |
272 | return (ale_entry[idx] >> start) & BITMASK(bits); | |
273 | } | |
274 | ||
275 | static inline void cpsw_ale_set_field(u32 *ale_entry, u32 start, u32 bits, | |
276 | u32 value) | |
277 | { | |
278 | int idx; | |
279 | ||
280 | value &= BITMASK(bits); | |
281 | idx = start / 32; | |
282 | start -= idx * 32; | |
283 | idx = 2 - idx; /* flip */ | |
284 | ale_entry[idx] &= ~(BITMASK(bits) << start); | |
285 | ale_entry[idx] |= (value << start); | |
286 | } | |
287 | ||
288 | #define DEFINE_ALE_FIELD(name, start, bits) \ | |
289 | static inline int cpsw_ale_get_##name(u32 *ale_entry) \ | |
290 | { \ | |
291 | return cpsw_ale_get_field(ale_entry, start, bits); \ | |
292 | } \ | |
293 | static inline void cpsw_ale_set_##name(u32 *ale_entry, u32 value) \ | |
294 | { \ | |
295 | cpsw_ale_set_field(ale_entry, start, bits, value); \ | |
296 | } | |
297 | ||
298 | DEFINE_ALE_FIELD(entry_type, 60, 2) | |
299 | DEFINE_ALE_FIELD(mcast_state, 62, 2) | |
300 | DEFINE_ALE_FIELD(port_mask, 66, 3) | |
301 | DEFINE_ALE_FIELD(ucast_type, 62, 2) | |
302 | DEFINE_ALE_FIELD(port_num, 66, 2) | |
303 | DEFINE_ALE_FIELD(blocked, 65, 1) | |
304 | DEFINE_ALE_FIELD(secure, 64, 1) | |
305 | DEFINE_ALE_FIELD(mcast, 40, 1) | |
306 | ||
307 | /* The MAC address field in the ALE entry cannot be macroized as above */ | |
308 | static inline void cpsw_ale_get_addr(u32 *ale_entry, u8 *addr) | |
309 | { | |
310 | int i; | |
311 | ||
312 | for (i = 0; i < 6; i++) | |
313 | addr[i] = cpsw_ale_get_field(ale_entry, 40 - 8*i, 8); | |
314 | } | |
315 | ||
0adb5b76 | 316 | static inline void cpsw_ale_set_addr(u32 *ale_entry, const u8 *addr) |
2b62997c CC |
317 | { |
318 | int i; | |
319 | ||
320 | for (i = 0; i < 6; i++) | |
321 | cpsw_ale_set_field(ale_entry, 40 - 8*i, 8, addr[i]); | |
322 | } | |
323 | ||
324 | static int cpsw_ale_read(struct cpsw_priv *priv, int idx, u32 *ale_entry) | |
325 | { | |
326 | int i; | |
327 | ||
328 | __raw_writel(idx, priv->ale_regs + ALE_TABLE_CONTROL); | |
329 | ||
330 | for (i = 0; i < ALE_ENTRY_WORDS; i++) | |
331 | ale_entry[i] = __raw_readl(priv->ale_regs + ALE_TABLE + 4 * i); | |
332 | ||
333 | return idx; | |
334 | } | |
335 | ||
336 | static int cpsw_ale_write(struct cpsw_priv *priv, int idx, u32 *ale_entry) | |
337 | { | |
338 | int i; | |
339 | ||
340 | for (i = 0; i < ALE_ENTRY_WORDS; i++) | |
341 | __raw_writel(ale_entry[i], priv->ale_regs + ALE_TABLE + 4 * i); | |
342 | ||
343 | __raw_writel(idx | ALE_TABLE_WRITE, priv->ale_regs + ALE_TABLE_CONTROL); | |
344 | ||
345 | return idx; | |
346 | } | |
347 | ||
0adb5b76 | 348 | static int cpsw_ale_match_addr(struct cpsw_priv *priv, const u8 *addr) |
2b62997c CC |
349 | { |
350 | u32 ale_entry[ALE_ENTRY_WORDS]; | |
351 | int type, idx; | |
352 | ||
353 | for (idx = 0; idx < priv->data.ale_entries; idx++) { | |
354 | u8 entry_addr[6]; | |
355 | ||
356 | cpsw_ale_read(priv, idx, ale_entry); | |
357 | type = cpsw_ale_get_entry_type(ale_entry); | |
358 | if (type != ALE_TYPE_ADDR && type != ALE_TYPE_VLAN_ADDR) | |
359 | continue; | |
360 | cpsw_ale_get_addr(ale_entry, entry_addr); | |
361 | if (memcmp(entry_addr, addr, 6) == 0) | |
362 | return idx; | |
363 | } | |
364 | return -ENOENT; | |
365 | } | |
366 | ||
367 | static int cpsw_ale_match_free(struct cpsw_priv *priv) | |
368 | { | |
369 | u32 ale_entry[ALE_ENTRY_WORDS]; | |
370 | int type, idx; | |
371 | ||
372 | for (idx = 0; idx < priv->data.ale_entries; idx++) { | |
373 | cpsw_ale_read(priv, idx, ale_entry); | |
374 | type = cpsw_ale_get_entry_type(ale_entry); | |
375 | if (type == ALE_TYPE_FREE) | |
376 | return idx; | |
377 | } | |
378 | return -ENOENT; | |
379 | } | |
380 | ||
381 | static int cpsw_ale_find_ageable(struct cpsw_priv *priv) | |
382 | { | |
383 | u32 ale_entry[ALE_ENTRY_WORDS]; | |
384 | int type, idx; | |
385 | ||
386 | for (idx = 0; idx < priv->data.ale_entries; idx++) { | |
387 | cpsw_ale_read(priv, idx, ale_entry); | |
388 | type = cpsw_ale_get_entry_type(ale_entry); | |
389 | if (type != ALE_TYPE_ADDR && type != ALE_TYPE_VLAN_ADDR) | |
390 | continue; | |
391 | if (cpsw_ale_get_mcast(ale_entry)) | |
392 | continue; | |
393 | type = cpsw_ale_get_ucast_type(ale_entry); | |
394 | if (type != ALE_UCAST_PERSISTANT && | |
395 | type != ALE_UCAST_OUI) | |
396 | return idx; | |
397 | } | |
398 | return -ENOENT; | |
399 | } | |
400 | ||
0adb5b76 | 401 | static int cpsw_ale_add_ucast(struct cpsw_priv *priv, const u8 *addr, |
2b62997c CC |
402 | int port, int flags) |
403 | { | |
404 | u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; | |
405 | int idx; | |
406 | ||
407 | cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR); | |
408 | cpsw_ale_set_addr(ale_entry, addr); | |
409 | cpsw_ale_set_ucast_type(ale_entry, ALE_UCAST_PERSISTANT); | |
410 | cpsw_ale_set_secure(ale_entry, (flags & ALE_SECURE) ? 1 : 0); | |
411 | cpsw_ale_set_blocked(ale_entry, (flags & ALE_BLOCKED) ? 1 : 0); | |
412 | cpsw_ale_set_port_num(ale_entry, port); | |
413 | ||
414 | idx = cpsw_ale_match_addr(priv, addr); | |
415 | if (idx < 0) | |
416 | idx = cpsw_ale_match_free(priv); | |
417 | if (idx < 0) | |
418 | idx = cpsw_ale_find_ageable(priv); | |
419 | if (idx < 0) | |
420 | return -ENOMEM; | |
421 | ||
422 | cpsw_ale_write(priv, idx, ale_entry); | |
423 | return 0; | |
424 | } | |
425 | ||
0adb5b76 JH |
426 | static int cpsw_ale_add_mcast(struct cpsw_priv *priv, const u8 *addr, |
427 | int port_mask) | |
2b62997c CC |
428 | { |
429 | u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; | |
430 | int idx, mask; | |
431 | ||
432 | idx = cpsw_ale_match_addr(priv, addr); | |
433 | if (idx >= 0) | |
434 | cpsw_ale_read(priv, idx, ale_entry); | |
435 | ||
436 | cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR); | |
437 | cpsw_ale_set_addr(ale_entry, addr); | |
438 | cpsw_ale_set_mcast_state(ale_entry, ALE_MCAST_FWD_2); | |
439 | ||
440 | mask = cpsw_ale_get_port_mask(ale_entry); | |
441 | port_mask |= mask; | |
442 | cpsw_ale_set_port_mask(ale_entry, port_mask); | |
443 | ||
444 | if (idx < 0) | |
445 | idx = cpsw_ale_match_free(priv); | |
446 | if (idx < 0) | |
447 | idx = cpsw_ale_find_ageable(priv); | |
448 | if (idx < 0) | |
449 | return -ENOMEM; | |
450 | ||
451 | cpsw_ale_write(priv, idx, ale_entry); | |
452 | return 0; | |
453 | } | |
454 | ||
455 | static inline void cpsw_ale_control(struct cpsw_priv *priv, int bit, int val) | |
456 | { | |
457 | u32 tmp, mask = BIT(bit); | |
458 | ||
459 | tmp = __raw_readl(priv->ale_regs + ALE_CONTROL); | |
460 | tmp &= ~mask; | |
461 | tmp |= val ? mask : 0; | |
462 | __raw_writel(tmp, priv->ale_regs + ALE_CONTROL); | |
463 | } | |
464 | ||
465 | #define cpsw_ale_enable(priv, val) cpsw_ale_control(priv, 31, val) | |
466 | #define cpsw_ale_clear(priv, val) cpsw_ale_control(priv, 30, val) | |
467 | #define cpsw_ale_vlan_aware(priv, val) cpsw_ale_control(priv, 2, val) | |
468 | ||
469 | static inline void cpsw_ale_port_state(struct cpsw_priv *priv, int port, | |
470 | int val) | |
471 | { | |
472 | int offset = ALE_PORTCTL + 4 * port; | |
473 | u32 tmp, mask = 0x3; | |
474 | ||
475 | tmp = __raw_readl(priv->ale_regs + offset); | |
476 | tmp &= ~mask; | |
477 | tmp |= val & mask; | |
478 | __raw_writel(tmp, priv->ale_regs + offset); | |
479 | } | |
480 | ||
481 | static struct cpsw_mdio_regs *mdio_regs; | |
482 | ||
483 | /* wait until hardware is ready for another user access */ | |
484 | static inline u32 wait_for_user_access(void) | |
485 | { | |
486 | u32 reg = 0; | |
487 | int timeout = MDIO_TIMEOUT; | |
488 | ||
489 | while (timeout-- && | |
490 | ((reg = __raw_readl(&mdio_regs->user[0].access)) & USERACCESS_GO)) | |
491 | udelay(10); | |
492 | ||
493 | if (timeout == -1) { | |
494 | printf("wait_for_user_access Timeout\n"); | |
495 | return -ETIMEDOUT; | |
496 | } | |
497 | return reg; | |
498 | } | |
499 | ||
500 | /* wait until hardware state machine is idle */ | |
501 | static inline void wait_for_idle(void) | |
502 | { | |
503 | int timeout = MDIO_TIMEOUT; | |
504 | ||
505 | while (timeout-- && | |
506 | ((__raw_readl(&mdio_regs->control) & CONTROL_IDLE) == 0)) | |
507 | udelay(10); | |
508 | ||
509 | if (timeout == -1) | |
510 | printf("wait_for_idle Timeout\n"); | |
511 | } | |
512 | ||
513 | static int cpsw_mdio_read(struct mii_dev *bus, int phy_id, | |
514 | int dev_addr, int phy_reg) | |
515 | { | |
f6d1f6e4 | 516 | int data; |
2b62997c CC |
517 | u32 reg; |
518 | ||
519 | if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK) | |
520 | return -EINVAL; | |
521 | ||
522 | wait_for_user_access(); | |
523 | reg = (USERACCESS_GO | USERACCESS_READ | (phy_reg << 21) | | |
524 | (phy_id << 16)); | |
525 | __raw_writel(reg, &mdio_regs->user[0].access); | |
526 | reg = wait_for_user_access(); | |
527 | ||
528 | data = (reg & USERACCESS_ACK) ? (reg & USERACCESS_DATA) : -1; | |
529 | return data; | |
530 | } | |
531 | ||
532 | static int cpsw_mdio_write(struct mii_dev *bus, int phy_id, int dev_addr, | |
533 | int phy_reg, u16 data) | |
534 | { | |
535 | u32 reg; | |
536 | ||
537 | if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK) | |
538 | return -EINVAL; | |
539 | ||
540 | wait_for_user_access(); | |
541 | reg = (USERACCESS_GO | USERACCESS_WRITE | (phy_reg << 21) | | |
542 | (phy_id << 16) | (data & USERACCESS_DATA)); | |
543 | __raw_writel(reg, &mdio_regs->user[0].access); | |
544 | wait_for_user_access(); | |
545 | ||
546 | return 0; | |
547 | } | |
548 | ||
4cc77895 | 549 | static void cpsw_mdio_init(const char *name, u32 mdio_base, u32 div) |
2b62997c CC |
550 | { |
551 | struct mii_dev *bus = mdio_alloc(); | |
552 | ||
553 | mdio_regs = (struct cpsw_mdio_regs *)mdio_base; | |
554 | ||
555 | /* set enable and clock divider */ | |
556 | __raw_writel(div | CONTROL_ENABLE, &mdio_regs->control); | |
557 | ||
558 | /* | |
559 | * wait for scan logic to settle: | |
560 | * the scan time consists of (a) a large fixed component, and (b) a | |
561 | * small component that varies with the mii bus frequency. These | |
562 | * were estimated using measurements at 1.1 and 2.2 MHz on tnetv107x | |
563 | * silicon. Since the effect of (b) was found to be largely | |
564 | * negligible, we keep things simple here. | |
565 | */ | |
566 | udelay(1000); | |
567 | ||
568 | bus->read = cpsw_mdio_read; | |
569 | bus->write = cpsw_mdio_write; | |
192bc694 | 570 | strcpy(bus->name, name); |
2b62997c CC |
571 | |
572 | mdio_register(bus); | |
573 | } | |
574 | ||
575 | /* Set a self-clearing bit in a register, and wait for it to clear */ | |
576 | static inline void setbit_and_wait_for_clear32(void *addr) | |
577 | { | |
578 | __raw_writel(CLEAR_BIT, addr); | |
579 | while (__raw_readl(addr) & CLEAR_BIT) | |
580 | ; | |
581 | } | |
582 | ||
583 | #define mac_hi(mac) (((mac)[0] << 0) | ((mac)[1] << 8) | \ | |
584 | ((mac)[2] << 16) | ((mac)[3] << 24)) | |
585 | #define mac_lo(mac) (((mac)[4] << 0) | ((mac)[5] << 8)) | |
586 | ||
587 | static void cpsw_set_slave_mac(struct cpsw_slave *slave, | |
588 | struct cpsw_priv *priv) | |
589 | { | |
4cc77895 M |
590 | #ifdef CONFIG_DM_ETH |
591 | struct eth_pdata *pdata = dev_get_platdata(priv->dev); | |
592 | ||
593 | writel(mac_hi(pdata->enetaddr), &slave->regs->sa_hi); | |
594 | writel(mac_lo(pdata->enetaddr), &slave->regs->sa_lo); | |
595 | #else | |
2b62997c CC |
596 | __raw_writel(mac_hi(priv->dev->enetaddr), &slave->regs->sa_hi); |
597 | __raw_writel(mac_lo(priv->dev->enetaddr), &slave->regs->sa_lo); | |
4cc77895 | 598 | #endif |
2b62997c CC |
599 | } |
600 | ||
601 | static void cpsw_slave_update_link(struct cpsw_slave *slave, | |
602 | struct cpsw_priv *priv, int *link) | |
603 | { | |
93ff2552 | 604 | struct phy_device *phy; |
2b62997c CC |
605 | u32 mac_control = 0; |
606 | ||
93ff2552 HS |
607 | phy = priv->phydev; |
608 | ||
609 | if (!phy) | |
610 | return; | |
611 | ||
2b62997c CC |
612 | phy_startup(phy); |
613 | *link = phy->link; | |
614 | ||
615 | if (*link) { /* link up */ | |
616 | mac_control = priv->data.mac_control; | |
617 | if (phy->speed == 1000) | |
618 | mac_control |= GIGABITEN; | |
619 | if (phy->duplex == DUPLEX_FULL) | |
620 | mac_control |= FULLDUPLEXEN; | |
621 | if (phy->speed == 100) | |
622 | mac_control |= MIIEN; | |
623 | } | |
624 | ||
625 | if (mac_control == slave->mac_control) | |
626 | return; | |
627 | ||
628 | if (mac_control) { | |
629 | printf("link up on port %d, speed %d, %s duplex\n", | |
630 | slave->slave_num, phy->speed, | |
631 | (phy->duplex == DUPLEX_FULL) ? "full" : "half"); | |
632 | } else { | |
633 | printf("link down on port %d\n", slave->slave_num); | |
634 | } | |
635 | ||
636 | __raw_writel(mac_control, &slave->sliver->mac_control); | |
637 | slave->mac_control = mac_control; | |
638 | } | |
639 | ||
640 | static int cpsw_update_link(struct cpsw_priv *priv) | |
641 | { | |
642 | int link = 0; | |
643 | struct cpsw_slave *slave; | |
644 | ||
7a022753 | 645 | for_active_slave(slave, priv) |
2b62997c | 646 | cpsw_slave_update_link(slave, priv, &link); |
48ec5291 | 647 | |
5a834c1f | 648 | return link; |
48ec5291 M |
649 | } |
650 | ||
2b62997c CC |
651 | static inline u32 cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num) |
652 | { | |
653 | if (priv->host_port == 0) | |
654 | return slave_num + 1; | |
655 | else | |
656 | return slave_num; | |
657 | } | |
658 | ||
659 | static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv) | |
660 | { | |
661 | u32 slave_port; | |
662 | ||
663 | setbit_and_wait_for_clear32(&slave->sliver->soft_reset); | |
664 | ||
665 | /* setup priority mapping */ | |
666 | __raw_writel(0x76543210, &slave->sliver->rx_pri_map); | |
667 | __raw_writel(0x33221100, &slave->regs->tx_pri_map); | |
668 | ||
669 | /* setup max packet size, and mac address */ | |
670 | __raw_writel(PKT_MAX, &slave->sliver->rx_maxlen); | |
671 | cpsw_set_slave_mac(slave, priv); | |
672 | ||
673 | slave->mac_control = 0; /* no link yet */ | |
674 | ||
675 | /* enable forwarding */ | |
676 | slave_port = cpsw_get_slave_port(priv, slave->slave_num); | |
677 | cpsw_ale_port_state(priv, slave_port, ALE_PORT_STATE_FORWARD); | |
678 | ||
0adb5b76 | 679 | cpsw_ale_add_mcast(priv, net_bcast_ethaddr, 1 << slave_port); |
48ec5291 | 680 | |
9c653aad | 681 | priv->phy_mask |= 1 << slave->data->phy_addr; |
2b62997c CC |
682 | } |
683 | ||
684 | static struct cpdma_desc *cpdma_desc_alloc(struct cpsw_priv *priv) | |
685 | { | |
686 | struct cpdma_desc *desc = priv->desc_free; | |
687 | ||
688 | if (desc) | |
689 | priv->desc_free = desc_read_ptr(desc, hw_next); | |
690 | return desc; | |
691 | } | |
692 | ||
693 | static void cpdma_desc_free(struct cpsw_priv *priv, struct cpdma_desc *desc) | |
694 | { | |
695 | if (desc) { | |
696 | desc_write(desc, hw_next, priv->desc_free); | |
697 | priv->desc_free = desc; | |
698 | } | |
699 | } | |
700 | ||
701 | static int cpdma_submit(struct cpsw_priv *priv, struct cpdma_chan *chan, | |
702 | void *buffer, int len) | |
703 | { | |
704 | struct cpdma_desc *desc, *prev; | |
705 | u32 mode; | |
706 | ||
707 | desc = cpdma_desc_alloc(priv); | |
708 | if (!desc) | |
709 | return -ENOMEM; | |
710 | ||
711 | if (len < PKT_MIN) | |
712 | len = PKT_MIN; | |
713 | ||
714 | mode = CPDMA_DESC_OWNER | CPDMA_DESC_SOP | CPDMA_DESC_EOP; | |
715 | ||
716 | desc_write(desc, hw_next, 0); | |
717 | desc_write(desc, hw_buffer, buffer); | |
718 | desc_write(desc, hw_len, len); | |
719 | desc_write(desc, hw_mode, mode | len); | |
720 | desc_write(desc, sw_buffer, buffer); | |
721 | desc_write(desc, sw_len, len); | |
722 | ||
723 | if (!chan->head) { | |
724 | /* simple case - first packet enqueued */ | |
725 | chan->head = desc; | |
726 | chan->tail = desc; | |
727 | chan_write(chan, hdp, desc); | |
728 | goto done; | |
729 | } | |
730 | ||
731 | /* not the first packet - enqueue at the tail */ | |
732 | prev = chan->tail; | |
733 | desc_write(prev, hw_next, desc); | |
734 | chan->tail = desc; | |
735 | ||
736 | /* next check if EOQ has been triggered already */ | |
737 | if (desc_read(prev, hw_mode) & CPDMA_DESC_EOQ) | |
738 | chan_write(chan, hdp, desc); | |
739 | ||
740 | done: | |
741 | if (chan->rxfree) | |
742 | chan_write(chan, rxfree, 1); | |
743 | return 0; | |
744 | } | |
745 | ||
746 | static int cpdma_process(struct cpsw_priv *priv, struct cpdma_chan *chan, | |
747 | void **buffer, int *len) | |
748 | { | |
749 | struct cpdma_desc *desc = chan->head; | |
750 | u32 status; | |
751 | ||
752 | if (!desc) | |
753 | return -ENOENT; | |
754 | ||
755 | status = desc_read(desc, hw_mode); | |
756 | ||
757 | if (len) | |
758 | *len = status & 0x7ff; | |
759 | ||
760 | if (buffer) | |
761 | *buffer = desc_read_ptr(desc, sw_buffer); | |
762 | ||
763 | if (status & CPDMA_DESC_OWNER) { | |
764 | if (chan_read(chan, hdp) == 0) { | |
765 | if (desc_read(desc, hw_mode) & CPDMA_DESC_OWNER) | |
766 | chan_write(chan, hdp, desc); | |
767 | } | |
768 | ||
769 | return -EBUSY; | |
770 | } | |
771 | ||
772 | chan->head = desc_read_ptr(desc, hw_next); | |
773 | chan_write(chan, cp, desc); | |
774 | ||
775 | cpdma_desc_free(priv, desc); | |
776 | return 0; | |
777 | } | |
778 | ||
bcd5eedf | 779 | static int _cpsw_init(struct cpsw_priv *priv, u8 *enetaddr) |
2b62997c | 780 | { |
2b62997c CC |
781 | struct cpsw_slave *slave; |
782 | int i, ret; | |
783 | ||
784 | /* soft reset the controller and initialize priv */ | |
785 | setbit_and_wait_for_clear32(&priv->regs->soft_reset); | |
786 | ||
787 | /* initialize and reset the address lookup engine */ | |
788 | cpsw_ale_enable(priv, 1); | |
789 | cpsw_ale_clear(priv, 1); | |
790 | cpsw_ale_vlan_aware(priv, 0); /* vlan unaware mode */ | |
791 | ||
792 | /* setup host port priority mapping */ | |
793 | __raw_writel(0x76543210, &priv->host_port_regs->cpdma_tx_pri_map); | |
794 | __raw_writel(0, &priv->host_port_regs->cpdma_rx_chan_map); | |
795 | ||
796 | /* disable priority elevation and enable statistics on all ports */ | |
797 | __raw_writel(0, &priv->regs->ptype); | |
798 | ||
799 | /* enable statistics collection only on the host port */ | |
800 | __raw_writel(BIT(priv->host_port), &priv->regs->stat_port_en); | |
454ac635 | 801 | __raw_writel(0x7, &priv->regs->stat_port_en); |
2b62997c CC |
802 | |
803 | cpsw_ale_port_state(priv, priv->host_port, ALE_PORT_STATE_FORWARD); | |
804 | ||
bcd5eedf | 805 | cpsw_ale_add_ucast(priv, enetaddr, priv->host_port, ALE_SECURE); |
0adb5b76 | 806 | cpsw_ale_add_mcast(priv, net_bcast_ethaddr, 1 << priv->host_port); |
2b62997c | 807 | |
7a022753 | 808 | for_active_slave(slave, priv) |
2b62997c CC |
809 | cpsw_slave_init(slave, priv); |
810 | ||
811 | cpsw_update_link(priv); | |
812 | ||
813 | /* init descriptor pool */ | |
814 | for (i = 0; i < NUM_DESCS; i++) { | |
815 | desc_write(&priv->descs[i], hw_next, | |
816 | (i == (NUM_DESCS - 1)) ? 0 : &priv->descs[i+1]); | |
817 | } | |
818 | priv->desc_free = &priv->descs[0]; | |
819 | ||
820 | /* initialize channels */ | |
821 | if (priv->data.version == CPSW_CTRL_VERSION_2) { | |
822 | memset(&priv->rx_chan, 0, sizeof(struct cpdma_chan)); | |
823 | priv->rx_chan.hdp = priv->dma_regs + CPDMA_RXHDP_VER2; | |
824 | priv->rx_chan.cp = priv->dma_regs + CPDMA_RXCP_VER2; | |
825 | priv->rx_chan.rxfree = priv->dma_regs + CPDMA_RXFREE; | |
826 | ||
827 | memset(&priv->tx_chan, 0, sizeof(struct cpdma_chan)); | |
828 | priv->tx_chan.hdp = priv->dma_regs + CPDMA_TXHDP_VER2; | |
829 | priv->tx_chan.cp = priv->dma_regs + CPDMA_TXCP_VER2; | |
830 | } else { | |
831 | memset(&priv->rx_chan, 0, sizeof(struct cpdma_chan)); | |
832 | priv->rx_chan.hdp = priv->dma_regs + CPDMA_RXHDP_VER1; | |
833 | priv->rx_chan.cp = priv->dma_regs + CPDMA_RXCP_VER1; | |
834 | priv->rx_chan.rxfree = priv->dma_regs + CPDMA_RXFREE; | |
835 | ||
836 | memset(&priv->tx_chan, 0, sizeof(struct cpdma_chan)); | |
837 | priv->tx_chan.hdp = priv->dma_regs + CPDMA_TXHDP_VER1; | |
838 | priv->tx_chan.cp = priv->dma_regs + CPDMA_TXCP_VER1; | |
839 | } | |
840 | ||
841 | /* clear dma state */ | |
842 | setbit_and_wait_for_clear32(priv->dma_regs + CPDMA_SOFTRESET); | |
843 | ||
844 | if (priv->data.version == CPSW_CTRL_VERSION_2) { | |
845 | for (i = 0; i < priv->data.channels; i++) { | |
846 | __raw_writel(0, priv->dma_regs + CPDMA_RXHDP_VER2 + 4 | |
847 | * i); | |
848 | __raw_writel(0, priv->dma_regs + CPDMA_RXFREE + 4 | |
849 | * i); | |
850 | __raw_writel(0, priv->dma_regs + CPDMA_RXCP_VER2 + 4 | |
851 | * i); | |
852 | __raw_writel(0, priv->dma_regs + CPDMA_TXHDP_VER2 + 4 | |
853 | * i); | |
854 | __raw_writel(0, priv->dma_regs + CPDMA_TXCP_VER2 + 4 | |
855 | * i); | |
856 | } | |
857 | } else { | |
858 | for (i = 0; i < priv->data.channels; i++) { | |
859 | __raw_writel(0, priv->dma_regs + CPDMA_RXHDP_VER1 + 4 | |
860 | * i); | |
861 | __raw_writel(0, priv->dma_regs + CPDMA_RXFREE + 4 | |
862 | * i); | |
863 | __raw_writel(0, priv->dma_regs + CPDMA_RXCP_VER1 + 4 | |
864 | * i); | |
865 | __raw_writel(0, priv->dma_regs + CPDMA_TXHDP_VER1 + 4 | |
866 | * i); | |
867 | __raw_writel(0, priv->dma_regs + CPDMA_TXCP_VER1 + 4 | |
868 | * i); | |
869 | ||
870 | } | |
871 | } | |
872 | ||
873 | __raw_writel(1, priv->dma_regs + CPDMA_TXCONTROL); | |
874 | __raw_writel(1, priv->dma_regs + CPDMA_RXCONTROL); | |
875 | ||
876 | /* submit rx descs */ | |
877 | for (i = 0; i < PKTBUFSRX; i++) { | |
1fd92db8 | 878 | ret = cpdma_submit(priv, &priv->rx_chan, net_rx_packets[i], |
2b62997c CC |
879 | PKTSIZE); |
880 | if (ret < 0) { | |
881 | printf("error %d submitting rx desc\n", ret); | |
882 | break; | |
883 | } | |
884 | } | |
885 | ||
886 | return 0; | |
887 | } | |
888 | ||
bcd5eedf | 889 | static void _cpsw_halt(struct cpsw_priv *priv) |
2b62997c | 890 | { |
2b62997c CC |
891 | writel(0, priv->dma_regs + CPDMA_TXCONTROL); |
892 | writel(0, priv->dma_regs + CPDMA_RXCONTROL); | |
893 | ||
894 | /* soft reset the controller and initialize priv */ | |
895 | setbit_and_wait_for_clear32(&priv->regs->soft_reset); | |
896 | ||
897 | /* clear dma state */ | |
898 | setbit_and_wait_for_clear32(priv->dma_regs + CPDMA_SOFTRESET); | |
899 | ||
2b62997c CC |
900 | } |
901 | ||
bcd5eedf | 902 | static int _cpsw_send(struct cpsw_priv *priv, void *packet, int length) |
2b62997c | 903 | { |
2b62997c CC |
904 | void *buffer; |
905 | int len; | |
906 | int timeout = CPDMA_TIMEOUT; | |
907 | ||
2b62997c CC |
908 | flush_dcache_range((unsigned long)packet, |
909 | (unsigned long)packet + length); | |
910 | ||
911 | /* first reap completed packets */ | |
912 | while (timeout-- && | |
913 | (cpdma_process(priv, &priv->tx_chan, &buffer, &len) >= 0)) | |
914 | ; | |
915 | ||
916 | if (timeout == -1) { | |
917 | printf("cpdma_process timeout\n"); | |
918 | return -ETIMEDOUT; | |
919 | } | |
920 | ||
921 | return cpdma_submit(priv, &priv->tx_chan, packet, length); | |
922 | } | |
923 | ||
bcd5eedf | 924 | static int _cpsw_recv(struct cpsw_priv *priv, uchar **pkt) |
2b62997c | 925 | { |
2b62997c CC |
926 | void *buffer; |
927 | int len; | |
bcd5eedf | 928 | int ret = -EAGAIN; |
2b62997c | 929 | |
bcd5eedf M |
930 | ret = cpdma_process(priv, &priv->rx_chan, &buffer, &len); |
931 | if (ret < 0) | |
932 | return ret; | |
2b62997c | 933 | |
bcd5eedf M |
934 | invalidate_dcache_range((unsigned long)buffer, |
935 | (unsigned long)buffer + PKTSIZE_ALIGN); | |
936 | *pkt = buffer; | |
937 | ||
938 | return len; | |
2b62997c CC |
939 | } |
940 | ||
941 | static void cpsw_slave_setup(struct cpsw_slave *slave, int slave_num, | |
942 | struct cpsw_priv *priv) | |
943 | { | |
944 | void *regs = priv->regs; | |
945 | struct cpsw_slave_data *data = priv->data.slave_data + slave_num; | |
946 | slave->slave_num = slave_num; | |
947 | slave->data = data; | |
948 | slave->regs = regs + data->slave_reg_ofs; | |
949 | slave->sliver = regs + data->sliver_reg_ofs; | |
950 | } | |
951 | ||
bcd5eedf | 952 | static int cpsw_phy_init(struct cpsw_priv *priv, struct cpsw_slave *slave) |
2b62997c | 953 | { |
2b62997c | 954 | struct phy_device *phydev; |
ef59bb7c | 955 | u32 supported = PHY_GBIT_FEATURES; |
2b62997c | 956 | |
cdd0729e | 957 | phydev = phy_connect(priv->bus, |
9c653aad | 958 | slave->data->phy_addr, |
bcd5eedf | 959 | priv->dev, |
cdd0729e | 960 | slave->data->phy_if); |
2b62997c | 961 | |
93ff2552 HS |
962 | if (!phydev) |
963 | return -1; | |
964 | ||
2b62997c CC |
965 | phydev->supported &= supported; |
966 | phydev->advertising = phydev->supported; | |
967 | ||
968 | priv->phydev = phydev; | |
969 | phy_config(phydev); | |
970 | ||
971 | return 1; | |
972 | } | |
973 | ||
bcd5eedf | 974 | int _cpsw_register(struct cpsw_priv *priv) |
2b62997c | 975 | { |
2b62997c | 976 | struct cpsw_slave *slave; |
bcd5eedf | 977 | struct cpsw_platform_data *data = &priv->data; |
2b62997c | 978 | void *regs = (void *)data->cpsw_base; |
2b62997c CC |
979 | |
980 | priv->slaves = malloc(sizeof(struct cpsw_slave) * data->slaves); | |
981 | if (!priv->slaves) { | |
2b62997c CC |
982 | return -ENOMEM; |
983 | } | |
984 | ||
2b62997c CC |
985 | priv->host_port = data->host_port_num; |
986 | priv->regs = regs; | |
987 | priv->host_port_regs = regs + data->host_port_reg_ofs; | |
988 | priv->dma_regs = regs + data->cpdma_reg_ofs; | |
989 | priv->ale_regs = regs + data->ale_reg_ofs; | |
2bf36ac6 | 990 | priv->descs = (void *)regs + data->bd_ram_ofs; |
2b62997c CC |
991 | |
992 | int idx = 0; | |
993 | ||
994 | for_each_slave(slave, priv) { | |
995 | cpsw_slave_setup(slave, idx, priv); | |
996 | idx = idx + 1; | |
997 | } | |
998 | ||
bcd5eedf M |
999 | cpsw_mdio_init(priv->dev->name, data->mdio_base, data->mdio_div); |
1000 | priv->bus = miiphy_get_dev_by_name(priv->dev->name); | |
1001 | for_active_slave(slave, priv) | |
1002 | cpsw_phy_init(priv, slave); | |
1003 | ||
1004 | return 0; | |
1005 | } | |
1006 | ||
4cc77895 | 1007 | #ifndef CONFIG_DM_ETH |
bcd5eedf M |
1008 | static int cpsw_init(struct eth_device *dev, bd_t *bis) |
1009 | { | |
1010 | struct cpsw_priv *priv = dev->priv; | |
1011 | ||
1012 | return _cpsw_init(priv, dev->enetaddr); | |
1013 | } | |
1014 | ||
1015 | static void cpsw_halt(struct eth_device *dev) | |
1016 | { | |
1017 | struct cpsw_priv *priv = dev->priv; | |
1018 | ||
1019 | return _cpsw_halt(priv); | |
1020 | } | |
1021 | ||
1022 | static int cpsw_send(struct eth_device *dev, void *packet, int length) | |
1023 | { | |
1024 | struct cpsw_priv *priv = dev->priv; | |
1025 | ||
1026 | return _cpsw_send(priv, packet, length); | |
1027 | } | |
1028 | ||
1029 | static int cpsw_recv(struct eth_device *dev) | |
1030 | { | |
1031 | struct cpsw_priv *priv = dev->priv; | |
1032 | uchar *pkt = NULL; | |
1033 | int len; | |
1034 | ||
1035 | len = _cpsw_recv(priv, &pkt); | |
1036 | ||
1037 | if (len > 0) { | |
1038 | net_process_received_packet(pkt, len); | |
1039 | cpdma_submit(priv, &priv->rx_chan, pkt, PKTSIZE); | |
1040 | } | |
1041 | ||
1042 | return len; | |
1043 | } | |
1044 | ||
1045 | int cpsw_register(struct cpsw_platform_data *data) | |
1046 | { | |
1047 | struct cpsw_priv *priv; | |
1048 | struct eth_device *dev; | |
1049 | int ret; | |
1050 | ||
1051 | dev = calloc(sizeof(*dev), 1); | |
1052 | if (!dev) | |
1053 | return -ENOMEM; | |
1054 | ||
1055 | priv = calloc(sizeof(*priv), 1); | |
1056 | if (!priv) { | |
1057 | free(dev); | |
1058 | return -ENOMEM; | |
1059 | } | |
1060 | ||
1061 | priv->dev = dev; | |
1062 | priv->data = *data; | |
1063 | ||
2b62997c CC |
1064 | strcpy(dev->name, "cpsw"); |
1065 | dev->iobase = 0; | |
1066 | dev->init = cpsw_init; | |
1067 | dev->halt = cpsw_halt; | |
1068 | dev->send = cpsw_send; | |
1069 | dev->recv = cpsw_recv; | |
1070 | dev->priv = priv; | |
1071 | ||
1072 | eth_register(dev); | |
1073 | ||
bcd5eedf M |
1074 | ret = _cpsw_register(priv); |
1075 | if (ret < 0) { | |
1076 | eth_unregister(dev); | |
1077 | free(dev); | |
1078 | free(priv); | |
1079 | return ret; | |
1080 | } | |
2b62997c CC |
1081 | |
1082 | return 1; | |
1083 | } | |
4cc77895 M |
1084 | #else |
1085 | static int cpsw_eth_start(struct udevice *dev) | |
1086 | { | |
1087 | struct eth_pdata *pdata = dev_get_platdata(dev); | |
1088 | struct cpsw_priv *priv = dev_get_priv(dev); | |
1089 | ||
1090 | return _cpsw_init(priv, pdata->enetaddr); | |
1091 | } | |
1092 | ||
1093 | static int cpsw_eth_send(struct udevice *dev, void *packet, int length) | |
1094 | { | |
1095 | struct cpsw_priv *priv = dev_get_priv(dev); | |
1096 | ||
1097 | return _cpsw_send(priv, packet, length); | |
1098 | } | |
1099 | ||
1100 | static int cpsw_eth_recv(struct udevice *dev, int flags, uchar **packetp) | |
1101 | { | |
1102 | struct cpsw_priv *priv = dev_get_priv(dev); | |
1103 | ||
1104 | return _cpsw_recv(priv, packetp); | |
1105 | } | |
1106 | ||
1107 | static int cpsw_eth_free_pkt(struct udevice *dev, uchar *packet, | |
1108 | int length) | |
1109 | { | |
1110 | struct cpsw_priv *priv = dev_get_priv(dev); | |
1111 | ||
1112 | return cpdma_submit(priv, &priv->rx_chan, packet, PKTSIZE); | |
1113 | } | |
1114 | ||
1115 | static void cpsw_eth_stop(struct udevice *dev) | |
1116 | { | |
1117 | struct cpsw_priv *priv = dev_get_priv(dev); | |
1118 | ||
1119 | return _cpsw_halt(priv); | |
1120 | } | |
1121 | ||
1122 | ||
1123 | static int cpsw_eth_probe(struct udevice *dev) | |
1124 | { | |
1125 | struct cpsw_priv *priv = dev_get_priv(dev); | |
1126 | ||
1127 | priv->dev = dev; | |
1128 | ||
1129 | return _cpsw_register(priv); | |
1130 | } | |
1131 | ||
1132 | static const struct eth_ops cpsw_eth_ops = { | |
1133 | .start = cpsw_eth_start, | |
1134 | .send = cpsw_eth_send, | |
1135 | .recv = cpsw_eth_recv, | |
1136 | .free_pkt = cpsw_eth_free_pkt, | |
1137 | .stop = cpsw_eth_stop, | |
1138 | }; | |
1139 | ||
1140 | static int cpsw_eth_ofdata_to_platdata(struct udevice *dev) | |
1141 | { | |
1142 | struct eth_pdata *pdata = dev_get_platdata(dev); | |
1143 | struct cpsw_priv *priv = dev_get_priv(dev); | |
1144 | const char *phy_mode; | |
1145 | const void *fdt = gd->fdt_blob; | |
1146 | int node = dev->of_offset; | |
1147 | int subnode; | |
1148 | int slave_index = 0; | |
1149 | uint32_t mac_hi, mac_lo; | |
1150 | fdt32_t gmii = 0; | |
1151 | int active_slave; | |
1152 | ||
1153 | pdata->iobase = dev_get_addr(dev); | |
1154 | priv->data.version = CPSW_CTRL_VERSION_2; | |
1155 | priv->data.bd_ram_ofs = CPSW_BD_OFFSET; | |
1156 | priv->data.ale_reg_ofs = CPSW_ALE_OFFSET; | |
1157 | priv->data.cpdma_reg_ofs = CPSW_CPDMA_OFFSET; | |
1158 | priv->data.mdio_div = CPSW_MDIO_DIV; | |
1159 | priv->data.host_port_reg_ofs = CPSW_HOST_PORT_OFFSET, | |
1160 | ||
1161 | pdata->phy_interface = -1; | |
1162 | ||
1163 | priv->data.cpsw_base = pdata->iobase; | |
1164 | priv->data.channels = fdtdec_get_int(fdt, node, "cpdma_channels", -1); | |
1165 | if (priv->data.channels <= 0) { | |
1166 | printf("error: cpdma_channels not found in dt\n"); | |
1167 | return -ENOENT; | |
1168 | } | |
1169 | ||
1170 | priv->data.slaves = fdtdec_get_int(fdt, node, "slaves", -1); | |
1171 | if (priv->data.slaves <= 0) { | |
1172 | printf("error: slaves not found in dt\n"); | |
1173 | return -ENOENT; | |
1174 | } | |
1175 | priv->data.slave_data = malloc(sizeof(struct cpsw_slave_data) * | |
1176 | priv->data.slaves); | |
1177 | ||
1178 | priv->data.ale_entries = fdtdec_get_int(fdt, node, "ale_entries", -1); | |
1179 | if (priv->data.ale_entries <= 0) { | |
1180 | printf("error: ale_entries not found in dt\n"); | |
1181 | return -ENOENT; | |
1182 | } | |
1183 | ||
1184 | priv->data.bd_ram_ofs = fdtdec_get_int(fdt, node, "bd_ram_size", -1); | |
1185 | if (priv->data.bd_ram_ofs <= 0) { | |
1186 | printf("error: bd_ram_size not found in dt\n"); | |
1187 | return -ENOENT; | |
1188 | } | |
1189 | ||
1190 | priv->data.mac_control = fdtdec_get_int(fdt, node, "mac_control", -1); | |
1191 | if (priv->data.mac_control <= 0) { | |
1192 | printf("error: ale_entries not found in dt\n"); | |
1193 | return -ENOENT; | |
1194 | } | |
1195 | ||
1196 | active_slave = fdtdec_get_int(fdt, node, "active_slave", 0); | |
1197 | priv->data.active_slave = active_slave; | |
1198 | ||
1199 | fdt_for_each_subnode(fdt, subnode, node) { | |
1200 | int len; | |
1201 | const char *name; | |
1202 | ||
1203 | name = fdt_get_name(fdt, subnode, &len); | |
1204 | if (!strncmp(name, "mdio", 4)) { | |
1205 | priv->data.mdio_base = fdtdec_get_addr(fdt, subnode, | |
1206 | "reg"); | |
1207 | } | |
1208 | ||
1209 | if (!strncmp(name, "slave", 5)) { | |
1210 | u32 phy_id[2]; | |
1211 | ||
1212 | if (slave_index >= priv->data.slaves) { | |
1213 | printf("error: num slaves and slave nodes did not match\n"); | |
1214 | return -EINVAL; | |
1215 | } | |
1216 | phy_mode = fdt_getprop(fdt, subnode, "phy-mode", NULL); | |
1217 | if (phy_mode) | |
1218 | priv->data.slave_data[slave_index].phy_if = | |
1219 | phy_get_interface_by_name(phy_mode); | |
1220 | fdtdec_get_int_array(fdt, subnode, "phy_id", phy_id, 2); | |
1221 | priv->data.slave_data[slave_index].phy_addr = phy_id[1]; | |
1222 | slave_index++; | |
1223 | } | |
1224 | ||
1225 | if (!strncmp(name, "cpsw-phy-sel", 12)) { | |
1226 | priv->data.gmii_sel = fdtdec_get_addr(fdt, subnode, | |
1227 | "reg"); | |
1228 | } | |
1229 | } | |
1230 | ||
1231 | priv->data.slave_data[0].slave_reg_ofs = CPSW_SLAVE0_OFFSET; | |
1232 | priv->data.slave_data[0].sliver_reg_ofs = CPSW_SLIVER0_OFFSET; | |
1233 | ||
1234 | if (priv->data.slaves == 2) { | |
1235 | priv->data.slave_data[1].slave_reg_ofs = CPSW_SLAVE1_OFFSET; | |
1236 | priv->data.slave_data[1].sliver_reg_ofs = CPSW_SLIVER1_OFFSET; | |
1237 | } | |
1238 | ||
1239 | subnode = fdtdec_lookup_phandle(fdt, node, "syscon"); | |
1240 | priv->data.mac_id = fdt_translate_address((void *)fdt, subnode, &gmii); | |
1241 | priv->data.mac_id += AM335X_GMII_SEL_OFFSET; | |
1242 | priv->data.mac_id += active_slave * 8; | |
1243 | ||
1244 | /* try reading mac address from efuse */ | |
1245 | mac_lo = readl(priv->data.mac_id); | |
1246 | mac_hi = readl(priv->data.mac_id + 4); | |
1247 | pdata->enetaddr[0] = mac_hi & 0xFF; | |
1248 | pdata->enetaddr[1] = (mac_hi & 0xFF00) >> 8; | |
1249 | pdata->enetaddr[2] = (mac_hi & 0xFF0000) >> 16; | |
1250 | pdata->enetaddr[3] = (mac_hi & 0xFF000000) >> 24; | |
1251 | pdata->enetaddr[4] = mac_lo & 0xFF; | |
1252 | pdata->enetaddr[5] = (mac_lo & 0xFF00) >> 8; | |
1253 | ||
1254 | pdata->phy_interface = priv->data.slave_data[active_slave].phy_if; | |
1255 | if (pdata->phy_interface == -1) { | |
1256 | debug("%s: Invalid PHY interface '%s'\n", __func__, phy_mode); | |
1257 | return -EINVAL; | |
1258 | } | |
1259 | switch (pdata->phy_interface) { | |
1260 | case PHY_INTERFACE_MODE_MII: | |
1261 | writel(MII_MODE_ENABLE, priv->data.gmii_sel); | |
1262 | break; | |
1263 | case PHY_INTERFACE_MODE_RMII: | |
1264 | writel(RMII_MODE_ENABLE, priv->data.gmii_sel); | |
1265 | break; | |
1266 | case PHY_INTERFACE_MODE_RGMII: | |
1267 | case PHY_INTERFACE_MODE_RGMII_ID: | |
1268 | case PHY_INTERFACE_MODE_RGMII_RXID: | |
1269 | case PHY_INTERFACE_MODE_RGMII_TXID: | |
1270 | writel(RGMII_MODE_ENABLE, priv->data.gmii_sel); | |
1271 | break; | |
1272 | } | |
1273 | return 0; | |
1274 | } | |
1275 | ||
1276 | ||
1277 | static const struct udevice_id cpsw_eth_ids[] = { | |
1278 | { .compatible = "ti,cpsw" }, | |
1279 | { .compatible = "ti,am335x-cpsw" }, | |
1280 | { } | |
1281 | }; | |
1282 | ||
1283 | U_BOOT_DRIVER(eth_cpsw) = { | |
1284 | .name = "eth_cpsw", | |
1285 | .id = UCLASS_ETH, | |
1286 | .of_match = cpsw_eth_ids, | |
1287 | .ofdata_to_platdata = cpsw_eth_ofdata_to_platdata, | |
1288 | .probe = cpsw_eth_probe, | |
1289 | .ops = &cpsw_eth_ops, | |
1290 | .priv_auto_alloc_size = sizeof(struct cpsw_priv), | |
1291 | .platdata_auto_alloc_size = sizeof(struct eth_pdata), | |
1292 | .flags = DM_FLAG_ALLOC_PRIV_DMA, | |
1293 | }; | |
1294 | #endif /* CONFIG_DM_ETH */ |