1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
|
/*
* Copyright 2011 Ettus Research LLC
* Copyright 2007 Free Software Foundation, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//Changes for USRP2P: status registers different (ethernet.h)
#include "ethernet.h"
#include "memory_map.h"
#include "eth_phy.h"
#include <eth_mac.h>
#include <pic.h>
#include <hal_io.h>
#include <nonstdio.h>
#include <stdbool.h>
#include <i2c.h>
#include "usrp2/fw_common.h"
#define VERBOSE 0
static ethernet_t ed_state;
static ethernet_link_changed_callback_t ed_callback = 0;
void
ethernet_register_link_changed_callback(ethernet_link_changed_callback_t new_callback)
{
ed_callback = new_callback;
}
static void
ed_set_mac_speed(int speed)
{
printf("Speed set to %d\n",speed);
/*
switch(speed){
case 10:
eth_mac->speed = 1;
break;
case 100:
eth_mac->speed = 2;
break;
case 1000:
eth_mac->speed = 4;
break;
default:
break;
}
*/
}
static void
ed_link_up(int speed)
{
// putstr("ed_link_up: "); puthex16_nl(speed);
ed_set_mac_speed(speed);
//turn on link LED for USRP2P
hal_set_leds(LED_RJ45, LED_RJ45);
if (ed_callback) // fire link changed callback
(*ed_callback)(speed);
}
static void
ed_link_down(void)
{
// putstr("ed_link_down\n");
//turn off link LED for USRP2P
hal_set_leds(0, LED_RJ45);
if (ed_callback) // fire link changed callback
(*ed_callback)(0);
}
static void
ed_link_speed_change(int speed)
{
ed_link_down();
ed_link_up(speed);
}
static void
print_flow_control(int flow_control)
{
static const char *flow_control_msg[4] = {
"NONE", "WE_TX", "WE_RX", "SYMMETRIC"
};
putstr("ethernet flow control: ");
puts(flow_control_msg[flow_control & 0x3]);
}
static void
check_flow_control_resolution(void)
{
static const unsigned char table[16] = {
// index = {local_asm, local_pause, partner_asm, partner_pause}
FC_NONE, FC_NONE, FC_NONE, FC_NONE,
FC_NONE, FC_SYMM, FC_NONE, FC_SYMM,
FC_NONE, FC_NONE, FC_NONE, FC_WE_TX,
FC_NONE, FC_SYMM, FC_WE_RX, FC_SYMM
};
int us = eth_mac_miim_read(PHY_AUTONEG_ADV);
int lp = eth_mac_miim_read(PHY_LP_ABILITY);
int index = (((us >> 10) & 0x3) << 2) | ((lp >> 10) & 0x3);
ed_state.flow_control = table[index];
if (1)
print_flow_control(ed_state.flow_control);
}
/*
* Read the PHY state register to determine link state and speed
*/
static void
ed_check_phy_state(void)
{
int phystat = eth_mac_miim_read(PHY_PHY_STATUS);
eth_link_state_t new_state = LS_UNKNOWN;
int new_speed = S_UNKNOWN;
if (VERBOSE){
putstr("PHYSTAT: ");
puthex16_nl(phystat);
}
if (phystat & PHYSTAT_LINK){ // link's up
if (VERBOSE)
puts(" LINK_GOOD");
new_state = LS_UP;
switch (phystat & PHYSTAT_SPEED_MASK){
case PHYSTAT_SPEED_10:
new_speed = 10;
break;
case PHYSTAT_SPEED_100:
new_speed = 100;
break;
case PHYSTAT_SPEED_1000:
new_speed = 1000;
break;
default:
new_speed = S_UNKNOWN;
break;
}
check_flow_control_resolution();
}
else { // link's down
if (VERBOSE)
puts(" NOT LINK_GOOD");
new_state = LS_DOWN;
new_speed = S_UNKNOWN;
}
if (new_state != ed_state.link_state){
ed_state.link_state = new_state; // remember new state
if (new_state == LS_UP)
ed_link_up(new_speed);
else if (new_state == LS_DOWN)
ed_link_down();
}
else if (new_state == LS_UP && new_speed != ed_state.link_speed){
ed_state.link_speed = new_speed; // remember new speed
ed_link_speed_change(new_speed);
}
}
/*
* This is fired when the ethernet PHY state changes
*/
static void
eth_phy_irq_handler(unsigned irq)
{
ed_check_phy_state();
eth_mac_miim_read(PHY_INT_STATUS);
// eth_mac_miim_write(PHY_INT_CLEAR, ~0); // clear all ints
}
void
ethernet_init(void)
{
eth_mac_init(ethernet_mac_addr());
ed_state.link_state = LS_UNKNOWN;
ed_state.link_speed = S_UNKNOWN;
// initialize MAC registers
// eth_mac->tx_hwmark = 0x1e;
//eth_mac->tx_lwmark = 0x19;
//eth_mac->crc_chk_en = 1;
//eth_mac->rx_max_length = 2048;
// configure PAUSE frame stuff
//eth_mac->tx_pause_en = 1; // pay attn to pause frames sent to us
//eth_mac->pause_quanta_set = 38; // a bit more than 1 max frame 16kb/512 + fudge
//eth_mac->pause_frame_send_en = 1; // enable sending pause frames
// setup PHY to interrupt on changes
unsigned mask =
(PHY_INT_ENABLE //master interrupt enable
| PHY_INT_LINK_STATUS_CHANGE
| PHY_INT_RX_STATUS_CHANGE
);
eth_mac_miim_read(PHY_INT_STATUS); //clear interrupts
eth_mac_miim_write(PHY_INT_MASK, mask); // enable the ones we want
//set the LED behavior to activity instead of link
unsigned led = (LED_ACTIVITY << PHY_LED_LINK_LSB) | (LED_TX << PHY_LED_TXRX_LSB);
eth_mac_miim_write(PHY_LED2, led);
pic_register_handler(IRQ_PHY, eth_phy_irq_handler);
// Advertise our flow control configuation.
//
// We and the link partner each specify two bits in the base page
// related to autoconfiguration: NWAY_AR_PAUSE and NWAY_AR_ASM_DIR.
// The bits say what a device is "willing" to do, not what may actually
// happen as a result of the negotiation. There are 4 cases:
//
// PAUSE ASM_DIR
//
// 0 0 I have no flow control capability.
//
// 1 0 I both assert and respond to flow control.
//
// 0 1 I assert flow control, but cannot respond. That is,
// I want to be able to send PAUSE frames, but will ignore any
// you send to me. (This is our configuration.)
//
// 1 1 I can both assert and respond to flow control AND I am willing
// to operate symmetrically OR asymmetrically in EITHER direction.
// (We hope the link partner advertises this, otherwise we don't
// get what we want.)
int t = eth_mac_miim_read(PHY_AUTONEG_ADV);
t &= ~(NWAY_AR_PAUSE | NWAY_AR_ASM_DIR);
t |= NWAY_AR_ASM_DIR;
// Say we can't to 10BASE-T or 100BASE-TX, half or full duplex
t &= ~(NWAY_AR_10T_HD_CAPS | NWAY_AR_10T_FD_CAPS | NWAY_AR_100TX_HD_CAPS | NWAY_AR_100TX_FD_CAPS);
eth_mac_miim_write(PHY_AUTONEG_ADV, t);
int r = eth_mac_miim_read(PHY_AUTONEG_ADV); // DEBUG, read back
if (t != r){
printf("PHY_AUTONEG_ADV: wrote 0x%x, got 0x%x\n", t, r);
}
// Restart autonegotation.
// We want to ensure that we're advertising our PAUSE capabilities.
t = eth_mac_miim_read(PHY_CTRL);
eth_mac_miim_write(PHY_CTRL, t | MII_CR_RESTART_AUTO_NEG);
}
int
ethernet_check_errors(void)
{
// these registers are reset when read
int r = 0;
/*
if (eth_mac_read_rmon(0x05) != 0)
r |= RME_RX_CRC;
if (eth_mac_read_rmon(0x06) != 0)
r |= RME_RX_FIFO_FULL;
if (eth_mac_read_rmon(0x07) != 0)
r |= RME_RX_2SHORT_2LONG;
if (eth_mac_read_rmon(0x25) != 0)
r |= RME_TX_JAM_DROP;
if (eth_mac_read_rmon(0x26) != 0)
r |= RME_TX_FIFO_UNDER;
if (eth_mac_read_rmon(0x27) != 0)
r |= RME_TX_FIFO_OVER;
*/
return r;
}
|