Merge branch 'davem-next' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik/netdev-2.6

This commit is contained in:
David S. Miller 2008-11-07 01:37:16 -08:00
commit 167c6274c3
39 changed files with 4013 additions and 289 deletions

View File

@ -194,6 +194,48 @@ or, for backwards compatibility, the option value. E.g.,
The parameters are as follows:
ad_select
Specifies the 802.3ad aggregation selection logic to use. The
possible values and their effects are:
stable or 0
The active aggregator is chosen by largest aggregate
bandwidth.
Reselection of the active aggregator occurs only when all
slaves of the active aggregator are down or the active
aggregator has no slaves.
This is the default value.
bandwidth or 1
The active aggregator is chosen by largest aggregate
bandwidth. Reselection occurs if:
- A slave is added to or removed from the bond
- Any slave's link state changes
- Any slave's 802.3ad association state changes
- The bond's adminstrative state changes to up
count or 2
The active aggregator is chosen by the largest number of
ports (slaves). Reselection occurs as described under the
"bandwidth" setting, above.
The bandwidth and count selection policies permit failover of
802.3ad aggregations when partial failure of the active aggregator
occurs. This keeps the aggregator with the highest availability
(either in bandwidth or in number of ports) active at all times.
This option was added in bonding version 3.4.0.
arp_interval
Specifies the ARP link monitoring frequency in milliseconds.
@ -551,6 +593,16 @@ num_grat_arp
affects only the active-backup mode. This option was added for
bonding version 3.3.0.
num_unsol_na
Specifies the number of unsolicited IPv6 Neighbor Advertisements
to be issued after a failover event. One unsolicited NA is issued
immediately after the failover.
The valid range is 0 - 255; the default value is 1. This option
affects only the active-backup mode. This option was added for
bonding version 3.4.0.
primary
A string (eth0, eth2, etc) specifying which slave is the

View File

@ -3853,6 +3853,12 @@ M: mhoffman@lightlink.com
L: lm-sensors@lm-sensors.org
S: Maintained
SMSC911x ETHERNET DRIVER
P: Steve Glendinning
M: steve.glendinning@smsc.com
L: netdev@vger.kernel.org
S: Supported
SMX UIO Interface
P: Ben Nizette
M: bn@niasdigital.com

View File

@ -61,6 +61,7 @@ config DUMMY
config BONDING
tristate "Bonding driver support"
depends on INET
depends on IPV6 || IPV6=n
---help---
Say 'Y' or 'M' if you wish to be able to 'bond' multiple Ethernet
Channels together. This is called 'Etherchannel' by Cisco,
@ -978,6 +979,20 @@ config SMC911X
called smc911x. If you want to compile it as a module, say M
here and read <file:Documentation/kbuild/modules.txt>
config SMSC911X
tristate "SMSC LAN911x/LAN921x families embedded ethernet support"
depends on ARM || SUPERH
select CRC32
select MII
select PHYLIB
---help---
Say Y here if you want support for SMSC LAN911x and LAN921x families
of ethernet controllers.
To compile this driver as a module, choose M here and read
<file:Documentation/networking/net-modules.txt>. The module
will be called smsc911x.
config NET_VENDOR_RACAL
bool "Racal-Interlan (Micom) NI cards"
depends on ISA

View File

@ -219,6 +219,7 @@ obj-$(CONFIG_S2IO) += s2io.o
obj-$(CONFIG_MYRI10GE) += myri10ge/
obj-$(CONFIG_SMC91X) += smc91x.o
obj-$(CONFIG_SMC911X) += smc911x.o
obj-$(CONFIG_SMSC911X) += smsc911x.o
obj-$(CONFIG_BFIN_MAC) += bfin_mac.o
obj-$(CONFIG_DM9000) += dm9000.o
obj-$(CONFIG_PASEMI_MAC) += pasemi_mac_driver.o

View File

@ -6,3 +6,6 @@ obj-$(CONFIG_BONDING) += bonding.o
bonding-objs := bond_main.o bond_3ad.o bond_alb.o bond_sysfs.o
ipv6-$(subst m,y,$(CONFIG_IPV6)) += bond_ipv6.o
bonding-objs += $(ipv6-y)

View File

