~ubuntu-branches/ubuntu/precise/gnuradio/precise

« back to all changes in this revision

Viewing changes to usrp2/firmware/lib/ethernet.c

  • Committer: Bazaar Package Importer
  • Author(s): Kamal Mostafa
  • Date: 2010-03-13 07:46:01 UTC
  • mfrom: (2.1.2 sid)
  • Revision ID: james.westby@ubuntu.com-20100313074601-zjsa893a87bozyh7
Tags: 3.2.2.dfsg-1ubuntu1
* Fix build for Ubuntu lucid (LP: #260406)
  - add binary package dep for libusrp0, libusrp2-0: adduser
  - debian/rules clean: remove pre-built Qt moc files

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
}