Imported Upstream version 3.2.2
[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 static void
90 print_flow_control(int flow_control)
91 {
92   static const char *flow_control_msg[4] = {
93     "NONE", "WE_TX", "WE_RX", "SYMMETRIC"
94   };
95   putstr("ethernet flow control: ");
96   puts(flow_control_msg[flow_control & 0x3]);
97 }
98
99 static void
100 check_flow_control_resolution(void)
101 {
102   static const unsigned char table[16] = {
103     // index = {local_asm, local_pause, partner_asm, partner_pause}
104     FC_NONE,  FC_NONE,  FC_NONE,  FC_NONE,
105     FC_NONE,  FC_SYMM,  FC_NONE,  FC_SYMM,
106     FC_NONE,  FC_NONE,  FC_NONE,  FC_WE_TX,
107     FC_NONE,  FC_SYMM,  FC_WE_RX, FC_SYMM
108   };
109
110   int us = eth_mac_miim_read(PHY_AUTONEG_ADV);
111   int lp = eth_mac_miim_read(PHY_LP_ABILITY);
112   int index = (((us >> 10) & 0x3) << 2) | ((lp >> 10) & 0x3);
113   ed_state.flow_control = table[index];
114
115   if (1)
116     print_flow_control(ed_state.flow_control);
117 }
118
119 /*
120  * Read the PHY state register to determine link state and speed
121  */
122 static void
123 ed_check_phy_state(void)
124 {
125   int lansr = eth_mac_miim_read(PHY_LINK_AN);
126   eth_link_state_t new_state = LS_UNKNOWN;
127   int new_speed = S_UNKNOWN;
128
129   if (VERBOSE){
130     putstr("LANSR: ");
131     puthex16_nl(lansr);
132   }
133
134   if (lansr & LANSR_LINK_GOOD){         // link's up
135     if (VERBOSE)
136       puts("  LINK_GOOD");
137
138     new_state = LS_UP;
139     switch (lansr & LANSR_SPEED_MASK){
140     case LANSR_SPEED_10:
141       new_speed = 10;
142       break;
143       
144     case LANSR_SPEED_100:
145       new_speed = 100;
146       break;
147       
148     case LANSR_SPEED_1000:
149       new_speed = 1000;
150       break;
151
152     default:
153       new_speed = S_UNKNOWN;
154       break;
155     }
156
157     check_flow_control_resolution();
158   }
159   else {                                // link's down
160     if (VERBOSE)
161       puts("  NOT LINK_GOOD");
162     
163     new_state = LS_DOWN;
164     new_speed = S_UNKNOWN;
165   }
166
167   if (new_state != ed_state.link_state){
168     ed_state.link_state = new_state;            // remember new state
169     if (new_state == LS_UP)
170       ed_link_up(new_speed);
171     else if (new_state == LS_DOWN)
172       ed_link_down();
173   }
174   else if (new_state == LS_UP && new_speed != ed_state.link_speed){
175     ed_state.link_speed = new_speed;            // remember new speed
176     ed_link_speed_change(new_speed);
177   }
178 }
179
180 /*
181  * This is fired when the ethernet PHY state changes
182  */
183 static void
184 eth_phy_irq_handler(unsigned irq)
185 {
186   ed_check_phy_state();
187   eth_mac_miim_write(PHY_INT_CLEAR, ~0);        // clear all ints
188 }
189
190 void
191 ethernet_init(void)
192 {
193   eth_mac_init(ethernet_mac_addr());
194
195   ed_state.link_state = LS_UNKNOWN;
196   ed_state.link_speed = S_UNKNOWN;
197
198   // initialize MAC registers
199   eth_mac->tx_hwmark = 0x1e;
200   eth_mac->tx_lwmark = 0x19;
201
202   eth_mac->crc_chk_en = 1;
203   eth_mac->rx_max_length = 2048;
204
205   // configure PAUSE frame stuff
206   eth_mac->tx_pause_en = 1;             // pay attn to pause frames sent to us
207
208   eth_mac->pause_quanta_set = 38;       // a bit more than 1 max frame 16kb/512 + fudge
209   eth_mac->pause_frame_send_en = 1;     // enable sending pause frames
210
211
212   // setup PHY to interrupt on changes
213
214   unsigned mask =
215     (PHY_INT_AN_CMPL            // auto-neg completed
216      | PHY_INT_NO_LINK          // no link after auto-neg
217      | PHY_INT_NO_HCD           // no highest common denominator
218      | PHY_INT_MAS_SLA_ERR      // couldn't resolve master/slave 
219      | PHY_INT_PRL_DET_FLT      // parallel detection fault
220      | PHY_INT_LNK_CNG          // link established or broken
221      | PHY_INT_SPD_CNG          // speed changed
222      );
223
224   eth_mac_miim_write(PHY_INT_CLEAR, ~0);        // clear all pending interrupts
225   eth_mac_miim_write(PHY_INT_MASK, mask);       // enable the ones we want
226
227   pic_register_handler(IRQ_PHY, eth_phy_irq_handler);
228
229   // Advertise our flow control configuation.
230   //
231   // We and the link partner each specify two bits in the base page
232   // related to autoconfiguration: NWAY_AR_PAUSE and NWAY_AR_ASM_DIR.
233   // The bits say what a device is "willing" to do, not what may actually
234   // happen as a result of the negotiation.  There are 4 cases:
235   //
236   // PAUSE  ASM_DIR
237   //
238   //  0        0        I have no flow control capability.
239   //
240   //  1        0        I both assert and respond to flow control.
241   //
242   //  0        1        I assert flow control, but cannot respond.  That is,
243   //                    I want to be able to send PAUSE frames, but will ignore any
244   //                    you send to me.  (This is our configuration.)
245   //
246   //  1        1        I can both assert and respond to flow control AND I am willing
247   //                    to operate symmetrically OR asymmetrically in EITHER direction.
248   //                    (We hope the link partner advertises this, otherwise we don't
249   //                    get what we want.)
250
251   int t = eth_mac_miim_read(PHY_AUTONEG_ADV);
252   t &= ~(NWAY_AR_PAUSE | NWAY_AR_ASM_DIR);
253   t |= NWAY_AR_ASM_DIR;
254
255   // Say we can't to 10BASE-T or 100BASE-TX, half or full duplex
256   t &= ~(NWAY_AR_10T_HD_CAPS | NWAY_AR_10T_FD_CAPS | NWAY_AR_100TX_HD_CAPS | NWAY_AR_100TX_FD_CAPS);
257
258   eth_mac_miim_write(PHY_AUTONEG_ADV, t);
259
260   // Restart autonegotation.  
261   // We want to ensure that we're advertising our PAUSE capabilities.
262   t = eth_mac_miim_read(PHY_CTRL);
263   eth_mac_miim_write(PHY_CTRL, t | MII_CR_RESTART_AUTO_NEG);
264 }
265
266 static bool 
267 unprogrammed(const u2_mac_addr_t *t)
268 {
269   int i;
270   bool all_zeros = true;
271   bool all_ones =  true;
272   for (i = 0; i < 6; i++){
273     all_zeros &= t->addr[i] == 0x00;
274     all_ones  &= t->addr[i] == 0xff;
275   }
276   return all_ones | all_zeros;
277 }
278
279 static int8_t src_addr_initialized = false;
280 static u2_mac_addr_t src_addr = {{
281     0x00, 0x50, 0xC2, 0x85, 0x3f, 0xff
282   }};
283
284 const u2_mac_addr_t *
285 ethernet_mac_addr(void)
286 {
287   if (!src_addr_initialized){    // fetch from eeprom
288     src_addr_initialized = true;
289
290     // if we're simulating, don't read the EEPROM model, it's REALLY slow
291     if (hwconfig_simulation_p())        
292       return &src_addr;
293     
294     u2_mac_addr_t tmp;
295     bool ok = eeprom_read(I2C_ADDR_MBOARD, MBOARD_MAC_ADDR, &tmp.addr[0], 6);
296     if (!ok || unprogrammed(&tmp)){
297       // use the default
298     }
299     else
300       src_addr = tmp;
301   }
302
303   return &src_addr;
304 }
305
306 bool
307 ethernet_set_mac_addr(const u2_mac_addr_t *t)
308 {
309   bool ok = eeprom_write(I2C_ADDR_MBOARD, MBOARD_MAC_ADDR, &t->addr[0], 6);
310   if (ok){
311     src_addr = *t;
312     src_addr_initialized = true;
313     eth_mac_set_addr(t);
314   }
315
316   return ok;
317 }
318
319 int
320 ethernet_check_errors(void)
321 {
322   // these registers are reset when read
323   
324   int   r = 0;
325   if (eth_mac_read_rmon(0x05) != 0)
326     r |= RME_RX_CRC;
327   if (eth_mac_read_rmon(0x06) != 0)
328     r |= RME_RX_FIFO_FULL;
329   if (eth_mac_read_rmon(0x07) != 0)
330     r |= RME_RX_2SHORT_2LONG;
331   
332   if (eth_mac_read_rmon(0x25) != 0)
333     r |= RME_TX_JAM_DROP;
334   if (eth_mac_read_rmon(0x26) != 0)
335     r |= RME_TX_FIFO_UNDER;
336   if (eth_mac_read_rmon(0x27) != 0)
337     r |= RME_TX_FIFO_OVER;
338
339   return r;
340 }