@ -27,6 +27,7 @@
#include <linux/netdevice.h>
#include <linux/spinlock.h>
#include <linux/ethtool.h>
#include <linux/etherdevice.h>
#include <linux/if_bonding.h>
#include <linux/pkt_sched.h>
#include <net/net_namespace.h>
@ -236,6 +237,17 @@ static inline struct aggregator *__get_next_agg(struct aggregator *aggregator)
return &(SLAVE_AD_INFO(slave->next).aggregator);
}
/*
* __agg_has_partner
*
* Return nonzero if aggregator has a partner (denoted by a non-zero ether
* address for the partner). Return 0 if not.
*/
static inline int __agg_has_partner(struct aggregator *agg)
{
return !is_zero_ether_addr(agg->partner_system.mac_addr_value);
}
/**
* __disable_port - disable the port's slave
* @port: the port we're looking at
@ -274,14 +286,14 @@ static inline int __port_is_enabled(struct port *port)
* __get_agg_selection_mode - get the aggregator selection mode
* @port: the port we're looking at
*
* Get the aggregator selection mode. Can be %BANDWIDTH or %COUNT.
* Get the aggregator selection mode. Can be %STABLE, %BANDWIDTH or %COUNT.
*/
static inline u32 __get_agg_selection_mode(struct port *port)
{
struct bonding *bond = __get_bond_by_port(port);
if (bond == NULL) {
return AD_BANDWIDTH;
return BOND_AD_STABLE;
}
return BOND_AD_INFO(bond).agg_select_mode;
@ -1414,9 +1426,82 @@ static void ad_port_selection_logic(struct port *port)
// else set ready=FALSE in all aggregator's ports
__set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator));
if (!__check_agg_selection_timer(port) && (aggregator = __get_first_agg(port))) {
aggregator = __get_first_agg(port);
ad_agg_selection_logic(aggregator);
}
/*
* Decide if "agg" is a better choice for the new active aggregator that
* the current best, according to the ad_select policy.
*/
static struct aggregator *ad_agg_selection_test(struct aggregator *best,
struct aggregator *curr)
{
/*
* 0. If no best, select current.
*
* 1. If the current agg is not individual, and the best is
* individual, select current.
*
* 2. If current agg is individual and the best is not, keep best.
*
* 3. Therefore, current and best are both individual or both not
* individual, so:
*
* 3a. If current agg partner replied, and best agg partner did not,
* select current.
*
* 3b. If current agg partner did not reply and best agg partner
* did reply, keep best.
*
* 4. Therefore, current and best both have partner replies or
* both do not, so perform selection policy:
*
* BOND_AD_COUNT: Select by count of ports. If count is equal,
* select by bandwidth.
*
* BOND_AD_STABLE, BOND_AD_BANDWIDTH: Select by bandwidth.
*/
if (!best)
return curr;
if (!curr->is_individual && best->is_individual)
return curr;
if (curr->is_individual && !best->is_individual)
return best;
if (__agg_has_partner(curr) && !__agg_has_partner(best))
return curr;
if (!__agg_has_partner(curr) && __agg_has_partner(best))
return best;
switch (__get_agg_selection_mode(curr->lag_ports)) {
case BOND_AD_COUNT:
if (curr->num_of_ports > best->num_of_ports)
return curr;
if (curr->num_of_ports < best->num_of_ports)
return best;
/*FALLTHROUGH*/
case BOND_AD_STABLE:
case BOND_AD_BANDWIDTH:
if (__get_agg_bandwidth(curr) > __get_agg_bandwidth(best))
return curr;
break;
default:
printk(KERN_WARNING DRV_NAME
": %s: Impossible agg select mode %d\n",
curr->slave->dev->master->name,
__get_agg_selection_mode(curr->lag_ports));
break;
}
return best;
}
/**
@ -1424,156 +1509,138 @@ static void ad_port_selection_logic(struct port *port)
* @aggregator: the aggregator we're looking at
*
* It is assumed that only one aggregator may be selected for a team.
* The logic of this function is to select (at first time) the aggregator with
* the most ports attached to it, and to reselect the active aggregator only if
* the previous aggregator has no more ports related to it.
*
* The logic of this function is to select the aggregator according to
* the ad_select policy:
*
* BOND_AD_STABLE: select the aggregator with the most ports attached to
* it, and to reselect the active aggregator only if the previous
* aggregator has no more ports related to it.
*
* BOND_AD_BANDWIDTH: select the aggregator with the highest total
* bandwidth, and reselect whenever a link state change takes place or the
* set of slaves in the bond changes.
*
* BOND_AD_COUNT: select the aggregator with largest number of ports
* (slaves), and reselect whenever a link state change takes place or the
* set of slaves in the bond changes.
*
* FIXME: this function MUST be called with the first agg in the bond, or
* __get_active_agg() won't work correctly. This function should be better
* called with the bond itself, and retrieve the first agg from it.
*/
static void ad_agg_selection_logic(struct aggregator *aggregator)
static void ad_agg_selection_logic(struct aggregator *agg)
{
struct aggregator *best_aggregator = NULL, *active_aggregator = NULL;
struct aggregator *last_active_aggregator = NULL, *origin_aggregator;
struct aggregator *best, *active, *origin;
struct port *port;
u16 num_of_aggs=0;
origin_aggregator = aggregator;
origin = agg;
//get current active aggregator
last_active_aggregator = __get_active_agg(aggregator);
active = __get_active_agg(agg);
best = active;
// search for the aggregator with the most ports attached to it.
do {
// count how many candidate lag's we have
if (aggregator->lag_ports) {
num_of_aggs++;
}
if (aggregator->is_active && !aggregator->is_individual && // if current aggregator is the active aggregator
MAC_ADDRESS_COMPARE(&(aggregator->partner_system), &(null_mac_addr))) { // and partner answers to 802.3ad PDUs
if (aggregator->num_of_ports) { // if any ports attached to the current aggregator
best_aggregator=NULL; // disregard the best aggregator that was chosen by now
break; // stop the selection of other aggregator if there are any ports attached to this active aggregator
} else { // no ports attached to this active aggregator
aggregator->is_active = 0; // mark this aggregator as not active anymore
}
}
if (aggregator->num_of_ports) { // if any ports attached
if (best_aggregator) { // if there is a candidte aggregator
//The reasons for choosing new best aggregator:
// 1. if current agg is NOT individual and the best agg chosen so far is individual OR
// current and best aggs are both individual or both not individual, AND
// 2a. current agg partner reply but best agg partner do not reply OR
// 2b. current agg partner reply OR current agg partner do not reply AND best agg partner also do not reply AND
// current has more ports/bandwidth, or same amount of ports but current has faster ports, THEN
// current agg become best agg so far
agg->is_active = 0;
//if current agg is NOT individual and the best agg chosen so far is individual change best_aggregator
if (!aggregator->is_individual && best_aggregator->is_individual) {
best_aggregator=aggregator;
}
// current and best aggs are both individual or both not individual
else if ((aggregator->is_individual && best_aggregator->is_individual) ||
(!aggregator->is_individual && !best_aggregator->is_individual)) {
// current and best aggs are both individual or both not individual AND
// current agg partner reply but best agg partner do not reply
if ((MAC_ADDRESS_COMPARE(&(aggregator->partner_system), &(null_mac_addr)) &&
!MAC_ADDRESS_COMPARE(&(best_aggregator->partner_system), &(null_mac_addr)))) {
best_aggregator=aggregator;
}
// current agg partner reply OR current agg partner do not reply AND best agg partner also do not reply
else if (! (!MAC_ADDRESS_COMPARE(&(aggregator->partner_system), &(null_mac_addr)) &&
MAC_ADDRESS_COMPARE(&(best_aggregator->partner_system), &(null_mac_addr)))) {
if ((__get_agg_selection_mode(aggregator->lag_ports) == AD_BANDWIDTH)&&
(__get_agg_bandwidth(aggregator) > __get_agg_bandwidth(best_aggregator))) {
best_aggregator=aggregator;
} else if (__get_agg_selection_mode(aggregator->lag_ports) == AD_COUNT) {
if (((aggregator->num_of_ports > best_aggregator->num_of_ports) &&
(aggregator->actor_oper_aggregator_key & AD_SPEED_KEY_BITS))||
((aggregator->num_of_ports == best_aggregator->num_of_ports) &&
((u16)(aggregator->actor_oper_aggregator_key & AD_SPEED_KEY_BITS) >
(u16)(best_aggregator->actor_oper_aggregator_key & AD_SPEED_KEY_BITS)))) {
best_aggregator=aggregator;
}
}
}
}
} else {
best_aggregator=aggregator;
}
}
aggregator->is_active = 0; // mark all aggregators as not active anymore
} while ((aggregator = __get_next_agg(aggregator)));
if (agg->num_of_ports)
best = ad_agg_selection_test(best, agg);
// if we have new aggregator selected, don't replace the old aggregator if it has an answering partner,
// or if both old aggregator and new aggregator don't have answering partner
if (best_aggregator) {
if (last_active_aggregator && last_active_aggregator->lag_ports && last_active_aggregator->lag_ports->is_enabled &&
(MAC_ADDRESS_COMPARE(&(last_active_aggregator->partner_system), &(null_mac_addr)) || // partner answers OR
(!MAC_ADDRESS_COMPARE(&(last_active_aggregator->partner_system), &(null_mac_addr)) && // both old and new
!MAC_ADDRESS_COMPARE(&(best_aggregator->partner_system), &(null_mac_addr)))) // partner do not answer
) {
// if new aggregator has link, and old aggregator does not, replace old aggregator.(do nothing)
// -> don't replace otherwise.
if (!(!last_active_aggregator->actor_oper_aggregator_key && best_aggregator->actor_oper_aggregator_key)) {
best_aggregator=NULL;
last_active_aggregator->is_active = 1; // don't replace good old aggregator
} while ((agg = __get_next_agg(agg)));
if (best &&
__get_agg_selection_mode(best->lag_ports) == BOND_AD_STABLE) {
/*
* For the STABLE policy, don't replace the old active
* aggregator if it's still active (it has an answering
* partner) or if both the best and active don't have an
* answering partner.
*/
if (active && active->lag_ports &&
active->lag_ports->is_enabled &&
(__agg_has_partner(active) ||
(!__agg_has_partner(active) && !__agg_has_partner(best)))) {
if (!(!active->actor_oper_aggregator_key &&
best->actor_oper_aggregator_key)) {
best = NULL;
active->is_active = 1;
}
}
}
if (best && (best == active)) {
best = NULL;
active->is_active = 1;
}
// if there is new best aggregator, activate it
if (best_aggregator) {
for (aggregator = __get_first_agg(best_aggregator->lag_ports);
aggregator;
aggregator = __get_next_agg(aggregator)) {
if (best) {
dprintk("best Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n",
best->aggregator_identifier, best->num_of_ports,
best->actor_oper_aggregator_key,
best->partner_oper_aggregator_key,
best->is_individual, best->is_active);
dprintk("best ports %p slave %p %s\n",
best->lag_ports, best->slave,
best->slave ? best->slave->dev->name : "NULL");
dprintk("Agg=%d; Ports=%d; a key=%d; p key=%d; Indiv=%d; Active=%d\n",
aggregator->aggregator_identifier, aggregator->num_of_ports,
aggregator->actor_oper_aggregator_key, aggregator->partner_oper_aggregator_key,
aggregator->is_individual, aggregator->is_active);
for (agg = __get_first_agg(best->lag_ports); agg;
agg = __get_next_agg(agg)) {
dprintk("Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n",
agg->aggregator_identifier, agg->num_of_ports,
agg->actor_oper_aggregator_key,
agg->partner_oper_aggregator_key,
agg->is_individual, agg->is_active);
}
// check if any partner replys
if (best_aggregator->is_individual) {
printk(KERN_WARNING DRV_NAME ": %s: Warning: No 802.3ad response from "
"the link partner for any adapters in the bond\n",
best_aggregator->slave->dev->master->name);
if (best->is_individual) {
printk(KERN_WARNING DRV_NAME ": %s: Warning: No 802.3ad"
" response from the link partner for any"
" adapters in the bond\n",
best->slave->dev->master->name);
}
// check if there are more than one aggregator
if (num_of_aggs > 1) {
dprintk("Warning: More than one Link Aggregation Group was "
"found in the bond. Only one group will function in the bond\n");
}
best_aggregator->is_active = 1;
dprintk("LAG %d choosed as the active LAG\n", best_aggregator->aggregator_identifier);
dprintk("Agg=%d; Ports=%d; a key=%d; p key=%d; Indiv=%d; Active=%d\n",
best_aggregator->aggregator_identifier, best_aggregator->num_of_ports,
best_aggregator->actor_oper_aggregator_key, best_aggregator->partner_oper_aggregator_key,
best_aggregator->is_individual, best_aggregator->is_active);
best->is_active = 1;
dprintk("LAG %d chosen as the active LAG\n",
best->aggregator_identifier);
dprintk("Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n",
best->aggregator_identifier, best->num_of_ports,
best->actor_oper_aggregator_key,
best->partner_oper_aggregator_key,
best->is_individual, best->is_active);
// disable the ports that were related to the former active_aggregator
if (last_active_aggregator) {
for (port=last_active_aggregator->lag_ports; port; port=port->next_port_in_aggregator) {
if (active) {
for (port = active->lag_ports; port;
port = port->next_port_in_aggregator) {
__disable_port(port);
}
}
}
// if the selected aggregator is of join individuals(partner_system is NULL), enable their ports
active_aggregator = __get_active_agg(origin_aggregator);
/*
* if the selected aggregator is of join individuals
* (partner_system is NULL), enable their ports
*/
active = __get_active_agg(origin);
if (active_aggregator) {
if (!MAC_ADDRESS_COMPARE(&(active_aggregator->partner_system), &(null_mac_addr))) {
for (port=active_aggregator->lag_ports; port; port=port->next_port_in_aggregator) {
if (active) {
if (!__agg_has_partner(active)) {
for (port = active->lag_ports; port;
port = port->next_port_in_aggregator) {
__enable_port(port);
}
}
}
if (origin->slave) {
struct bonding *bond;
bond = bond_get_bond_by_slave(origin->slave);
if (bond)
bond_3ad_set_carrier(bond);
}
}
/**
@ -1830,6 +1897,19 @@ static void ad_initialize_lacpdu(struct lacpdu *lacpdu)
// Check aggregators status in team every T seconds
#define AD_AGGREGATOR_SELECTION_TIMER 8
/*
* bond_3ad_initiate_agg_selection(struct bonding *bond)
*
* Set the aggregation selection timer, to initiate an agg selection in
* the very near future. Called during first initialization, and during
* any down to up transitions of the bond.
*/
void bond_3ad_initiate_agg_selection(struct bonding *bond, int timeout)
{
BOND_AD_INFO(bond).agg_select_timer = timeout;
BOND_AD_INFO(bond).agg_select_mode = bond->params.ad_select;
}
static u16 aggregator_identifier;
/**
@ -1854,9 +1934,9 @@ void bond_3ad_initialize(struct bonding *bond, u16 tick_resolution, int lacp_fas
// initialize how many times this module is called in one second(should be about every 100ms)
ad_ticks_per_sec = tick_resolution;
// initialize the aggregator selection timer(to activate an aggregation selection after initialize)
BOND_AD_INFO(bond).agg_select_timer = (AD_AGGREGATOR_SELECTION_TIMER * ad_ticks_per_sec);
BOND_AD_INFO(bond).agg_select_mode = AD_BANDWIDTH;
bond_3ad_initiate_agg_selection(bond,
AD_AGGREGATOR_SELECTION_TIMER *
ad_ticks_per_sec);
}
}

View File

@ -42,10 +42,11 @@ typedef struct mac_addr {
u8 mac_addr_value[ETH_ALEN];
} mac_addr_t;
typedef enum {
AD_BANDWIDTH = 0,
AD_COUNT
} agg_selection_t;
enum {
BOND_AD_STABLE = 0,
BOND_AD_BANDWIDTH = 1,
BOND_AD_COUNT = 2,
};
// rx machine states(43.4.11 in the 802.3ad standard)
typedef enum {
@ -277,6 +278,7 @@ void bond_3ad_initialize(struct bonding *bond, u16 tick_resolution, int lacp_fas
int bond_3ad_bind_slave(struct slave *slave);
void bond_3ad_unbind_slave(struct slave *slave);
void bond_3ad_state_machine_handler(struct work_struct *);
void bond_3ad_initiate_agg_selection(struct bonding *bond, int timeout);
void bond_3ad_adapter_speed_changed(struct slave *slave);
void bond_3ad_adapter_duplex_changed(struct slave *slave);
void bond_3ad_handle_link_change(struct slave *slave, char link);

View File

@ -346,14 +346,18 @@ static void rlb_update_entry_from_arp(struct bonding *bond, struct arp_pkt *arp)
static int rlb_arp_recv(struct sk_buff *skb, struct net_device *bond_dev, struct packet_type *ptype, struct net_device *orig_dev)
{
struct bonding *bond = bond_dev->priv;
struct bonding *bond;
struct arp_pkt *arp = (struct arp_pkt *)skb->data;
int res = NET_RX_DROP;
if (dev_net(bond_dev) != &init_net)
goto out;
if (!(bond_dev->flags & IFF_MASTER))
while (bond_dev->priv_flags & IFF_802_1Q_VLAN)
bond_dev = vlan_dev_real_dev(bond_dev);
if (!(bond_dev->priv_flags & IFF_BONDING) ||
!(bond_dev->flags & IFF_MASTER))
goto out;
if (!arp) {
@ -368,6 +372,9 @@ static int rlb_arp_recv(struct sk_buff *skb, struct net_device *bond_dev, struct
if (arp->op_code == htons(ARPOP_REPLY)) {
/* update rx hash table for this ARP */
printk("rar: update orig %s bond_dev %s\n", orig_dev->name,
bond_dev->name);
bond = bond_dev->priv;
rlb_update_entry_from_arp(bond, arp);
dprintk("Server received an ARP Reply from client\n");
}
@ -818,7 +825,7 @@ static int rlb_initialize(struct bonding *bond)
/*initialize packet type*/
pk_type->type = __constant_htons(ETH_P_ARP);
pk_type->dev = bond->dev;
pk_type->dev = NULL;
pk_type->func = rlb_arp_recv;
/* register to receive ARPs */

View File

@ -0,0 +1,218 @@
/*
* Copyright(c) 2008 Hewlett-Packard Development Company, L.P.
*
* 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 2 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, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The full GNU General Public License is included in this distribution in the
* file called LICENSE.
*
*/
//#define BONDING_DEBUG 1
#include <linux/types.h>
#include <linux/if_vlan.h>
#include <net/ipv6.h>
#include <net/ndisc.h>
#include <net/addrconf.h>
#include "bonding.h"
/*
* Assign bond->master_ipv6 to the next IPv6 address in the list, or
* zero it out if there are none.
*/
static void bond_glean_dev_ipv6(struct net_device *dev, struct in6_addr *addr)
{
struct inet6_dev *idev;
struct inet6_ifaddr *ifa;
if (!dev)
return;
idev = in6_dev_get(dev);
if (!idev)
return;
read_lock_bh(&idev->lock);
ifa = idev->addr_list;
if (ifa)
ipv6_addr_copy(addr, &ifa->addr);
else
ipv6_addr_set(addr, 0, 0, 0, 0);
read_unlock_bh(&idev->lock);
in6_dev_put(idev);
}
static void bond_na_send(struct net_device *slave_dev,
struct in6_addr *daddr,
int router,
unsigned short vlan_id)
{
struct in6_addr mcaddr;
struct icmp6hdr icmp6h = {
.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
};
struct sk_buff *skb;
icmp6h.icmp6_router = router;
icmp6h.icmp6_solicited = 0;
icmp6h.icmp6_override = 1;
addrconf_addr_solict_mult(daddr, &mcaddr);
dprintk("ipv6 na on slave %s: dest %pI6, src %pI6\n",
slave->name, &mcaddr, daddr);
skb = ndisc_build_skb(slave_dev, &mcaddr, daddr, &icmp6h, daddr,
ND_OPT_TARGET_LL_ADDR);
if (!skb) {
printk(KERN_ERR DRV_NAME ": NA packet allocation failed\n");
return;
}
if (vlan_id) {
skb = vlan_put_tag(skb, vlan_id);
if (!skb) {
printk(KERN_ERR DRV_NAME ": failed to insert VLAN tag\n");
return;
}
}
ndisc_send_skb(skb, slave_dev, NULL, &mcaddr, daddr, &icmp6h);
}
/*
* Kick out an unsolicited Neighbor Advertisement for an IPv6 address on
* the bonding master. This will help the switch learn our address
* if in active-backup mode.
*
* Caller must hold curr_slave_lock for read or better
*/
void bond_send_unsolicited_na(struct bonding *bond)
{
struct slave *slave = bond->curr_active_slave;
struct vlan_entry *vlan;
struct inet6_dev *idev;
int is_router;
dprintk("bond_send_unsol_na: bond %s slave %s\n", bond->dev->name,
slave ? slave->dev->name : "NULL");
if (!slave || !bond->send_unsol_na ||
test_bit(__LINK_STATE_LINKWATCH_PENDING, &slave->dev->state))
return;
bond->send_unsol_na--;
idev = in6_dev_get(bond->dev);
if (!idev)
return;
is_router = !!idev->cnf.forwarding;
in6_dev_put(idev);
if (!ipv6_addr_any(&bond->master_ipv6))
bond_na_send(slave->dev, &bond->master_ipv6, is_router, 0);
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
if (!ipv6_addr_any(&vlan->vlan_ipv6)) {
bond_na_send(slave->dev, &vlan->vlan_ipv6, is_router,
vlan->vlan_id);
}
}
}
/*
* bond_inet6addr_event: handle inet6addr notifier chain events.
*
* We keep track of device IPv6 addresses primarily to use as source
* addresses in NS probes.
*
* We track one IPv6 for the main device (if it has one).
*/
static int bond_inet6addr_event(struct notifier_block *this,
unsigned long event,
void *ptr)
{
struct inet6_ifaddr *ifa = ptr;
struct net_device *vlan_dev, *event_dev = ifa->idev->dev;
struct bonding *bond;
struct vlan_entry *vlan;
if (dev_net(event_dev) != &init_net)
return NOTIFY_DONE;
list_for_each_entry(bond, &bond_dev_list, bond_list) {
if (bond->dev == event_dev) {
switch (event) {
case NETDEV_UP:
if (ipv6_addr_any(&bond->master_ipv6))
ipv6_addr_copy(&bond->master_ipv6,
&ifa->addr);
return NOTIFY_OK;
case NETDEV_DOWN:
if (ipv6_addr_equal(&bond->master_ipv6,
&ifa->addr))
bond_glean_dev_ipv6(bond->dev,
&bond->master_ipv6);
return NOTIFY_OK;
default:
return NOTIFY_DONE;
}
}
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
vlan_dev = vlan_group_get_device(bond->vlgrp,
vlan->vlan_id);
if (vlan_dev == event_dev) {
switch (event) {
case NETDEV_UP:
if (ipv6_addr_any(&vlan->vlan_ipv6))
ipv6_addr_copy(&vlan->vlan_ipv6,
&ifa->addr);
return NOTIFY_OK;
case NETDEV_DOWN:
if (ipv6_addr_equal(&vlan->vlan_ipv6,
&ifa->addr))
bond_glean_dev_ipv6(vlan_dev,
&vlan->vlan_ipv6);
return NOTIFY_OK;
default:
return NOTIFY_DONE;
}
}
}
}
return NOTIFY_DONE;
}
static struct notifier_block bond_inet6addr_notifier = {
.notifier_call = bond_inet6addr_event,
};
void bond_register_ipv6_notifier(void)
{
register_inet6addr_notifier(&bond_inet6addr_notifier);
}
void bond_unregister_ipv6_notifier(void)
{
unregister_inet6addr_notifier(&bond_inet6addr_notifier);
}

View File

@ -89,6 +89,7 @@
static int max_bonds = BOND_DEFAULT_MAX_BONDS;
static int num_grat_arp = 1;
static int num_unsol_na = 1;
static int miimon = BOND_LINK_MON_INTERV;
static int updelay = 0;
static int downdelay = 0;
@ -96,6 +97,7 @@ static int use_carrier = 1;
static char *mode = NULL;
static char *primary = NULL;
static char *lacp_rate = NULL;
static char *ad_select = NULL;
static char *xmit_hash_policy = NULL;
static int arp_interval = BOND_LINK_ARP_INTERV;
static char *arp_ip_target[BOND_MAX_ARP_TARGETS] = { NULL, };
@ -107,6 +109,8 @@ module_param(max_bonds, int, 0);
MODULE_PARM_DESC(max_bonds, "Max number of bonded devices");
module_param(num_grat_arp, int, 0644);
MODULE_PARM_DESC(num_grat_arp, "Number of gratuitous ARP packets to send on failover event");
module_param(num_unsol_na, int, 0644);
MODULE_PARM_DESC(num_unsol_na, "Number of unsolicited IPv6 Neighbor Advertisements packets to send on failover event");
module_param(miimon, int, 0);
MODULE_PARM_DESC(miimon, "Link check interval in milliseconds");
module_param(updelay, int, 0);
@ -127,6 +131,8 @@ MODULE_PARM_DESC(primary, "Primary network device to use");
module_param(lacp_rate, charp, 0);
MODULE_PARM_DESC(lacp_rate, "LACPDU tx rate to request from 802.3ad partner "
"(slow/fast)");
module_param(ad_select, charp, 0);
MODULE_PARM_DESC(ad_select, "803.ad aggregation selection logic: stable (0, default), bandwidth (1), count (2)");
module_param(xmit_hash_policy, charp, 0);
MODULE_PARM_DESC(xmit_hash_policy, "XOR hashing method: 0 for layer 2 (default)"
", 1 for layer 3+4");
@ -197,6 +203,13 @@ struct bond_parm_tbl fail_over_mac_tbl[] = {
{ NULL, -1},
};
struct bond_parm_tbl ad_select_tbl[] = {
{ "stable", BOND_AD_STABLE},
{ "bandwidth", BOND_AD_BANDWIDTH},
{ "count", BOND_AD_COUNT},
{ NULL, -1},
};
/*-------------------------- Forward declarations ---------------------------*/
static void bond_send_gratuitous_arp(struct bonding *bond);
@ -242,14 +255,13 @@ static int bond_add_vlan(struct bonding *bond, unsigned short vlan_id)
dprintk("bond: %s, vlan id %d\n",
(bond ? bond->dev->name: "None"), vlan_id);
vlan = kmalloc(sizeof(struct vlan_entry), GFP_KERNEL);
vlan = kzalloc(sizeof(struct vlan_entry), GFP_KERNEL);
if (!vlan) {
return -ENOMEM;
}
INIT_LIST_HEAD(&vlan->vlan_list);
vlan->vlan_id = vlan_id;
vlan->vlan_ip = 0;
write_lock_bh(&bond->lock);
@ -1208,6 +1220,9 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
bond->send_grat_arp = bond->params.num_grat_arp;
bond_send_gratuitous_arp(bond);
bond->send_unsol_na = bond->params.num_unsol_na;
bond_send_unsolicited_na(bond);
write_unlock_bh(&bond->curr_slave_lock);
read_unlock(&bond->lock);
@ -2463,6 +2478,12 @@ void bond_mii_monitor(struct work_struct *work)
read_unlock(&bond->curr_slave_lock);
}
if (bond->send_unsol_na) {
read_lock(&bond->curr_slave_lock);
bond_send_unsolicited_na(bond);
read_unlock(&bond->curr_slave_lock);
}
if (bond_miimon_inspect(bond)) {
read_unlock(&bond->lock);
rtnl_lock();
@ -3158,6 +3179,12 @@ void bond_activebackup_arp_mon(struct work_struct *work)
read_unlock(&bond->curr_slave_lock);
}
if (bond->send_unsol_na) {
read_lock(&bond->curr_slave_lock);
bond_send_unsolicited_na(bond);
read_unlock(&bond->curr_slave_lock);
}
if (bond_ab_arp_inspect(bond, delta_in_ticks)) {
read_unlock(&bond->lock);
rtnl_lock();
@ -3301,6 +3328,8 @@ static void bond_info_show_master(struct seq_file *seq)
seq_puts(seq, "\n802.3ad info\n");
seq_printf(seq, "LACP rate: %s\n",
(bond->params.lacp_fast) ? "fast" : "slow");
seq_printf(seq, "Aggregator selection policy (ad_select): %s\n",
ad_select_tbl[bond->params.ad_select].modename);
if (bond_3ad_get_active_agg_info(bond, &ad_info)) {
seq_printf(seq, "bond %s has no active aggregator\n",
@ -3807,6 +3836,7 @@ static int bond_open(struct net_device *bond_dev)
queue_delayed_work(bond->wq, &bond->ad_work, 0);
/* register to receive LACPDUs */
bond_register_lacpdu(bond);
bond_3ad_initiate_agg_selection(bond, 1);
}
return 0;
@ -3827,6 +3857,7 @@ static int bond_close(struct net_device *bond_dev)
write_lock_bh(&bond->lock);
bond->send_grat_arp = 0;
bond->send_unsol_na = 0;
/* signal timers not to re-arm */
bond->kill_timers = 1;
@ -4542,6 +4573,7 @@ static int bond_init(struct net_device *bond_dev, struct bond_params *params)
bond->primary_slave = NULL;
bond->dev = bond_dev;
bond->send_grat_arp = 0;
bond->send_unsol_na = 0;
bond->setup_by_slave = 0;
INIT_LIST_HEAD(&bond->vlan_list);
@ -4744,6 +4776,23 @@ static int bond_check_params(struct bond_params *params)
}
}
if (ad_select) {
params->ad_select = bond_parse_parm(ad_select, ad_select_tbl);
if (params->ad_select == -1) {
printk(KERN_ERR DRV_NAME
": Error: Invalid ad_select \"%s\"\n",
ad_select == NULL ? "NULL" : ad_select);
return -EINVAL;
}
if (bond_mode != BOND_MODE_8023AD) {
printk(KERN_WARNING DRV_NAME
": ad_select param only affects 802.3ad mode\n");
}
} else {
params->ad_select = BOND_AD_STABLE;
}
if (max_bonds < 0 || max_bonds > INT_MAX) {
printk(KERN_WARNING DRV_NAME
": Warning: max_bonds (%d) not in range %d-%d, so it "
@ -4791,6 +4840,13 @@ static int bond_check_params(struct bond_params *params)
num_grat_arp = 1;
}
if (num_unsol_na < 0 || num_unsol_na > 255) {
printk(KERN_WARNING DRV_NAME
": Warning: num_unsol_na (%d) not in range 0-255 so it "
"was reset to 1 \n", num_unsol_na);
num_unsol_na = 1;
}
/* reset values for 802.3ad */
if (bond_mode == BOND_MODE_8023AD) {
if (!miimon) {
@ -4992,6 +5048,7 @@ static int bond_check_params(struct bond_params *params)
params->xmit_policy = xmit_hashtype;
params->miimon = miimon;
params->num_grat_arp = num_grat_arp;
params->num_unsol_na = num_unsol_na;
params->arp_interval = arp_interval;
params->arp_validate = arp_validate_value;
params->updelay = updelay;
@ -5144,6 +5201,7 @@ static int __init bonding_init(void)
register_netdevice_notifier(&bond_netdev_notifier);
register_inetaddr_notifier(&bond_inetaddr_notifier);
bond_register_ipv6_notifier();
goto out;
err:
@ -5166,6 +5224,7 @@ static void __exit bonding_exit(void)
{
unregister_netdevice_notifier(&bond_netdev_notifier);
unregister_inetaddr_notifier(&bond_inetaddr_notifier);
bond_unregister_ipv6_notifier();
bond_destroy_sysfs();

View File

@ -48,6 +48,7 @@ extern struct list_head bond_dev_list;
extern struct bond_params bonding_defaults;
extern struct bond_parm_tbl bond_mode_tbl[];
extern struct bond_parm_tbl bond_lacp_tbl[];
extern struct bond_parm_tbl ad_select_tbl[];
extern struct bond_parm_tbl xmit_hashtype_tbl[];
extern struct bond_parm_tbl arp_validate_tbl[];
extern struct bond_parm_tbl fail_over_mac_tbl[];
@ -944,6 +945,53 @@ out:
}
static DEVICE_ATTR(lacp_rate, S_IRUGO | S_IWUSR, bonding_show_lacp, bonding_store_lacp);
static ssize_t bonding_show_ad_select(struct device *d,
struct device_attribute *attr,
char *buf)
{
struct bonding *bond = to_bond(d);
return sprintf(buf, "%s %d\n",
ad_select_tbl[bond->params.ad_select].modename,
bond->params.ad_select);
}
static ssize_t bonding_store_ad_select(struct device *d,
struct device_attribute *attr,
const char *buf, size_t count)
{
int new_value, ret = count;
struct bonding *bond = to_bond(d);
if (bond->dev->flags & IFF_UP) {
printk(KERN_ERR DRV_NAME
": %s: Unable to update ad_select because interface "
"is up.\n", bond->dev->name);
ret = -EPERM;
goto out;
}
new_value = bond_parse_parm(buf, ad_select_tbl);
if (new_value != -1) {
bond->params.ad_select = new_value;
printk(KERN_INFO DRV_NAME
": %s: Setting ad_select to %s (%d).\n",
bond->dev->name, ad_select_tbl[new_value].modename,
new_value);
} else {
printk(KERN_ERR DRV_NAME
": %s: Ignoring invalid ad_select value %.*s.\n",
bond->dev->name, (int)strlen(buf) - 1, buf);
ret = -EINVAL;
}
out:
return ret;
}
static DEVICE_ATTR(ad_select, S_IRUGO | S_IWUSR, bonding_show_ad_select, bonding_store_ad_select);
/*
* Show and set the number of grat ARP to send after a failover event.
*/
@ -983,6 +1031,47 @@ out:
return ret;
}
static DEVICE_ATTR(num_grat_arp, S_IRUGO | S_IWUSR, bonding_show_n_grat_arp, bonding_store_n_grat_arp);
/*
* Show and set the number of unsolicted NA's to send after a failover event.
*/
static ssize_t bonding_show_n_unsol_na(struct device *d,
struct device_attribute *attr,
char *buf)
{
struct bonding *bond = to_bond(d);
return sprintf(buf, "%d\n", bond->params.num_unsol_na);
}
static ssize_t bonding_store_n_unsol_na(struct device *d,
struct device_attribute *attr,
const char *buf, size_t count)
{
int new_value, ret = count;
struct bonding *bond = to_bond(d);
if (sscanf(buf, "%d", &new_value) != 1) {
printk(KERN_ERR DRV_NAME
": %s: no num_unsol_na value specified.\n",
bond->dev->name);
ret = -EINVAL;
goto out;
}
if (new_value < 0 || new_value > 255) {
printk(KERN_ERR DRV_NAME
": %s: Invalid num_unsol_na value %d not in range 0-255; rejected.\n",
bond->dev->name, new_value);
ret = -EINVAL;
goto out;
} else {
bond->params.num_unsol_na = new_value;
}
out:
return ret;
}
static DEVICE_ATTR(num_unsol_na, S_IRUGO | S_IWUSR, bonding_show_n_unsol_na, bonding_store_n_unsol_na);
/*
* Show and set the MII monitor interval. There are two tricky bits
* here. First, if MII monitoring is activated, then we must disable
@ -1418,8 +1507,10 @@ static struct attribute *per_bond_attrs[] = {
&dev_attr_downdelay.attr,
&dev_attr_updelay.attr,
&dev_attr_lacp_rate.attr,
&dev_attr_ad_select.attr,
&dev_attr_xmit_hash_policy.attr,
&dev_attr_num_grat_arp.attr,
&dev_attr_num_unsol_na.attr,
&dev_attr_miimon.attr,
&dev_attr_primary.attr,
&dev_attr_use_carrier.attr,

View File

@ -19,16 +19,19 @@
#include <linux/proc_fs.h>
#include <linux/if_bonding.h>
#include <linux/kobject.h>
#include <linux/in6.h>
#include "bond_3ad.h"
#include "bond_alb.h"
#define DRV_VERSION "3.3.0"
#define DRV_RELDATE "June 10, 2008"
#define DRV_VERSION "3.5.0"
#define DRV_RELDATE "November 4, 2008"
#define DRV_NAME "bonding"
#define DRV_DESCRIPTION "Ethernet Channel Bonding Driver"
#define BOND_MAX_ARP_TARGETS 16
extern struct list_head bond_dev_list;
#ifdef BONDING_DEBUG
#define dprintk(fmt, args...) \
printk(KERN_DEBUG \
@ -126,6 +129,7 @@ struct bond_params {
int xmit_policy;
int miimon;
int num_grat_arp;
int num_unsol_na;
int arp_interval;
int arp_validate;
int use_carrier;
@ -133,6 +137,7 @@ struct bond_params {
int updelay;
int downdelay;
int lacp_fast;
int ad_select;
char primary[IFNAMSIZ];
__be32 arp_targets[BOND_MAX_ARP_TARGETS];
};
@ -148,6 +153,9 @@ struct vlan_entry {
struct list_head vlan_list;
__be32 vlan_ip;
unsigned short vlan_id;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
struct in6_addr vlan_ipv6;
#endif
};
struct slave {
@ -195,6 +203,7 @@ struct bonding {
rwlock_t curr_slave_lock;
s8 kill_timers;
s8 send_grat_arp;
s8 send_unsol_na;
s8 setup_by_slave;
struct net_device_stats stats;
#ifdef CONFIG_PROC_FS
@ -218,6 +227,9 @@ struct bonding {
struct delayed_work arp_work;
struct delayed_work alb_work;
struct delayed_work ad_work;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
struct in6_addr master_ipv6;
#endif
};
/**
@ -341,5 +353,24 @@ extern struct bond_parm_tbl xmit_hashtype_tbl[];
extern struct bond_parm_tbl arp_validate_tbl[];
extern struct bond_parm_tbl fail_over_mac_tbl[];
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
void bond_send_unsolicited_na(struct bonding *bond);
void bond_register_ipv6_notifier(void);
void bond_unregister_ipv6_notifier(void);
#else
static inline void bond_send_unsolicited_na(struct bonding *bond)
{
return;
}
static inline void bond_register_ipv6_notifier(void)
{
return;
}
static inline void bond_unregister_ipv6_notifier(void)
{
return;
}
#endif
#endif /* _LINUX_BONDING_H */

View File

@ -40,7 +40,7 @@
#include <asm/io.h>
#define DRV_NAME "ehea"
#define DRV_VERSION "EHEA_0095"
#define DRV_VERSION "EHEA_0096"
/* eHEA capability flags */
#define DLPAR_PORT_ADD_REM 1

View File

@ -125,6 +125,7 @@ typedef struct local_info_t {
u_short tx_queue_len;
cardtype_t cardtype;
u_short sent;
u_char __iomem *base;
} local_info_t;
#define MC_FILTERBREAK 64
@ -242,6 +243,7 @@ static int fmvj18x_probe(struct pcmcia_device *link)
lp = netdev_priv(dev);
link->priv = dev;
lp->p_dev = link;
lp->base = NULL;
/* The io structure describes IO port mapping */
link->io.NumPorts1 = 32;
@ -442,8 +444,10 @@ static int fmvj18x_config(struct pcmcia_device *link)
dev->irq = link->irq.AssignedIRQ;
dev->base_addr = link->io.BasePort1;
if (link->io.BasePort2 != 0)
fmvj18x_setup_mfc(link);
if (link->io.BasePort2 != 0) {
ret = fmvj18x_setup_mfc(link);
if (ret != 0) goto failed;
}
ioaddr = dev->base_addr;
@ -610,10 +614,10 @@ static int fmvj18x_setup_mfc(struct pcmcia_device *link)
{
win_req_t req;
memreq_t mem;
u_char __iomem *base;
int i, j;
int i;
struct net_device *dev = link->priv;
unsigned int ioaddr;
local_info_t *lp = netdev_priv(dev);
/* Allocate a small memory window */
req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
@ -625,25 +629,32 @@ static int fmvj18x_setup_mfc(struct pcmcia_device *link)
return -1;
}
base = ioremap(req.Base, req.Size);
lp->base = ioremap(req.Base, req.Size);
if (lp->base == NULL) {
printk(KERN_NOTICE "fmvj18x_cs: ioremap failed\n");
return -1;
}
mem.Page = 0;
mem.CardOffset = 0;
pcmcia_map_mem_page(link->win, &mem);
i = pcmcia_map_mem_page(link->win, &mem);
if (i != 0) {
iounmap(lp->base);
lp->base = NULL;
cs_error(link, MapMemPage, i);
return -1;
}
ioaddr = dev->base_addr;
writeb(0x47, base+0x800); /* Config Option Register of LAN */
writeb(0x0, base+0x802); /* Config and Status Register */
writeb(0x47, lp->base+0x800); /* Config Option Register of LAN */
writeb(0x0, lp->base+0x802); /* Config and Status Register */
writeb(ioaddr & 0xff, base+0x80a); /* I/O Base(Low) of LAN */
writeb((ioaddr >> 8) & 0xff, base+0x80c); /* I/O Base(High) of LAN */
writeb(ioaddr & 0xff, lp->base+0x80a); /* I/O Base(Low) of LAN */
writeb((ioaddr >> 8) & 0xff, lp->base+0x80c); /* I/O Base(High) of LAN */
writeb(0x45, base+0x820); /* Config Option Register of Modem */
writeb(0x8, base+0x822); /* Config and Status Register */
writeb(0x45, lp->base+0x820); /* Config Option Register of Modem */
writeb(0x8, lp->base+0x822); /* Config and Status Register */
iounmap(base);
j = pcmcia_release_window(link->win);
if (j != 0)
cs_error(link, ReleaseWindow, j);
return 0;
}
@ -651,8 +662,25 @@ static int fmvj18x_setup_mfc(struct pcmcia_device *link)
static void fmvj18x_release(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
local_info_t *lp = netdev_priv(dev);
u_char __iomem *tmp;
int j;
DEBUG(0, "fmvj18x_release(0x%p)\n", link);
if (lp->base != NULL) {
tmp = lp->base;
lp->base = NULL; /* set NULL before iounmap */
iounmap(tmp);
j = pcmcia_release_window(link->win);
if (j != 0)
cs_error(link, ReleaseWindow, j);
}
pcmcia_disable_device(link);
}
static int fmvj18x_suspend(struct pcmcia_device *link)
@ -783,6 +811,13 @@ static irqreturn_t fjn_interrupt(int dummy, void *dev_id)
outb(D_TX_INTR, ioaddr + TX_INTR);
outb(D_RX_INTR, ioaddr + RX_INTR);
if (lp->base != NULL) {
/* Ack interrupt for multifunction card */
writeb(0x01, lp->base+0x802);
writeb(0x09, lp->base+0x822);
}
return IRQ_HANDLED;
} /* fjn_interrupt */

View File

@ -126,6 +126,27 @@ static struct phy_driver lan8700_driver = {
.driver = { .owner = THIS_MODULE, }
};
static struct phy_driver lan911x_int_driver = {
.phy_id = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */
.phy_id_mask = 0xfffffff0,
.name = "SMSC LAN911x Internal PHY",
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause
| SUPPORTED_Asym_Pause),
.flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
/* basic functions */
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.config_init = smsc_phy_config_init,
/* IRQ related */
.ack_interrupt = smsc_phy_ack_interrupt,
.config_intr = smsc_phy_config_intr,
.driver = { .owner = THIS_MODULE, }
};
static int __init smsc_init(void)
{
int ret;
@ -142,8 +163,14 @@ static int __init smsc_init(void)
if (ret)
goto err3;
ret = phy_driver_register (&lan911x_int_driver);
if (ret)
goto err4;
return 0;
err4:
phy_driver_unregister (&lan8700_driver);
err3:
phy_driver_unregister (&lan8187_driver);
err2:
@ -154,6 +181,7 @@ err1:
static void __exit smsc_exit(void)
{
phy_driver_unregister (&lan911x_int_driver);
phy_driver_unregister (&lan8700_driver);
phy_driver_unregister (&lan8187_driver);
phy_driver_unregister (&lan83c185_driver);

View File

@ -12,3 +12,11 @@ config SFC
To compile this driver as a module, choose M here. The module
will be called sfc.
config SFC_MTD
bool "Solarflare Solarstorm SFC4000 flash MTD support"
depends on SFC && MTD
default y
help
This exposes the on-board flash memory as an MTD device (e.g.
/dev/mtd1). This makes it possible to upload new boot code
to the NIC.

View File

@ -1,5 +1,6 @@
sfc-y += efx.o falcon.o tx.o rx.o falcon_xmac.o \
selftest.o ethtool.o xfp_phy.o \
mdio_10g.o tenxpress.o boards.o sfe4001.o
sfc-$(CONFIG_SFC_MTD) += mtd.o
obj-$(CONFIG_SFC) += sfc.o

View File

@ -11,6 +11,7 @@
#include "phy.h"
#include "boards.h"
#include "efx.h"
#include "workarounds.h"
/* Macros for unpacking the board revision */
/* The revision info is in host byte order. */
@ -51,10 +52,129 @@ static void board_blink(struct efx_nic *efx, bool blink)
}
}
/*****************************************************************************
* Support for LM87 sensor chip used on several boards
*/
#define LM87_REG_ALARMS1 0x41
#define LM87_REG_ALARMS2 0x42
#define LM87_IN_LIMITS(nr, _min, _max) \
0x2B + (nr) * 2, _max, 0x2C + (nr) * 2, _min
#define LM87_AIN_LIMITS(nr, _min, _max) \
0x3B + (nr), _max, 0x1A + (nr), _min
#define LM87_TEMP_INT_LIMITS(_min, _max) \
0x39, _max, 0x3A, _min
#define LM87_TEMP_EXT1_LIMITS(_min, _max) \
0x37, _max, 0x38, _min
#define LM87_ALARM_TEMP_INT 0x10
#define LM87_ALARM_TEMP_EXT1 0x20
#if defined(CONFIG_SENSORS_LM87) || defined(CONFIG_SENSORS_LM87_MODULE)
static int efx_init_lm87(struct efx_nic *efx, struct i2c_board_info *info,
const u8 *reg_values)
{
struct i2c_client *client = i2c_new_device(&efx->i2c_adap, info);
int rc;
if (!client)
return -EIO;
while (*reg_values) {
u8 reg = *reg_values++;
u8 value = *reg_values++;
rc = i2c_smbus_write_byte_data(client, reg, value);
if (rc)
goto err;
}
efx->board_info.hwmon_client = client;
return 0;
err:
i2c_unregister_device(client);
return rc;
}
static void efx_fini_lm87(struct efx_nic *efx)
{
i2c_unregister_device(efx->board_info.hwmon_client);
}
static int efx_check_lm87(struct efx_nic *efx, unsigned mask)
{
struct i2c_client *client = efx->board_info.hwmon_client;
s32 alarms1, alarms2;
/* If link is up then do not monitor temperature */
if (EFX_WORKAROUND_7884(efx) && efx->link_up)
return 0;
alarms1 = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS1);
alarms2 = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS2);
if (alarms1 < 0)
return alarms1;
if (alarms2 < 0)
return alarms2;
alarms1 &= mask;
alarms2 &= mask >> 8;
if (alarms1 || alarms2) {
EFX_ERR(efx,
"LM87 detected a hardware failure (status %02x:%02x)"
"%s%s\n",
alarms1, alarms2,
(alarms1 & LM87_ALARM_TEMP_INT) ? " INTERNAL" : "",
(alarms1 & LM87_ALARM_TEMP_EXT1) ? " EXTERNAL" : "");
return -ERANGE;
}
return 0;
}
#else /* !CONFIG_SENSORS_LM87 */
static inline int
efx_init_lm87(struct efx_nic *efx, struct i2c_board_info *info,
const u8 *reg_values)
{
return 0;
}
static inline void efx_fini_lm87(struct efx_nic *efx)
{
}
static inline int efx_check_lm87(struct efx_nic *efx, unsigned mask)
{
return 0;
}
#endif /* CONFIG_SENSORS_LM87 */
/*****************************************************************************
* Support for the SFE4002
*
*/
static u8 sfe4002_lm87_channel = 0x03; /* use AIN not FAN inputs */
static const u8 sfe4002_lm87_regs[] = {
LM87_IN_LIMITS(0, 0x83, 0x91), /* 2.5V: 1.8V +/- 5% */
LM87_IN_LIMITS(1, 0x51, 0x5a), /* Vccp1: 1.2V +/- 5% */
LM87_IN_LIMITS(2, 0xb6, 0xca), /* 3.3V: 3.3V +/- 5% */
LM87_IN_LIMITS(3, 0xb0, 0xc9), /* 5V: 4.6-5.2V */
LM87_IN_LIMITS(4, 0xb0, 0xe0), /* 12V: 11-14V */
LM87_IN_LIMITS(5, 0x44, 0x4b), /* Vccp2: 1.0V +/- 5% */
LM87_AIN_LIMITS(0, 0xa0, 0xb2), /* AIN1: 1.66V +/- 5% */
LM87_AIN_LIMITS(1, 0x91, 0xa1), /* AIN2: 1.5V +/- 5% */
LM87_TEMP_INT_LIMITS(10, 60), /* board */
LM87_TEMP_EXT1_LIMITS(10, 70), /* Falcon */
0
};
static struct i2c_board_info sfe4002_hwmon_info = {
I2C_BOARD_INFO("lm87", 0x2e),
.platform_data = &sfe4002_lm87_channel,
.irq = -1,
};
/****************************************************************************/
/* LED allocations. Note that on rev A0 boards the schematic and the reality
* differ: red and green are swapped. Below is the fixed (A1) layout (there
@ -84,11 +204,27 @@ static void sfe4002_fault_led(struct efx_nic *efx, bool state)
QUAKE_LED_OFF);
}
static int sfe4002_check_hw(struct efx_nic *efx)
{
/* A0 board rev. 4002s report a temperature fault the whole time
* (bad sensor) so we mask it out. */
unsigned alarm_mask =
(efx->board_info.major == 0 && efx->board_info.minor == 0) ?
~LM87_ALARM_TEMP_EXT1 : ~0;
return efx_check_lm87(efx, alarm_mask);
}
static int sfe4002_init(struct efx_nic *efx)
{
int rc = efx_init_lm87(efx, &sfe4002_hwmon_info, sfe4002_lm87_regs);
if (rc)
return rc;
efx->board_info.monitor = sfe4002_check_hw;
efx->board_info.init_leds = sfe4002_init_leds;
efx->board_info.set_fault_led = sfe4002_fault_led;
efx->board_info.blink = board_blink;
efx->board_info.fini = efx_fini_lm87;
return 0;
}

View File

@ -77,11 +77,6 @@ static int napi_weight = 64;
*/
unsigned int efx_monitor_interval = 1 * HZ;
/* This controls whether or not the hardware monitor will trigger a
* reset when it detects an error condition.
*/
static unsigned int monitor_reset = true;
/* This controls whether or not the driver will initialise devices
* with invalid MAC addresses stored in the EEPROM or flash. If true,
* such devices will be initialised with a random locally-generated
@ -1176,17 +1171,6 @@ static void efx_monitor(struct work_struct *data)
rc = falcon_check_xmac(efx);
mutex_unlock(&efx->mac_lock);
if (rc) {
if (monitor_reset) {
EFX_ERR(efx, "hardware monitor detected a fault: "
"triggering reset\n");
efx_schedule_reset(efx, RESET_TYPE_MONITOR);
} else {
EFX_ERR(efx, "hardware monitor detected a fault, "
"skipping reset\n");
}
}
queue_delayed_work(efx->workqueue, &efx->monitor_work,
efx_monitor_interval);
}
@ -1358,12 +1342,11 @@ static void efx_watchdog(struct net_device *net_dev)
{
struct efx_nic *efx = netdev_priv(net_dev);
EFX_ERR(efx, "TX stuck with stop_count=%d port_enabled=%d: %s\n",
atomic_read(&efx->netif_stop_count), efx->port_enabled,
monitor_reset ? "resetting channels" : "skipping reset");
EFX_ERR(efx, "TX stuck with stop_count=%d port_enabled=%d:"
" resetting channels\n",
atomic_read(&efx->netif_stop_count), efx->port_enabled);
if (monitor_reset)
efx_schedule_reset(efx, RESET_TYPE_MONITOR);
efx_schedule_reset(efx, RESET_TYPE_TX_WATCHDOG);
}
@ -1459,6 +1442,7 @@ static int efx_netdev_event(struct notifier_block *this,
struct efx_nic *efx = netdev_priv(net_dev);
strcpy(efx->name, net_dev->name);
efx_mtd_rename(efx);
}
return NOTIFY_DONE;
@ -1550,6 +1534,7 @@ void efx_reset_down(struct efx_nic *efx, struct ethtool_cmd *ecmd)
efx_stop_all(efx);
mutex_lock(&efx->mac_lock);
mutex_lock(&efx->spi_lock);
rc = falcon_xmac_get_settings(efx, ecmd);
if (rc)
@ -1582,6 +1567,7 @@ int efx_reset_up(struct efx_nic *efx, struct ethtool_cmd *ecmd, bool ok)
EFX_ERR(efx, "could not restore PHY settings\n");
}
mutex_unlock(&efx->spi_lock);
mutex_unlock(&efx->mac_lock);
if (ok) {
@ -1777,6 +1763,7 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type,
memset(efx, 0, sizeof(*efx));
spin_lock_init(&efx->biu_lock);
spin_lock_init(&efx->phy_lock);
mutex_init(&efx->spi_lock);
INIT_WORK(&efx->reset_work, efx_reset_work);
INIT_DELAYED_WORK(&efx->monitor_work, efx_monitor);
efx->pci_dev = pci_dev;
@ -1911,6 +1898,8 @@ static void efx_pci_remove(struct pci_dev *pci_dev)
if (!efx)
return;
efx_mtd_remove(efx);
/* Mark the NIC as fini, then stop the interface */
rtnl_lock();
efx->state = STATE_FINI;
@ -2077,6 +2066,7 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,
EFX_LOG(efx, "initialisation successful\n");
efx_mtd_probe(efx); /* allowed to fail */
return 0;
fail5:

View File

@ -58,6 +58,16 @@ extern int efx_port_dummy_op_int(struct efx_nic *efx);
extern void efx_port_dummy_op_void(struct efx_nic *efx);
extern void efx_port_dummy_op_blink(struct efx_nic *efx, bool blink);
/* MTD */
#ifdef CONFIG_SFC_MTD
extern int efx_mtd_probe(struct efx_nic *efx);
extern void efx_mtd_rename(struct efx_nic *efx);
extern void efx_mtd_remove(struct efx_nic *efx);
#else
static inline int efx_mtd_probe(struct efx_nic *efx) { return 0; }
static inline void efx_mtd_rename(struct efx_nic *efx) {}
static inline void efx_mtd_remove(struct efx_nic *efx) {}
#endif
extern unsigned int efx_monitor_interval;

View File

@ -72,7 +72,7 @@ extern const char *efx_loopback_mode_names[];
* @RESET_TYPE_ALL: reset everything but PCI core blocks
* @RESET_TYPE_WORLD: reset everything, save & restore PCI config
* @RESET_TYPE_DISABLE: disable NIC
* @RESET_TYPE_MONITOR: reset due to hardware monitor
* @RESET_TYPE_TX_WATCHDOG: reset due to TX watchdog
* @RESET_TYPE_INT_ERROR: reset due to internal error
* @RESET_TYPE_RX_RECOVERY: reset to recover from RX datapath errors
* @RESET_TYPE_RX_DESC_FETCH: pcie error during rx descriptor fetch
@ -86,7 +86,7 @@ enum reset_type {
RESET_TYPE_WORLD = 2,
RESET_TYPE_DISABLE = 3,
RESET_TYPE_MAX_METHOD,
RESET_TYPE_MONITOR,
RESET_TYPE_TX_WATCHDOG,
RESET_TYPE_INT_ERROR,
RESET_TYPE_RX_RECOVERY,
RESET_TYPE_RX_DESC_FETCH,

View File

@ -172,10 +172,7 @@ static struct efx_ethtool_stat efx_ethtool_stats[] = {
/* Number of ethtool statistics */
#define EFX_ETHTOOL_NUM_STATS ARRAY_SIZE(efx_ethtool_stats)
/* EEPROM range with gPXE configuration */
#define EFX_ETHTOOL_EEPROM_MAGIC 0xEFAB
#define EFX_ETHTOOL_EEPROM_MIN 0x100U
#define EFX_ETHTOOL_EEPROM_MAX 0x400U
/**************************************************************************
*
@ -545,8 +542,8 @@ static int efx_ethtool_get_eeprom_len(struct net_device *net_dev)
if (!spi)
return 0;
return min(spi->size, EFX_ETHTOOL_EEPROM_MAX) -
min(spi->size, EFX_ETHTOOL_EEPROM_MIN);
return min(spi->size, EFX_EEPROM_BOOTCONFIG_END) -
min(spi->size, EFX_EEPROM_BOOTCONFIG_START);
}
static int efx_ethtool_get_eeprom(struct net_device *net_dev,
@ -557,8 +554,10 @@ static int efx_ethtool_get_eeprom(struct net_device *net_dev,
size_t len;
int rc;
rc = falcon_spi_read(spi, eeprom->offset + EFX_ETHTOOL_EEPROM_MIN,
mutex_lock(&efx->spi_lock);
rc = falcon_spi_read(spi, eeprom->offset + EFX_EEPROM_BOOTCONFIG_START,
eeprom->len, &len, buf);
mutex_unlock(&efx->spi_lock);
eeprom->magic = EFX_ETHTOOL_EEPROM_MAGIC;
eeprom->len = len;
return rc;
@ -575,8 +574,10 @@ static int efx_ethtool_set_eeprom(struct net_device *net_dev,
if (eeprom->magic != EFX_ETHTOOL_EEPROM_MAGIC)
return -EINVAL;
rc = falcon_spi_write(spi, eeprom->offset + EFX_ETHTOOL_EEPROM_MIN,
mutex_lock(&efx->spi_lock);
rc = falcon_spi_write(spi, eeprom->offset + EFX_EEPROM_BOOTCONFIG_START,
eeprom->len, &len, buf);
mutex_unlock(&efx->spi_lock);
eeprom->len = len;
return rc;
}

View File

@ -1628,7 +1628,7 @@ static int falcon_spi_wait(struct efx_nic *efx)
}
}
static int falcon_spi_cmd(const struct efx_spi_device *spi,
int falcon_spi_cmd(const struct efx_spi_device *spi,
unsigned int command, int address,
const void *in, void *out, unsigned int len)
{
@ -1641,6 +1641,7 @@ static int falcon_spi_cmd(const struct efx_spi_device *spi,
/* Input validation */
if (len > FALCON_SPI_MAX_LEN)
return -EINVAL;
BUG_ON(!mutex_is_locked(&efx->spi_lock));
/* Check SPI not currently being accessed */
rc = falcon_spi_wait(efx);
@ -1699,8 +1700,7 @@ efx_spi_munge_command(const struct efx_spi_device *spi,
return command | (((address >> 8) & spi->munge_address) << 3);
}
static int falcon_spi_fast_wait(const struct efx_spi_device *spi)
int falcon_spi_fast_wait(const struct efx_spi_device *spi)
{
u8 status;
int i, rc;
@ -2253,13 +2253,15 @@ int falcon_read_nvram(struct efx_nic *efx, struct falcon_nvconfig *nvconfig_out)
__le16 *word, *limit;
u32 csum;
region = kmalloc(NVCONFIG_END, GFP_KERNEL);
region = kmalloc(FALCON_NVCONFIG_END, GFP_KERNEL);
if (!region)
return -ENOMEM;
nvconfig = region + NVCONFIG_OFFSET;
spi = efx->spi_flash ? efx->spi_flash : efx->spi_eeprom;
rc = falcon_spi_read(spi, 0, NVCONFIG_END, NULL, region);
mutex_lock(&efx->spi_lock);
rc = falcon_spi_read(spi, 0, FALCON_NVCONFIG_END, NULL, region);
mutex_unlock(&efx->spi_lock);
if (rc) {
EFX_ERR(efx, "Failed to read %s\n",
efx->spi_flash ? "flash" : "EEPROM");
@ -2283,7 +2285,7 @@ int falcon_read_nvram(struct efx_nic *efx, struct falcon_nvconfig *nvconfig_out)
limit = (__le16 *) (nvconfig + 1);
} else {
word = region;
limit = region + NVCONFIG_END;
limit = region + FALCON_NVCONFIG_END;
}
for (csum = 0; word < limit; ++word)
csum += le16_to_cpu(*word);
@ -2555,6 +2557,11 @@ static int falcon_spi_device_init(struct efx_nic *efx,
SPI_DEV_TYPE_FIELD(device_type, SPI_DEV_TYPE_ADDR_LEN);
spi_device->munge_address = (spi_device->size == 1 << 9 &&
spi_device->addr_len == 1);
spi_device->erase_command =
SPI_DEV_TYPE_FIELD(device_type, SPI_DEV_TYPE_ERASE_CMD);
spi_device->erase_size =
1 << SPI_DEV_TYPE_FIELD(device_type,
SPI_DEV_TYPE_ERASE_SIZE);
spi_device->block_size =
1 << SPI_DEV_TYPE_FIELD(device_type,
SPI_DEV_TYPE_BLOCK_SIZE);

View File

@ -1150,7 +1150,6 @@ struct falcon_nvconfig_board_v3 {
(((type) >> EFX_LOW_BIT(field)) & EFX_MASK32(EFX_WIDTH(field)))
#define NVCONFIG_OFFSET 0x300
#define NVCONFIG_END 0x400
#define NVCONFIG_BOARD_MAGIC_NUM 0xFA1C
struct falcon_nvconfig {

View File

@ -260,6 +260,41 @@ void mdio_clause45_phy_reconfigure(struct efx_nic *efx)
MDIO_MMDREG_CTRL1, ctrl2);
}
static void mdio_clause45_set_mmd_lpower(struct efx_nic *efx,
int lpower, int mmd)
{
int phy = efx->mii.phy_id;
int stat = mdio_clause45_read(efx, phy, mmd, MDIO_MMDREG_STAT1);
int ctrl1, ctrl2;
EFX_TRACE(efx, "Setting low power mode for MMD %d to %d\n",
mmd, lpower);
if (stat & (1 << MDIO_MMDREG_STAT1_LPABLE_LBN)) {
ctrl1 = ctrl2 = mdio_clause45_read(efx, phy,
mmd, MDIO_MMDREG_CTRL1);
if (lpower)
ctrl2 |= (1 << MDIO_MMDREG_CTRL1_LPOWER_LBN);
else
ctrl2 &= ~(1 << MDIO_MMDREG_CTRL1_LPOWER_LBN);
if (ctrl1 != ctrl2)
mdio_clause45_write(efx, phy, mmd,
MDIO_MMDREG_CTRL1, ctrl2);
}
}
void mdio_clause45_set_mmds_lpower(struct efx_nic *efx,
int low_power, unsigned int mmd_mask)
{
int mmd = 0;
while (mmd_mask) {
if (mmd_mask & 1)
mdio_clause45_set_mmd_lpower(efx, low_power, mmd);
mmd_mask = (mmd_mask >> 1);
mmd++;
}
}
/**
* mdio_clause45_get_settings - Read (some of) the PHY settings over MDIO.
* @efx: Efx NIC

View File

@ -54,6 +54,9 @@
/* Loopback bit for WIS, PCS, PHYSX and DTEXS */
#define MDIO_MMDREG_CTRL1_LBACK_LBN (14)
#define MDIO_MMDREG_CTRL1_LBACK_WIDTH (1)
/* Low power */
#define MDIO_MMDREG_CTRL1_LPOWER_LBN (11)
#define MDIO_MMDREG_CTRL1_LPOWER_WIDTH (1)
/* Bits in MMDREG_STAT1 */
#define MDIO_MMDREG_STAT1_FAULT_LBN (7)
@ -240,6 +243,10 @@ extern void mdio_clause45_transmit_disable(struct efx_nic *efx);
/* Generic part of reconfigure: set/clear loopback bits */
extern void mdio_clause45_phy_reconfigure(struct efx_nic *efx);
/* Set the power state of the specified MMDs */
extern void mdio_clause45_set_mmds_lpower(struct efx_nic *efx,
int low_power, unsigned int mmd_mask);
/* Read (some of) the PHY settings over MDIO */
extern void mdio_clause45_get_settings(struct efx_nic *efx,
struct ethtool_cmd *ecmd);

268
drivers/net/sfc/mtd.c Normal file
View File

@ -0,0 +1,268 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2005-2006 Fen Systems Ltd.
* Copyright 2006-2008 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation, incorporated herein by reference.
*/
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/delay.h>
#define EFX_DRIVER_NAME "sfc_mtd"
#include "net_driver.h"
#include "spi.h"
#define EFX_SPI_VERIFY_BUF_LEN 16
struct efx_mtd {
const struct efx_spi_device *spi;
struct mtd_info mtd;
char name[IFNAMSIZ + 20];
};
/* SPI utilities */
static int efx_spi_slow_wait(struct efx_mtd *efx_mtd, bool uninterruptible)
{
const struct efx_spi_device *spi = efx_mtd->spi;
struct efx_nic *efx = spi->efx;
u8 status;
int rc, i;
/* Wait up to 4s for flash/EEPROM to finish a slow operation. */
for (i = 0; i < 40; i++) {
__set_current_state(uninterruptible ?
TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE);
schedule_timeout(HZ / 10);
rc = falcon_spi_cmd(spi, SPI_RDSR, -1, NULL,
&status, sizeof(status));
if (rc)
return rc;
if (!(status & SPI_STATUS_NRDY))
return 0;
if (signal_pending(current))
return -EINTR;
}
EFX_ERR(efx, "timed out waiting for %s\n", efx_mtd->name);
return -ETIMEDOUT;
}
static int efx_spi_unlock(const struct efx_spi_device *spi)
{
const u8 unlock_mask = (SPI_STATUS_BP2 | SPI_STATUS_BP1 |
SPI_STATUS_BP0);
u8 status;
int rc;
rc = falcon_spi_cmd(spi, SPI_RDSR, -1, NULL, &status, sizeof(status));
if (rc)
return rc;
if (!(status & unlock_mask))
return 0; /* already unlocked */
rc = falcon_spi_cmd(spi, SPI_WREN, -1, NULL, NULL, 0);
if (rc)
return rc;
rc = falcon_spi_cmd(spi, SPI_SST_EWSR, -1, NULL, NULL, 0);
if (rc)
return rc;
status &= ~unlock_mask;
rc = falcon_spi_cmd(spi, SPI_WRSR, -1, &status, NULL, sizeof(status));
if (rc)
return rc;
rc = falcon_spi_fast_wait(spi);
if (rc)
return rc;
return 0;
}
static int efx_spi_erase(struct efx_mtd *efx_mtd, loff_t start, size_t len)
{
const struct efx_spi_device *spi = efx_mtd->spi;
unsigned pos, block_len;
u8 empty[EFX_SPI_VERIFY_BUF_LEN];
u8 buffer[EFX_SPI_VERIFY_BUF_LEN];
int rc;
if (len != spi->erase_size)
return -EINVAL;
if (spi->erase_command == 0)
return -EOPNOTSUPP;
rc = efx_spi_unlock(spi);
if (rc)
return rc;
rc = falcon_spi_cmd(spi, SPI_WREN, -1, NULL, NULL, 0);
if (rc)
return rc;
rc = falcon_spi_cmd(spi, spi->erase_command, start, NULL, NULL, 0);
if (rc)
return rc;
rc = efx_spi_slow_wait(efx_mtd, false);
/* Verify the entire region has been wiped */
memset(empty, 0xff, sizeof(empty));
for (pos = 0; pos < len; pos += block_len) {
block_len = min(len - pos, sizeof(buffer));
rc = falcon_spi_read(spi, start + pos, block_len, NULL, buffer);
if (rc)
return rc;
if (memcmp(empty, buffer, block_len))
return -EIO;
/* Avoid locking up the system */
cond_resched();
if (signal_pending(current))
return -EINTR;
}
return rc;
}
/* MTD interface */
static int efx_mtd_read(struct mtd_info *mtd, loff_t start, size_t len,
size_t *retlen, u8 *buffer)
{
struct efx_mtd *efx_mtd = mtd->priv;
const struct efx_spi_device *spi = efx_mtd->spi;
struct efx_nic *efx = spi->efx;
int rc;
rc = mutex_lock_interruptible(&efx->spi_lock);
if (rc)
return rc;
rc = falcon_spi_read(spi, FALCON_FLASH_BOOTCODE_START + start,
len, retlen, buffer);
mutex_unlock(&efx->spi_lock);
return rc;
}
static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
{
struct efx_mtd *efx_mtd = mtd->priv;
struct efx_nic *efx = efx_mtd->spi->efx;
int rc;
rc = mutex_lock_interruptible(&efx->spi_lock);
if (rc)
return rc;
rc = efx_spi_erase(efx_mtd, FALCON_FLASH_BOOTCODE_START + erase->addr,
erase->len);
mutex_unlock(&efx->spi_lock);
if (rc == 0) {
erase->state = MTD_ERASE_DONE;
} else {
erase->state = MTD_ERASE_FAILED;
erase->fail_addr = 0xffffffff;
}
mtd_erase_callback(erase);
return rc;
}
static int efx_mtd_write(struct mtd_info *mtd, loff_t start,
size_t len, size_t *retlen, const u8 *buffer)
{
struct efx_mtd *efx_mtd = mtd->priv;
const struct efx_spi_device *spi = efx_mtd->spi;
struct efx_nic *efx = spi->efx;
int rc;
rc = mutex_lock_interruptible(&efx->spi_lock);
if (rc)
return rc;
rc = falcon_spi_write(spi, FALCON_FLASH_BOOTCODE_START + start,
len, retlen, buffer);
mutex_unlock(&efx->spi_lock);
return rc;
}
static void efx_mtd_sync(struct mtd_info *mtd)
{
struct efx_mtd *efx_mtd = mtd->priv;
struct efx_nic *efx = efx_mtd->spi->efx;
int rc;
mutex_lock(&efx->spi_lock);
rc = efx_spi_slow_wait(efx_mtd, true);
mutex_unlock(&efx->spi_lock);
if (rc)
EFX_ERR(efx, "%s sync failed (%d)\n", efx_mtd->name, rc);
return;
}
void efx_mtd_remove(struct efx_nic *efx)
{
if (efx->spi_flash && efx->spi_flash->mtd) {
struct efx_mtd *efx_mtd = efx->spi_flash->mtd;
int rc;
for (;;) {
rc = del_mtd_device(&efx_mtd->mtd);
if (rc != -EBUSY)
break;
ssleep(1);
}
WARN_ON(rc);
kfree(efx_mtd);
}
}
void efx_mtd_rename(struct efx_nic *efx)
{
if (efx->spi_flash && efx->spi_flash->mtd) {
struct efx_mtd *efx_mtd = efx->spi_flash->mtd;
snprintf(efx_mtd->name, sizeof(efx_mtd->name),
"%s sfc_flash_bootrom", efx->name);
}
}
int efx_mtd_probe(struct efx_nic *efx)
{
struct efx_spi_device *spi = efx->spi_flash;
struct efx_mtd *efx_mtd;
if (!spi || spi->size <= FALCON_FLASH_BOOTCODE_START)
return -ENODEV;
efx_mtd = kzalloc(sizeof(*efx_mtd), GFP_KERNEL);
if (!efx_mtd)
return -ENOMEM;
efx_mtd->spi = spi;
spi->mtd = efx_mtd;
efx_mtd->mtd.type = MTD_NORFLASH;
efx_mtd->mtd.flags = MTD_CAP_NORFLASH;
efx_mtd->mtd.size = spi->size - FALCON_FLASH_BOOTCODE_START;
efx_mtd->mtd.erasesize = spi->erase_size;
efx_mtd->mtd.writesize = 1;
efx_mtd_rename(efx);
efx_mtd->mtd.owner = THIS_MODULE;
efx_mtd->mtd.priv = efx_mtd;
efx_mtd->mtd.name = efx_mtd->name;
efx_mtd->mtd.erase = efx_mtd_erase;
efx_mtd->mtd.read = efx_mtd_read;
efx_mtd->mtd.write = efx_mtd_write;
efx_mtd->mtd.sync = efx_mtd_sync;
if (add_mtd_device(&efx_mtd->mtd)) {
kfree(efx_mtd);
spi->mtd = NULL;
/* add_mtd_device() returns 1 if the MTD table is full */
return -ENOMEM;
}
return 0;
}

View File

@ -414,6 +414,7 @@ struct efx_blinker {
* @init_leds: Sets up board LEDs
* @set_fault_led: Turns the fault LED on or off
* @blink: Starts/stops blinking
* @monitor: Board-specific health check function
* @fini: Cleanup function
* @blinker: used to blink LEDs in software
* @hwmon_client: I2C client for hardware monitor
@ -428,6 +429,7 @@ struct efx_board {
* have a separate init callback that happens later than
* board init. */
int (*init_leds)(struct efx_nic *efx);
int (*monitor) (struct efx_nic *nic);
void (*set_fault_led) (struct efx_nic *efx, bool state);
void (*blink) (struct efx_nic *efx, bool start);
void (*fini) (struct efx_nic *nic);
@ -525,11 +527,15 @@ struct efx_phy_operations {
* @enum efx_phy_mode - PHY operating mode flags
* @PHY_MODE_NORMAL: on and should pass traffic
* @PHY_MODE_TX_DISABLED: on with TX disabled
* @PHY_MODE_LOW_POWER: set to low power through MDIO
* @PHY_MODE_OFF: switched off through external control
* @PHY_MODE_SPECIAL: on but will not pass traffic
*/
enum efx_phy_mode {
PHY_MODE_NORMAL = 0,
PHY_MODE_TX_DISABLED = 1,
PHY_MODE_LOW_POWER = 2,
PHY_MODE_OFF = 4,
PHY_MODE_SPECIAL = 8,
};
@ -655,6 +661,7 @@ union efx_multicast_hash {
* This field will be %NULL if no flash device is present.
* @spi_eeprom: SPI EEPROM device
* This field will be %NULL if no EEPROM device is present.
* @spi_lock: SPI bus lock
* @n_rx_nodesc_drop_cnt: RX no descriptor drop count
* @nic_data: Hardware dependant state
* @mac_lock: MAC access lock. Protects @port_enabled, @phy_mode,
@ -731,6 +738,7 @@ struct efx_nic {
struct efx_spi_device *spi_flash;
struct efx_spi_device *spi_eeprom;
struct mutex spi_lock;
unsigned n_rx_nodesc_drop_cnt;

View File

@ -21,6 +21,7 @@
#include "falcon_hwdefs.h"
#include "falcon_io.h"
#include "mac.h"
#include "workarounds.h"
/**************************************************************************
*
@ -65,48 +66,9 @@
#define P1_SPARE_LBN 4
#define P1_SPARE_WIDTH 4
/**************************************************************************
*
* Temperature Sensor
*
**************************************************************************/
#define MAX6647 0x4e
#define RLTS 0x00
#define RLTE 0x01
#define RSL 0x02
#define RCL 0x03
#define RCRA 0x04
#define RLHN 0x05
#define RLLI 0x06
#define RRHI 0x07
#define RRLS 0x08
#define WCRW 0x0a
#define WLHO 0x0b
#define WRHA 0x0c
#define WRLN 0x0e
#define OSHT 0x0f
#define REET 0x10
#define RIET 0x11
#define RWOE 0x19
#define RWOI 0x20
#define HYS 0x21
#define QUEUE 0x22
#define MFID 0xfe
#define REVID 0xff
/* Status bits */
#define MAX6647_BUSY (1 << 7) /* ADC is converting */
#define MAX6647_LHIGH (1 << 6) /* Local high temp. alarm */
#define MAX6647_LLOW (1 << 5) /* Local low temp. alarm */
#define MAX6647_RHIGH (1 << 4) /* Remote high temp. alarm */
#define MAX6647_RLOW (1 << 3) /* Remote low temp. alarm */
#define MAX6647_FAULT (1 << 2) /* DXN/DXP short/open circuit */
#define MAX6647_EOT (1 << 1) /* Remote junction overtemp. */
#define MAX6647_IOT (1 << 0) /* Local junction overtemp. */
static const u8 xgphy_max_temperature = 90;
/* Temperature Sensor */
#define MAX664X_REG_RSL 0x02
#define MAX664X_REG_WLHO 0x0B
static void sfe4001_poweroff(struct efx_nic *efx)
{
@ -119,7 +81,7 @@ static void sfe4001_poweroff(struct efx_nic *efx)
i2c_smbus_write_byte_data(ioexp_client, P0_CONFIG, 0xff);
/* Clear any over-temperature alert */
i2c_smbus_read_byte_data(hwmon_client, RSL);
i2c_smbus_read_byte_data(hwmon_client, MAX664X_REG_RSL);
}
static int sfe4001_poweron(struct efx_nic *efx)
@ -131,7 +93,7 @@ static int sfe4001_poweron(struct efx_nic *efx)
u8 out;
/* Clear any previous over-temperature alert */
rc = i2c_smbus_read_byte_data(hwmon_client, RSL);
rc = i2c_smbus_read_byte_data(hwmon_client, MAX664X_REG_RSL);
if (rc < 0)
return rc;
@ -209,6 +171,34 @@ fail_on:
return rc;
}
static int sfe4001_check_hw(struct efx_nic *efx)
{
s32 status;
/* If XAUI link is up then do not monitor */
if (EFX_WORKAROUND_7884(efx) && falcon_xaui_link_ok(efx))
return 0;
/* Check the powered status of the PHY. Lack of power implies that
* the MAX6647 has shut down power to it, probably due to a temp.
* alarm. Reading the power status rather than the MAX6647 status
* directly because the later is read-to-clear and would thus
* start to power up the PHY again when polled, causing us to blip
* the power undesirably.
* We know we can read from the IO expander because we did
* it during power-on. Assume failure now is bad news. */
status = i2c_smbus_read_byte_data(efx->board_info.ioexp_client, P1_IN);
if (status >= 0 &&
(status & ((1 << P1_AFE_PWD_LBN) | (1 << P1_DSP_PWD25_LBN))) != 0)
return 0;
/* Use board power control, not PHY power control */
sfe4001_poweroff(efx);
efx->phy_mode = PHY_MODE_OFF;
return (status < 0) ? -EIO : -ERANGE;
}
/* On SFE4001 rev A2 and later, we can control the FLASH_CFG_1 pin
* using the 3V3X output of the IO-expander. Allow the user to set
* this when the device is stopped, and keep it stopped then.
@ -261,35 +251,34 @@ static void sfe4001_fini(struct efx_nic *efx)
i2c_unregister_device(efx->board_info.hwmon_client);
}
static struct i2c_board_info sfe4001_hwmon_info = {
I2C_BOARD_INFO("max6647", 0x4e),
.irq = -1,
};
/* This board uses an I2C expander to provider power to the PHY, which needs to
* be turned on before the PHY can be used.
* Context: Process context, rtnl lock held
*/
int sfe4001_init(struct efx_nic *efx)
{
struct i2c_client *hwmon_client;
int rc;
hwmon_client = i2c_new_dummy(&efx->i2c_adap, MAX6647);
if (!hwmon_client)
#if defined(CONFIG_SENSORS_LM90) || defined(CONFIG_SENSORS_LM90_MODULE)
efx->board_info.hwmon_client =
i2c_new_device(&efx->i2c_adap, &sfe4001_hwmon_info);
#else
efx->board_info.hwmon_client =
i2c_new_dummy(&efx->i2c_adap, sfe4001_hwmon_info.addr);
#endif
if (!efx->board_info.hwmon_client)
return -EIO;
efx->board_info.hwmon_client = hwmon_client;
/* Set DSP over-temperature alert threshold */
EFX_INFO(efx, "DSP cut-out at %dC\n", xgphy_max_temperature);
rc = i2c_smbus_write_byte_data(hwmon_client, WLHO,
xgphy_max_temperature);
/* Raise board/PHY high limit from 85 to 90 degrees Celsius */
rc = i2c_smbus_write_byte_data(efx->board_info.hwmon_client,
MAX664X_REG_WLHO, 90);
if (rc)
goto fail_ioexp;
/* Read it back and verify */
rc = i2c_smbus_read_byte_data(hwmon_client, RLHN);
if (rc < 0)
goto fail_ioexp;
if (rc != xgphy_max_temperature) {
rc = -EFAULT;
goto fail_ioexp;
}
goto fail_hwmon;
efx->board_info.ioexp_client = i2c_new_dummy(&efx->i2c_adap, PCA9539);
if (!efx->board_info.ioexp_client) {
@ -301,6 +290,7 @@ int sfe4001_init(struct efx_nic *efx)
* blink code. */
efx->board_info.blink = tenxpress_phy_blink;
efx->board_info.monitor = sfe4001_check_hw;
efx->board_info.fini = sfe4001_fini;
rc = sfe4001_poweron(efx);
@ -319,6 +309,6 @@ fail_on:
fail_ioexp:
i2c_unregister_device(efx->board_info.ioexp_client);
fail_hwmon:
i2c_unregister_device(hwmon_client);
i2c_unregister_device(efx->board_info.hwmon_client);
return rc;
}

View File

@ -25,6 +25,7 @@
#define SPI_WRDI 0x04 /* Reset write enable latch */
#define SPI_RDSR 0x05 /* Read status register */
#define SPI_WREN 0x06 /* Set write enable latch */
#define SPI_SST_EWSR 0x50 /* SST: Enable write to status register */
#define SPI_STATUS_WPEN 0x80 /* Write-protect pin enabled */
#define SPI_STATUS_BP2 0x10 /* Block protection bit 2 */
@ -36,6 +37,7 @@
/**
* struct efx_spi_device - an Efx SPI (Serial Peripheral Interface) device
* @efx: The Efx controller that owns this device
* @mtd: MTD state
* @device_id: Controller's id for the device
* @size: Size (in bytes)
* @addr_len: Number of address bytes in read/write commands
@ -44,23 +46,51 @@
* use bit 3 of the command byte as address bit A8, rather
* than having a two-byte address. If this flag is set, then
* commands should be munged in this way.
* @erase_command: Erase command (or 0 if sector erase not needed).
* @erase_size: Erase sector size (in bytes)
* Erase commands affect sectors with this size and alignment.
* This must be a power of two.
* @block_size: Write block size (in bytes).
* Write commands are limited to blocks with this size and alignment.
* @read: Read function for the device
* @write: Write function for the device
*/
struct efx_spi_device {
struct efx_nic *efx;
#ifdef CONFIG_SFC_MTD
void *mtd;
#endif
int device_id;
unsigned int size;
unsigned int addr_len;
unsigned int munge_address:1;
u8 erase_command;
unsigned int erase_size;
unsigned int block_size;
};
int falcon_spi_cmd(const struct efx_spi_device *spi, unsigned int command,
int address, const void* in, void *out, unsigned int len);
int falcon_spi_fast_wait(const struct efx_spi_device *spi);
int falcon_spi_read(const struct efx_spi_device *spi, loff_t start,
size_t len, size_t *retlen, u8 *buffer);
int falcon_spi_write(const struct efx_spi_device *spi, loff_t start,
size_t len, size_t *retlen, const u8 *buffer);
/*
* SFC4000 flash is partitioned into:
* 0-0x400 chip and board config (see falcon_hwdefs.h)
* 0x400-0x8000 unused (or may contain VPD if EEPROM not present)
* 0x8000-end boot code (mapped to PCI expansion ROM)
* SFC4000 small EEPROM (size < 0x400) is used for VPD only.
* SFC4000 large EEPROM (size >= 0x400) is partitioned into:
* 0-0x400 chip and board config
* configurable VPD
* 0x800-0x1800 boot config
* Aside from the chip and board config, all of these are optional and may
* be absent or truncated depending on the devices used.
*/
#define FALCON_NVCONFIG_END 0x400U
#define FALCON_FLASH_BOOTCODE_START 0x8000U
#define EFX_EEPROM_BOOTCONFIG_START 0x800U
#define EFX_EEPROM_BOOTCONFIG_END 0x1800U
#endif /* EFX_SPI_H */

View File

@ -376,6 +376,7 @@ static int tenxpress_phy_check_hw(struct efx_nic *efx)
{
struct tenxpress_phy_data *phy_data = efx->phy_data;
bool link_ok;
int rc = 0;
link_ok = tenxpress_link_ok(efx, true);
@ -391,7 +392,22 @@ static int tenxpress_phy_check_hw(struct efx_nic *efx)
atomic_set(&phy_data->bad_crc_count, 0);
}
return 0;
rc = efx->board_info.monitor(efx);
if (rc) {
EFX_ERR(efx, "Board sensor %s; shutting down PHY\n",
(rc == -ERANGE) ? "reported fault" : "failed");
if (efx->phy_mode & PHY_MODE_OFF) {
/* Assume that board has shut PHY off */
phy_data->phy_mode = PHY_MODE_OFF;
} else {
efx->phy_mode |= PHY_MODE_LOW_POWER;
mdio_clause45_set_mmds_lpower(efx, true,
efx->phy_op->mmds);
phy_data->phy_mode |= PHY_MODE_LOW_POWER;
}
}
return rc;
}
static void tenxpress_phy_fini(struct efx_nic *efx)

View File

@ -22,6 +22,8 @@
#define EFX_WORKAROUND_5147 EFX_WORKAROUND_ALWAYS
/* RX PCIe double split performance issue */
#define EFX_WORKAROUND_7575 EFX_WORKAROUND_ALWAYS
/* Bit-bashed I2C reads cause performance drop */
#define EFX_WORKAROUND_7884 EFX_WORKAROUND_ALWAYS
/* TX pkt parser problem with <= 16 byte TXes */
#define EFX_WORKAROUND_9141 EFX_WORKAROUND_ALWAYS
/* Low rate CRC errors require XAUI reset */

View File

@ -128,6 +128,15 @@ static int xfp_phy_check_hw(struct efx_nic *efx)
if (link_up != efx->link_up)
falcon_xmac_sim_phy_event(efx);
rc = efx->board_info.monitor(efx);
if (rc) {
struct xfp_phy_data *phy_data = efx->phy_data;
EFX_ERR(efx, "XFP sensor alert; putting PHY into low power\n");
efx->phy_mode |= PHY_MODE_LOW_POWER;
mdio_clause45_set_mmds_lpower(efx, 1, XFP_REQUIRED_DEVS);
phy_data->phy_mode |= PHY_MODE_LOW_POWER;
}
return rc;
}

2091
drivers/net/smsc911x.c Normal file

File diff suppressed because it is too large Load Diff

394
drivers/net/smsc911x.h Normal file
View File

@ -0,0 +1,394 @@
/***************************************************************************
*
* Copyright (C) 2004-2008 SMSC
* Copyright (C) 2005-2008 ARM
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
***************************************************************************/
#ifndef __SMSC911X_H__
#define __SMSC911X_H__
#define SMSC_CAN_USE_32BIT 1
#define TX_FIFO_LOW_THRESHOLD ((u32)1600)
#define SMSC911X_EEPROM_SIZE ((u32)7)
#define USE_DEBUG 0
/* This is the maximum number of packets to be received every
* NAPI poll */
#define SMSC_NAPI_WEIGHT 16
/* implements a PHY loopback test at initialisation time, to ensure a packet
* can be succesfully looped back */
#define USE_PHY_WORK_AROUND
#define DPRINTK(nlevel, klevel, fmt, args...) \
((void)((NETIF_MSG_##nlevel & pdata->msg_enable) && \
printk(KERN_##klevel "%s: %s: " fmt "\n", \
pdata->dev->name, __func__, ## args)))
#if USE_DEBUG >= 1
#define SMSC_WARNING(nlevel, fmt, args...) \
DPRINTK(nlevel, WARNING, fmt, ## args)
#else
#define SMSC_WARNING(nlevel, fmt, args...) \
({ do {} while (0); 0; })
#endif
#if USE_DEBUG >= 2
#define SMSC_TRACE(nlevel, fmt, args...) \
DPRINTK(nlevel, INFO, fmt, ## args)
#else
#define SMSC_TRACE(nlevel, fmt, args...) \
({ do {} while (0); 0; })
#endif
#ifdef CONFIG_DEBUG_SPINLOCK
#define SMSC_ASSERT_MAC_LOCK(pdata) \
WARN_ON(!spin_is_locked(&pdata->mac_lock))
#else
#define SMSC_ASSERT_MAC_LOCK(pdata) do {} while (0)
#endif /* CONFIG_DEBUG_SPINLOCK */
#define FLOW_CTRL_TX (1)
#define FLOW_CTRL_RX (2)
/* SMSC911x registers and bitfields */
#define RX_DATA_FIFO 0x00
#define TX_DATA_FIFO 0x20
#define TX_CMD_A_ON_COMP_ 0x80000000
#define TX_CMD_A_BUF_END_ALGN_ 0x03000000
#define TX_CMD_A_4_BYTE_ALGN_ 0x00000000
#define TX_CMD_A_16_BYTE_ALGN_ 0x01000000
#define TX_CMD_A_32_BYTE_ALGN_ 0x02000000
#define TX_CMD_A_DATA_OFFSET_ 0x001F0000
#define TX_CMD_A_FIRST_SEG_ 0x00002000
#define TX_CMD_A_LAST_SEG_ 0x00001000
#define TX_CMD_A_BUF_SIZE_ 0x000007FF
#define TX_CMD_B_PKT_TAG_ 0xFFFF0000
#define TX_CMD_B_ADD_CRC_DISABLE_ 0x00002000
#define TX_CMD_B_DISABLE_PADDING_ 0x00001000
#define TX_CMD_B_PKT_BYTE_LENGTH_ 0x000007FF
#define RX_STATUS_FIFO 0x40
#define RX_STS_ES_ 0x00008000
#define RX_STS_MCAST_ 0x00000400
#define RX_STATUS_FIFO_PEEK 0x44
#define TX_STATUS_FIFO 0x48
#define TX_STS_ES_ 0x00008000
#define TX_STATUS_FIFO_PEEK 0x4C
#define ID_REV 0x50
#define ID_REV_CHIP_ID_ 0xFFFF0000
#define ID_REV_REV_ID_ 0x0000FFFF
#define INT_CFG 0x54
#define INT_CFG_INT_DEAS_ 0xFF000000
#define INT_CFG_INT_DEAS_CLR_ 0x00004000
#define INT_CFG_INT_DEAS_STS_ 0x00002000
#define INT_CFG_IRQ_INT_ 0x00001000
#define INT_CFG_IRQ_EN_ 0x00000100
#define INT_CFG_IRQ_POL_ 0x00000010
#define INT_CFG_IRQ_TYPE_ 0x00000001
#define INT_STS 0x58
#define INT_STS_SW_INT_ 0x80000000
#define INT_STS_TXSTOP_INT_ 0x02000000
#define INT_STS_RXSTOP_INT_ 0x01000000
#define INT_STS_RXDFH_INT_ 0x00800000
#define INT_STS_RXDF_INT_ 0x00400000
#define INT_STS_TX_IOC_ 0x00200000
#define INT_STS_RXD_INT_ 0x00100000
#define INT_STS_GPT_INT_ 0x00080000
#define INT_STS_PHY_INT_ 0x00040000
#define INT_STS_PME_INT_ 0x00020000
#define INT_STS_TXSO_ 0x00010000
#define INT_STS_RWT_ 0x00008000
#define INT_STS_RXE_ 0x00004000
#define INT_STS_TXE_ 0x00002000
#define INT_STS_TDFU_ 0x00000800
#define INT_STS_TDFO_ 0x00000400
#define INT_STS_TDFA_ 0x00000200
#define INT_STS_TSFF_ 0x00000100
#define INT_STS_TSFL_ 0x00000080
#define INT_STS_RXDF_ 0x00000040
#define INT_STS_RDFL_ 0x00000020
#define INT_STS_RSFF_ 0x00000010
#define INT_STS_RSFL_ 0x00000008
#define INT_STS_GPIO2_INT_ 0x00000004
#define INT_STS_GPIO1_INT_ 0x00000002
#define INT_STS_GPIO0_INT_ 0x00000001
#define INT_EN 0x5C
#define INT_EN_SW_INT_EN_ 0x80000000
#define INT_EN_TXSTOP_INT_EN_ 0x02000000
#define INT_EN_RXSTOP_INT_EN_ 0x01000000
#define INT_EN_RXDFH_INT_EN_ 0x00800000
#define INT_EN_TIOC_INT_EN_ 0x00200000
#define INT_EN_RXD_INT_EN_ 0x00100000
#define INT_EN_GPT_INT_EN_ 0x00080000
#define INT_EN_PHY_INT_EN_ 0x00040000
#define INT_EN_PME_INT_EN_ 0x00020000
#define INT_EN_TXSO_EN_ 0x00010000
#define INT_EN_RWT_EN_ 0x00008000
#define INT_EN_RXE_EN_ 0x00004000
#define INT_EN_TXE_EN_ 0x00002000
#define INT_EN_TDFU_EN_ 0x00000800
#define INT_EN_TDFO_EN_ 0x00000400
#define INT_EN_TDFA_EN_ 0x00000200
#define INT_EN_TSFF_EN_ 0x00000100
#define INT_EN_TSFL_EN_ 0x00000080
#define INT_EN_RXDF_EN_ 0x00000040
#define INT_EN_RDFL_EN_ 0x00000020
#define INT_EN_RSFF_EN_ 0x00000010
#define INT_EN_RSFL_EN_ 0x00000008
#define INT_EN_GPIO2_INT_ 0x00000004
#define INT_EN_GPIO1_INT_ 0x00000002
#define INT_EN_GPIO0_INT_ 0x00000001
#define BYTE_TEST 0x64
#define FIFO_INT 0x68
#define FIFO_INT_TX_AVAIL_LEVEL_ 0xFF000000
#define FIFO_INT_TX_STS_LEVEL_ 0x00FF0000
#define FIFO_INT_RX_AVAIL_LEVEL_ 0x0000FF00
#define FIFO_INT_RX_STS_LEVEL_ 0x000000FF
#define RX_CFG 0x6C
#define RX_CFG_RX_END_ALGN_ 0xC0000000
#define RX_CFG_RX_END_ALGN4_ 0x00000000
#define RX_CFG_RX_END_ALGN16_ 0x40000000
#define RX_CFG_RX_END_ALGN32_ 0x80000000
#define RX_CFG_RX_DMA_CNT_ 0x0FFF0000
#define RX_CFG_RX_DUMP_ 0x00008000
#define RX_CFG_RXDOFF_ 0x00001F00
#define TX_CFG 0x70
#define TX_CFG_TXS_DUMP_ 0x00008000
#define TX_CFG_TXD_DUMP_ 0x00004000
#define TX_CFG_TXSAO_ 0x00000004
#define TX_CFG_TX_ON_ 0x00000002
#define TX_CFG_STOP_TX_ 0x00000001
#define HW_CFG 0x74
#define HW_CFG_TTM_ 0x00200000
#define HW_CFG_SF_ 0x00100000
#define HW_CFG_TX_FIF_SZ_ 0x000F0000
#define HW_CFG_TR_ 0x00003000
#define HW_CFG_SRST_ 0x00000001
/* only available on 115/117 */
#define HW_CFG_PHY_CLK_SEL_ 0x00000060
#define HW_CFG_PHY_CLK_SEL_INT_PHY_ 0x00000000
#define HW_CFG_PHY_CLK_SEL_EXT_PHY_ 0x00000020
#define HW_CFG_PHY_CLK_SEL_CLK_DIS_ 0x00000040
#define HW_CFG_SMI_SEL_ 0x00000010
#define HW_CFG_EXT_PHY_DET_ 0x00000008
#define HW_CFG_EXT_PHY_EN_ 0x00000004
#define HW_CFG_SRST_TO_ 0x00000002
/* only available on 116/118 */
#define HW_CFG_32_16_BIT_MODE_ 0x00000004
#define RX_DP_CTRL 0x78
#define RX_DP_CTRL_RX_FFWD_ 0x80000000
#define RX_FIFO_INF 0x7C
#define RX_FIFO_INF_RXSUSED_ 0x00FF0000
#define RX_FIFO_INF_RXDUSED_ 0x0000FFFF
#define TX_FIFO_INF 0x80
#define TX_FIFO_INF_TSUSED_ 0x00FF0000
#define TX_FIFO_INF_TDFREE_ 0x0000FFFF
#define PMT_CTRL 0x84
#define PMT_CTRL_PM_MODE_ 0x00003000
#define PMT_CTRL_PM_MODE_D0_ 0x00000000
#define PMT_CTRL_PM_MODE_D1_ 0x00001000
#define PMT_CTRL_PM_MODE_D2_ 0x00002000
#define PMT_CTRL_PM_MODE_D3_ 0x00003000
#define PMT_CTRL_PHY_RST_ 0x00000400
#define PMT_CTRL_WOL_EN_ 0x00000200
#define PMT_CTRL_ED_EN_ 0x00000100
#define PMT_CTRL_PME_TYPE_ 0x00000040
#define PMT_CTRL_WUPS_ 0x00000030
#define PMT_CTRL_WUPS_NOWAKE_ 0x00000000
#define PMT_CTRL_WUPS_ED_ 0x00000010
#define PMT_CTRL_WUPS_WOL_ 0x00000020
#define PMT_CTRL_WUPS_MULTI_ 0x00000030
#define PMT_CTRL_PME_IND_ 0x00000008
#define PMT_CTRL_PME_POL_ 0x00000004
#define PMT_CTRL_PME_EN_ 0x00000002
#define PMT_CTRL_READY_ 0x00000001
#define GPIO_CFG 0x88
#define GPIO_CFG_LED3_EN_ 0x40000000
#define GPIO_CFG_LED2_EN_ 0x20000000
#define GPIO_CFG_LED1_EN_ 0x10000000
#define GPIO_CFG_GPIO2_INT_POL_ 0x04000000
#define GPIO_CFG_GPIO1_INT_POL_ 0x02000000
#define GPIO_CFG_GPIO0_INT_POL_ 0x01000000
#define GPIO_CFG_EEPR_EN_ 0x00700000
#define GPIO_CFG_GPIOBUF2_ 0x00040000
#define GPIO_CFG_GPIOBUF1_ 0x00020000
#define GPIO_CFG_GPIOBUF0_ 0x00010000
#define GPIO_CFG_GPIODIR2_ 0x00000400
#define GPIO_CFG_GPIODIR1_ 0x00000200
#define GPIO_CFG_GPIODIR0_ 0x00000100
#define GPIO_CFG_GPIOD4_ 0x00000020
#define GPIO_CFG_GPIOD3_ 0x00000010
#define GPIO_CFG_GPIOD2_ 0x00000004
#define GPIO_CFG_GPIOD1_ 0x00000002
#define GPIO_CFG_GPIOD0_ 0x00000001
#define GPT_CFG 0x8C
#define GPT_CFG_TIMER_EN_ 0x20000000
#define GPT_CFG_GPT_LOAD_ 0x0000FFFF
#define GPT_CNT 0x90
#define GPT_CNT_GPT_CNT_ 0x0000FFFF
#define WORD_SWAP 0x98
#define FREE_RUN 0x9C
#define RX_DROP 0xA0
#define MAC_CSR_CMD 0xA4
#define MAC_CSR_CMD_CSR_BUSY_ 0x80000000
#define MAC_CSR_CMD_R_NOT_W_ 0x40000000
#define MAC_CSR_CMD_CSR_ADDR_ 0x000000FF
#define MAC_CSR_DATA 0xA8
#define AFC_CFG 0xAC
#define AFC_CFG_AFC_HI_ 0x00FF0000
#define AFC_CFG_AFC_LO_ 0x0000FF00
#define AFC_CFG_BACK_DUR_ 0x000000F0
#define AFC_CFG_FCMULT_ 0x00000008
#define AFC_CFG_FCBRD_ 0x00000004
#define AFC_CFG_FCADD_ 0x00000002
#define AFC_CFG_FCANY_ 0x00000001
#define E2P_CMD 0xB0
#define E2P_CMD_EPC_BUSY_ 0x80000000
#define E2P_CMD_EPC_CMD_ 0x70000000
#define E2P_CMD_EPC_CMD_READ_ 0x00000000
#define E2P_CMD_EPC_CMD_EWDS_ 0x10000000
#define E2P_CMD_EPC_CMD_EWEN_ 0x20000000
#define E2P_CMD_EPC_CMD_WRITE_ 0x30000000
#define E2P_CMD_EPC_CMD_WRAL_ 0x40000000
#define E2P_CMD_EPC_CMD_ERASE_ 0x50000000
#define E2P_CMD_EPC_CMD_ERAL_ 0x60000000
#define E2P_CMD_EPC_CMD_RELOAD_ 0x70000000
#define E2P_CMD_EPC_TIMEOUT_ 0x00000200
#define E2P_CMD_MAC_ADDR_LOADED_ 0x00000100
#define E2P_CMD_EPC_ADDR_ 0x000000FF
#define E2P_DATA 0xB4
#define E2P_DATA_EEPROM_DATA_ 0x000000FF
#define LAN_REGISTER_EXTENT 0x00000100
/*
* MAC Control and Status Register (Indirect Address)
* Offset (through the MAC_CSR CMD and DATA port)
*/
#define MAC_CR 0x01
#define MAC_CR_RXALL_ 0x80000000
#define MAC_CR_HBDIS_ 0x10000000
#define MAC_CR_RCVOWN_ 0x00800000
#define MAC_CR_LOOPBK_ 0x00200000
#define MAC_CR_FDPX_ 0x00100000
#define MAC_CR_MCPAS_ 0x00080000
#define MAC_CR_PRMS_ 0x00040000
#define MAC_CR_INVFILT_ 0x00020000
#define MAC_CR_PASSBAD_ 0x00010000
#define MAC_CR_HFILT_ 0x00008000
#define MAC_CR_HPFILT_ 0x00002000
#define MAC_CR_LCOLL_ 0x00001000
#define MAC_CR_BCAST_ 0x00000800
#define MAC_CR_DISRTY_ 0x00000400
#define MAC_CR_PADSTR_ 0x00000100
#define MAC_CR_BOLMT_MASK_ 0x000000C0
#define MAC_CR_DFCHK_ 0x00000020
#define MAC_CR_TXEN_ 0x00000008
#define MAC_CR_RXEN_ 0x00000004
#define ADDRH 0x02
#define ADDRL 0x03
#define HASHH 0x04
#define HASHL 0x05
#define MII_ACC 0x06
#define MII_ACC_PHY_ADDR_ 0x0000F800
#define MII_ACC_MIIRINDA_ 0x000007C0
#define MII_ACC_MII_WRITE_ 0x00000002
#define MII_ACC_MII_BUSY_ 0x00000001
#define MII_DATA 0x07
#define FLOW 0x08
#define FLOW_FCPT_ 0xFFFF0000
#define FLOW_FCPASS_ 0x00000004
#define FLOW_FCEN_ 0x00000002
#define FLOW_FCBSY_ 0x00000001
#define VLAN1 0x09
#define VLAN2 0x0A
#define WUFF 0x0B
#define WUCSR 0x0C
#define WUCSR_GUE_ 0x00000200
#define WUCSR_WUFR_ 0x00000040
#define WUCSR_MPR_ 0x00000020
#define WUCSR_WAKE_EN_ 0x00000004
#define WUCSR_MPEN_ 0x00000002
/*
* Phy definitions (vendor-specific)
*/
#define LAN9118_PHY_ID 0x00C0001C
#define MII_INTSTS 0x1D
#define MII_INTMSK 0x1E
#define PHY_INTMSK_AN_RCV_ (1 << 1)
#define PHY_INTMSK_PDFAULT_ (1 << 2)
#define PHY_INTMSK_AN_ACK_ (1 << 3)
#define PHY_INTMSK_LNKDOWN_ (1 << 4)
#define PHY_INTMSK_RFAULT_ (1 << 5)
#define PHY_INTMSK_AN_COMP_ (1 << 6)
#define PHY_INTMSK_ENERGYON_ (1 << 7)
#define PHY_INTMSK_DEFAULT_ (PHY_INTMSK_ENERGYON_ | \
PHY_INTMSK_AN_COMP_ | \
PHY_INTMSK_RFAULT_ | \
PHY_INTMSK_LNKDOWN_)
#define ADVERTISE_PAUSE_ALL (ADVERTISE_PAUSE_CAP | \
ADVERTISE_PAUSE_ASYM)
#define LPA_PAUSE_ALL (LPA_PAUSE_CAP | \
LPA_PAUSE_ASYM)
#endif /* __SMSC911X_H__ */

42
include/linux/smsc911x.h Normal file
View File

@ -0,0 +1,42 @@
/***************************************************************************
*
* Copyright (C) 2004-2008 SMSC
* Copyright (C) 2005-2008 ARM
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
***************************************************************************/
#ifndef __LINUX_SMSC911X_H__
#define __LINUX_SMSC911X_H__
#include <linux/phy.h>
/* platform_device configuration data, should be assigned to
* the platform_device's dev.platform_data */
struct smsc911x_platform_config {
unsigned int irq_polarity;
unsigned int irq_type;
phy_interface_t phy_interface;
};
/* Constants for platform_device irq polarity configuration */
#define SMSC911X_IRQ_POLARITY_ACTIVE_LOW 0
#define SMSC911X_IRQ_POLARITY_ACTIVE_HIGH 1
/* Constants for platform_device irq type configuration */
#define SMSC911X_IRQ_TYPE_OPEN_DRAIN 0
#define SMSC911X_IRQ_TYPE_PUSH_PULL 1
#endif /* __LINUX_SMSC911X_H__ */

View File

@ -108,6 +108,20 @@ extern void ndisc_send_redirect(struct sk_buff *skb,
extern int ndisc_mc_map(struct in6_addr *addr, char *buf, struct net_device *dev, int dir);
extern struct sk_buff *ndisc_build_skb(struct net_device *dev,
const struct in6_addr *daddr,
const struct in6_addr *saddr,
struct icmp6hdr *icmp6h,
const struct in6_addr *target,
int llinfo);
extern void ndisc_send_skb(struct sk_buff *skb,
struct net_device *dev,
struct neighbour *neigh,
const struct in6_addr *daddr,
const struct in6_addr *saddr,
struct icmp6hdr *icmp6h);
/*

View File

@ -437,38 +437,20 @@ static void pndisc_destructor(struct pneigh_entry *n)
ipv6_dev_mc_dec(dev, &maddr);
}
/*
* Send a Neighbour Advertisement
*/
static void __ndisc_send(struct net_device *dev,
struct neighbour *neigh,
struct sk_buff *ndisc_build_skb(struct net_device *dev,
const struct in6_addr *daddr,
const struct in6_addr *saddr,
struct icmp6hdr *icmp6h, const struct in6_addr *target,
struct icmp6hdr *icmp6h,
const struct in6_addr *target,
int llinfo)
{
struct flowi fl;
struct dst_entry *dst;
struct net *net = dev_net(dev);
struct sock *sk = net->ipv6.ndisc_sk;
struct sk_buff *skb;
struct icmp6hdr *hdr;
struct inet6_dev *idev;
int len;
int err;
u8 *opt, type;
type = icmp6h->icmp6_type;
icmpv6_flow_init(sk, &fl, type, saddr, daddr, dev->ifindex);
dst = icmp6_dst_alloc(dev, neigh, daddr);
if (!dst)
return;
err = xfrm_lookup(&dst, &fl, NULL, 0);
if (err < 0)
return;
u8 *opt;
if (!dev->addr_len)
llinfo = 0;
@ -485,8 +467,7 @@ static void __ndisc_send(struct net_device *dev,
ND_PRINTK0(KERN_ERR
"ICMPv6 ND: %s() failed to allocate an skb.\n",
__func__);
dst_release(dst);
return;
return NULL;
}
skb_reserve(skb, LL_RESERVED_SPACE(dev));
@ -513,6 +494,42 @@ static void __ndisc_send(struct net_device *dev,
csum_partial((__u8 *) hdr,
len, 0));
return skb;
}
EXPORT_SYMBOL(ndisc_build_skb);
void ndisc_send_skb(struct sk_buff *skb,
struct net_device *dev,
struct neighbour *neigh,
const struct in6_addr *daddr,
const struct in6_addr *saddr,
struct icmp6hdr *icmp6h)
{
struct flowi fl;
struct dst_entry *dst;
struct net *net = dev_net(dev);
struct sock *sk = net->ipv6.ndisc_sk;
struct inet6_dev *idev;
int err;
u8 type;
type = icmp6h->icmp6_type;
icmpv6_flow_init(sk, &fl, type, saddr, daddr, dev->ifindex);
dst = icmp6_dst_alloc(dev, neigh, daddr);
if (!dst) {
kfree_skb(skb);
return;
}
err = xfrm_lookup(&dst, &fl, NULL, 0);
if (err < 0) {
kfree_skb(skb);
return;
}
skb->dst = dst;
idev = in6_dev_get(dst->dev);
@ -529,6 +546,27 @@ static void __ndisc_send(struct net_device *dev,
in6_dev_put(idev);
}
EXPORT_SYMBOL(ndisc_send_skb);
/*
* Send a Neighbour Discover packet
*/
static void __ndisc_send(struct net_device *dev,
struct neighbour *neigh,
const struct in6_addr *daddr,
const struct in6_addr *saddr,
struct icmp6hdr *icmp6h, const struct in6_addr *target,
int llinfo)
{
struct sk_buff *skb;
skb = ndisc_build_skb(dev, daddr, saddr, icmp6h, target, llinfo);
if (!skb)
return;
ndisc_send_skb(skb, dev, neigh, daddr, saddr, icmp6h);
}
static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
const struct in6_addr *daddr,
const struct in6_addr *solicited_addr,