treewide: drop executable file attrib for non-executable files
[oweals/u-boot_mod.git] / u-boot / board / ar7240 / common / athrs27_phy.c
1
2 /*
3  * This file is subject to the terms and conditions of the GNU General Public
4  * License.  See the file "COPYING" in the main directory of this archive
5  * for more details.
6  *
7  * Copyright © 2007 Atheros Communications, Inc.,  All Rights Reserved.
8  */
9
10 /*
11  * Manage the atheros ethernet PHY.
12  *
13  * All definitions in this file are operating system independent!
14  */
15
16 #include <config.h>
17 #include <linux/types.h>
18 #include <common.h>
19 #include <miiphy.h>
20 #include "phy.h"
21 #include <asm/addrspace.h>
22 #include "ar7240_soc.h"
23 #include "athrs27_phy.h"
24
25 /* PHY selections and access functions */
26 #define DRV_PRINT(DBG_SW,X)
27
28 #define ATHR_LAN_PORT_VLAN      1
29 #define ATHR_WAN_PORT_VLAN      2
30 #define ENET_UNIT_LAN           1
31 #define ENET_UNIT_WAN           0
32
33 #define ATHR_PHY0_ADDR          0x0
34 #define ATHR_PHY1_ADDR          0x1
35 #define ATHR_PHY2_ADDR          0x2
36 #define ATHR_PHY3_ADDR          0x3
37 #define ATHR_PHY4_ADDR          0x4
38
39 #define TRUE    1
40 #define FALSE   0
41
42 #define MODULE_NAME     "ATHRS27"
43
44 /*
45  * Track per-PHY port information.
46  */
47 typedef struct {
48         int isEnetPort;                         /* normal enet port */
49         int isPhyAlive;                         /* last known state of link */
50         int ethUnit;                            /* MAC associated with this phy port */
51         uint32_t phyBase;
52         uint32_t phyAddr;                       /* PHY registers associated with this phy port */
53         uint32_t VLANTableSetting;      /* Value to be written to VLAN table */
54 } athrPhyInfo_t;
55
56 /*
57  * Per-PHY information, indexed by PHY unit number.
58  */
59 static athrPhyInfo_t athrPhyInfo[] = {
60         /* port 1 -- LAN port 1 */
61         { TRUE, FALSE, ENET_UNIT_LAN, 0, ATHR_PHY0_ADDR, ATHR_LAN_PORT_VLAN },
62
63         /* port 2 -- LAN port 2 */
64         { TRUE, FALSE, ENET_UNIT_LAN, 0, ATHR_PHY1_ADDR, ATHR_LAN_PORT_VLAN },
65
66         /* port 3 -- LAN port 3 */
67         { TRUE, FALSE, ENET_UNIT_LAN, 0, ATHR_PHY2_ADDR, ATHR_LAN_PORT_VLAN },
68
69         /* port 4 --  LAN port 4 */
70         { TRUE, FALSE, ENET_UNIT_LAN, 0, ATHR_PHY3_ADDR, ATHR_LAN_PORT_VLAN },
71
72         /* port 5 -- WAN Port 5 */
73         { TRUE, FALSE, ENET_UNIT_WAN, 0, ATHR_PHY4_ADDR, ATHR_LAN_PORT_VLAN },
74
75         /* port 0 -- cpu port 0 */
76         { FALSE, TRUE, ENET_UNIT_LAN, 0, 0x00, ATHR_LAN_PORT_VLAN },
77 };
78
79 #define ATHR_PHY_MAX 5
80
81 /* Convenience macros to access myPhyInfo */
82 #define ATHR_IS_ENET_PORT(phyUnit)                      (athrPhyInfo[phyUnit].isEnetPort)
83 #define ATHR_IS_PHY_ALIVE(phyUnit)                      (athrPhyInfo[phyUnit].isPhyAlive)
84 #define ATHR_ETHUNIT(phyUnit)                           (athrPhyInfo[phyUnit].ethUnit)
85 #define ATHR_PHYBASE(phyUnit)                           (athrPhyInfo[phyUnit].phyBase)
86 #define ATHR_PHYADDR(phyUnit)                           (athrPhyInfo[phyUnit].phyAddr)
87 #define ATHR_VLAN_TABLE_SETTING(phyUnit)        (athrPhyInfo[phyUnit].VLANTableSetting)
88 #define ATHR_IS_ETHUNIT(phyUnit, ethUnit)       (ATHR_IS_ENET_PORT(phyUnit) && ATHR_ETHUNIT(phyUnit) == (ethUnit))
89 #define ATHR_IS_WAN_PORT(phyUnit)                       (!(ATHR_ETHUNIT(phyUnit) == ENET_UNIT_LAN))
90
91 /* Forward references */
92 int athrs27_phy_is_link_alive(int phyUnit);
93 uint32_t athrs27_reg_read(uint32_t reg_addr);
94 void athrs27_reg_write(uint32_t reg_addr, uint32_t reg_val);
95 unsigned int s27_rd_phy(unsigned int phy_addr, unsigned int reg_addr);
96 void s27_wr_phy(unsigned int phy_addr, unsigned int reg_addr, unsigned int write_data);
97 void athrs27_reg_rmw(unsigned int s27_addr, unsigned int s27_write_data);
98
99 void athrs27_powersave_off(int phy_addr){
100         s27_wr_phy(phy_addr, ATHR_DEBUG_PORT_ADDRESS, 0x29);
101         s27_wr_phy(phy_addr, ATHR_DEBUG_PORT_DATA, 0x36c0);
102 }
103
104 void athrs27_sleep_off(int phy_addr){
105         s27_wr_phy(phy_addr, ATHR_DEBUG_PORT_ADDRESS, 0xb);
106         s27_wr_phy(phy_addr, ATHR_DEBUG_PORT_DATA, 0x3c00);
107 }
108
109 void athrs27_force_100M(int phyAddr, int duplex){
110         /*
111         *  Force MDI and MDX to alternate ports
112         *  Phy 0,2 and 4 -- MDI
113         *  Phy 1 and 3 -- MDX
114         */
115         if(phyAddr % 2){
116                 s27_wr_phy(phyAddr, ATHR_PHY_FUNC_CONTROL, 0x820);
117         } else {
118                 s27_wr_phy(phyAddr, ATHR_PHY_FUNC_CONTROL, 0x800);
119         }
120
121         s27_wr_phy(phyAddr, 0x1d, 0x29);
122         s27_wr_phy(phyAddr, 0x1e, 0x0);
123         s27_wr_phy(phyAddr, 0x10, 0xc60);
124         s27_wr_phy(phyAddr, ATHR_PHY_CONTROL, (0xa000 | (duplex << 8)));
125 }
126
127 void athrs27_force_10M(int phyAddr, int duplex){
128         athrs27_powersave_off(phyAddr);
129         athrs27_sleep_off(phyAddr);
130
131         s27_wr_phy(phyAddr, ATHR_PHY_CONTROL, (0x8000 | (duplex << 8)));
132 }
133
134 int athrs27_reg_init(void){
135         /* if using header for register configuration, we have to     */
136         /* configure s27 register after frame transmission is enabled */
137         athrs27_reg_rmw(0x8, (1 << 28));  /* Set WAN port is connected to GE0 */
138
139 #if defined(S27_FORCE_100M)
140         athrs27_force_100M(ATHR_PHY4_ADDR, 1);
141 #elif defined(S27_FORCE_10M)
142         athrs27_force_10M(ATHR_PHY4_ADDR, 1);
143 #else
144         s27_wr_phy(ATHR_PHY4_ADDR,ATHR_PHY_CONTROL, 0x9000);
145 #endif
146
147 #ifdef S27_PHY_DEBUG
148         printf(MODULE_NAME":OPERATIONAL_MODE_REG0:%x\n", athrs27_reg_read(OPERATIONAL_MODE_REG0));
149         printf(MODULE_NAME":REG 0x4-->:%x\n", athrs27_reg_read(0x4));
150         printf(MODULE_NAME":REG 0x2c-->:%x\n", athrs27_reg_read(0x2c));
151         printf(MODULE_NAME":REG 0x8-->:%x\n", athrs27_reg_read(0x8));
152 #endif
153
154         return(0);
155 }
156  
157 int athrs27_reg_init_lan(void){
158         int i = 60;
159         int phyUnit;
160         uint32_t phyAddr = 0;
161         #if S27_PHY_DEBUG
162         uint32_t rd_val;
163         #endif
164
165         /* reset switch */
166 #if S27_PHY_DEBUG
167         printf(MODULE_NAME ": resetting s27\n");
168 #endif
169
170         athrs27_reg_write(0x0, athrs27_reg_read(0x0) | 0x80000000);
171
172         while(i--){
173                 sysMsDelay(100);
174
175                 if(!(athrs27_reg_read(0x0) & 0x80000000)){
176                         break;
177                 }
178         }
179
180 #if S27_PHY_DEBUG
181         printf(MODULE_NAME ": s27 reset done\n");
182 #endif
183
184         athrs27_reg_write(PORT_STATUS_REGISTER0, 0x4e);
185
186         athrs27_reg_rmw(OPERATIONAL_MODE_REG0, (1 << 6));  /* Set GMII mode */
187
188         if(is_emu() || is_wasp()){
189                 athrs27_reg_rmw(0x2c, ((1 << 26) | (1 << 16) | 0x1)); /* FiX ME: EBU debug */
190         }
191
192         for(phyUnit = 0; phyUnit < ATHR_PHY_MAX; phyUnit++){
193                 phyAddr = ATHR_PHYADDR(phyUnit);
194
195 #if defined(S27_FORCE_100M)
196                 athrs27_force_100M(phyAddr, 1);
197 #elif defined(S27_FORCE_10M)
198                 athrs27_force_10M(phyAddr, 1);
199 #else
200                 s27_wr_phy(phyAddr, ATHR_PHY_CONTROL, 0x9000);
201 #endif
202
203 #if S27_PHY_DEBUG
204                 rd_val = s27_rd_phy(phyAddr, ATHR_PHY_FUNC_CONTROL);
205                 printf("S27 ATHR_PHY_FUNC_CONTROL (%d):%x\n", phyAddr, rd_val);
206
207                 rd_val = s27_rd_phy(phyAddr, ATHR_PHY_ID1);
208                 printf("S27 PHY ID  (%d) :%x\n", phyAddr, rd_val);
209
210                 rd_val = s27_rd_phy(phyAddr, ATHR_PHY_SPEC_STATUS);
211                 printf("S27 PHY CTRL  (%d) :%x\n", phyAddr, rd_val);
212
213                 rd_val = s27_rd_phy(phyAddr, ATHR_PHY_STATUS);
214                 printf("S27 ATHR PHY STATUS  (%d) :%x\n", phyAddr, rd_val);
215 #endif
216         }
217
218         /*
219         * status[1:0]=2'h2;   - (0x10 - 1000 Mbps , 0x01 - 100Mbps, 0x0 - 10 Mbps)
220         * status[2]=1'h1;     - Tx Mac En
221         * status[3]=1'h1;     - Rx Mac En
222         * status[4]=1'h1;     - Tx Flow Ctrl En
223         * status[5]=1'h1;     - Rx Flow Ctrl En
224         * status[6]=1'h1;     - Duplex Mode
225         */
226         athrs27_reg_write(PORT_STATUS_REGISTER1, 0x200);  /* LAN - 1 */
227         athrs27_reg_write(PORT_STATUS_REGISTER2, 0x200);  /* LAN - 2 */
228         athrs27_reg_write(PORT_STATUS_REGISTER3, 0x200);  /* LAN - 3 */
229         athrs27_reg_write(PORT_STATUS_REGISTER4, 0x200);  /* LAN - 4 */
230
231         if(is_emu()){
232                 athrs27_reg_write(PORT_STATUS_REGISTER1, 0x4C);  /* LAN - 1 */
233                 athrs27_reg_write(PORT_STATUS_REGISTER2, 0x4c);  /* LAN - 2 */
234                 athrs27_reg_write(PORT_STATUS_REGISTER3, 0x4c);  /* LAN - 3 */
235                 athrs27_reg_write(PORT_STATUS_REGISTER4, 0x4c);  /* LAN - 4 */
236         }
237
238         /* QM Control */
239         athrs27_reg_write(0x38, 0xc000050e);
240
241         /*
242         * status[11]=1'h0;    - CPU Disable
243         * status[7] = 1'b1;   - Learn One Lock
244         * status[14] = 1'b0;  - Learn Enable
245         */
246 #ifdef ATHEROS_HEADER_EN
247         athrs27_reg_write(PORT_CONTROL_REGISTER0, 0x4804);
248 #else
249         /* Atheros Header Disable */
250         athrs27_reg_write(PORT_CONTROL_REGISTER0, 0x4004);
251 #endif
252
253         /* Tag Priority Mapping */
254         athrs27_reg_write(0x70, 0xfa50);
255
256         /* Enable ARP packets to CPU port */
257         athrs27_reg_write(S27_ARL_TBL_CTRL_REG, (athrs27_reg_read(S27_ARL_TBL_CTRL_REG) | 0x100000));
258
259         /* Enable Broadcast packets to CPU port */
260         athrs27_reg_write(S27_FLD_MASK_REG, (athrs27_reg_read(S27_FLD_MASK_REG) | S27_ENABLE_CPU_BROADCAST | S27_ENABLE_CPU_BCAST_FWD ));
261
262         return(0);
263 }
264
265 /******************************************************************************
266 *
267 * athrs27_phy_is_link_alive - test to see if the specified link is alive
268 *
269 * RETURNS:
270 *    TRUE  --> link is alive
271 *    FALSE --> link is down
272 */
273 int athrs27_phy_is_link_alive(int phyUnit){
274         uint16_t phyHwStatus;
275         uint32_t phyAddr;
276
277         phyAddr = ATHR_PHYADDR(phyUnit);
278
279         phyHwStatus = s27_rd_phy(phyAddr, ATHR_PHY_SPEC_STATUS);
280
281         if(phyHwStatus & ATHR_STATUS_LINK_PASS){
282                 return(TRUE);
283         }
284
285         return(FALSE);
286 }
287
288 /******************************************************************************
289 *
290 * athrs27_phy_setup - reset and setup the PHY associated with
291 * the specified MAC unit number.
292 *   
293 * Resets the associated PHY port.
294 *   
295 * RETURNS:
296 *    TRUE  --> associated PHY is alive
297 *    FALSE --> no LINKs on this ethernet unit
298 */
299 int athrs27_phy_setup(int ethUnit){
300         int foundPhy = FALSE;
301         int phyUnit;
302         int liveLinks = 0;
303         uint16_t phyHwStatus;
304         uint16_t timeout;
305         uint32_t phyAddr = 0;
306 #if S27_PHY_DEBUG
307         uint32_t rd_val = 0;
308 #endif
309
310         /* See if there's any configuration data for this enet */
311         /* start auto negogiation on each phy */
312         for(phyUnit = 0; phyUnit < ATHR_PHY_MAX; phyUnit++) {
313                 foundPhy = TRUE;
314                 phyAddr = ATHR_PHYADDR(phyUnit);
315
316                 if(!ATHR_IS_ETHUNIT(phyUnit, ethUnit)){
317                         continue;
318                 }
319
320                 if(!is_emu()){
321                         s27_wr_phy(phyAddr, ATHR_AUTONEG_ADVERT, ATHR_ADVERTISE_ALL);
322                         s27_wr_phy(phyAddr, ATHR_PHY_CONTROL, ATHR_CTRL_AUTONEGOTIATION_ENABLE | ATHR_CTRL_SOFTWARE_RESET);
323                 } else  {
324                         if(ATHR_ETHUNIT(phyUnit) == ENET_UNIT_WAN){
325                                 s27_wr_phy(phyAddr, ATHR_AUTONEG_ADVERT, ATHR_ADVERTISE_ALL);
326                                 s27_wr_phy(phyAddr, 0x9, 0x0); //donot advertise 1000Mbps mode
327                                 s27_wr_phy(phyAddr, ATHR_PHY_CONTROL, 0x0);
328                                 s27_wr_phy(phyAddr, ATHR_PHY_CONTROL, ATHR_CTRL_AUTONEGOTIATION_ENABLE | ATHR_CTRL_SOFTWARE_RESET);
329                         } else {
330                                 s27_wr_phy(phyAddr, ATHR_AUTONEG_ADVERT, (ATHR_ADVERTISE_ASYM_PAUSE | ATHR_ADVERTISE_PAUSE | ATHR_ADVERTISE_10HALF | ATHR_ADVERTISE_10FULL));
331                                 s27_wr_phy(phyAddr, 0x9, 0x0); //donot advertise 1000Mbps mode
332                                 s27_wr_phy(phyAddr, ATHR_PHY_CONTROL, 0x0);
333                                 s27_wr_phy(phyAddr, ATHR_PHY_CONTROL, ATHR_CTRL_AUTONEGOTIATION_ENABLE | ATHR_CTRL_SOFTWARE_RESET);
334                         }
335                 }
336
337 #if S27_PHY_DEBUG
338                 rd_val = s27_rd_phy(phyAddr,ATHR_PHY_CONTROL);
339                 printf("%s ATHR_PHY_CONTROL %d: 0x%x\n",__func__,phyAddr,rd_val);
340
341                 rd_val = s27_rd_phy(phyAddr,ATHR_PHY_SPEC_STATUS);
342                 printf("%s ATHR_PHY_SPEC_STAUS %d: 0x%x\n",__func__,phyAddr,rd_val);
343 #endif
344         }
345
346         if(!foundPhy){
347                 return(FALSE); /* No PHY's configured for this ethUnit */
348         }
349
350         /*
351         * After the phy is reset, it takes a little while before
352         * it can respond properly.
353         */
354         if(ethUnit == ENET_UNIT_LAN){
355                 sysMsDelay(100); // changed by lsz, sysMsDelay(1000);
356         } else {
357                 sysMsDelay(300); // changed by lsz, sysMsDelay(3000);
358         }
359
360         /*
361         * Wait up to 3 seconds for ALL associated PHYs to finish
362         * autonegotiation.  The only way we get out of here sooner is
363         * if ALL PHYs are connected AND finish autonegotiation.
364         */
365         for(phyUnit = 0; phyUnit < ATHR_PHY_MAX; phyUnit++) {
366                 if(ATHR_ETHUNIT(phyUnit) == ENET_UNIT_WAN){
367                         continue;
368                 }
369
370                 timeout = 20;
371
372                 for(;;){
373                         phyHwStatus = s27_rd_phy(phyAddr, ATHR_PHY_CONTROL);
374
375                         if(ATHR_RESET_DONE(phyHwStatus)){
376                                 DRV_PRINT(DRV_DEBUG_PHYSETUP, ("Port %d, Neg Success\n", phyUnit));
377                                 break;
378                         }
379
380                         if(timeout == 0){
381                                 DRV_PRINT(DRV_DEBUG_PHYSETUP, ("Port %d, Negogiation timeout\n", phyUnit));
382                                 break;
383                         }
384
385                         if(--timeout == 0){
386                                 DRV_PRINT(DRV_DEBUG_PHYSETUP, ("Port %d, Negogiation timeout\n", phyUnit));
387                                 break;
388                         }
389
390                         sysMsDelay(150);
391                 }
392
393                 /* fix IOT */
394                 s27_wr_phy(phyUnit, 29, 0x14);
395                 s27_wr_phy(phyUnit, 30, 0x1352);
396
397 #ifdef S27_VER_1_0
398                 /* turn off power saving */
399                 s27_wr_phy(phyUnit, 29, 41);
400                 s27_wr_phy(phyUnit, 30, 0);
401                 printf("def_ S27_VER_1_0\n");
402 #endif
403         }
404
405         /*
406         * All PHYs have had adequate time to autonegotiate.
407         * Now initialize software status.
408         *
409         * It's possible that some ports may take a bit longer
410         * to autonegotiate; but we can't wait forever.  They'll
411         * get noticed by mv_phyCheckStatusChange during regular
412         * polling activities.
413         */
414         for (phyUnit = 0; phyUnit < ATHR_PHY_MAX; phyUnit++) {
415                 if(!ATHR_IS_ETHUNIT(phyUnit, ethUnit)){
416                         continue;
417                 }
418
419                 if(athrs27_phy_is_link_alive(phyUnit)){
420                         liveLinks++;
421                         ATHR_IS_PHY_ALIVE(phyUnit) = TRUE;
422                 } else {
423                         ATHR_IS_PHY_ALIVE(phyUnit) = FALSE;
424                 }
425
426                 DRV_PRINT(DRV_DEBUG_PHYSETUP, ("eth%d: Phy Specific Status=%4.4x\n", ethUnit, s27_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_SPEC_STATUS)));
427         }
428
429         return(liveLinks > 0);
430 }
431
432 /******************************************************************************
433 *
434 * athrs27_phy_is_fdx - Determines whether the phy ports associated with the
435 * specified device are FULL or HALF duplex.
436 *
437 * RETURNS:
438 *    1 --> FULL
439 *    0 --> HALF
440 */
441 int athrs27_phy_is_fdx(int ethUnit,int phyUnit){
442         uint32_t phyAddr;
443         uint16_t phyHwStatus;
444         int ii = 200;
445
446         if(ethUnit == ENET_UNIT_LAN){
447                 return(TRUE);
448         }
449
450         for(phyUnit = 0; phyUnit < ATHR_PHY_MAX; phyUnit++){
451                 if(!ATHR_IS_ETHUNIT(phyUnit, ethUnit)){
452                         continue;
453                 }
454
455                 if(athrs27_phy_is_link_alive(phyUnit)){
456                         phyAddr = ATHR_PHYADDR(phyUnit);
457
458                         do {
459                                 phyHwStatus = s27_rd_phy(phyAddr, ATHR_PHY_SPEC_STATUS);
460
461                                 if(phyHwStatus & ATHR_STATUS_RESOVLED){
462                                         break;
463                                 }
464
465                                 sysMsDelay(10);
466                         } while(--ii);
467
468                         if(phyHwStatus & ATHER_STATUS_FULL_DUPLEX){
469                                 return(TRUE);
470                         }
471                 }
472         }
473
474         return(FALSE);
475 }
476 /******************************************************************************
477 *
478 * athrs27_phy_speed - Determines the speed of phy ports associated with the
479 * specified device.
480 *
481 * RETURNS:
482 *               ATHR_PHY_SPEED_10T, AG7240_PHY_SPEED_100T;
483 *               ATHR_PHY_SPEED_1000T;
484 */
485 int athrs27_phy_speed(int ethUnit, int phyUnit){
486         uint16_t phyHwStatus;
487         uint32_t phyAddr;
488         int ii = 200;
489         int phySpeed = 0;
490
491         for(phyUnit = 0; phyUnit < ATHR_PHY_MAX; phyUnit++){
492                 if(!ATHR_IS_ETHUNIT(phyUnit, ethUnit)){
493                         continue;
494                 }
495
496                 phyAddr = ATHR_PHYADDR(phyUnit);
497                 phySpeed = _10BASET;
498
499                 if(athrs27_phy_is_link_alive(phyUnit)){
500                         do {
501                                 phyHwStatus = s27_rd_phy(phyAddr, ATHR_PHY_SPEC_STATUS);
502
503                                 if(phyHwStatus & ATHR_STATUS_RESOVLED){
504                                         break;
505                                 }
506
507                                 sysMsDelay(10);
508                         } while(--ii);
509
510                         phyHwStatus = ((phyHwStatus & ATHER_STATUS_LINK_MASK) >> ATHER_STATUS_LINK_SHIFT);
511
512                         switch(phyHwStatus){
513                                 case 0:
514                                         phySpeed = _10BASET;
515                                         break;
516                                 case 1:
517                                         phySpeed = _100BASET;
518                                         break;
519                                 case 2:
520                                         phySpeed = _1000BASET;
521                                         break;
522                                 default:
523                                         printf("## Error: unknown eth speed!\n");
524                         }
525                 }
526
527                 phy_reg_write(1, phyAddr, ATHR_DEBUG_PORT_ADDRESS, 0x18);
528
529                 if(phySpeed == _100BASET){
530                         phy_reg_write(1, phyAddr, ATHR_DEBUG_PORT_DATA, 0xba8);
531                 } else {
532                         phy_reg_write(1, phyAddr, ATHR_DEBUG_PORT_DATA, 0x2ea);
533                 }
534         }
535
536         if(ethUnit == ENET_UNIT_LAN){
537                 phySpeed = _1000BASET;
538         }
539
540         return(phySpeed);
541 }
542
543 /*****************************************************************************
544 *
545 * athr_phy_is_up -- checks for significant changes in PHY state.
546 *
547 * A "significant change" is:
548 *     dropped link (e.g. ethernet cable unplugged) OR
549 *     autonegotiation completed + link (e.g. ethernet cable plugged in)
550 *
551 * When a PHY is plugged in, phyLinkGained is called.
552 * When a PHY is unplugged, phyLinkLost is called.
553 */
554 int athrs27_phy_is_up(int ethUnit){
555         athrPhyInfo_t *lastStatus;
556         int linkCount = 0;
557         int lostLinks = 0;
558         int gainedLinks = 0;
559         int phyUnit;
560         uint32_t phyAddr;
561         uint16_t phyHwStatus, phyHwControl;
562
563         for(phyUnit = 0; phyUnit < ATHR_PHY_MAX; phyUnit++){
564                 if(!ATHR_IS_ETHUNIT(phyUnit, ethUnit)){
565                         continue;
566                 }
567
568                 phyAddr = ATHR_PHYADDR(phyUnit);
569
570                 lastStatus = &athrPhyInfo[phyUnit];
571
572                 if(lastStatus->isPhyAlive){ /* last known link status was ALIVE */
573                         phyHwStatus = s27_rd_phy(phyAddr, ATHR_PHY_SPEC_STATUS);
574
575                         /* See if we've lost link */
576                         if(phyHwStatus & ATHR_STATUS_LINK_PASS){
577                                 linkCount++;
578                         } else {
579                                 lostLinks++;
580                                 DRV_PRINT(DRV_DEBUG_PHYCHANGE,("\nenet%d port%d down\n", ethUnit, phyUnit));
581                                 lastStatus->isPhyAlive = FALSE;
582                         }
583                 } else { /* last known link status was DEAD */
584                         /* Check for reset complete */
585                         if(is_emu()){
586                                 phyHwStatus = s27_rd_phy(phyAddr, ATHR_PHY_STATUS);
587
588                                 if(phyAddr % 2){
589                                         s27_wr_phy(phyAddr, ATHR_PHY_FUNC_CONTROL, 0x820);
590                                 } else {
591                                         s27_wr_phy(phyAddr, ATHR_PHY_FUNC_CONTROL, 0x800);
592                                 }
593
594                                 if((phyHwStatus & 0x4) == 0){
595                                         s27_wr_phy(phyAddr, 0x9, 0x0);
596
597                                         if(phyAddr != 0x4){
598                                                 s27_wr_phy(phyAddr, 0x4, 0x41);
599                                         }
600
601                                         s27_wr_phy(phyAddr, 0x0, 0x9000);
602                                 }
603                         }
604
605                         phyHwStatus = s27_rd_phy(phyAddr, ATHR_PHY_CONTROL);
606
607                         if(!ATHR_RESET_DONE(phyHwStatus)){
608                                 continue;
609                         }
610
611                         phyHwControl = s27_rd_phy(phyAddr, ATHR_PHY_CONTROL);
612                         phyHwStatus  = s27_rd_phy(phyAddr, ATHR_PHY_STATUS);
613
614                         /* Check for AutoNegotiation complete */
615                         if((!(phyHwControl & ATHR_CTRL_AUTONEGOTIATION_ENABLE)) || ATHR_AUTONEG_DONE(phyHwStatus)){
616                                 phyHwStatus = s27_rd_phy(phyAddr, ATHR_PHY_SPEC_STATUS);
617
618                                 if(phyHwStatus & ATHR_STATUS_LINK_PASS){
619                                         gainedLinks++;
620                                         linkCount++;
621                                         DRV_PRINT(DRV_DEBUG_PHYCHANGE,("\nenet%d port%d up\n", ethUnit, phyUnit));
622                                         lastStatus->isPhyAlive = TRUE;
623                                 }
624                         }
625                 }
626         }
627
628         return (linkCount);
629 }
630
631 unsigned int athrs27_reg_read(unsigned int s27_addr){
632         unsigned int addr_temp;
633         unsigned int s27_rd_csr_low, s27_rd_csr_high, s27_rd_csr;
634         unsigned int data, unit = 0;
635         unsigned int phy_address, reg_address;
636
637         addr_temp = s27_addr >> 2;
638         data = addr_temp >> 7;
639
640         phy_address = 0x1f;
641         reg_address = 0x10;
642
643         if(is_ar7240()){
644                 unit = 0;
645         } else if(is_ar7241() || is_ar7242() || is_wasp()){
646                 unit = 1;
647         }
648
649         phy_reg_write(unit, phy_address, reg_address, data);
650
651         phy_address = (0x17 & ((addr_temp >> 4) | 0x10));
652         reg_address = ((addr_temp << 1) & 0x1e);
653         s27_rd_csr_low = (uint32_t)phy_reg_read(unit, phy_address, reg_address);
654
655         reg_address = reg_address | 0x1;
656         s27_rd_csr_high = (uint32_t)phy_reg_read(unit, phy_address, reg_address);
657         s27_rd_csr = (s27_rd_csr_high << 16) | s27_rd_csr_low;
658
659         return(s27_rd_csr);
660 }
661
662 void athrs27_reg_write(unsigned int s27_addr, unsigned int s27_write_data){
663         unsigned int addr_temp;
664         unsigned int data;
665         unsigned int phy_address;
666         unsigned int reg_address,unit = 0;
667
668         addr_temp = (s27_addr ) >> 2;
669         data = addr_temp >> 7;
670
671         phy_address = 0x1f;
672         reg_address = 0x10;
673
674         if(is_ar7240()){
675                 unit = 0;
676         } else if(is_ar7241() || is_ar7242() || is_wasp()){
677                 unit = 1;
678         }
679
680         phy_reg_write(unit,phy_address, reg_address, data);
681
682         phy_address = (0x17 & ((addr_temp >> 4) | 0x10));
683
684         reg_address = (((addr_temp << 1) & 0x1e) | 0x1);
685         data = (s27_write_data >> 16) & 0xffff;
686         phy_reg_write(unit, phy_address, reg_address, data);
687
688         reg_address = ((addr_temp << 1) & 0x1e);
689         data = s27_write_data & 0xffff;
690         phy_reg_write(unit, phy_address, reg_address, data);
691 }
692
693 void athrs27_reg_rmw(unsigned int s27_addr, unsigned int s27_write_data){
694         int val = athrs27_reg_read(s27_addr);
695         athrs27_reg_write(s27_addr,(val | s27_write_data));
696 }
697
698 unsigned int s27_rd_phy(unsigned int phy_addr, unsigned int reg_addr){
699         unsigned int rddata;
700         unsigned int i = 0;
701
702         /* MDIO_CMD is set for read */
703         rddata = athrs27_reg_read(0x98);
704         rddata = (rddata & 0x0) | (reg_addr << 16) | (phy_addr << 21) | (1 << 27) | (1 << 30) | (1 << 31);
705         athrs27_reg_write(0x98, rddata);
706
707         rddata = athrs27_reg_read(0x98);
708         rddata = rddata & (1 << 31);
709
710         // Check MDIO_BUSY status
711         while(rddata){
712                 // TODO: do we need this?
713                 i++;
714
715                 if(i > 824)     {
716                         printf("## Error: MDIO_BUSY!\n");
717                         break;
718                 }
719
720                 rddata = athrs27_reg_read(0x98);
721                 rddata = rddata & (1 << 31);
722         }
723
724         /* Read the data from phy */
725         rddata = athrs27_reg_read(0x98) & 0xffff;
726
727         return(rddata);
728 }
729
730 void s27_wr_phy(unsigned int phy_addr, unsigned int reg_addr, unsigned int write_data){
731         unsigned int rddata;
732         unsigned int i = 0;
733
734         /* MDIO_CMD is set for read */
735         rddata = athrs27_reg_read(0x98);
736         rddata = (rddata & 0x0) | (write_data & 0xffff) | (reg_addr << 16) | (phy_addr << 21) | (0 << 27) | (1 << 30) | (1 << 31);
737         athrs27_reg_write(0x98, rddata);
738
739         rddata = athrs27_reg_read(0x98);
740         rddata = rddata & (1 << 31);
741
742         // Check MDIO_BUSY status
743         while(rddata){
744                 // TODO: do we need this?
745                 i++;
746
747                 if(i > 824)     {
748                         printf("## Error: MDIO_BUSY!\n");
749                         break;
750                 }
751
752                 rddata = athrs27_reg_read(0x98);
753                 rddata = rddata & (1 << 31);
754         }
755
756 }
757
758 int athrs27_mdc_check(void){
759         int i;
760
761         for(i = 0; i < 4000; i++){
762                 if(athrs27_reg_read(0x10c) != 0x18007fff){
763                         return(-1);
764                 }
765         }
766
767         return(0);
768 }