]>
Commit | Line | Data |
---|---|---|
42d1f039 WD |
1 | /* |
2 | * tsec.c | |
97d80fc3 | 3 | * Freescale Three Speed Ethernet Controller driver |
42d1f039 WD |
4 | * |
5 | * This software may be used and distributed according to the | |
6 | * terms of the GNU Public License, Version 2, incorporated | |
7 | * herein by reference. | |
8 | * | |
97d80fc3 | 9 | * Copyright 2004 Freescale Semiconductor. |
42d1f039 | 10 | * (C) Copyright 2003, Motorola, Inc. |
42d1f039 WD |
11 | * author Andy Fleming |
12 | * | |
13 | */ | |
14 | ||
15 | #include <config.h> | |
16 | #include <mpc85xx.h> | |
17 | #include <common.h> | |
18 | #include <malloc.h> | |
19 | #include <net.h> | |
20 | #include <command.h> | |
21 | ||
22 | #if defined(CONFIG_TSEC_ENET) | |
23 | #include "tsec.h" | |
24 | ||
25 | #define TX_BUF_CNT 2 | |
26 | ||
42d1f039 WD |
27 | static uint rxIdx; /* index of the current RX buffer */ |
28 | static uint txIdx; /* index of the current TX buffer */ | |
29 | ||
30 | typedef volatile struct rtxbd { | |
31 | txbd8_t txbd[TX_BUF_CNT]; | |
32 | rxbd8_t rxbd[PKTBUFSRX]; | |
33 | } RTXBD; | |
34 | ||
97d80fc3 WD |
35 | struct tsec_info_struct { |
36 | unsigned int phyaddr; | |
d9b94f28 | 37 | u32 flags; |
97d80fc3 WD |
38 | unsigned int phyregidx; |
39 | }; | |
40 | ||
41 | ||
42 | /* The tsec_info structure contains 3 values which the | |
43 | * driver uses to determine how to operate a given ethernet | |
44 | * device. For now, the structure is initialized with the | |
45 | * knowledge that all current implementations have 2 TSEC | |
46 | * devices, and one FEC. The information needed is: | |
47 | * phyaddr - The address of the PHY which is attached to | |
9d46ea4a | 48 | * the given device. |
97d80fc3 | 49 | * |
d9b94f28 JL |
50 | * flags - This variable indicates whether the device |
51 | * supports gigabit speed ethernet, and whether it should be | |
52 | * in reduced mode. | |
97d80fc3 WD |
53 | * |
54 | * phyregidx - This variable specifies which ethernet device | |
9d46ea4a WD |
55 | * controls the MII Management registers which are connected |
56 | * to the PHY. For 8540/8560, only TSEC1 (index 0) has | |
57 | * access to the PHYs, so all of the entries have "0". | |
97d80fc3 WD |
58 | * |
59 | * The values specified in the table are taken from the board's | |
60 | * config file in include/configs/. When implementing a new | |
61 | * board with ethernet capability, it is necessary to define: | |
62 | * TSEC1_PHY_ADDR | |
63 | * TSEC1_PHYIDX | |
64 | * TSEC2_PHY_ADDR | |
65 | * TSEC2_PHYIDX | |
66 | * | |
67 | * and for 8560: | |
68 | * FEC_PHY_ADDR | |
69 | * FEC_PHYIDX | |
70 | */ | |
71 | static struct tsec_info_struct tsec_info[] = { | |
f046ccd1 | 72 | #if defined(CONFIG_MPC85XX_TSEC1) || defined(CONFIG_MPC83XX_TSEC1) |
d9b94f28 | 73 | {TSEC1_PHY_ADDR, TSEC_GIGABIT, TSEC1_PHYIDX}, |
9d46ea4a WD |
74 | #else |
75 | { 0, 0, 0}, | |
97d80fc3 | 76 | #endif |
f046ccd1 | 77 | #if defined(CONFIG_MPC85XX_TSEC2) || defined(CONFIG_MPC83XX_TSEC2) |
d9b94f28 | 78 | {TSEC2_PHY_ADDR, TSEC_GIGABIT, TSEC2_PHYIDX}, |
9d46ea4a WD |
79 | #else |
80 | { 0, 0, 0}, | |
97d80fc3 WD |
81 | #endif |
82 | #ifdef CONFIG_MPC85XX_FEC | |
83 | {FEC_PHY_ADDR, 0, FEC_PHYIDX}, | |
9d46ea4a | 84 | #else |
f046ccd1 | 85 | # if defined(CONFIG_MPC85XX_TSEC3) || defined(CONFIG_MPC83XX_TSEC3) |
d9b94f28 JL |
86 | {TSEC3_PHY_ADDR, TSEC_GIGABIT | TSEC_REDUCED, TSEC3_PHYIDX}, |
87 | # else | |
9d46ea4a | 88 | { 0, 0, 0}, |
d9b94f28 | 89 | # endif |
f046ccd1 | 90 | # if defined(CONFIG_MPC85XX_TSEC4) || defined(CONFIG_MPC83XX_TSEC4) |
d9b94f28 JL |
91 | {TSEC4_PHY_ADDR, TSEC_REDUCED, TSEC4_PHYIDX}, |
92 | # else | |
93 | { 0, 0, 0}, | |
94 | # endif | |
97d80fc3 WD |
95 | #endif |
96 | }; | |
97 | ||
d9b94f28 | 98 | #define MAXCONTROLLERS (4) |
97d80fc3 WD |
99 | |
100 | static int relocated = 0; | |
101 | ||
102 | static struct tsec_private *privlist[MAXCONTROLLERS]; | |
103 | ||
42d1f039 WD |
104 | #ifdef __GNUC__ |
105 | static RTXBD rtx __attribute__ ((aligned(8))); | |
106 | #else | |
107 | #error "rtx must be 64-bit aligned" | |
108 | #endif | |
109 | ||
110 | static int tsec_send(struct eth_device* dev, volatile void *packet, int length); | |
111 | static int tsec_recv(struct eth_device* dev); | |
112 | static int tsec_init(struct eth_device* dev, bd_t * bd); | |
113 | static void tsec_halt(struct eth_device* dev); | |
97d80fc3 WD |
114 | static void init_registers(volatile tsec_t *regs); |
115 | static void startup_tsec(struct eth_device *dev); | |
116 | static int init_phy(struct eth_device *dev); | |
117 | void write_phy_reg(struct tsec_private *priv, uint regnum, uint value); | |
118 | uint read_phy_reg(struct tsec_private *priv, uint regnum); | |
119 | struct phy_info * get_phy_info(struct eth_device *dev); | |
120 | void phy_run_commands(struct tsec_private *priv, struct phy_cmd *cmd); | |
121 | static void adjust_link(struct eth_device *dev); | |
122 | static void relocate_cmds(void); | |
123 | ||
124 | /* Initialize device structure. Returns success if PHY | |
125 | * initialization succeeded (i.e. if it recognizes the PHY) | |
126 | */ | |
d9b94f28 | 127 | int tsec_initialize(bd_t *bis, int index, char *devname) |
42d1f039 WD |
128 | { |
129 | struct eth_device* dev; | |
130 | int i; | |
97d80fc3 | 131 | struct tsec_private *priv; |
42d1f039 WD |
132 | |
133 | dev = (struct eth_device*) malloc(sizeof *dev); | |
134 | ||
97d80fc3 | 135 | if(NULL == dev) |
42d1f039 WD |
136 | return 0; |
137 | ||
138 | memset(dev, 0, sizeof *dev); | |
139 | ||
97d80fc3 WD |
140 | priv = (struct tsec_private *) malloc(sizeof(*priv)); |
141 | ||
142 | if(NULL == priv) | |
143 | return 0; | |
144 | ||
145 | privlist[index] = priv; | |
146 | priv->regs = (volatile tsec_t *)(TSEC_BASE_ADDR + index*TSEC_SIZE); | |
147 | priv->phyregs = (volatile tsec_t *)(TSEC_BASE_ADDR + | |
148 | tsec_info[index].phyregidx*TSEC_SIZE); | |
149 | ||
150 | priv->phyaddr = tsec_info[index].phyaddr; | |
d9b94f28 | 151 | priv->flags = tsec_info[index].flags; |
97d80fc3 | 152 | |
d9b94f28 | 153 | sprintf(dev->name, devname); |
42d1f039 | 154 | dev->iobase = 0; |
97d80fc3 | 155 | dev->priv = priv; |
42d1f039 WD |
156 | dev->init = tsec_init; |
157 | dev->halt = tsec_halt; | |
158 | dev->send = tsec_send; | |
159 | dev->recv = tsec_recv; | |
160 | ||
161 | /* Tell u-boot to get the addr from the env */ | |
162 | for(i=0;i<6;i++) | |
163 | dev->enetaddr[i] = 0; | |
164 | ||
165 | eth_register(dev); | |
166 | ||
7abf0c58 | 167 | |
97d80fc3 WD |
168 | /* Reset the MAC */ |
169 | priv->regs->maccfg1 |= MACCFG1_SOFT_RESET; | |
170 | priv->regs->maccfg1 &= ~(MACCFG1_SOFT_RESET); | |
7abf0c58 | 171 | |
97d80fc3 WD |
172 | /* Try to initialize PHY here, and return */ |
173 | return init_phy(dev); | |
42d1f039 WD |
174 | } |
175 | ||
176 | ||
177 | /* Initializes data structures and registers for the controller, | |
9d46ea4a | 178 | * and brings the interface up. Returns the link status, meaning |
97d80fc3 WD |
179 | * that it returns success if the link is up, failure otherwise. |
180 | * This allows u-boot to find the first active controller. */ | |
42d1f039 WD |
181 | int tsec_init(struct eth_device* dev, bd_t * bd) |
182 | { | |
42d1f039 WD |
183 | uint tempval; |
184 | char tmpbuf[MAC_ADDR_LEN]; | |
185 | int i; | |
97d80fc3 WD |
186 | struct tsec_private *priv = (struct tsec_private *)dev->priv; |
187 | volatile tsec_t *regs = priv->regs; | |
42d1f039 WD |
188 | |
189 | /* Make sure the controller is stopped */ | |
190 | tsec_halt(dev); | |
191 | ||
97d80fc3 | 192 | /* Init MACCFG2. Defaults to GMII */ |
42d1f039 WD |
193 | regs->maccfg2 = MACCFG2_INIT_SETTINGS; |
194 | ||
195 | /* Init ECNTRL */ | |
196 | regs->ecntrl = ECNTRL_INIT_SETTINGS; | |
197 | ||
198 | /* Copy the station address into the address registers. | |
199 | * Backwards, because little endian MACS are dumb */ | |
200 | for(i=0;i<MAC_ADDR_LEN;i++) { | |
97d80fc3 | 201 | tmpbuf[MAC_ADDR_LEN - 1 - i] = dev->enetaddr[i]; |
42d1f039 WD |
202 | } |
203 | (uint)(regs->macstnaddr1) = *((uint *)(tmpbuf)); | |
204 | ||
205 | tempval = *((uint *)(tmpbuf +4)); | |
206 | ||
207 | (uint)(regs->macstnaddr2) = tempval; | |
208 | ||
42d1f039 WD |
209 | /* reset the indices to zero */ |
210 | rxIdx = 0; | |
211 | txIdx = 0; | |
212 | ||
213 | /* Clear out (for the most part) the other registers */ | |
214 | init_registers(regs); | |
215 | ||
216 | /* Ready the device for tx/rx */ | |
97d80fc3 | 217 | startup_tsec(dev); |
42d1f039 | 218 | |
97d80fc3 WD |
219 | /* If there's no link, fail */ |
220 | return priv->link; | |
42d1f039 WD |
221 | |
222 | } | |
223 | ||
224 | ||
97d80fc3 WD |
225 | /* Write value to the device's PHY through the registers |
226 | * specified in priv, modifying the register specified in regnum. | |
227 | * It will wait for the write to be done (or for a timeout to | |
228 | * expire) before exiting | |
229 | */ | |
230 | void write_phy_reg(struct tsec_private *priv, uint regnum, uint value) | |
231 | { | |
232 | volatile tsec_t *regbase = priv->phyregs; | |
233 | uint phyid = priv->phyaddr; | |
234 | int timeout=1000000; | |
235 | ||
236 | regbase->miimadd = (phyid << 8) | regnum; | |
237 | regbase->miimcon = value; | |
f046ccd1 | 238 | asm("sync"); |
97d80fc3 WD |
239 | |
240 | timeout=1000000; | |
241 | while((regbase->miimind & MIIMIND_BUSY) && timeout--); | |
242 | } | |
243 | ||
244 | ||
245 | /* Reads register regnum on the device's PHY through the | |
9d46ea4a | 246 | * registers specified in priv. It lowers and raises the read |
97d80fc3 WD |
247 | * command, and waits for the data to become valid (miimind |
248 | * notvalid bit cleared), and the bus to cease activity (miimind | |
249 | * busy bit cleared), and then returns the value | |
250 | */ | |
251 | uint read_phy_reg(struct tsec_private *priv, uint regnum) | |
42d1f039 WD |
252 | { |
253 | uint value; | |
97d80fc3 WD |
254 | volatile tsec_t *regbase = priv->phyregs; |
255 | uint phyid = priv->phyaddr; | |
42d1f039 | 256 | |
97d80fc3 WD |
257 | /* Put the address of the phy, and the register |
258 | * number into MIIMADD */ | |
259 | regbase->miimadd = (phyid << 8) | regnum; | |
42d1f039 WD |
260 | |
261 | /* Clear the command register, and wait */ | |
262 | regbase->miimcom = 0; | |
f046ccd1 | 263 | asm("sync"); |
42d1f039 WD |
264 | |
265 | /* Initiate a read command, and wait */ | |
266 | regbase->miimcom = MIIM_READ_COMMAND; | |
f046ccd1 | 267 | asm("sync"); |
42d1f039 WD |
268 | |
269 | /* Wait for the the indication that the read is done */ | |
270 | while((regbase->miimind & (MIIMIND_NOTVALID | MIIMIND_BUSY))); | |
271 | ||
272 | /* Grab the value read from the PHY */ | |
273 | value = regbase->miimstat; | |
274 | ||
275 | return value; | |
276 | } | |
277 | ||
97d80fc3 WD |
278 | |
279 | /* Discover which PHY is attached to the device, and configure it | |
280 | * properly. If the PHY is not recognized, then return 0 | |
281 | * (failure). Otherwise, return 1 | |
282 | */ | |
283 | static int init_phy(struct eth_device *dev) | |
42d1f039 | 284 | { |
97d80fc3 WD |
285 | struct tsec_private *priv = (struct tsec_private *)dev->priv; |
286 | struct phy_info *curphy; | |
42d1f039 WD |
287 | |
288 | /* Assign a Physical address to the TBI */ | |
3c2b3d45 | 289 | |
3dd7f0f0 WD |
290 | { |
291 | volatile tsec_t *regs = (volatile tsec_t *)(TSEC_BASE_ADDR); | |
292 | regs->tbipa = TBIPA_VALUE; | |
293 | regs = (volatile tsec_t *)(TSEC_BASE_ADDR + TSEC_SIZE); | |
294 | regs->tbipa = TBIPA_VALUE; | |
f046ccd1 | 295 | asm("sync"); |
3dd7f0f0 WD |
296 | } |
297 | ||
298 | /* Reset MII (due to new addresses) */ | |
299 | priv->phyregs->miimcfg = MIIMCFG_RESET; | |
f046ccd1 | 300 | asm("sync"); |
3dd7f0f0 | 301 | priv->phyregs->miimcfg = MIIMCFG_INIT_VALUE; |
f046ccd1 | 302 | asm("sync"); |
3dd7f0f0 | 303 | while(priv->phyregs->miimind & MIIMIND_BUSY); |
42d1f039 | 304 | |
97d80fc3 WD |
305 | if(0 == relocated) |
306 | relocate_cmds(); | |
42d1f039 | 307 | |
97d80fc3 WD |
308 | /* Get the cmd structure corresponding to the attached |
309 | * PHY */ | |
310 | curphy = get_phy_info(dev); | |
42d1f039 | 311 | |
97d80fc3 WD |
312 | if(NULL == curphy) { |
313 | printf("%s: No PHY found\n", dev->name); | |
42d1f039 | 314 | |
97d80fc3 WD |
315 | return 0; |
316 | } | |
42d1f039 | 317 | |
97d80fc3 | 318 | priv->phyinfo = curphy; |
42d1f039 | 319 | |
97d80fc3 | 320 | phy_run_commands(priv, priv->phyinfo->config); |
42d1f039 | 321 | |
97d80fc3 WD |
322 | return 1; |
323 | } | |
42d1f039 | 324 | |
42d1f039 | 325 | |
97d80fc3 WD |
326 | /* Returns which value to write to the control register. */ |
327 | /* For 10/100, the value is slightly different */ | |
328 | uint mii_cr_init(uint mii_reg, struct tsec_private *priv) | |
329 | { | |
d9b94f28 | 330 | if(priv->flags & TSEC_GIGABIT) |
97d80fc3 | 331 | return MIIM_CONTROL_INIT; |
42d1f039 | 332 | else |
97d80fc3 WD |
333 | return MIIM_CR_INIT; |
334 | } | |
42d1f039 | 335 | |
42d1f039 | 336 | |
97d80fc3 WD |
337 | /* Parse the status register for link, and then do |
338 | * auto-negotiation */ | |
339 | uint mii_parse_sr(uint mii_reg, struct tsec_private *priv) | |
340 | { | |
341 | uint timeout = TSEC_TIMEOUT; | |
42d1f039 | 342 | |
97d80fc3 WD |
343 | if(mii_reg & MIIM_STATUS_LINK) |
344 | priv->link = 1; | |
345 | else | |
346 | priv->link = 0; | |
42d1f039 | 347 | |
97d80fc3 WD |
348 | if(priv->link) { |
349 | while((!(mii_reg & MIIM_STATUS_AN_DONE)) && timeout--) | |
350 | mii_reg = read_phy_reg(priv, MIIM_STATUS); | |
42d1f039 WD |
351 | } |
352 | ||
97d80fc3 WD |
353 | return 0; |
354 | } | |
42d1f039 | 355 | |
42d1f039 | 356 | |
97d80fc3 WD |
357 | /* Parse the 88E1011's status register for speed and duplex |
358 | * information */ | |
359 | uint mii_parse_88E1011_psr(uint mii_reg, struct tsec_private *priv) | |
360 | { | |
361 | uint speed; | |
362 | ||
363 | if(mii_reg & MIIM_88E1011_PHYSTAT_DUPLEX) | |
364 | priv->duplexity = 1; | |
365 | else | |
366 | priv->duplexity = 0; | |
367 | ||
368 | speed = (mii_reg &MIIM_88E1011_PHYSTAT_SPEED); | |
369 | ||
370 | switch(speed) { | |
371 | case MIIM_88E1011_PHYSTAT_GBIT: | |
372 | priv->speed = 1000; | |
373 | break; | |
374 | case MIIM_88E1011_PHYSTAT_100: | |
375 | priv->speed = 100; | |
376 | break; | |
377 | default: | |
378 | priv->speed = 10; | |
42d1f039 WD |
379 | } |
380 | ||
97d80fc3 WD |
381 | return 0; |
382 | } | |
42d1f039 | 383 | |
97d80fc3 WD |
384 | |
385 | /* Parse the cis8201's status register for speed and duplex | |
386 | * information */ | |
387 | uint mii_parse_cis8201(uint mii_reg, struct tsec_private *priv) | |
388 | { | |
389 | uint speed; | |
390 | ||
391 | if(mii_reg & MIIM_CIS8201_AUXCONSTAT_DUPLEX) | |
392 | priv->duplexity = 1; | |
393 | else | |
394 | priv->duplexity = 0; | |
395 | ||
396 | speed = mii_reg & MIIM_CIS8201_AUXCONSTAT_SPEED; | |
397 | switch(speed) { | |
398 | case MIIM_CIS8201_AUXCONSTAT_GBIT: | |
399 | priv->speed = 1000; | |
400 | break; | |
401 | case MIIM_CIS8201_AUXCONSTAT_100: | |
402 | priv->speed = 100; | |
403 | break; | |
404 | default: | |
405 | priv->speed = 10; | |
406 | break; | |
42d1f039 WD |
407 | } |
408 | ||
97d80fc3 WD |
409 | return 0; |
410 | } | |
42d1f039 | 411 | |
97d80fc3 WD |
412 | |
413 | /* Parse the DM9161's status register for speed and duplex | |
414 | * information */ | |
415 | uint mii_parse_dm9161_scsr(uint mii_reg, struct tsec_private *priv) | |
416 | { | |
417 | if(mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_100H)) | |
418 | priv->speed = 100; | |
419 | else | |
420 | priv->speed = 10; | |
421 | ||
422 | if(mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_10F)) | |
423 | priv->duplexity = 1; | |
424 | else | |
425 | priv->duplexity = 0; | |
426 | ||
427 | return 0; | |
428 | } | |
429 | ||
430 | ||
431 | /* Hack to write all 4 PHYs with the LED values */ | |
432 | uint mii_cis8204_fixled(uint mii_reg, struct tsec_private *priv) | |
433 | { | |
434 | uint phyid; | |
435 | volatile tsec_t *regbase = priv->phyregs; | |
436 | int timeout=1000000; | |
437 | ||
438 | for(phyid=0;phyid<4;phyid++) { | |
439 | regbase->miimadd = (phyid << 8) | mii_reg; | |
440 | regbase->miimcon = MIIM_CIS8204_SLEDCON_INIT; | |
f046ccd1 | 441 | asm("sync"); |
97d80fc3 WD |
442 | |
443 | timeout=1000000; | |
444 | while((regbase->miimind & MIIMIND_BUSY) && timeout--); | |
42d1f039 | 445 | } |
42d1f039 | 446 | |
97d80fc3 | 447 | return MIIM_CIS8204_SLEDCON_INIT; |
42d1f039 WD |
448 | } |
449 | ||
d9b94f28 JL |
450 | uint mii_cis8204_setmode(uint mii_reg, struct tsec_private *priv) |
451 | { | |
452 | if (priv->flags & TSEC_REDUCED) | |
453 | return MIIM_CIS8204_EPHYCON_INIT | MIIM_CIS8204_EPHYCON_RGMII; | |
454 | else | |
455 | return MIIM_CIS8204_EPHYCON_INIT; | |
456 | } | |
42d1f039 | 457 | |
97d80fc3 WD |
458 | /* Initialized required registers to appropriate values, zeroing |
459 | * those we don't care about (unless zero is bad, in which case, | |
460 | * choose a more appropriate value) */ | |
461 | static void init_registers(volatile tsec_t *regs) | |
42d1f039 WD |
462 | { |
463 | /* Clear IEVENT */ | |
464 | regs->ievent = IEVENT_INIT_CLEAR; | |
465 | ||
466 | regs->imask = IMASK_INIT_CLEAR; | |
467 | ||
468 | regs->hash.iaddr0 = 0; | |
469 | regs->hash.iaddr1 = 0; | |
470 | regs->hash.iaddr2 = 0; | |
471 | regs->hash.iaddr3 = 0; | |
472 | regs->hash.iaddr4 = 0; | |
473 | regs->hash.iaddr5 = 0; | |
474 | regs->hash.iaddr6 = 0; | |
475 | regs->hash.iaddr7 = 0; | |
476 | ||
477 | regs->hash.gaddr0 = 0; | |
478 | regs->hash.gaddr1 = 0; | |
479 | regs->hash.gaddr2 = 0; | |
480 | regs->hash.gaddr3 = 0; | |
481 | regs->hash.gaddr4 = 0; | |
482 | regs->hash.gaddr5 = 0; | |
483 | regs->hash.gaddr6 = 0; | |
484 | regs->hash.gaddr7 = 0; | |
485 | ||
486 | regs->rctrl = 0x00000000; | |
487 | ||
488 | /* Init RMON mib registers */ | |
489 | memset((void *)&(regs->rmon), 0, sizeof(rmon_mib_t)); | |
490 | ||
491 | regs->rmon.cam1 = 0xffffffff; | |
492 | regs->rmon.cam2 = 0xffffffff; | |
493 | ||
494 | regs->mrblr = MRBLR_INIT_SETTINGS; | |
495 | ||
496 | regs->minflr = MINFLR_INIT_SETTINGS; | |
497 | ||
498 | regs->attr = ATTR_INIT_SETTINGS; | |
499 | regs->attreli = ATTRELI_INIT_SETTINGS; | |
500 | ||
501 | } | |
502 | ||
97d80fc3 WD |
503 | |
504 | /* Configure maccfg2 based on negotiated speed and duplex | |
505 | * reported by PHY handling code */ | |
506 | static void adjust_link(struct eth_device *dev) | |
507 | { | |
508 | struct tsec_private *priv = (struct tsec_private *)dev->priv; | |
509 | volatile tsec_t *regs = priv->regs; | |
510 | ||
511 | if(priv->link) { | |
512 | if(priv->duplexity != 0) | |
513 | regs->maccfg2 |= MACCFG2_FULL_DUPLEX; | |
514 | else | |
515 | regs->maccfg2 &= ~(MACCFG2_FULL_DUPLEX); | |
516 | ||
517 | switch(priv->speed) { | |
518 | case 1000: | |
519 | regs->maccfg2 = ((regs->maccfg2&~(MACCFG2_IF)) | |
520 | | MACCFG2_GMII); | |
521 | break; | |
522 | case 100: | |
523 | case 10: | |
524 | regs->maccfg2 = ((regs->maccfg2&~(MACCFG2_IF)) | |
525 | | MACCFG2_MII); | |
d9b94f28 JL |
526 | |
527 | /* If We're in reduced mode, we | |
528 | * need to say whether we're 10 | |
529 | * or 100 MB. */ | |
530 | if ((priv->speed == 100) | |
531 | && (priv->flags & TSEC_REDUCED)) | |
532 | regs->ecntrl |= ECNTRL_R100; | |
533 | else | |
534 | regs->ecntrl &= ~(ECNTRL_R100); | |
97d80fc3 WD |
535 | break; |
536 | default: | |
537 | printf("%s: Speed was bad\n", dev->name); | |
538 | break; | |
539 | } | |
540 | ||
541 | printf("Speed: %d, %s duplex\n", priv->speed, | |
542 | (priv->duplexity) ? "full" : "half"); | |
543 | ||
544 | } else { | |
545 | printf("%s: No link.\n", dev->name); | |
546 | } | |
547 | } | |
548 | ||
549 | ||
550 | /* Set up the buffers and their descriptors, and bring up the | |
551 | * interface */ | |
552 | static void startup_tsec(struct eth_device *dev) | |
42d1f039 WD |
553 | { |
554 | int i; | |
97d80fc3 WD |
555 | struct tsec_private *priv = (struct tsec_private *)dev->priv; |
556 | volatile tsec_t *regs = priv->regs; | |
42d1f039 WD |
557 | |
558 | /* Point to the buffer descriptors */ | |
559 | regs->tbase = (unsigned int)(&rtx.txbd[txIdx]); | |
560 | regs->rbase = (unsigned int)(&rtx.rxbd[rxIdx]); | |
561 | ||
562 | /* Initialize the Rx Buffer descriptors */ | |
563 | for (i = 0; i < PKTBUFSRX; i++) { | |
564 | rtx.rxbd[i].status = RXBD_EMPTY; | |
565 | rtx.rxbd[i].length = 0; | |
566 | rtx.rxbd[i].bufPtr = (uint)NetRxPackets[i]; | |
567 | } | |
568 | rtx.rxbd[PKTBUFSRX -1].status |= RXBD_WRAP; | |
569 | ||
570 | /* Initialize the TX Buffer Descriptors */ | |
571 | for(i=0; i<TX_BUF_CNT; i++) { | |
572 | rtx.txbd[i].status = 0; | |
573 | rtx.txbd[i].length = 0; | |
574 | rtx.txbd[i].bufPtr = 0; | |
575 | } | |
576 | rtx.txbd[TX_BUF_CNT -1].status |= TXBD_WRAP; | |
577 | ||
97d80fc3 WD |
578 | /* Start up the PHY */ |
579 | phy_run_commands(priv, priv->phyinfo->startup); | |
580 | adjust_link(dev); | |
581 | ||
42d1f039 WD |
582 | /* Enable Transmit and Receive */ |
583 | regs->maccfg1 |= (MACCFG1_RX_EN | MACCFG1_TX_EN); | |
584 | ||
585 | /* Tell the DMA it is clear to go */ | |
586 | regs->dmactrl |= DMACTRL_INIT_SETTINGS; | |
587 | regs->tstat = TSTAT_CLEAR_THALT; | |
588 | regs->dmactrl &= ~(DMACTRL_GRS | DMACTRL_GTS); | |
589 | } | |
590 | ||
9d46ea4a | 591 | /* This returns the status bits of the device. The return value |
42d1f039 | 592 | * is never checked, and this is what the 8260 driver did, so we |
9d46ea4a | 593 | * do the same. Presumably, this would be zero if there were no |
42d1f039 WD |
594 | * errors */ |
595 | static int tsec_send(struct eth_device* dev, volatile void *packet, int length) | |
596 | { | |
597 | int i; | |
598 | int result = 0; | |
97d80fc3 WD |
599 | struct tsec_private *priv = (struct tsec_private *)dev->priv; |
600 | volatile tsec_t *regs = priv->regs; | |
42d1f039 WD |
601 | |
602 | /* Find an empty buffer descriptor */ | |
603 | for(i=0; rtx.txbd[txIdx].status & TXBD_READY; i++) { | |
604 | if (i >= TOUT_LOOP) { | |
8b07a110 | 605 | debug ("%s: tsec: tx buffers full\n", dev->name); |
42d1f039 WD |
606 | return result; |
607 | } | |
608 | } | |
609 | ||
610 | rtx.txbd[txIdx].bufPtr = (uint)packet; | |
611 | rtx.txbd[txIdx].length = length; | |
612 | rtx.txbd[txIdx].status |= (TXBD_READY | TXBD_LAST | TXBD_CRC | TXBD_INTERRUPT); | |
613 | ||
614 | /* Tell the DMA to go */ | |
615 | regs->tstat = TSTAT_CLEAR_THALT; | |
616 | ||
617 | /* Wait for buffer to be transmitted */ | |
618 | for(i=0; rtx.txbd[txIdx].status & TXBD_READY; i++) { | |
619 | if (i >= TOUT_LOOP) { | |
8b07a110 | 620 | debug ("%s: tsec: tx error\n", dev->name); |
42d1f039 WD |
621 | return result; |
622 | } | |
623 | } | |
624 | ||
625 | txIdx = (txIdx + 1) % TX_BUF_CNT; | |
626 | result = rtx.txbd[txIdx].status & TXBD_STATS; | |
627 | ||
628 | return result; | |
629 | } | |
630 | ||
631 | static int tsec_recv(struct eth_device* dev) | |
632 | { | |
633 | int length; | |
97d80fc3 WD |
634 | struct tsec_private *priv = (struct tsec_private *)dev->priv; |
635 | volatile tsec_t *regs = priv->regs; | |
42d1f039 WD |
636 | |
637 | while(!(rtx.rxbd[rxIdx].status & RXBD_EMPTY)) { | |
638 | ||
639 | length = rtx.rxbd[rxIdx].length; | |
640 | ||
641 | /* Send the packet up if there were no errors */ | |
642 | if (!(rtx.rxbd[rxIdx].status & RXBD_STATS)) { | |
643 | NetReceive(NetRxPackets[rxIdx], length - 4); | |
97d80fc3 WD |
644 | } else { |
645 | printf("Got error %x\n", | |
646 | (rtx.rxbd[rxIdx].status & RXBD_STATS)); | |
42d1f039 WD |
647 | } |
648 | ||
649 | rtx.rxbd[rxIdx].length = 0; | |
650 | ||
651 | /* Set the wrap bit if this is the last element in the list */ | |
652 | rtx.rxbd[rxIdx].status = RXBD_EMPTY | (((rxIdx + 1) == PKTBUFSRX) ? RXBD_WRAP : 0); | |
653 | ||
654 | rxIdx = (rxIdx + 1) % PKTBUFSRX; | |
655 | } | |
656 | ||
657 | if(regs->ievent&IEVENT_BSY) { | |
658 | regs->ievent = IEVENT_BSY; | |
659 | regs->rstat = RSTAT_CLEAR_RHALT; | |
660 | } | |
661 | ||
662 | return -1; | |
663 | ||
664 | } | |
665 | ||
666 | ||
97d80fc3 | 667 | /* Stop the interface */ |
42d1f039 WD |
668 | static void tsec_halt(struct eth_device* dev) |
669 | { | |
97d80fc3 WD |
670 | struct tsec_private *priv = (struct tsec_private *)dev->priv; |
671 | volatile tsec_t *regs = priv->regs; | |
42d1f039 WD |
672 | |
673 | regs->dmactrl &= ~(DMACTRL_GRS | DMACTRL_GTS); | |
674 | regs->dmactrl |= (DMACTRL_GRS | DMACTRL_GTS); | |
675 | ||
676 | while(!(regs->ievent & (IEVENT_GRSC | IEVENT_GTSC))); | |
677 | ||
678 | regs->maccfg1 &= ~(MACCFG1_TX_EN | MACCFG1_RX_EN); | |
679 | ||
97d80fc3 WD |
680 | /* Shut down the PHY, as needed */ |
681 | phy_run_commands(priv, priv->phyinfo->shutdown); | |
682 | } | |
683 | ||
684 | ||
685 | struct phy_info phy_info_M88E1011S = { | |
686 | 0x01410c6, | |
687 | "Marvell 88E1011S", | |
688 | 4, | |
689 | (struct phy_cmd[]) { /* config */ | |
690 | /* Reset and configure the PHY */ | |
691 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
692 | {0x1d, 0x1f, NULL}, | |
693 | {0x1e, 0x200c, NULL}, | |
694 | {0x1d, 0x5, NULL}, | |
695 | {0x1e, 0x0, NULL}, | |
696 | {0x1e, 0x100, NULL}, | |
697 | {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL}, | |
698 | {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, | |
699 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
700 | {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, | |
701 | {miim_end,} | |
702 | }, | |
703 | (struct phy_cmd[]) { /* startup */ | |
704 | /* Status is read once to clear old link state */ | |
705 | {MIIM_STATUS, miim_read, NULL}, | |
706 | /* Auto-negotiate */ | |
707 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
708 | /* Read the status */ | |
709 | {MIIM_88E1011_PHY_STATUS, miim_read, &mii_parse_88E1011_psr}, | |
710 | {miim_end,} | |
711 | }, | |
712 | (struct phy_cmd[]) { /* shutdown */ | |
713 | {miim_end,} | |
714 | }, | |
715 | }; | |
716 | ||
9d46ea4a WD |
717 | struct phy_info phy_info_M88E1111S = { |
718 | 0x01410cc, | |
719 | "Marvell 88E1111S", | |
720 | 4, | |
721 | (struct phy_cmd[]) { /* config */ | |
722 | /* Reset and configure the PHY */ | |
723 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
724 | {0x1d, 0x1f, NULL}, | |
725 | {0x1e, 0x200c, NULL}, | |
726 | {0x1d, 0x5, NULL}, | |
727 | {0x1e, 0x0, NULL}, | |
728 | {0x1e, 0x100, NULL}, | |
729 | {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL}, | |
730 | {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, | |
731 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
732 | {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, | |
733 | {miim_end,} | |
734 | }, | |
735 | (struct phy_cmd[]) { /* startup */ | |
736 | /* Status is read once to clear old link state */ | |
737 | {MIIM_STATUS, miim_read, NULL}, | |
738 | /* Auto-negotiate */ | |
739 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
740 | /* Read the status */ | |
741 | {MIIM_88E1011_PHY_STATUS, miim_read, &mii_parse_88E1011_psr}, | |
742 | {miim_end,} | |
743 | }, | |
744 | (struct phy_cmd[]) { /* shutdown */ | |
745 | {miim_end,} | |
746 | }, | |
747 | }; | |
748 | ||
97d80fc3 WD |
749 | struct phy_info phy_info_cis8204 = { |
750 | 0x3f11, | |
751 | "Cicada Cis8204", | |
752 | 6, | |
753 | (struct phy_cmd[]) { /* config */ | |
754 | /* Override PHY config settings */ | |
755 | {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL}, | |
756 | /* Configure some basic stuff */ | |
757 | {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, | |
758 | {MIIM_CIS8204_SLED_CON, MIIM_CIS8204_SLEDCON_INIT, &mii_cis8204_fixled}, | |
d9b94f28 | 759 | {MIIM_CIS8204_EPHY_CON, MIIM_CIS8204_EPHYCON_INIT, &mii_cis8204_setmode}, |
97d80fc3 WD |
760 | {miim_end,} |
761 | }, | |
762 | (struct phy_cmd[]) { /* startup */ | |
763 | /* Read the Status (2x to make sure link is right) */ | |
764 | {MIIM_STATUS, miim_read, NULL}, | |
765 | /* Auto-negotiate */ | |
766 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
767 | /* Read the status */ | |
768 | {MIIM_CIS8201_AUX_CONSTAT, miim_read, &mii_parse_cis8201}, | |
769 | {miim_end,} | |
770 | }, | |
771 | (struct phy_cmd[]) { /* shutdown */ | |
772 | {miim_end,} | |
773 | }, | |
774 | }; | |
775 | ||
776 | /* Cicada 8201 */ | |
777 | struct phy_info phy_info_cis8201 = { | |
778 | 0xfc41, | |
779 | "CIS8201", | |
780 | 4, | |
781 | (struct phy_cmd[]) { /* config */ | |
782 | /* Override PHY config settings */ | |
783 | {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL}, | |
784 | /* Set up the interface mode */ | |
785 | {MIIM_CIS8201_EXT_CON1, MIIM_CIS8201_EXTCON1_INIT, NULL}, | |
786 | /* Configure some basic stuff */ | |
787 | {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, | |
788 | {miim_end,} | |
789 | }, | |
790 | (struct phy_cmd[]) { /* startup */ | |
791 | /* Read the Status (2x to make sure link is right) */ | |
792 | {MIIM_STATUS, miim_read, NULL}, | |
793 | /* Auto-negotiate */ | |
794 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
795 | /* Read the status */ | |
796 | {MIIM_CIS8201_AUX_CONSTAT, miim_read, &mii_parse_cis8201}, | |
797 | {miim_end,} | |
798 | }, | |
799 | (struct phy_cmd[]) { /* shutdown */ | |
800 | {miim_end,} | |
801 | }, | |
802 | }; | |
803 | ||
804 | ||
805 | struct phy_info phy_info_dm9161 = { | |
806 | 0x0181b88, | |
807 | "Davicom DM9161E", | |
808 | 4, | |
809 | (struct phy_cmd[]) { /* config */ | |
810 | {MIIM_CONTROL, MIIM_DM9161_CR_STOP, NULL}, | |
811 | /* Do not bypass the scrambler/descrambler */ | |
812 | {MIIM_DM9161_SCR, MIIM_DM9161_SCR_INIT, NULL}, | |
813 | /* Clear 10BTCSR to default */ | |
814 | {MIIM_DM9161_10BTCSR, MIIM_DM9161_10BTCSR_INIT, NULL}, | |
815 | /* Configure some basic stuff */ | |
816 | {MIIM_CONTROL, MIIM_CR_INIT, NULL}, | |
817 | /* Restart Auto Negotiation */ | |
818 | {MIIM_CONTROL, MIIM_DM9161_CR_RSTAN, NULL}, | |
819 | {miim_end,} | |
820 | }, | |
821 | (struct phy_cmd[]) { /* startup */ | |
822 | /* Status is read once to clear old link state */ | |
823 | {MIIM_STATUS, miim_read, NULL}, | |
824 | /* Auto-negotiate */ | |
825 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
826 | /* Read the status */ | |
827 | {MIIM_DM9161_SCSR, miim_read, &mii_parse_dm9161_scsr}, | |
828 | {miim_end,} | |
829 | }, | |
830 | (struct phy_cmd[]) { /* shutdown */ | |
831 | {miim_end,} | |
832 | }, | |
833 | }; | |
834 | ||
3dd7f0f0 WD |
835 | uint mii_parse_lxt971_sr2(uint mii_reg, struct tsec_private *priv) |
836 | { | |
3c2b3d45 WD |
837 | unsigned int speed; |
838 | if (priv->link) { | |
839 | speed = mii_reg & MIIM_LXT971_SR2_SPEED_MASK; | |
840 | ||
841 | switch (speed) { | |
842 | case MIIM_LXT971_SR2_10HDX: | |
843 | priv->speed = 10; | |
844 | priv->duplexity = 0; | |
845 | break; | |
846 | case MIIM_LXT971_SR2_10FDX: | |
847 | priv->speed = 10; | |
848 | priv->duplexity = 1; | |
849 | break; | |
850 | case MIIM_LXT971_SR2_100HDX: | |
851 | priv->speed = 100; | |
852 | priv->duplexity = 0; | |
853 | default: | |
854 | priv->speed = 100; | |
855 | priv->duplexity = 1; | |
856 | break; | |
857 | } | |
858 | } else { | |
859 | priv->speed = 0; | |
860 | priv->duplexity = 0; | |
861 | } | |
862 | ||
863 | return 0; | |
3dd7f0f0 WD |
864 | } |
865 | ||
9d46ea4a WD |
866 | static struct phy_info phy_info_lxt971 = { |
867 | 0x0001378e, | |
868 | "LXT971", | |
869 | 4, | |
870 | (struct phy_cmd []) { /* config */ | |
3dd7f0f0 | 871 | { MIIM_CR, MIIM_CR_INIT, mii_cr_init }, /* autonegotiate */ |
9d46ea4a WD |
872 | { miim_end, } |
873 | }, | |
874 | (struct phy_cmd []) { /* startup - enable interrupts */ | |
875 | /* { 0x12, 0x00f2, NULL }, */ | |
9d46ea4a | 876 | { MIIM_STATUS, miim_read, NULL }, |
3dd7f0f0 WD |
877 | { MIIM_STATUS, miim_read, &mii_parse_sr }, |
878 | { MIIM_LXT971_SR2, miim_read, &mii_parse_lxt971_sr2 }, | |
9d46ea4a WD |
879 | { miim_end, } |
880 | }, | |
881 | (struct phy_cmd []) { /* shutdown - disable interrupts */ | |
882 | { miim_end, } | |
883 | }, | |
884 | }; | |
885 | ||
97d80fc3 WD |
886 | struct phy_info *phy_info[] = { |
887 | #if 0 | |
888 | &phy_info_cis8201, | |
889 | #endif | |
890 | &phy_info_cis8204, | |
891 | &phy_info_M88E1011S, | |
9d46ea4a | 892 | &phy_info_M88E1111S, |
97d80fc3 | 893 | &phy_info_dm9161, |
9d46ea4a | 894 | &phy_info_lxt971, |
97d80fc3 WD |
895 | NULL |
896 | }; | |
897 | ||
898 | ||
899 | /* Grab the identifier of the device's PHY, and search through | |
9d46ea4a | 900 | * all of the known PHYs to see if one matches. If so, return |
97d80fc3 WD |
901 | * it, if not, return NULL */ |
902 | struct phy_info * get_phy_info(struct eth_device *dev) | |
903 | { | |
904 | struct tsec_private *priv = (struct tsec_private *)dev->priv; | |
905 | uint phy_reg, phy_ID; | |
906 | int i; | |
907 | struct phy_info *theInfo = NULL; | |
908 | ||
909 | /* Grab the bits from PHYIR1, and put them in the upper half */ | |
910 | phy_reg = read_phy_reg(priv, MIIM_PHYIR1); | |
911 | phy_ID = (phy_reg & 0xffff) << 16; | |
912 | ||
913 | /* Grab the bits from PHYIR2, and put them in the lower half */ | |
914 | phy_reg = read_phy_reg(priv, MIIM_PHYIR2); | |
915 | phy_ID |= (phy_reg & 0xffff); | |
916 | ||
917 | /* loop through all the known PHY types, and find one that */ | |
918 | /* matches the ID we read from the PHY. */ | |
919 | for(i=0; phy_info[i]; i++) { | |
920 | if(phy_info[i]->id == (phy_ID >> phy_info[i]->shift)) | |
921 | theInfo = phy_info[i]; | |
922 | } | |
923 | ||
924 | if(theInfo == NULL) | |
925 | { | |
926 | printf("%s: PHY id %x is not supported!\n", dev->name, phy_ID); | |
927 | return NULL; | |
928 | } else { | |
929 | printf("%s: PHY is %s (%x)\n", dev->name, theInfo->name, | |
930 | phy_ID); | |
931 | } | |
932 | ||
933 | return theInfo; | |
42d1f039 | 934 | } |
7abf0c58 | 935 | |
97d80fc3 WD |
936 | |
937 | /* Execute the given series of commands on the given device's | |
938 | * PHY, running functions as necessary*/ | |
939 | void phy_run_commands(struct tsec_private *priv, struct phy_cmd *cmd) | |
940 | { | |
941 | int i; | |
942 | uint result; | |
943 | volatile tsec_t *phyregs = priv->phyregs; | |
944 | ||
945 | phyregs->miimcfg = MIIMCFG_RESET; | |
946 | ||
947 | phyregs->miimcfg = MIIMCFG_INIT_VALUE; | |
948 | ||
949 | while(phyregs->miimind & MIIMIND_BUSY); | |
950 | ||
951 | for(i=0;cmd->mii_reg != miim_end;i++) { | |
952 | if(cmd->mii_data == miim_read) { | |
953 | result = read_phy_reg(priv, cmd->mii_reg); | |
954 | ||
955 | if(cmd->funct != NULL) | |
956 | (*(cmd->funct))(result, priv); | |
957 | ||
958 | } else { | |
959 | if(cmd->funct != NULL) | |
960 | result = (*(cmd->funct))(cmd->mii_reg, priv); | |
961 | else | |
962 | result = cmd->mii_data; | |
963 | ||
964 | write_phy_reg(priv, cmd->mii_reg, result); | |
965 | ||
966 | } | |
967 | cmd++; | |
968 | } | |
969 | } | |
970 | ||
971 | ||
972 | /* Relocate the function pointers in the phy cmd lists */ | |
973 | static void relocate_cmds(void) | |
974 | { | |
975 | struct phy_cmd **cmdlistptr; | |
976 | struct phy_cmd *cmd; | |
977 | int i,j,k; | |
978 | DECLARE_GLOBAL_DATA_PTR; | |
979 | ||
980 | for(i=0; phy_info[i]; i++) { | |
981 | /* First thing's first: relocate the pointers to the | |
982 | * PHY command structures (the structs were done) */ | |
983 | phy_info[i] = (struct phy_info *) ((uint)phy_info[i] | |
984 | + gd->reloc_off); | |
985 | phy_info[i]->name += gd->reloc_off; | |
986 | phy_info[i]->config = | |
987 | (struct phy_cmd *)((uint)phy_info[i]->config | |
988 | + gd->reloc_off); | |
989 | phy_info[i]->startup = | |
990 | (struct phy_cmd *)((uint)phy_info[i]->startup | |
991 | + gd->reloc_off); | |
992 | phy_info[i]->shutdown = | |
993 | (struct phy_cmd *)((uint)phy_info[i]->shutdown | |
994 | + gd->reloc_off); | |
995 | ||
996 | cmdlistptr = &phy_info[i]->config; | |
997 | j=0; | |
998 | for(;cmdlistptr <= &phy_info[i]->shutdown;cmdlistptr++) { | |
999 | k=0; | |
1000 | for(cmd=*cmdlistptr;cmd->mii_reg != miim_end;cmd++) { | |
1001 | /* Only relocate non-NULL pointers */ | |
1002 | if(cmd->funct) | |
1003 | cmd->funct += gd->reloc_off; | |
1004 | ||
1005 | k++; | |
1006 | } | |
1007 | j++; | |
1008 | } | |
1009 | } | |
1010 | ||
1011 | relocated = 1; | |
1012 | } | |
1013 | ||
1014 | ||
7abf0c58 | 1015 | #ifndef CONFIG_BITBANGMII |
97d80fc3 WD |
1016 | |
1017 | struct tsec_private * get_priv_for_phy(unsigned char phyaddr) | |
1018 | { | |
1019 | int i; | |
1020 | ||
1021 | for(i=0;i<MAXCONTROLLERS;i++) { | |
1022 | if(privlist[i]->phyaddr == phyaddr) | |
1023 | return privlist[i]; | |
1024 | } | |
1025 | ||
1026 | return NULL; | |
1027 | } | |
1028 | ||
7abf0c58 WD |
1029 | /* |
1030 | * Read a MII PHY register. | |
1031 | * | |
1032 | * Returns: | |
97d80fc3 | 1033 | * 0 on success |
7abf0c58 | 1034 | */ |
97d80fc3 | 1035 | int miiphy_read(unsigned char addr, unsigned char reg, unsigned short *value) |
7abf0c58 | 1036 | { |
97d80fc3 WD |
1037 | unsigned short ret; |
1038 | struct tsec_private *priv = get_priv_for_phy(addr); | |
1039 | ||
1040 | if(NULL == priv) { | |
1041 | printf("Can't read PHY at address %d\n", addr); | |
1042 | return -1; | |
1043 | } | |
7abf0c58 | 1044 | |
97d80fc3 WD |
1045 | ret = (unsigned short)read_phy_reg(priv, reg); |
1046 | *value = ret; | |
7abf0c58 WD |
1047 | |
1048 | return 0; | |
1049 | } | |
1050 | ||
1051 | /* | |
1052 | * Write a MII PHY register. | |
1053 | * | |
1054 | * Returns: | |
97d80fc3 | 1055 | * 0 on success |
7abf0c58 | 1056 | */ |
97d80fc3 | 1057 | int miiphy_write(unsigned char addr, unsigned char reg, unsigned short value) |
7abf0c58 | 1058 | { |
97d80fc3 WD |
1059 | struct tsec_private *priv = get_priv_for_phy(addr); |
1060 | ||
1061 | if(NULL == priv) { | |
1062 | printf("Can't write PHY at address %d\n", addr); | |
1063 | return -1; | |
1064 | } | |
7abf0c58 | 1065 | |
97d80fc3 | 1066 | write_phy_reg(priv, reg, value); |
7abf0c58 WD |
1067 | |
1068 | return 0; | |
1069 | } | |
97d80fc3 | 1070 | |
7abf0c58 | 1071 | #endif /* CONFIG_BITBANGMII */ |
97d80fc3 | 1072 | |
42d1f039 | 1073 | #endif /* CONFIG_TSEC_ENET */ |