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