Merged r9433:9527 from features/gr-usrp2 into trunk. Adds usrp2 and gr-usrp2 top...
[debian/gnuradio] / usrp2 / firmware / lib / ethernet.c
1 /*
2  * Copyright 2007 Free Software Foundation, Inc.
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include "ethernet.h"
19 #include "memory_map.h"
20 #include "eth_phy.h"
21 #include "eth_mac.h"
22 #include "eth_mac_regs.h"
23 #include "pic.h"
24 #include "hal_io.h"
25 #include "nonstdio.h"
26 #include "bool.h"
27 #include "i2c.h"
28 #include "usrp2_i2c_addr.h"
29
30
31 #define VERBOSE 0
32
33 static ethernet_t ed_state;
34 static ethernet_link_changed_callback_t ed_callback = 0;
35
36 void 
37 ethernet_register_link_changed_callback(ethernet_link_changed_callback_t new_callback)
38 {
39   ed_callback = new_callback;
40 }
41
42
43 static void
44 ed_set_mac_speed(int speed)
45 {
46   switch(speed){
47   case 10:
48     eth_mac->speed = 1;
49     break;
50   case 100:
51     eth_mac->speed = 2;
52     break;
53   case 1000:
54     eth_mac->speed = 4;
55     break;
56   default:
57     break;
58   }
59 }
60
61 static void
62 ed_link_up(int speed)
63 {
64   // putstr("ed_link_up: "); puthex16_nl(speed);
65
66   ed_set_mac_speed(speed);
67
68   if (ed_callback)              // fire link changed callback
69     (*ed_callback)(speed);
70 }
71
72 static void
73 ed_link_down(void)
74 {
75   // putstr("ed_link_down\n");
76
77   if (ed_callback)              // fire link changed callback
78     (*ed_callback)(0);
79 }
80
81
82 static void
83 ed_link_speed_change(int speed)
84 {
85   ed_link_down();
86   ed_link_up(speed);
87 }
88
89 /*
90  * Read the PHY state register to determine link state and speed
91  */
92 static void
93 ed_check_phy_state(void)
94 {
95   int lansr = eth_mac_miim_read(PHY_LINK_AN);
96   eth_link_state_t new_state = LS_UNKNOWN;
97   int new_speed = S_UNKNOWN;
98
99   if (VERBOSE){
100     putstr("LANSR: ");
101     puthex16_nl(lansr);
102   }
103
104   if (lansr & LANSR_LINK_GOOD){         // link's up
105     if (VERBOSE)
106       puts("  LINK_GOOD");
107
108     new_state = LS_UP;
109     switch (lansr & LANSR_SPEED_MASK){
110     case LANSR_SPEED_10:
111       new_speed = 10;
112       break;
113       
114     case LANSR_SPEED_100:
115       new_speed = 100;
116       break;
117       
118     case LANSR_SPEED_1000:
119       new_speed = 1000;
120       break;
121
122     default:
123       new_speed = S_UNKNOWN;
124       break;
125     }
126   }
127   else {                                // link's down
128     if (VERBOSE)
129       puts("  NOT LINK_GOOD");
130     
131     new_state = LS_DOWN;
132     new_speed = S_UNKNOWN;
133   }
134
135   if (new_state != ed_state.link_state){
136     ed_state.link_state = new_state;            // remember new state
137     if (new_state == LS_UP)
138       ed_link_up(new_speed);
139     else if (new_state == LS_DOWN)
140       ed_link_down();
141   }
142   else if (new_state == LS_UP && new_speed != ed_state.link_speed){
143     ed_state.link_speed = new_speed;            // remember new speed
144     ed_link_speed_change(new_speed);
145   }
146 }
147
148 /*
149  * This is fired when the ethernet PHY state changes
150  */
151 static void
152 eth_phy_irq_handler(unsigned irq)
153 {
154   ed_check_phy_state();
155   eth_mac_miim_write(PHY_INT_CLEAR, ~0);        // clear all ints
156 }
157
158 void
159 ethernet_init(void)
160 {
161   eth_mac_init(ethernet_mac_addr());
162
163   ed_state.link_state = LS_UNKNOWN;
164   ed_state.link_speed = S_UNKNOWN;
165
166   // initialize MAC registers
167   eth_mac->tx_hwmark = 0x1e;
168   eth_mac->tx_lwmark = 0x19;
169
170   eth_mac->crc_chk_en = 1;
171   eth_mac->rx_max_length = 2048;
172
173   // configure PAUSE frame stuff
174   eth_mac->tx_pause_en = 1;             // pay attn to pause frames sent to us
175
176   eth_mac->pause_quanta_set = 38;       // a bit more than 1 max frame 16kb/512 + fudge
177   eth_mac->pause_frame_send_en = 1;     // enable sending pause frames
178
179
180   // setup PHY to interrupt on changes
181
182   unsigned mask =
183     (PHY_INT_AN_CMPL            // auto-neg completed
184      | PHY_INT_NO_LINK          // no link after auto-neg
185      | PHY_INT_NO_HCD           // no highest common denominator
186      | PHY_INT_MAS_SLA_ERR      // couldn't resolve master/slave 
187      | PHY_INT_PRL_DET_FLT      // parallel detection fault
188      | PHY_INT_LNK_CNG          // link established or broken
189      | PHY_INT_SPD_CNG          // speed changed
190      );
191
192   eth_mac_miim_write(PHY_INT_CLEAR, ~0);        // clear all pending interrupts
193   eth_mac_miim_write(PHY_INT_MASK, mask);       // enable the ones we want
194
195   pic_register_handler(IRQ_PHY, eth_phy_irq_handler);
196
197   // Advertise that we handle PAUSE frames and asymmetric pause direction.
198   int t = eth_mac_miim_read(PHY_AUTONEG_ADV);
199   eth_mac_miim_write(PHY_AUTONEG_ADV, t | NWAY_AR_PAUSE | NWAY_AR_ASM_DIR);
200
201   // Restart autonegotation.  
202   // We want to ensure that we're advertising our PAUSE capabilities.
203   t = eth_mac_miim_read(PHY_CTRL);
204   eth_mac_miim_write(PHY_CTRL, t | MII_CR_RESTART_AUTO_NEG);
205 }
206
207 static bool 
208 unprogrammed(const u2_mac_addr_t *t)
209 {
210   int i;
211   bool all_zeros = true;
212   bool all_ones =  true;
213   for (i = 0; i < 6; i++){
214     all_zeros &= t->addr[i] == 0x00;
215     all_ones  &= t->addr[i] == 0xff;
216   }
217   return all_ones | all_zeros;
218 }
219
220 static int8_t src_addr_initialized = false;
221 static u2_mac_addr_t src_addr = {{
222     0x00, 0x50, 0xC2, 0x85, 0x3f, 0xff
223   }};
224
225 const u2_mac_addr_t *
226 ethernet_mac_addr(void)
227 {
228   if (!src_addr_initialized){    // fetch from eeprom
229     src_addr_initialized = true;
230
231     // if we're simulating, don't read the EEPROM model, it's REALLY slow
232     if (hwconfig_simulation_p())        
233       return &src_addr;
234     
235     u2_mac_addr_t tmp;
236     bool ok = eeprom_read(I2C_ADDR_MBOARD, MBOARD_MAC_ADDR, &tmp.addr[0], 6);
237     if (!ok || unprogrammed(&tmp)){
238       // use the default
239     }
240     else
241       src_addr = tmp;
242   }
243
244   return &src_addr;
245 }
246
247 bool
248 ethernet_set_mac_addr(const u2_mac_addr_t *t)
249 {
250   bool ok = eeprom_write(I2C_ADDR_MBOARD, MBOARD_MAC_ADDR, &t->addr[0], 6);
251   if (ok){
252     src_addr = *t;
253     src_addr_initialized = true;
254     eth_mac_set_addr(t);
255   }
256
257   return ok;
258 }
259
260 int
261 ethernet_check_errors(void)
262 {
263   // these registers are reset when read
264   
265   int   r = 0;
266   if (eth_mac_read_rmon(0x05) != 0)
267     r |= RME_RX_CRC;
268   if (eth_mac_read_rmon(0x06) != 0)
269     r |= RME_RX_FIFO_FULL;
270   if (eth_mac_read_rmon(0x07) != 0)
271     r |= RME_RX_2SHORT_2LONG;
272   
273   if (eth_mac_read_rmon(0x25) != 0)
274     r |= RME_TX_JAM_DROP;
275   if (eth_mac_read_rmon(0x26) != 0)
276     r |= RME_TX_FIFO_UNDER;
277   if (eth_mac_read_rmon(0x27) != 0)
278     r |= RME_TX_FIFO_OVER;
279
280   return r;
281 }