Statistics
| Branch: | Tag: | Revision:

root / usrp2 / firmware / lib / ethernet.c @ 7eea883c

History | View | Annotate | Download (8.3 kB)

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
}