Statistics
| Branch: | Tag: | Revision:

root / usrp2 / firmware / lib / ethernet.c @ e0fcbaee

History | View | Annotate | Download (6.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
  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
}