blob: f860dae8b009f6deec85afb2516c8885ed6b10c1 [file] [log] [blame]
wdenk42d1f032003-10-15 23:53:47 +00001/*
2 * tsec.c
wdenk97d80fc2004-06-09 00:34:46 +00003 * Freescale Three Speed Ethernet Controller driver
wdenk42d1f032003-10-15 23:53:47 +00004 *
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 *
wdenk97d80fc2004-06-09 00:34:46 +00009 * Copyright 2004 Freescale Semiconductor.
wdenk42d1f032003-10-15 23:53:47 +000010 * (C) Copyright 2003, Motorola, Inc.
wdenk42d1f032003-10-15 23:53:47 +000011 * 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"
Marian Balakowicz63ff0042005-10-28 22:30:33 +020024#include "miiphy.h"
wdenk42d1f032003-10-15 23:53:47 +000025
Marian Balakowicz63ff0042005-10-28 22:30:33 +020026#define TX_BUF_CNT 2
wdenk42d1f032003-10-15 23:53:47 +000027
wdenk42d1f032003-10-15 23:53:47 +000028static uint rxIdx; /* index of the current RX buffer */
29static uint txIdx; /* index of the current TX buffer */
30
31typedef volatile struct rtxbd {
32 txbd8_t txbd[TX_BUF_CNT];
33 rxbd8_t rxbd[PKTBUFSRX];
34} RTXBD;
35
wdenk97d80fc2004-06-09 00:34:46 +000036struct tsec_info_struct {
37 unsigned int phyaddr;
Jon Loeligerd9b94f22005-07-25 14:05:07 -050038 u32 flags;
wdenk97d80fc2004-06-09 00:34:46 +000039 unsigned int phyregidx;
40};
41
42
43/* The tsec_info structure contains 3 values which the
44 * driver uses to determine how to operate a given ethernet
45 * device. For now, the structure is initialized with the
46 * knowledge that all current implementations have 2 TSEC
47 * devices, and one FEC. The information needed is:
48 * phyaddr - The address of the PHY which is attached to
wdenk9d46ea42005-03-14 23:56:42 +000049 * the given device.
wdenk97d80fc2004-06-09 00:34:46 +000050 *
Jon Loeligerd9b94f22005-07-25 14:05:07 -050051 * flags - This variable indicates whether the device
52 * supports gigabit speed ethernet, and whether it should be
53 * in reduced mode.
wdenk97d80fc2004-06-09 00:34:46 +000054 *
55 * phyregidx - This variable specifies which ethernet device
wdenk9d46ea42005-03-14 23:56:42 +000056 * controls the MII Management registers which are connected
57 * to the PHY. For 8540/8560, only TSEC1 (index 0) has
58 * access to the PHYs, so all of the entries have "0".
wdenk97d80fc2004-06-09 00:34:46 +000059 *
60 * The values specified in the table are taken from the board's
61 * config file in include/configs/. When implementing a new
62 * board with ethernet capability, it is necessary to define:
63 * TSEC1_PHY_ADDR
64 * TSEC1_PHYIDX
65 * TSEC2_PHY_ADDR
66 * TSEC2_PHYIDX
67 *
68 * and for 8560:
69 * FEC_PHY_ADDR
70 * FEC_PHYIDX
71 */
72static struct tsec_info_struct tsec_info[] = {
Eran Libertyf046ccd2005-07-28 10:08:46 -050073#if defined(CONFIG_MPC85XX_TSEC1) || defined(CONFIG_MPC83XX_TSEC1)
Jon Loeligerd9b94f22005-07-25 14:05:07 -050074 {TSEC1_PHY_ADDR, TSEC_GIGABIT, TSEC1_PHYIDX},
wdenk9d46ea42005-03-14 23:56:42 +000075#else
76 { 0, 0, 0},
wdenk97d80fc2004-06-09 00:34:46 +000077#endif
Eran Libertyf046ccd2005-07-28 10:08:46 -050078#if defined(CONFIG_MPC85XX_TSEC2) || defined(CONFIG_MPC83XX_TSEC2)
Jon Loeligerd9b94f22005-07-25 14:05:07 -050079 {TSEC2_PHY_ADDR, TSEC_GIGABIT, TSEC2_PHYIDX},
wdenk9d46ea42005-03-14 23:56:42 +000080#else
81 { 0, 0, 0},
wdenk97d80fc2004-06-09 00:34:46 +000082#endif
83#ifdef CONFIG_MPC85XX_FEC
84 {FEC_PHY_ADDR, 0, FEC_PHYIDX},
wdenk9d46ea42005-03-14 23:56:42 +000085#else
Eran Libertyf046ccd2005-07-28 10:08:46 -050086# if defined(CONFIG_MPC85XX_TSEC3) || defined(CONFIG_MPC83XX_TSEC3)
Jon Loeligerd9b94f22005-07-25 14:05:07 -050087 {TSEC3_PHY_ADDR, TSEC_GIGABIT | TSEC_REDUCED, TSEC3_PHYIDX},
88# else
wdenk9d46ea42005-03-14 23:56:42 +000089 { 0, 0, 0},
Jon Loeligerd9b94f22005-07-25 14:05:07 -050090# endif
Eran Libertyf046ccd2005-07-28 10:08:46 -050091# if defined(CONFIG_MPC85XX_TSEC4) || defined(CONFIG_MPC83XX_TSEC4)
Jon Loeligerd9b94f22005-07-25 14:05:07 -050092 {TSEC4_PHY_ADDR, TSEC_REDUCED, TSEC4_PHYIDX},
93# else
94 { 0, 0, 0},
95# endif
wdenk97d80fc2004-06-09 00:34:46 +000096#endif
97};
98
Jon Loeligerd9b94f22005-07-25 14:05:07 -050099#define MAXCONTROLLERS (4)
wdenk97d80fc2004-06-09 00:34:46 +0000100
101static int relocated = 0;
102
103static struct tsec_private *privlist[MAXCONTROLLERS];
104
wdenk42d1f032003-10-15 23:53:47 +0000105#ifdef __GNUC__
106static RTXBD rtx __attribute__ ((aligned(8)));
107#else
108#error "rtx must be 64-bit aligned"
109#endif
110
111static int tsec_send(struct eth_device* dev, volatile void *packet, int length);
112static int tsec_recv(struct eth_device* dev);
113static int tsec_init(struct eth_device* dev, bd_t * bd);
114static void tsec_halt(struct eth_device* dev);
wdenk97d80fc2004-06-09 00:34:46 +0000115static void init_registers(volatile tsec_t *regs);
116static void startup_tsec(struct eth_device *dev);
117static int init_phy(struct eth_device *dev);
118void write_phy_reg(struct tsec_private *priv, uint regnum, uint value);
119uint read_phy_reg(struct tsec_private *priv, uint regnum);
120struct phy_info * get_phy_info(struct eth_device *dev);
121void phy_run_commands(struct tsec_private *priv, struct phy_cmd *cmd);
122static void adjust_link(struct eth_device *dev);
123static void relocate_cmds(void);
Marian Balakowicz63ff0042005-10-28 22:30:33 +0200124static int tsec_miiphy_write(char *devname, unsigned char addr,
125 unsigned char reg, unsigned short value);
126static int tsec_miiphy_read(char *devname, unsigned char addr,
127 unsigned char reg, unsigned short *value);
wdenk7abf0c52004-04-18 21:45:42 +0000128
wdenk97d80fc2004-06-09 00:34:46 +0000129/* Initialize device structure. Returns success if PHY
130 * initialization succeeded (i.e. if it recognizes the PHY)
131 */
Jon Loeligerd9b94f22005-07-25 14:05:07 -0500132int tsec_initialize(bd_t *bis, int index, char *devname)
wdenk42d1f032003-10-15 23:53:47 +0000133{
134 struct eth_device* dev;
135 int i;
wdenk97d80fc2004-06-09 00:34:46 +0000136 struct tsec_private *priv;
wdenk42d1f032003-10-15 23:53:47 +0000137
138 dev = (struct eth_device*) malloc(sizeof *dev);
139
wdenk97d80fc2004-06-09 00:34:46 +0000140 if(NULL == dev)
wdenk42d1f032003-10-15 23:53:47 +0000141 return 0;
142
143 memset(dev, 0, sizeof *dev);
144
wdenk97d80fc2004-06-09 00:34:46 +0000145 priv = (struct tsec_private *) malloc(sizeof(*priv));
146
147 if(NULL == priv)
148 return 0;
149
150 privlist[index] = priv;
151 priv->regs = (volatile tsec_t *)(TSEC_BASE_ADDR + index*TSEC_SIZE);
152 priv->phyregs = (volatile tsec_t *)(TSEC_BASE_ADDR +
153 tsec_info[index].phyregidx*TSEC_SIZE);
154
155 priv->phyaddr = tsec_info[index].phyaddr;
Jon Loeligerd9b94f22005-07-25 14:05:07 -0500156 priv->flags = tsec_info[index].flags;
wdenk97d80fc2004-06-09 00:34:46 +0000157
Jon Loeligerd9b94f22005-07-25 14:05:07 -0500158 sprintf(dev->name, devname);
wdenk42d1f032003-10-15 23:53:47 +0000159 dev->iobase = 0;
wdenk97d80fc2004-06-09 00:34:46 +0000160 dev->priv = priv;
wdenk42d1f032003-10-15 23:53:47 +0000161 dev->init = tsec_init;
162 dev->halt = tsec_halt;
163 dev->send = tsec_send;
164 dev->recv = tsec_recv;
165
166 /* Tell u-boot to get the addr from the env */
167 for(i=0;i<6;i++)
168 dev->enetaddr[i] = 0;
169
170 eth_register(dev);
171
wdenk7abf0c52004-04-18 21:45:42 +0000172
wdenk97d80fc2004-06-09 00:34:46 +0000173 /* Reset the MAC */
174 priv->regs->maccfg1 |= MACCFG1_SOFT_RESET;
175 priv->regs->maccfg1 &= ~(MACCFG1_SOFT_RESET);
wdenk7abf0c52004-04-18 21:45:42 +0000176
Marian Balakowicz63ff0042005-10-28 22:30:33 +0200177#if defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII) \
178 && !defined(BITBANGMII)
179 miiphy_register(dev->name, tsec_miiphy_read, tsec_miiphy_write);
180#endif
181
wdenk97d80fc2004-06-09 00:34:46 +0000182 /* Try to initialize PHY here, and return */
183 return init_phy(dev);
wdenk42d1f032003-10-15 23:53:47 +0000184}
185
186
187/* Initializes data structures and registers for the controller,
wdenk9d46ea42005-03-14 23:56:42 +0000188 * and brings the interface up. Returns the link status, meaning
wdenk97d80fc2004-06-09 00:34:46 +0000189 * that it returns success if the link is up, failure otherwise.
190 * This allows u-boot to find the first active controller. */
wdenk42d1f032003-10-15 23:53:47 +0000191int tsec_init(struct eth_device* dev, bd_t * bd)
192{
wdenk42d1f032003-10-15 23:53:47 +0000193 uint tempval;
194 char tmpbuf[MAC_ADDR_LEN];
195 int i;
wdenk97d80fc2004-06-09 00:34:46 +0000196 struct tsec_private *priv = (struct tsec_private *)dev->priv;
197 volatile tsec_t *regs = priv->regs;
wdenk42d1f032003-10-15 23:53:47 +0000198
199 /* Make sure the controller is stopped */
200 tsec_halt(dev);
201
wdenk97d80fc2004-06-09 00:34:46 +0000202 /* Init MACCFG2. Defaults to GMII */
wdenk42d1f032003-10-15 23:53:47 +0000203 regs->maccfg2 = MACCFG2_INIT_SETTINGS;
204
205 /* Init ECNTRL */
206 regs->ecntrl = ECNTRL_INIT_SETTINGS;
207
208 /* Copy the station address into the address registers.
209 * Backwards, because little endian MACS are dumb */
210 for(i=0;i<MAC_ADDR_LEN;i++) {
wdenk97d80fc2004-06-09 00:34:46 +0000211 tmpbuf[MAC_ADDR_LEN - 1 - i] = dev->enetaddr[i];
wdenk42d1f032003-10-15 23:53:47 +0000212 }
Wolfgang Denk77ddac92005-10-13 16:45:02 +0200213 regs->macstnaddr1 = *((uint *)(tmpbuf));
wdenk42d1f032003-10-15 23:53:47 +0000214
215 tempval = *((uint *)(tmpbuf +4));
216
Wolfgang Denk77ddac92005-10-13 16:45:02 +0200217 regs->macstnaddr2 = tempval;
wdenk42d1f032003-10-15 23:53:47 +0000218
wdenk42d1f032003-10-15 23:53:47 +0000219 /* reset the indices to zero */
220 rxIdx = 0;
221 txIdx = 0;
222
223 /* Clear out (for the most part) the other registers */
224 init_registers(regs);
225
226 /* Ready the device for tx/rx */
wdenk97d80fc2004-06-09 00:34:46 +0000227 startup_tsec(dev);
wdenk42d1f032003-10-15 23:53:47 +0000228
wdenk97d80fc2004-06-09 00:34:46 +0000229 /* If there's no link, fail */
230 return priv->link;
wdenk42d1f032003-10-15 23:53:47 +0000231
232}
233
234
wdenk97d80fc2004-06-09 00:34:46 +0000235/* Write value to the device's PHY through the registers
236 * specified in priv, modifying the register specified in regnum.
237 * It will wait for the write to be done (or for a timeout to
238 * expire) before exiting
239 */
240void write_phy_reg(struct tsec_private *priv, uint regnum, uint value)
241{
242 volatile tsec_t *regbase = priv->phyregs;
243 uint phyid = priv->phyaddr;
244 int timeout=1000000;
245
246 regbase->miimadd = (phyid << 8) | regnum;
247 regbase->miimcon = value;
Eran Libertyf046ccd2005-07-28 10:08:46 -0500248 asm("sync");
wdenk97d80fc2004-06-09 00:34:46 +0000249
250 timeout=1000000;
251 while((regbase->miimind & MIIMIND_BUSY) && timeout--);
252}
253
254
255/* Reads register regnum on the device's PHY through the
wdenk9d46ea42005-03-14 23:56:42 +0000256 * registers specified in priv. It lowers and raises the read
wdenk97d80fc2004-06-09 00:34:46 +0000257 * command, and waits for the data to become valid (miimind
258 * notvalid bit cleared), and the bus to cease activity (miimind
259 * busy bit cleared), and then returns the value
260 */
261uint read_phy_reg(struct tsec_private *priv, uint regnum)
wdenk42d1f032003-10-15 23:53:47 +0000262{
263 uint value;
wdenk97d80fc2004-06-09 00:34:46 +0000264 volatile tsec_t *regbase = priv->phyregs;
265 uint phyid = priv->phyaddr;
wdenk42d1f032003-10-15 23:53:47 +0000266
wdenk97d80fc2004-06-09 00:34:46 +0000267 /* Put the address of the phy, and the register
268 * number into MIIMADD */
269 regbase->miimadd = (phyid << 8) | regnum;
wdenk42d1f032003-10-15 23:53:47 +0000270
271 /* Clear the command register, and wait */
272 regbase->miimcom = 0;
Eran Libertyf046ccd2005-07-28 10:08:46 -0500273 asm("sync");
wdenk42d1f032003-10-15 23:53:47 +0000274
275 /* Initiate a read command, and wait */
276 regbase->miimcom = MIIM_READ_COMMAND;
Eran Libertyf046ccd2005-07-28 10:08:46 -0500277 asm("sync");
wdenk42d1f032003-10-15 23:53:47 +0000278
279 /* Wait for the the indication that the read is done */
280 while((regbase->miimind & (MIIMIND_NOTVALID | MIIMIND_BUSY)));
281
282 /* Grab the value read from the PHY */
283 value = regbase->miimstat;
284
285 return value;
286}
287
wdenk97d80fc2004-06-09 00:34:46 +0000288
289/* Discover which PHY is attached to the device, and configure it
290 * properly. If the PHY is not recognized, then return 0
291 * (failure). Otherwise, return 1
292 */
293static int init_phy(struct eth_device *dev)
wdenk42d1f032003-10-15 23:53:47 +0000294{
wdenk97d80fc2004-06-09 00:34:46 +0000295 struct tsec_private *priv = (struct tsec_private *)dev->priv;
296 struct phy_info *curphy;
wdenk42d1f032003-10-15 23:53:47 +0000297
298 /* Assign a Physical address to the TBI */
wdenk3c2b3d42005-04-05 23:32:21 +0000299
wdenk3dd7f0f2005-04-04 23:43:44 +0000300 {
301 volatile tsec_t *regs = (volatile tsec_t *)(TSEC_BASE_ADDR);
302 regs->tbipa = TBIPA_VALUE;
303 regs = (volatile tsec_t *)(TSEC_BASE_ADDR + TSEC_SIZE);
304 regs->tbipa = TBIPA_VALUE;
Eran Libertyf046ccd2005-07-28 10:08:46 -0500305 asm("sync");
wdenk3dd7f0f2005-04-04 23:43:44 +0000306 }
307
308 /* Reset MII (due to new addresses) */
309 priv->phyregs->miimcfg = MIIMCFG_RESET;
Eran Libertyf046ccd2005-07-28 10:08:46 -0500310 asm("sync");
wdenk3dd7f0f2005-04-04 23:43:44 +0000311 priv->phyregs->miimcfg = MIIMCFG_INIT_VALUE;
Eran Libertyf046ccd2005-07-28 10:08:46 -0500312 asm("sync");
wdenk3dd7f0f2005-04-04 23:43:44 +0000313 while(priv->phyregs->miimind & MIIMIND_BUSY);
wdenk42d1f032003-10-15 23:53:47 +0000314
wdenk97d80fc2004-06-09 00:34:46 +0000315 if(0 == relocated)
316 relocate_cmds();
wdenk42d1f032003-10-15 23:53:47 +0000317
wdenk97d80fc2004-06-09 00:34:46 +0000318 /* Get the cmd structure corresponding to the attached
319 * PHY */
320 curphy = get_phy_info(dev);
wdenk42d1f032003-10-15 23:53:47 +0000321
wdenk97d80fc2004-06-09 00:34:46 +0000322 if(NULL == curphy) {
323 printf("%s: No PHY found\n", dev->name);
wdenk42d1f032003-10-15 23:53:47 +0000324
wdenk97d80fc2004-06-09 00:34:46 +0000325 return 0;
wdenk42d1f032003-10-15 23:53:47 +0000326 }
327
wdenk97d80fc2004-06-09 00:34:46 +0000328 priv->phyinfo = curphy;
wdenk42d1f032003-10-15 23:53:47 +0000329
wdenk97d80fc2004-06-09 00:34:46 +0000330 phy_run_commands(priv, priv->phyinfo->config);
wdenk42d1f032003-10-15 23:53:47 +0000331
wdenk97d80fc2004-06-09 00:34:46 +0000332 return 1;
wdenk42d1f032003-10-15 23:53:47 +0000333}
334
335
wdenk97d80fc2004-06-09 00:34:46 +0000336/* Returns which value to write to the control register. */
337/* For 10/100, the value is slightly different */
338uint mii_cr_init(uint mii_reg, struct tsec_private *priv)
339{
Jon Loeligerd9b94f22005-07-25 14:05:07 -0500340 if(priv->flags & TSEC_GIGABIT)
wdenk97d80fc2004-06-09 00:34:46 +0000341 return MIIM_CONTROL_INIT;
342 else
343 return MIIM_CR_INIT;
344}
345
346
347/* Parse the status register for link, and then do
348 * auto-negotiation */
349uint mii_parse_sr(uint mii_reg, struct tsec_private *priv)
350{
Stefan Roese5810dc32005-09-21 18:20:22 +0200351 /*
352 * Wait if PHY is capable of autonegotiation and autonegotiation is not complete
353 */
354 mii_reg = read_phy_reg(priv, MIIM_STATUS);
355 if ((mii_reg & PHY_BMSR_AUTN_ABLE) && !(mii_reg & PHY_BMSR_AUTN_COMP)) {
356 int i = 0;
wdenk97d80fc2004-06-09 00:34:46 +0000357
Stefan Roese5810dc32005-09-21 18:20:22 +0200358 puts ("Waiting for PHY auto negotiation to complete");
359 while (!((mii_reg & PHY_BMSR_AUTN_COMP) && (mii_reg & MIIM_STATUS_LINK))) {
360 /*
361 * Timeout reached ?
362 */
363 if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
364 puts (" TIMEOUT !\n");
365 priv->link = 0;
366 break;
367 }
wdenk97d80fc2004-06-09 00:34:46 +0000368
Stefan Roese5810dc32005-09-21 18:20:22 +0200369 if ((i++ % 1000) == 0) {
370 putc ('.');
371 }
372 udelay (1000); /* 1 ms */
wdenk97d80fc2004-06-09 00:34:46 +0000373 mii_reg = read_phy_reg(priv, MIIM_STATUS);
Stefan Roese5810dc32005-09-21 18:20:22 +0200374 }
375 puts (" done\n");
376 priv->link = 1;
377 udelay (500000); /* another 500 ms (results in faster booting) */
378 } else {
379 priv->link = 1;
wdenk97d80fc2004-06-09 00:34:46 +0000380 }
381
382 return 0;
383}
384
385
386/* Parse the 88E1011's status register for speed and duplex
387 * information */
388uint mii_parse_88E1011_psr(uint mii_reg, struct tsec_private *priv)
389{
390 uint speed;
391
Stefan Roese5810dc32005-09-21 18:20:22 +0200392 mii_reg = read_phy_reg(priv, MIIM_88E1011_PHY_STATUS);
393
394 if (!((mii_reg & MIIM_88E1011_PHYSTAT_SPDDONE) &&
395 (mii_reg & MIIM_88E1011_PHYSTAT_LINK))) {
396 int i = 0;
397
398 puts ("Waiting for PHY realtime link");
399 while (!((mii_reg & MIIM_88E1011_PHYSTAT_SPDDONE) &&
400 (mii_reg & MIIM_88E1011_PHYSTAT_LINK))) {
401 /*
402 * Timeout reached ?
403 */
404 if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
405 puts (" TIMEOUT !\n");
406 priv->link = 0;
407 break;
408 }
409
410 if ((i++ % 1000) == 0) {
411 putc ('.');
412 }
413 udelay (1000); /* 1 ms */
414 mii_reg = read_phy_reg(priv, MIIM_88E1011_PHY_STATUS);
415 }
416 puts (" done\n");
417 udelay (500000); /* another 500 ms (results in faster booting) */
418 }
419
wdenk97d80fc2004-06-09 00:34:46 +0000420 if(mii_reg & MIIM_88E1011_PHYSTAT_DUPLEX)
421 priv->duplexity = 1;
422 else
423 priv->duplexity = 0;
424
425 speed = (mii_reg &MIIM_88E1011_PHYSTAT_SPEED);
426
427 switch(speed) {
428 case MIIM_88E1011_PHYSTAT_GBIT:
429 priv->speed = 1000;
430 break;
431 case MIIM_88E1011_PHYSTAT_100:
432 priv->speed = 100;
433 break;
434 default:
435 priv->speed = 10;
436 }
437
438 return 0;
439}
440
441
442/* Parse the cis8201's status register for speed and duplex
443 * information */
444uint mii_parse_cis8201(uint mii_reg, struct tsec_private *priv)
445{
446 uint speed;
447
448 if(mii_reg & MIIM_CIS8201_AUXCONSTAT_DUPLEX)
449 priv->duplexity = 1;
450 else
451 priv->duplexity = 0;
452
453 speed = mii_reg & MIIM_CIS8201_AUXCONSTAT_SPEED;
454 switch(speed) {
455 case MIIM_CIS8201_AUXCONSTAT_GBIT:
456 priv->speed = 1000;
457 break;
458 case MIIM_CIS8201_AUXCONSTAT_100:
459 priv->speed = 100;
460 break;
461 default:
462 priv->speed = 10;
463 break;
464 }
465
466 return 0;
467}
468
469
470/* Parse the DM9161's status register for speed and duplex
471 * information */
472uint mii_parse_dm9161_scsr(uint mii_reg, struct tsec_private *priv)
473{
474 if(mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_100H))
475 priv->speed = 100;
476 else
477 priv->speed = 10;
478
479 if(mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_10F))
480 priv->duplexity = 1;
481 else
482 priv->duplexity = 0;
483
484 return 0;
485}
486
487
488/* Hack to write all 4 PHYs with the LED values */
489uint mii_cis8204_fixled(uint mii_reg, struct tsec_private *priv)
490{
491 uint phyid;
492 volatile tsec_t *regbase = priv->phyregs;
493 int timeout=1000000;
494
495 for(phyid=0;phyid<4;phyid++) {
496 regbase->miimadd = (phyid << 8) | mii_reg;
497 regbase->miimcon = MIIM_CIS8204_SLEDCON_INIT;
Eran Libertyf046ccd2005-07-28 10:08:46 -0500498 asm("sync");
wdenk97d80fc2004-06-09 00:34:46 +0000499
500 timeout=1000000;
501 while((regbase->miimind & MIIMIND_BUSY) && timeout--);
502 }
503
504 return MIIM_CIS8204_SLEDCON_INIT;
505}
506
Jon Loeligerd9b94f22005-07-25 14:05:07 -0500507uint mii_cis8204_setmode(uint mii_reg, struct tsec_private *priv)
508{
509 if (priv->flags & TSEC_REDUCED)
510 return MIIM_CIS8204_EPHYCON_INIT | MIIM_CIS8204_EPHYCON_RGMII;
511 else
512 return MIIM_CIS8204_EPHYCON_INIT;
513}
wdenk97d80fc2004-06-09 00:34:46 +0000514
515/* Initialized required registers to appropriate values, zeroing
516 * those we don't care about (unless zero is bad, in which case,
517 * choose a more appropriate value) */
518static void init_registers(volatile tsec_t *regs)
wdenk42d1f032003-10-15 23:53:47 +0000519{
520 /* Clear IEVENT */
521 regs->ievent = IEVENT_INIT_CLEAR;
522
523 regs->imask = IMASK_INIT_CLEAR;
524
525 regs->hash.iaddr0 = 0;
526 regs->hash.iaddr1 = 0;
527 regs->hash.iaddr2 = 0;
528 regs->hash.iaddr3 = 0;
529 regs->hash.iaddr4 = 0;
530 regs->hash.iaddr5 = 0;
531 regs->hash.iaddr6 = 0;
532 regs->hash.iaddr7 = 0;
533
534 regs->hash.gaddr0 = 0;
535 regs->hash.gaddr1 = 0;
536 regs->hash.gaddr2 = 0;
537 regs->hash.gaddr3 = 0;
538 regs->hash.gaddr4 = 0;
539 regs->hash.gaddr5 = 0;
540 regs->hash.gaddr6 = 0;
541 regs->hash.gaddr7 = 0;
542
543 regs->rctrl = 0x00000000;
544
545 /* Init RMON mib registers */
546 memset((void *)&(regs->rmon), 0, sizeof(rmon_mib_t));
547
548 regs->rmon.cam1 = 0xffffffff;
549 regs->rmon.cam2 = 0xffffffff;
550
551 regs->mrblr = MRBLR_INIT_SETTINGS;
552
553 regs->minflr = MINFLR_INIT_SETTINGS;
554
555 regs->attr = ATTR_INIT_SETTINGS;
556 regs->attreli = ATTRELI_INIT_SETTINGS;
557
558}
559
wdenk97d80fc2004-06-09 00:34:46 +0000560
561/* Configure maccfg2 based on negotiated speed and duplex
562 * reported by PHY handling code */
563static void adjust_link(struct eth_device *dev)
564{
565 struct tsec_private *priv = (struct tsec_private *)dev->priv;
566 volatile tsec_t *regs = priv->regs;
567
568 if(priv->link) {
569 if(priv->duplexity != 0)
570 regs->maccfg2 |= MACCFG2_FULL_DUPLEX;
571 else
572 regs->maccfg2 &= ~(MACCFG2_FULL_DUPLEX);
573
574 switch(priv->speed) {
575 case 1000:
576 regs->maccfg2 = ((regs->maccfg2&~(MACCFG2_IF))
577 | MACCFG2_GMII);
578 break;
579 case 100:
580 case 10:
581 regs->maccfg2 = ((regs->maccfg2&~(MACCFG2_IF))
582 | MACCFG2_MII);
Jon Loeligerd9b94f22005-07-25 14:05:07 -0500583
Jon Loeligerde1d0a62005-08-01 13:20:47 -0500584 /* If We're in reduced mode, we need
585 * to say whether we're 10 or 100 MB.
586 */
587 if ((priv->speed == 100)
588 && (priv->flags & TSEC_REDUCED))
Jon Loeligerd9b94f22005-07-25 14:05:07 -0500589 regs->ecntrl |= ECNTRL_R100;
590 else
591 regs->ecntrl &= ~(ECNTRL_R100);
wdenk97d80fc2004-06-09 00:34:46 +0000592 break;
593 default:
594 printf("%s: Speed was bad\n", dev->name);
595 break;
596 }
597
598 printf("Speed: %d, %s duplex\n", priv->speed,
599 (priv->duplexity) ? "full" : "half");
600
601 } else {
602 printf("%s: No link.\n", dev->name);
603 }
604}
605
606
607/* Set up the buffers and their descriptors, and bring up the
608 * interface */
609static void startup_tsec(struct eth_device *dev)
wdenk42d1f032003-10-15 23:53:47 +0000610{
611 int i;
wdenk97d80fc2004-06-09 00:34:46 +0000612 struct tsec_private *priv = (struct tsec_private *)dev->priv;
613 volatile tsec_t *regs = priv->regs;
wdenk42d1f032003-10-15 23:53:47 +0000614
615 /* Point to the buffer descriptors */
616 regs->tbase = (unsigned int)(&rtx.txbd[txIdx]);
617 regs->rbase = (unsigned int)(&rtx.rxbd[rxIdx]);
618
619 /* Initialize the Rx Buffer descriptors */
620 for (i = 0; i < PKTBUFSRX; i++) {
621 rtx.rxbd[i].status = RXBD_EMPTY;
622 rtx.rxbd[i].length = 0;
623 rtx.rxbd[i].bufPtr = (uint)NetRxPackets[i];
624 }
625 rtx.rxbd[PKTBUFSRX -1].status |= RXBD_WRAP;
626
627 /* Initialize the TX Buffer Descriptors */
628 for(i=0; i<TX_BUF_CNT; i++) {
629 rtx.txbd[i].status = 0;
630 rtx.txbd[i].length = 0;
631 rtx.txbd[i].bufPtr = 0;
632 }
633 rtx.txbd[TX_BUF_CNT -1].status |= TXBD_WRAP;
634
wdenk97d80fc2004-06-09 00:34:46 +0000635 /* Start up the PHY */
636 phy_run_commands(priv, priv->phyinfo->startup);
637 adjust_link(dev);
638
wdenk42d1f032003-10-15 23:53:47 +0000639 /* Enable Transmit and Receive */
640 regs->maccfg1 |= (MACCFG1_RX_EN | MACCFG1_TX_EN);
641
642 /* Tell the DMA it is clear to go */
643 regs->dmactrl |= DMACTRL_INIT_SETTINGS;
644 regs->tstat = TSTAT_CLEAR_THALT;
645 regs->dmactrl &= ~(DMACTRL_GRS | DMACTRL_GTS);
646}
647
wdenk9d46ea42005-03-14 23:56:42 +0000648/* This returns the status bits of the device. The return value
wdenk42d1f032003-10-15 23:53:47 +0000649 * is never checked, and this is what the 8260 driver did, so we
wdenk9d46ea42005-03-14 23:56:42 +0000650 * do the same. Presumably, this would be zero if there were no
wdenk42d1f032003-10-15 23:53:47 +0000651 * errors */
652static int tsec_send(struct eth_device* dev, volatile void *packet, int length)
653{
654 int i;
655 int result = 0;
wdenk97d80fc2004-06-09 00:34:46 +0000656 struct tsec_private *priv = (struct tsec_private *)dev->priv;
657 volatile tsec_t *regs = priv->regs;
wdenk42d1f032003-10-15 23:53:47 +0000658
659 /* Find an empty buffer descriptor */
660 for(i=0; rtx.txbd[txIdx].status & TXBD_READY; i++) {
661 if (i >= TOUT_LOOP) {
wdenk8b07a112004-07-10 21:45:47 +0000662 debug ("%s: tsec: tx buffers full\n", dev->name);
wdenk42d1f032003-10-15 23:53:47 +0000663 return result;
664 }
665 }
666
667 rtx.txbd[txIdx].bufPtr = (uint)packet;
668 rtx.txbd[txIdx].length = length;
669 rtx.txbd[txIdx].status |= (TXBD_READY | TXBD_LAST | TXBD_CRC | TXBD_INTERRUPT);
670
671 /* Tell the DMA to go */
672 regs->tstat = TSTAT_CLEAR_THALT;
673
674 /* Wait for buffer to be transmitted */
675 for(i=0; rtx.txbd[txIdx].status & TXBD_READY; i++) {
676 if (i >= TOUT_LOOP) {
wdenk8b07a112004-07-10 21:45:47 +0000677 debug ("%s: tsec: tx error\n", dev->name);
wdenk42d1f032003-10-15 23:53:47 +0000678 return result;
679 }
680 }
681
682 txIdx = (txIdx + 1) % TX_BUF_CNT;
683 result = rtx.txbd[txIdx].status & TXBD_STATS;
684
685 return result;
686}
687
688static int tsec_recv(struct eth_device* dev)
689{
690 int length;
wdenk97d80fc2004-06-09 00:34:46 +0000691 struct tsec_private *priv = (struct tsec_private *)dev->priv;
692 volatile tsec_t *regs = priv->regs;
wdenk42d1f032003-10-15 23:53:47 +0000693
694 while(!(rtx.rxbd[rxIdx].status & RXBD_EMPTY)) {
695
696 length = rtx.rxbd[rxIdx].length;
697
698 /* Send the packet up if there were no errors */
699 if (!(rtx.rxbd[rxIdx].status & RXBD_STATS)) {
700 NetReceive(NetRxPackets[rxIdx], length - 4);
wdenk97d80fc2004-06-09 00:34:46 +0000701 } else {
702 printf("Got error %x\n",
703 (rtx.rxbd[rxIdx].status & RXBD_STATS));
wdenk42d1f032003-10-15 23:53:47 +0000704 }
705
706 rtx.rxbd[rxIdx].length = 0;
707
708 /* Set the wrap bit if this is the last element in the list */
709 rtx.rxbd[rxIdx].status = RXBD_EMPTY | (((rxIdx + 1) == PKTBUFSRX) ? RXBD_WRAP : 0);
710
711 rxIdx = (rxIdx + 1) % PKTBUFSRX;
712 }
713
714 if(regs->ievent&IEVENT_BSY) {
715 regs->ievent = IEVENT_BSY;
716 regs->rstat = RSTAT_CLEAR_RHALT;
717 }
718
719 return -1;
720
721}
722
723
wdenk97d80fc2004-06-09 00:34:46 +0000724/* Stop the interface */
wdenk42d1f032003-10-15 23:53:47 +0000725static void tsec_halt(struct eth_device* dev)
726{
wdenk97d80fc2004-06-09 00:34:46 +0000727 struct tsec_private *priv = (struct tsec_private *)dev->priv;
728 volatile tsec_t *regs = priv->regs;
wdenk42d1f032003-10-15 23:53:47 +0000729
730 regs->dmactrl &= ~(DMACTRL_GRS | DMACTRL_GTS);
731 regs->dmactrl |= (DMACTRL_GRS | DMACTRL_GTS);
732
733 while(!(regs->ievent & (IEVENT_GRSC | IEVENT_GTSC)));
734
735 regs->maccfg1 &= ~(MACCFG1_TX_EN | MACCFG1_RX_EN);
736
wdenk97d80fc2004-06-09 00:34:46 +0000737 /* Shut down the PHY, as needed */
738 phy_run_commands(priv, priv->phyinfo->shutdown);
wdenk42d1f032003-10-15 23:53:47 +0000739}
wdenk7abf0c52004-04-18 21:45:42 +0000740
wdenk97d80fc2004-06-09 00:34:46 +0000741
742struct phy_info phy_info_M88E1011S = {
743 0x01410c6,
744 "Marvell 88E1011S",
745 4,
746 (struct phy_cmd[]) { /* config */
747 /* Reset and configure the PHY */
748 {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL},
749 {0x1d, 0x1f, NULL},
750 {0x1e, 0x200c, NULL},
751 {0x1d, 0x5, NULL},
752 {0x1e, 0x0, NULL},
753 {0x1e, 0x100, NULL},
754 {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL},
755 {MIIM_ANAR, MIIM_ANAR_INIT, NULL},
756 {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL},
757 {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init},
758 {miim_end,}
759 },
760 (struct phy_cmd[]) { /* startup */
761 /* Status is read once to clear old link state */
762 {MIIM_STATUS, miim_read, NULL},
763 /* Auto-negotiate */
764 {MIIM_STATUS, miim_read, &mii_parse_sr},
765 /* Read the status */
766 {MIIM_88E1011_PHY_STATUS, miim_read, &mii_parse_88E1011_psr},
767 {miim_end,}
768 },
769 (struct phy_cmd[]) { /* shutdown */
770 {miim_end,}
771 },
772};
773
wdenk9d46ea42005-03-14 23:56:42 +0000774struct phy_info phy_info_M88E1111S = {
775 0x01410cc,
776 "Marvell 88E1111S",
777 4,
778 (struct phy_cmd[]) { /* config */
779 /* Reset and configure the PHY */
780 {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL},
781 {0x1d, 0x1f, NULL},
782 {0x1e, 0x200c, NULL},
783 {0x1d, 0x5, NULL},
784 {0x1e, 0x0, NULL},
785 {0x1e, 0x100, NULL},
786 {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL},
787 {MIIM_ANAR, MIIM_ANAR_INIT, NULL},
788 {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL},
789 {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init},
790 {miim_end,}
791 },
792 (struct phy_cmd[]) { /* startup */
793 /* Status is read once to clear old link state */
794 {MIIM_STATUS, miim_read, NULL},
795 /* Auto-negotiate */
796 {MIIM_STATUS, miim_read, &mii_parse_sr},
797 /* Read the status */
798 {MIIM_88E1011_PHY_STATUS, miim_read, &mii_parse_88E1011_psr},
799 {miim_end,}
800 },
801 (struct phy_cmd[]) { /* shutdown */
802 {miim_end,}
803 },
804};
805
wdenk97d80fc2004-06-09 00:34:46 +0000806struct phy_info phy_info_cis8204 = {
807 0x3f11,
808 "Cicada Cis8204",
809 6,
810 (struct phy_cmd[]) { /* config */
811 /* Override PHY config settings */
812 {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL},
813 /* Configure some basic stuff */
814 {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init},
815 {MIIM_CIS8204_SLED_CON, MIIM_CIS8204_SLEDCON_INIT, &mii_cis8204_fixled},
Jon Loeligerd9b94f22005-07-25 14:05:07 -0500816 {MIIM_CIS8204_EPHY_CON, MIIM_CIS8204_EPHYCON_INIT, &mii_cis8204_setmode},
wdenk97d80fc2004-06-09 00:34:46 +0000817 {miim_end,}
818 },
819 (struct phy_cmd[]) { /* startup */
820 /* Read the Status (2x to make sure link is right) */
821 {MIIM_STATUS, miim_read, NULL},
822 /* Auto-negotiate */
823 {MIIM_STATUS, miim_read, &mii_parse_sr},
824 /* Read the status */
825 {MIIM_CIS8201_AUX_CONSTAT, miim_read, &mii_parse_cis8201},
826 {miim_end,}
827 },
828 (struct phy_cmd[]) { /* shutdown */
829 {miim_end,}
830 },
831};
832
833/* Cicada 8201 */
834struct phy_info phy_info_cis8201 = {
835 0xfc41,
836 "CIS8201",
837 4,
838 (struct phy_cmd[]) { /* config */
839 /* Override PHY config settings */
840 {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL},
841 /* Set up the interface mode */
842 {MIIM_CIS8201_EXT_CON1, MIIM_CIS8201_EXTCON1_INIT, NULL},
843 /* Configure some basic stuff */
844 {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init},
845 {miim_end,}
846 },
847 (struct phy_cmd[]) { /* startup */
848 /* Read the Status (2x to make sure link is right) */
849 {MIIM_STATUS, miim_read, NULL},
850 /* Auto-negotiate */
851 {MIIM_STATUS, miim_read, &mii_parse_sr},
852 /* Read the status */
853 {MIIM_CIS8201_AUX_CONSTAT, miim_read, &mii_parse_cis8201},
854 {miim_end,}
855 },
856 (struct phy_cmd[]) { /* shutdown */
857 {miim_end,}
858 },
859};
860
861
862struct phy_info phy_info_dm9161 = {
863 0x0181b88,
864 "Davicom DM9161E",
865 4,
866 (struct phy_cmd[]) { /* config */
867 {MIIM_CONTROL, MIIM_DM9161_CR_STOP, NULL},
868 /* Do not bypass the scrambler/descrambler */
869 {MIIM_DM9161_SCR, MIIM_DM9161_SCR_INIT, NULL},
870 /* Clear 10BTCSR to default */
871 {MIIM_DM9161_10BTCSR, MIIM_DM9161_10BTCSR_INIT, NULL},
872 /* Configure some basic stuff */
873 {MIIM_CONTROL, MIIM_CR_INIT, NULL},
874 /* Restart Auto Negotiation */
875 {MIIM_CONTROL, MIIM_DM9161_CR_RSTAN, NULL},
876 {miim_end,}
877 },
878 (struct phy_cmd[]) { /* startup */
879 /* Status is read once to clear old link state */
880 {MIIM_STATUS, miim_read, NULL},
881 /* Auto-negotiate */
882 {MIIM_STATUS, miim_read, &mii_parse_sr},
883 /* Read the status */
884 {MIIM_DM9161_SCSR, miim_read, &mii_parse_dm9161_scsr},
885 {miim_end,}
886 },
887 (struct phy_cmd[]) { /* shutdown */
888 {miim_end,}
889 },
890};
891
wdenk3dd7f0f2005-04-04 23:43:44 +0000892uint mii_parse_lxt971_sr2(uint mii_reg, struct tsec_private *priv)
893{
wdenk3c2b3d42005-04-05 23:32:21 +0000894 unsigned int speed;
895 if (priv->link) {
896 speed = mii_reg & MIIM_LXT971_SR2_SPEED_MASK;
wdenk3dd7f0f2005-04-04 23:43:44 +0000897
wdenk3c2b3d42005-04-05 23:32:21 +0000898 switch (speed) {
899 case MIIM_LXT971_SR2_10HDX:
900 priv->speed = 10;
901 priv->duplexity = 0;
902 break;
903 case MIIM_LXT971_SR2_10FDX:
904 priv->speed = 10;
905 priv->duplexity = 1;
906 break;
907 case MIIM_LXT971_SR2_100HDX:
908 priv->speed = 100;
909 priv->duplexity = 0;
910 default:
911 priv->speed = 100;
912 priv->duplexity = 1;
913 break;
914 }
915 } else {
916 priv->speed = 0;
917 priv->duplexity = 0;
918 }
wdenk3dd7f0f2005-04-04 23:43:44 +0000919
wdenk3c2b3d42005-04-05 23:32:21 +0000920 return 0;
wdenk3dd7f0f2005-04-04 23:43:44 +0000921}
922
wdenk9d46ea42005-03-14 23:56:42 +0000923static struct phy_info phy_info_lxt971 = {
924 0x0001378e,
925 "LXT971",
926 4,
927 (struct phy_cmd []) { /* config */
wdenk3dd7f0f2005-04-04 23:43:44 +0000928 { MIIM_CR, MIIM_CR_INIT, mii_cr_init }, /* autonegotiate */
wdenk9d46ea42005-03-14 23:56:42 +0000929 { miim_end, }
930 },
931 (struct phy_cmd []) { /* startup - enable interrupts */
932 /* { 0x12, 0x00f2, NULL }, */
wdenk9d46ea42005-03-14 23:56:42 +0000933 { MIIM_STATUS, miim_read, NULL },
wdenk3dd7f0f2005-04-04 23:43:44 +0000934 { MIIM_STATUS, miim_read, &mii_parse_sr },
935 { MIIM_LXT971_SR2, miim_read, &mii_parse_lxt971_sr2 },
wdenk9d46ea42005-03-14 23:56:42 +0000936 { miim_end, }
937 },
938 (struct phy_cmd []) { /* shutdown - disable interrupts */
939 { miim_end, }
940 },
941};
942
wdenk97d80fc2004-06-09 00:34:46 +0000943struct phy_info *phy_info[] = {
944#if 0
945 &phy_info_cis8201,
946#endif
947 &phy_info_cis8204,
948 &phy_info_M88E1011S,
wdenk9d46ea42005-03-14 23:56:42 +0000949 &phy_info_M88E1111S,
wdenk97d80fc2004-06-09 00:34:46 +0000950 &phy_info_dm9161,
wdenk9d46ea42005-03-14 23:56:42 +0000951 &phy_info_lxt971,
wdenk97d80fc2004-06-09 00:34:46 +0000952 NULL
953};
954
955
956/* Grab the identifier of the device's PHY, and search through
wdenk9d46ea42005-03-14 23:56:42 +0000957 * all of the known PHYs to see if one matches. If so, return
wdenk97d80fc2004-06-09 00:34:46 +0000958 * it, if not, return NULL */
959struct phy_info * get_phy_info(struct eth_device *dev)
960{
961 struct tsec_private *priv = (struct tsec_private *)dev->priv;
962 uint phy_reg, phy_ID;
963 int i;
964 struct phy_info *theInfo = NULL;
965
966 /* Grab the bits from PHYIR1, and put them in the upper half */
967 phy_reg = read_phy_reg(priv, MIIM_PHYIR1);
968 phy_ID = (phy_reg & 0xffff) << 16;
969
970 /* Grab the bits from PHYIR2, and put them in the lower half */
971 phy_reg = read_phy_reg(priv, MIIM_PHYIR2);
972 phy_ID |= (phy_reg & 0xffff);
973
974 /* loop through all the known PHY types, and find one that */
975 /* matches the ID we read from the PHY. */
976 for(i=0; phy_info[i]; i++) {
977 if(phy_info[i]->id == (phy_ID >> phy_info[i]->shift))
978 theInfo = phy_info[i];
979 }
980
981 if(theInfo == NULL)
982 {
983 printf("%s: PHY id %x is not supported!\n", dev->name, phy_ID);
984 return NULL;
985 } else {
Stefan Roese5810dc32005-09-21 18:20:22 +0200986 debug("%s: PHY is %s (%x)\n", dev->name, theInfo->name, phy_ID);
wdenk97d80fc2004-06-09 00:34:46 +0000987 }
988
989 return theInfo;
990}
991
992
993/* Execute the given series of commands on the given device's
994 * PHY, running functions as necessary*/
995void phy_run_commands(struct tsec_private *priv, struct phy_cmd *cmd)
996{
997 int i;
998 uint result;
999 volatile tsec_t *phyregs = priv->phyregs;
1000
1001 phyregs->miimcfg = MIIMCFG_RESET;
1002
1003 phyregs->miimcfg = MIIMCFG_INIT_VALUE;
1004
1005 while(phyregs->miimind & MIIMIND_BUSY);
1006
1007 for(i=0;cmd->mii_reg != miim_end;i++) {
1008 if(cmd->mii_data == miim_read) {
1009 result = read_phy_reg(priv, cmd->mii_reg);
1010
1011 if(cmd->funct != NULL)
1012 (*(cmd->funct))(result, priv);
1013
1014 } else {
1015 if(cmd->funct != NULL)
1016 result = (*(cmd->funct))(cmd->mii_reg, priv);
1017 else
1018 result = cmd->mii_data;
1019
1020 write_phy_reg(priv, cmd->mii_reg, result);
1021
1022 }
1023 cmd++;
1024 }
1025}
1026
1027
1028/* Relocate the function pointers in the phy cmd lists */
1029static void relocate_cmds(void)
1030{
1031 struct phy_cmd **cmdlistptr;
1032 struct phy_cmd *cmd;
1033 int i,j,k;
1034 DECLARE_GLOBAL_DATA_PTR;
1035
1036 for(i=0; phy_info[i]; i++) {
1037 /* First thing's first: relocate the pointers to the
1038 * PHY command structures (the structs were done) */
1039 phy_info[i] = (struct phy_info *) ((uint)phy_info[i]
1040 + gd->reloc_off);
1041 phy_info[i]->name += gd->reloc_off;
1042 phy_info[i]->config =
1043 (struct phy_cmd *)((uint)phy_info[i]->config
1044 + gd->reloc_off);
1045 phy_info[i]->startup =
1046 (struct phy_cmd *)((uint)phy_info[i]->startup
1047 + gd->reloc_off);
1048 phy_info[i]->shutdown =
1049 (struct phy_cmd *)((uint)phy_info[i]->shutdown
1050 + gd->reloc_off);
1051
1052 cmdlistptr = &phy_info[i]->config;
1053 j=0;
1054 for(;cmdlistptr <= &phy_info[i]->shutdown;cmdlistptr++) {
1055 k=0;
1056 for(cmd=*cmdlistptr;cmd->mii_reg != miim_end;cmd++) {
1057 /* Only relocate non-NULL pointers */
1058 if(cmd->funct)
1059 cmd->funct += gd->reloc_off;
1060
1061 k++;
1062 }
1063 j++;
1064 }
1065 }
1066
1067 relocated = 1;
1068}
1069
1070
Marian Balakowicz63ff0042005-10-28 22:30:33 +02001071#if defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII) \
1072 && !defined(BITBANGMII)
wdenk97d80fc2004-06-09 00:34:46 +00001073
1074struct tsec_private * get_priv_for_phy(unsigned char phyaddr)
1075{
1076 int i;
1077
1078 for(i=0;i<MAXCONTROLLERS;i++) {
1079 if(privlist[i]->phyaddr == phyaddr)
1080 return privlist[i];
1081 }
1082
1083 return NULL;
1084}
1085
wdenk7abf0c52004-04-18 21:45:42 +00001086/*
1087 * Read a MII PHY register.
1088 *
1089 * Returns:
wdenk97d80fc2004-06-09 00:34:46 +00001090 * 0 on success
wdenk7abf0c52004-04-18 21:45:42 +00001091 */
Marian Balakowicz63ff0042005-10-28 22:30:33 +02001092static int tsec_miiphy_read(char *devname, unsigned char addr,
1093 unsigned char reg, unsigned short *value)
wdenk7abf0c52004-04-18 21:45:42 +00001094{
wdenk97d80fc2004-06-09 00:34:46 +00001095 unsigned short ret;
1096 struct tsec_private *priv = get_priv_for_phy(addr);
wdenk7abf0c52004-04-18 21:45:42 +00001097
wdenk97d80fc2004-06-09 00:34:46 +00001098 if(NULL == priv) {
1099 printf("Can't read PHY at address %d\n", addr);
1100 return -1;
1101 }
1102
1103 ret = (unsigned short)read_phy_reg(priv, reg);
1104 *value = ret;
wdenk7abf0c52004-04-18 21:45:42 +00001105
1106 return 0;
1107}
1108
1109/*
1110 * Write a MII PHY register.
1111 *
1112 * Returns:
wdenk97d80fc2004-06-09 00:34:46 +00001113 * 0 on success
wdenk7abf0c52004-04-18 21:45:42 +00001114 */
Marian Balakowicz63ff0042005-10-28 22:30:33 +02001115static int tsec_miiphy_write(char *devname, unsigned char addr,
1116 unsigned char reg, unsigned short value)
wdenk7abf0c52004-04-18 21:45:42 +00001117{
wdenk97d80fc2004-06-09 00:34:46 +00001118 struct tsec_private *priv = get_priv_for_phy(addr);
wdenk7abf0c52004-04-18 21:45:42 +00001119
wdenk97d80fc2004-06-09 00:34:46 +00001120 if(NULL == priv) {
1121 printf("Can't write PHY at address %d\n", addr);
1122 return -1;
1123 }
1124
1125 write_phy_reg(priv, reg, value);
wdenk7abf0c52004-04-18 21:45:42 +00001126
1127 return 0;
1128}
wdenk97d80fc2004-06-09 00:34:46 +00001129
Marian Balakowicz63ff0042005-10-28 22:30:33 +02001130#endif /* defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII)
1131 && !defined(BITBANGMII) */
wdenk97d80fc2004-06-09 00:34:46 +00001132
wdenk42d1f032003-10-15 23:53:47 +00001133#endif /* CONFIG_TSEC_ENET */